|
21 | 21 | #include "sequencer.h"
|
22 | 22 | #include "revision.h"
|
23 | 23 | #include "merge-recursive.h"
|
| 24 | +#include "revision.h" |
| 25 | +#include "log-tree.h" |
24 | 26 |
|
25 | 27 | /**
|
26 | 28 | * Returns 1 if the file is empty or does not exist, 0 otherwise.
|
@@ -787,6 +789,129 @@ static int parse_mail(struct am_state *state, const char *mail)
|
787 | 789 | return ret;
|
788 | 790 | }
|
789 | 791 |
|
| 792 | +/** |
| 793 | + * Sets commit_id to the commit hash where the mail was generated from. |
| 794 | + * Returns 0 on success, -1 on failure. |
| 795 | + */ |
| 796 | +static int get_mail_commit_sha1(unsigned char *commit_id, const char *mail) |
| 797 | +{ |
| 798 | + struct strbuf sb = STRBUF_INIT; |
| 799 | + FILE *fp = xfopen(mail, "r"); |
| 800 | + const char *x; |
| 801 | + |
| 802 | + if (strbuf_getline(&sb, fp, '\n')) |
| 803 | + return -1; |
| 804 | + |
| 805 | + if (!skip_prefix(sb.buf, "From ", &x)) |
| 806 | + return -1; |
| 807 | + |
| 808 | + if (get_sha1_hex(x, commit_id) < 0) |
| 809 | + return -1; |
| 810 | + |
| 811 | + strbuf_release(&sb); |
| 812 | + fclose(fp); |
| 813 | + return 0; |
| 814 | +} |
| 815 | + |
| 816 | +/** |
| 817 | + * Sets state->msg, state->author_name, state->author_email, state->author_date |
| 818 | + * to the commit's respective info. |
| 819 | + */ |
| 820 | +static void get_commit_info(struct am_state *state, struct commit *commit) |
| 821 | +{ |
| 822 | + const char *buffer, *ident_line, *author_date, *msg; |
| 823 | + size_t ident_len; |
| 824 | + struct ident_split ident_split; |
| 825 | + struct strbuf sb = STRBUF_INIT; |
| 826 | + |
| 827 | + buffer = logmsg_reencode(commit, NULL, get_commit_output_encoding()); |
| 828 | + |
| 829 | + ident_line = find_commit_header(buffer, "author", &ident_len); |
| 830 | + |
| 831 | + if (split_ident_line(&ident_split, ident_line, ident_len) < 0) { |
| 832 | + strbuf_add(&sb, ident_line, ident_len); |
| 833 | + die(_("invalid ident line: %s"), sb.buf); |
| 834 | + } |
| 835 | + |
| 836 | + assert(!state->author_name); |
| 837 | + if (ident_split.name_begin) { |
| 838 | + strbuf_add(&sb, ident_split.name_begin, |
| 839 | + ident_split.name_end - ident_split.name_begin); |
| 840 | + state->author_name = strbuf_detach(&sb, NULL); |
| 841 | + } else |
| 842 | + state->author_name = xstrdup(""); |
| 843 | + |
| 844 | + assert(!state->author_email); |
| 845 | + if (ident_split.mail_begin) { |
| 846 | + strbuf_add(&sb, ident_split.mail_begin, |
| 847 | + ident_split.mail_end - ident_split.mail_begin); |
| 848 | + state->author_email = strbuf_detach(&sb, NULL); |
| 849 | + } else |
| 850 | + state->author_email = xstrdup(""); |
| 851 | + |
| 852 | + author_date = show_ident_date(&ident_split, DATE_MODE(NORMAL)); |
| 853 | + strbuf_addstr(&sb, author_date); |
| 854 | + assert(!state->author_date); |
| 855 | + state->author_date = strbuf_detach(&sb, NULL); |
| 856 | + |
| 857 | + assert(!state->msg); |
| 858 | + msg = strstr(buffer, "\n\n"); |
| 859 | + if (!msg) |
| 860 | + die(_("unable to parse commit %s"), sha1_to_hex(commit->object.sha1)); |
| 861 | + state->msg = xstrdup(msg + 2); |
| 862 | + state->msg_len = strlen(state->msg); |
| 863 | +} |
| 864 | + |
| 865 | +/** |
| 866 | + * Writes `commit` as a patch to the state directory's "patch" file. |
| 867 | + */ |
| 868 | +static void write_commit_patch(const struct am_state *state, struct commit *commit) |
| 869 | +{ |
| 870 | + struct rev_info rev_info; |
| 871 | + FILE *fp; |
| 872 | + |
| 873 | + fp = xfopen(am_path(state, "patch"), "w"); |
| 874 | + init_revisions(&rev_info, NULL); |
| 875 | + rev_info.diff = 1; |
| 876 | + rev_info.abbrev = 0; |
| 877 | + rev_info.disable_stdin = 1; |
| 878 | + rev_info.show_root_diff = 1; |
| 879 | + rev_info.diffopt.output_format = DIFF_FORMAT_PATCH; |
| 880 | + rev_info.no_commit_id = 1; |
| 881 | + DIFF_OPT_SET(&rev_info.diffopt, BINARY); |
| 882 | + DIFF_OPT_SET(&rev_info.diffopt, FULL_INDEX); |
| 883 | + rev_info.diffopt.use_color = 0; |
| 884 | + rev_info.diffopt.file = fp; |
| 885 | + rev_info.diffopt.close_file = 1; |
| 886 | + add_pending_object(&rev_info, &commit->object, ""); |
| 887 | + diff_setup_done(&rev_info.diffopt); |
| 888 | + log_tree_commit(&rev_info, commit); |
| 889 | +} |
| 890 | + |
| 891 | +/** |
| 892 | + * Like parse_mail(), but parses the mail by looking up its commit ID |
| 893 | + * directly. This is used in --rebasing mode to bypass git-mailinfo's munging |
| 894 | + * of patches. |
| 895 | + * |
| 896 | + * Will always return 0 as the patch should never be skipped. |
| 897 | + */ |
| 898 | +static int parse_mail_rebase(struct am_state *state, const char *mail) |
| 899 | +{ |
| 900 | + struct commit *commit; |
| 901 | + unsigned char commit_sha1[GIT_SHA1_RAWSZ]; |
| 902 | + |
| 903 | + if (get_mail_commit_sha1(commit_sha1, mail) < 0) |
| 904 | + die(_("could not parse %s"), mail); |
| 905 | + |
| 906 | + commit = lookup_commit_or_die(commit_sha1, mail); |
| 907 | + |
| 908 | + get_commit_info(state, commit); |
| 909 | + |
| 910 | + write_commit_patch(state, commit); |
| 911 | + |
| 912 | + return 0; |
| 913 | +} |
| 914 | + |
790 | 915 | /**
|
791 | 916 | * Applies current patch with git-apply. Returns 0 on success, -1 otherwise. If
|
792 | 917 | * `index_file` is not NULL, the patch will be applied to that index.
|
@@ -1019,7 +1144,14 @@ static void am_run(struct am_state *state, int resume)
|
1019 | 1144 | validate_resume_state(state);
|
1020 | 1145 | resume = 0;
|
1021 | 1146 | } else {
|
1022 |
| - if (parse_mail(state, mail)) |
| 1147 | + int skip; |
| 1148 | + |
| 1149 | + if (state->rebasing) |
| 1150 | + skip = parse_mail_rebase(state, mail); |
| 1151 | + else |
| 1152 | + skip = parse_mail(state, mail); |
| 1153 | + |
| 1154 | + if (skip) |
1023 | 1155 | goto next; /* mail should be skipped */
|
1024 | 1156 |
|
1025 | 1157 | write_author_script(state);
|
|
0 commit comments