|
17 | 17 | #include "cache.h" |
18 | 18 |
|
19 | 19 | // We default to the merge behaviour, since that's what most people would |
20 | | -// expect |
| 20 | +// expect. |
| 21 | +// |
| 22 | +// --check turns on checking that the working tree matches the |
| 23 | +// files that are being modified, but doesn't apply the patch |
| 24 | +// --stat does just a diffstat, and doesn't actually apply |
| 25 | +// --show-files shows the directory changes |
| 26 | +// |
21 | 27 | static int merge_patch = 1; |
22 | 28 | static int diffstat = 0; |
23 | | -static int check = 1; |
24 | | -static const char apply_usage[] = "git-apply <patch>"; |
| 29 | +static int check = 0; |
| 30 | +static int apply = 1; |
| 31 | +static int show_files = 0; |
| 32 | +static const char apply_usage[] = "git-apply [--stat] [--check] [--show-files] <patch>"; |
25 | 33 |
|
26 | 34 | /* |
27 | 35 | * For "diff-stat" like behaviour, we keep track of the biggest change |
@@ -723,44 +731,90 @@ static void show_stats(struct patch *patch) |
723 | 731 | add, pluses, del, minuses); |
724 | 732 | } |
725 | 733 |
|
726 | | -static void check_patch(struct patch *patch) |
| 734 | +static int check_patch(struct patch *patch) |
727 | 735 | { |
| 736 | + struct stat st; |
728 | 737 | const char *old_name = patch->old_name; |
729 | 738 | const char *new_name = patch->new_name; |
730 | 739 |
|
731 | 740 | if (old_name) { |
732 | | - if (cache_name_pos(old_name, strlen(old_name)) < 0) |
733 | | - die("file %s does not exist", old_name); |
| 741 | + int pos = cache_name_pos(old_name, strlen(old_name)); |
| 742 | + int changed; |
| 743 | + |
| 744 | + if (pos < 0) |
| 745 | + return error("%s: does not exist in index", old_name); |
734 | 746 | if (patch->is_new < 0) |
735 | 747 | patch->is_new = 0; |
| 748 | + if (lstat(old_name, &st) < 0) |
| 749 | + return error("%s: %s\n", strerror(errno)); |
| 750 | + changed = ce_match_stat(active_cache[pos], &st); |
| 751 | + if (changed) |
| 752 | + return error("%s: does not match index", old_name); |
| 753 | + if (!patch->old_mode) |
| 754 | + patch->old_mode = st.st_mode; |
736 | 755 | } |
| 756 | + |
737 | 757 | if (new_name && (patch->is_new | patch->is_rename | patch->is_copy)) { |
738 | 758 | if (cache_name_pos(new_name, strlen(new_name)) >= 0) |
739 | | - die("file %s already exists", new_name); |
| 759 | + return error("%s: already exists in index", new_name); |
| 760 | + if (!lstat(new_name, &st)) |
| 761 | + return error("%s: already exists in working directory", new_name); |
| 762 | + if (errno != ENOENT) |
| 763 | + return error("%s: %s", new_name, strerror(errno)); |
740 | 764 | } |
| 765 | + return 0; |
741 | 766 | } |
742 | 767 |
|
743 | | -static void apply_patch_list(struct patch *patch) |
| 768 | +static int check_patch_list(struct patch *patch) |
744 | 769 | { |
745 | | - int files, adds, dels; |
| 770 | + int error = 0; |
| 771 | + |
| 772 | + for (;patch ; patch = patch->next) |
| 773 | + error |= check_patch(patch); |
| 774 | + return error; |
| 775 | +} |
| 776 | + |
| 777 | +static void show_file(int c, unsigned int mode, const char *name) |
| 778 | +{ |
| 779 | + printf("%c %o %s\n", c, mode, name); |
| 780 | +} |
746 | 781 |
|
747 | | - files = adds = dels = 0; |
748 | | - if (!patch) |
749 | | - die("no patch found"); |
750 | | - do { |
751 | | - if (check) |
752 | | - check_patch(patch); |
753 | | - |
754 | | - if (diffstat) { |
755 | | - files++; |
756 | | - adds += patch->lines_added; |
757 | | - dels += patch->lines_deleted; |
758 | | - show_stats(patch); |
| 782 | +static void show_file_list(struct patch *patch) |
| 783 | +{ |
| 784 | + for (;patch ; patch = patch->next) { |
| 785 | + if (patch->is_rename) { |
| 786 | + show_file('-', patch->old_mode, patch->old_name); |
| 787 | + show_file('+', patch->new_mode, patch->new_name); |
| 788 | + continue; |
| 789 | + } |
| 790 | + if (patch->is_copy || patch->is_new) { |
| 791 | + show_file('+', patch->new_mode, patch->new_name); |
| 792 | + continue; |
| 793 | + } |
| 794 | + if (patch->is_delete) { |
| 795 | + show_file('-', patch->old_mode, patch->old_name); |
| 796 | + continue; |
| 797 | + } |
| 798 | + if (patch->old_mode && patch->new_mode && patch->old_mode != patch->new_mode) { |
| 799 | + printf("M %o:%o %s\n", patch->old_mode, patch->new_mode, patch->old_name); |
| 800 | + continue; |
759 | 801 | } |
760 | | - } while ((patch = patch->next) != NULL); |
| 802 | + printf("M %o %s\n", patch->old_mode, patch->old_name); |
| 803 | + } |
| 804 | +} |
761 | 805 |
|
762 | | - if (diffstat) |
763 | | - printf(" %d files changed, %d insertions(+), %d deletions(-)\n", files, adds, dels); |
| 806 | +static void stat_patch_list(struct patch *patch) |
| 807 | +{ |
| 808 | + int files, adds, dels; |
| 809 | + |
| 810 | + for (files = adds = dels = 0 ; patch ; patch = patch->next) { |
| 811 | + files++; |
| 812 | + adds += patch->lines_added; |
| 813 | + dels += patch->lines_deleted; |
| 814 | + show_stats(patch); |
| 815 | + } |
| 816 | + |
| 817 | + printf(" %d files changed, %d insertions(+), %d deletions(-)\n", files, adds, dels); |
764 | 818 | } |
765 | 819 |
|
766 | 820 | static void patch_stats(struct patch *patch) |
@@ -806,7 +860,14 @@ static int apply_patch(int fd) |
806 | 860 | size -= nr; |
807 | 861 | } |
808 | 862 |
|
809 | | - apply_patch_list(list); |
| 863 | + if ((check || apply) && check_patch_list(list) < 0) |
| 864 | + exit(1); |
| 865 | + |
| 866 | + if (show_files) |
| 867 | + show_file_list(list); |
| 868 | + |
| 869 | + if (diffstat) |
| 870 | + stat_patch_list(list); |
810 | 871 |
|
811 | 872 | free(buffer); |
812 | 873 | return 0; |
@@ -834,10 +895,19 @@ int main(int argc, char **argv) |
834 | 895 | continue; |
835 | 896 | } |
836 | 897 | if (!strcmp(arg, "--stat")) { |
837 | | - check = 0; |
| 898 | + apply = 0; |
838 | 899 | diffstat = 1; |
839 | 900 | continue; |
840 | 901 | } |
| 902 | + if (!strcmp(arg, "--check")) { |
| 903 | + apply = 0; |
| 904 | + check = 1; |
| 905 | + continue; |
| 906 | + } |
| 907 | + if (!strcmp(arg, "--show-files")) { |
| 908 | + show_files = 1; |
| 909 | + continue; |
| 910 | + } |
841 | 911 | fd = open(arg, O_RDONLY); |
842 | 912 | if (fd < 0) |
843 | 913 | usage(apply_usage); |
|
0 commit comments