Adding a new script that creates a local patch/* branch
[rsync/rsync.git] / packaging / branch-from-patch
CommitLineData
06886d36
WD
1#!/usr/bin/perl
2
3use strict;
4use warnings;
5use Getopt::Long;
6
7&Getopt::Long::Configure('bundling');
8&usage if !&GetOptions(
9 'branch|b=s' => \( my $master_branch = 'master' ),
10 'skip-check' => \( my $skip_branch_check ),
11 'delete' => \( my $delete_local_branches ),
12 'help|h' => \( my $help_opt ),
13);
14&usage if $help_opt;
15
16my %local_branch;
17open PIPE, '-|', 'git branch -l' or die "Unable to fork: $!\n";
18while (<PIPE>) {
19 if (m# patch/(.*)#) {
20 $local_branch{$1} = 1;
21 }
22}
23close PIPE;
24
25if ($delete_local_branches) {
26 foreach my $name (sort keys %local_branch) {
27 my $branch = "patch/$name";
28 system 'git', 'branch', '-D', $branch and exit 1;
29 }
30 %local_branch = ( );
31}
32
33open IN, '-|', 'git status' or die $!;
34my $status = join('', <IN>);
35close IN;
36die "The checkout is not clean:\n", $status unless $status =~ /\nnothing to commit \(working directory clean\)/;
37die "The checkout is not on the $master_branch branch.\n" unless $status =~ /^# On branch $master_branch\n/;
38
39my @patch_list;
40foreach (@ARGV) {
41 if (!-f $_) {
42 die "File not found: $_\n";
43 }
44 die "Filename is not a .diff file: $_\n" unless /\.diff$/;
45 push @patch_list, $_;
46}
47
48exit unless @patch_list;
49
50my(%scanned, %created, %info);
51
52foreach my $patch (@patch_list) {
53 my($where, $name) = $patch =~ m{^(.*?)([^/]+)\.diff$};
54 next if $scanned{$name}++;
55
56 open IN, '<', $patch or die "Unable to open $patch: $!\n";
57
58 my $info = '';
59 my $commit;
60 while (<IN>) {
61 if (m#^based-on: (\S+)#) {
62 $commit = $1;
63 last;
64 }
65 last if m#^index .*\.\..* \d#;
66 last if m#^diff --git #;
67 last if m#^--- (old|a)/#;
68 $info .= $_;
69 }
70 close IN;
71
72 $info =~ s/\s+\Z/\n/;
73
74 my $parent = $master_branch;
75 my @patches = $info =~ m#patch -p1 <patches/(\S+)\.diff#g;
76 if (@patches) {
77 if ($patches[-1] eq $name) {
78 pop @patches;
79 } else {
80 warn "No identity patch line in $patch\n";
81 }
82 if (@patches) {
83 $parent = pop @patches;
84 if (!$scanned{$parent}) {
85 unless (-f "$where$parent.diff") {
86 die "Unknown parent of $patch: $parent\n";
87 }
88 # Add parent to @patch_list so that we will look for the
89 # parent's parent. Any duplicates will just be ignored.
90 push @patch_list, "$where$parent.diff";
91 }
92 }
93 } else {
94 warn "No patch lines found in $patch\n";
95 }
96
97 $info{$name} = [ $parent, $info, $commit ];
98}
99
100foreach my $patch (@patch_list) {
101 create_branch($patch);
102}
103
104system 'git', 'checkout', $master_branch and exit 1;
105
106exit;
107
108sub create_branch
109{
110 my($patch) = @_;
111 my($where, $name) = $patch =~ m{^(.*?)([^/]+)\.diff$};
112
113 return if $created{$name}++;
114
115 my $ref = $info{$name};
116 my($parent, $info, $commit) = @$ref;
117
118 my $parent_branch;
119 if ($parent eq $master_branch) {
120 $parent_branch = $master_branch;
121 $parent_branch = $commit if defined $commit;
122 } else {
123 create_branch("$where/$parent.diff");
124 $parent_branch = "patch/$parent";
125 }
126
127 my $branch = "patch/$name";
128 print "\n", '=' x 64, "\nProcessing $branch ($parent_branch)\n";
129
130 if ($local_branch{$name}) {
131 system 'git', 'branch', '-D', $branch and exit 1;
132 }
133
134 system 'git', 'checkout', '-b', $branch, $parent_branch and exit 1;
135
136 open OUT, '>', "PATCH.$name" or die $!;
137 print OUT $info;
138 close OUT;
139 system 'git', 'add', "PATCH.$name" and exit 1;
140
141 open IN, '<', $patch or die "Unable to open $patch: $!\n";
142 $_ = join('', <IN>);
143 close IN;
144
145 open PIPE, '|-', 'patch -p1' or die $!;
146 print PIPE $_;
147 close PIPE;
148
149 system 'rm -f *.orig */*.orig';
150
151 while (m#\nnew file mode (\d+)\s+--- /dev/null\s+\Q+++\E b/(.*)#g) {
152 chmod oct($1), $2;
153 system 'git', 'add', $2;
154 }
155
156 while (1) {
157 system 'git status';
158 print 'Press Enter to commit, Ctrl-C to abort, or type a wild-name to add a new file: ';
159 $_ = <STDIN>;
160 last if /^$/;
161 chomp;
162 system "git add $_";
163 }
164
165 while (system 'git', 'commit', '-a', '-m', "Creating branch from $name.diff.") {
166 exit 1 if system '/bin/zsh';
167 }
168}
169
170sub usage
171{
172 die <<EOT;
173Usage branch-from-patch [OPTIONS] patches/DIFF...
174
175Options:
176-b, --branch=BRANCH Create branches relative to BRANCH if no "based-on"
177 header was found in the patch file.
178 --skip-check Skip the check that ensures starting with a clean branch.
179 --delete Delete all the local patch/* branches, not just the ones
180 that are being recreated.
181-h, --help Output this help message.
182EOT
183}