|
18 | 18 | #include "quote.h"
|
19 | 19 | #include "log-tree.h"
|
20 | 20 | #include "wt-status.h"
|
| 21 | +#include "hashmap.h" |
21 | 22 |
|
22 | 23 | #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
|
23 | 24 |
|
@@ -2652,3 +2653,180 @@ int skip_unnecessary_picks(void)
|
2652 | 2653 |
|
2653 | 2654 | return 0;
|
2654 | 2655 | }
|
| 2656 | + |
| 2657 | +struct subject2item_entry { |
| 2658 | + struct hashmap_entry entry; |
| 2659 | + int i; |
| 2660 | + char subject[FLEX_ARRAY]; |
| 2661 | +}; |
| 2662 | + |
| 2663 | +static int subject2item_cmp(const struct subject2item_entry *a, |
| 2664 | + const struct subject2item_entry *b, const void *key) |
| 2665 | +{ |
| 2666 | + return key ? strcmp(a->subject, key) : strcmp(a->subject, b->subject); |
| 2667 | +} |
| 2668 | + |
| 2669 | +/* |
| 2670 | + * Rearrange the todo list that has both "pick sha1 msg" and |
| 2671 | + * "pick sha1 fixup!/squash! msg" appears in it so that the latter |
| 2672 | + * comes immediately after the former, and change "pick" to |
| 2673 | + * "fixup"/"squash". |
| 2674 | + * |
| 2675 | + * Note that if the config has specified a custom instruction format |
| 2676 | + * each log message will be re-retrieved in order to normalize the |
| 2677 | + * autosquash arrangement |
| 2678 | + */ |
| 2679 | +int rearrange_squash(void) |
| 2680 | +{ |
| 2681 | + const char *todo_file = rebase_path_todo(); |
| 2682 | + struct todo_list todo_list = TODO_LIST_INIT; |
| 2683 | + struct hashmap subject2item; |
| 2684 | + int res = 0, rearranged = 0, *next, *tail, fd, i; |
| 2685 | + char **subjects; |
| 2686 | + |
| 2687 | + /* TODO: refactor. We do this a lot */ |
| 2688 | + fd = open(todo_file, O_RDONLY); |
| 2689 | + if (fd < 0) |
| 2690 | + return error_errno(_("Could not open %s"), todo_file); |
| 2691 | + if (strbuf_read(&todo_list.buf, fd, 0) < 0) { |
| 2692 | + close(fd); |
| 2693 | + return error(_("Could not read %s."), todo_file); |
| 2694 | + } |
| 2695 | + close(fd); |
| 2696 | + if (parse_insn_buffer(todo_list.buf.buf, &todo_list) < 0) { |
| 2697 | + todo_list_release(&todo_list); |
| 2698 | + return -1; |
| 2699 | + } |
| 2700 | + |
| 2701 | + hashmap_init(&subject2item, (hashmap_cmp_fn) subject2item_cmp, |
| 2702 | + todo_list.nr); |
| 2703 | + ALLOC_ARRAY(next, todo_list.nr); |
| 2704 | + ALLOC_ARRAY(tail, todo_list.nr); |
| 2705 | + ALLOC_ARRAY(subjects, todo_list.nr); |
| 2706 | + for (i = 0; i < todo_list.nr; i++) { |
| 2707 | + struct strbuf buf = STRBUF_INIT; |
| 2708 | + struct todo_item *item = todo_list.items + i; |
| 2709 | + const char *commit_buffer, *subject, *p; |
| 2710 | + int i2 = -1; |
| 2711 | + struct subject2item_entry *entry; |
| 2712 | + |
| 2713 | + next[i] = tail[i] = -1; |
| 2714 | + if (item->command >= TODO_EXEC) { |
| 2715 | + subjects[i] = NULL; |
| 2716 | + continue; |
| 2717 | + } |
| 2718 | + |
| 2719 | + item->commit->util = item; |
| 2720 | + |
| 2721 | + parse_commit(item->commit); |
| 2722 | + commit_buffer = get_commit_buffer(item->commit, NULL); |
| 2723 | + find_commit_subject(commit_buffer, &subject); |
| 2724 | + format_subject(&buf, subject, " "); |
| 2725 | + subject = subjects[i] = buf.buf; |
| 2726 | + unuse_commit_buffer(item->commit, commit_buffer); |
| 2727 | + if ((skip_prefix(subject, "fixup! ", &p) || |
| 2728 | + skip_prefix(subject, "squash! ", &p))) { |
| 2729 | + struct commit *commit2; |
| 2730 | + |
| 2731 | + for (;;) { |
| 2732 | + while (isspace(*p)) |
| 2733 | + p++; |
| 2734 | + if (!skip_prefix(p, "fixup! ", &p) && |
| 2735 | + !skip_prefix(p, "squash! ", &p)) |
| 2736 | + break; |
| 2737 | + } |
| 2738 | + |
| 2739 | + if ((entry = hashmap_get_from_hash(&subject2item, |
| 2740 | + strhash(p), p))) |
| 2741 | + i2 = entry->i; |
| 2742 | + else if (!strchr(p, ' ') && |
| 2743 | + (commit2 = lookup_commit_reference_by_name(p)) && |
| 2744 | + commit2->util) |
| 2745 | + i2 = (struct todo_item *)commit2->util |
| 2746 | + - todo_list.items; |
| 2747 | + else { |
| 2748 | + /* copy can be a prefix of the commit subject */ |
| 2749 | + for (i2 = 0; i2 < i; i2++) |
| 2750 | + if (subjects[i2] && |
| 2751 | + starts_with(subjects[i2], p)) |
| 2752 | + break; |
| 2753 | + if (i2 == i) |
| 2754 | + i2 = -1; |
| 2755 | + } |
| 2756 | + } |
| 2757 | + if (i2 >= 0) { |
| 2758 | + rearranged = 1; |
| 2759 | + todo_list.items[i].command = |
| 2760 | + starts_with(subject, "fixup!") ? |
| 2761 | + TODO_FIXUP : TODO_SQUASH; |
| 2762 | + if (next[i2] < 0) |
| 2763 | + next[i2] = i; |
| 2764 | + else |
| 2765 | + next[tail[i2]] = i; |
| 2766 | + tail[i2] = i; |
| 2767 | + } |
| 2768 | + else if (!hashmap_get_from_hash(&subject2item, |
| 2769 | + strhash(subject), subject)) { |
| 2770 | + FLEX_ALLOC_MEM(entry, subject, buf.buf, buf.len); |
| 2771 | + entry->i = i; |
| 2772 | + hashmap_entry_init(entry, strhash(entry->subject)); |
| 2773 | + hashmap_put(&subject2item, entry); |
| 2774 | + } |
| 2775 | + strbuf_detach(&buf, NULL); |
| 2776 | + } |
| 2777 | + |
| 2778 | + if (rearranged) { |
| 2779 | + struct strbuf buf = STRBUF_INIT; |
| 2780 | + char *format = NULL; |
| 2781 | + |
| 2782 | + git_config_get_string("rebase.instructionFormat", &format); |
| 2783 | + for (i = 0; i < todo_list.nr; i++) { |
| 2784 | + enum todo_command command = todo_list.items[i].command; |
| 2785 | + int cur = i; |
| 2786 | + |
| 2787 | + if (is_fixup(command)) |
| 2788 | + continue; |
| 2789 | + |
| 2790 | + while (cur >= 0) { |
| 2791 | + int offset = todo_list.items[cur].offset_in_buf; |
| 2792 | + int end_offset = cur + 1 < todo_list.nr ? |
| 2793 | + todo_list.items[cur + 1].offset_in_buf : |
| 2794 | + todo_list.buf.len; |
| 2795 | + char *bol = todo_list.buf.buf + offset; |
| 2796 | + char *eol = todo_list.buf.buf + end_offset; |
| 2797 | + |
| 2798 | + /* replace 'pick', by 'fixup' or 'squash' */ |
| 2799 | + command = todo_list.items[cur].command; |
| 2800 | + if (is_fixup(command)) { |
| 2801 | + strbuf_addstr(&buf, |
| 2802 | + todo_command_info[command].str); |
| 2803 | + bol += strcspn(bol, " \t"); |
| 2804 | + } |
| 2805 | + |
| 2806 | + strbuf_add(&buf, bol, eol - bol); |
| 2807 | + |
| 2808 | + cur = next[cur]; |
| 2809 | + } |
| 2810 | + } |
| 2811 | + |
| 2812 | + fd = open(todo_file, O_WRONLY); |
| 2813 | + if (fd < 0) |
| 2814 | + res |= error_errno(_("Could not open %s"), todo_file); |
| 2815 | + else if (write(fd, buf.buf, buf.len) < 0) |
| 2816 | + res |= error_errno(_("Could not read %s."), todo_file); |
| 2817 | + else if (ftruncate(fd, buf.len) < 0) |
| 2818 | + res |= error_errno(_("Could not finish %s"), todo_file); |
| 2819 | + close(fd); |
| 2820 | + strbuf_release(&buf); |
| 2821 | + } |
| 2822 | + |
| 2823 | + free(next); |
| 2824 | + free(tail); |
| 2825 | + for (i = 0; i < todo_list.nr; i++) |
| 2826 | + free(subjects[i]); |
| 2827 | + free(subjects); |
| 2828 | + hashmap_free(&subject2item, 1); |
| 2829 | + todo_list_release(&todo_list); |
| 2830 | + |
| 2831 | + return res; |
| 2832 | +} |
0 commit comments