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