summaryrefslogtreecommitdiff
path: root/packaging/branch-from-patch
diff options
context:
space:
mode:
authorWayne Davison <wayned@samba.org>2009-05-23 13:40:34 -0700
committerWayne Davison <wayned@samba.org>2009-05-23 13:40:34 -0700
commit06886d36cfdcfb94f0d67f8964d22ee7c7872018 (patch)
tree916d21a6a556839de737e482ded7365103217476 /packaging/branch-from-patch
parent9ba4d44fa8a4fb1b3d79602d0d8744d464f5c60c (diff)
downloadrsync-06886d36cfdcfb94f0d67f8964d22ee7c7872018.tar.gz
Adding a new script that creates a local patch/* branch
for each file given on the command-line.
Diffstat (limited to 'packaging/branch-from-patch')
-rwxr-xr-xpackaging/branch-from-patch183
1 files changed, 183 insertions, 0 deletions
diff --git a/packaging/branch-from-patch b/packaging/branch-from-patch
new file mode 100755
index 00000000..bf854b17
--- /dev/null
+++ b/packaging/branch-from-patch
@@ -0,0 +1,183 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use Getopt::Long;
+
+&Getopt::Long::Configure('bundling');
+&usage if !&GetOptions(
+ 'branch|b=s' => \( my $master_branch = 'master' ),
+ 'skip-check' => \( my $skip_branch_check ),
+ 'delete' => \( my $delete_local_branches ),
+ 'help|h' => \( my $help_opt ),
+);
+&usage if $help_opt;
+
+my %local_branch;
+open PIPE, '-|', 'git branch -l' or die "Unable to fork: $!\n";
+while (<PIPE>) {
+ if (m# patch/(.*)#) {
+ $local_branch{$1} = 1;
+ }
+}
+close PIPE;
+
+if ($delete_local_branches) {
+ foreach my $name (sort keys %local_branch) {
+ my $branch = "patch/$name";
+ system 'git', 'branch', '-D', $branch and exit 1;
+ }
+ %local_branch = ( );
+}
+
+open IN, '-|', 'git status' or die $!;
+my $status = join('', <IN>);
+close IN;
+die "The checkout is not clean:\n", $status unless $status =~ /\nnothing to commit \(working directory clean\)/;
+die "The checkout is not on the $master_branch branch.\n" unless $status =~ /^# On branch $master_branch\n/;
+
+my @patch_list;
+foreach (@ARGV) {
+ if (!-f $_) {
+ die "File not found: $_\n";
+ }
+ die "Filename is not a .diff file: $_\n" unless /\.diff$/;
+ push @patch_list, $_;
+}
+
+exit unless @patch_list;
+
+my(%scanned, %created, %info);
+
+foreach my $patch (@patch_list) {
+ my($where, $name) = $patch =~ m{^(.*?)([^/]+)\.diff$};
+ next if $scanned{$name}++;
+
+ open IN, '<', $patch or die "Unable to open $patch: $!\n";
+
+ my $info = '';
+ my $commit;
+ while (<IN>) {
+ if (m#^based-on: (\S+)#) {
+ $commit = $1;
+ last;
+ }
+ last if m#^index .*\.\..* \d#;
+ last if m#^diff --git #;
+ last if m#^--- (old|a)/#;
+ $info .= $_;
+ }
+ close IN;
+
+ $info =~ s/\s+\Z/\n/;
+
+ my $parent = $master_branch;
+ my @patches = $info =~ m#patch -p1 <patches/(\S+)\.diff#g;
+ if (@patches) {
+ if ($patches[-1] eq $name) {
+ pop @patches;
+ } else {
+ warn "No identity patch line in $patch\n";
+ }
+ if (@patches) {
+ $parent = pop @patches;
+ if (!$scanned{$parent}) {
+ unless (-f "$where$parent.diff") {
+ die "Unknown parent of $patch: $parent\n";
+ }
+ # Add parent to @patch_list so that we will look for the
+ # parent's parent. Any duplicates will just be ignored.
+ push @patch_list, "$where$parent.diff";
+ }
+ }
+ } else {
+ warn "No patch lines found in $patch\n";
+ }
+
+ $info{$name} = [ $parent, $info, $commit ];
+}
+
+foreach my $patch (@patch_list) {
+ create_branch($patch);
+}
+
+system 'git', 'checkout', $master_branch and exit 1;
+
+exit;
+
+sub create_branch
+{
+ my($patch) = @_;
+ my($where, $name) = $patch =~ m{^(.*?)([^/]+)\.diff$};
+
+ return if $created{$name}++;
+
+ my $ref = $info{$name};
+ my($parent, $info, $commit) = @$ref;
+
+ my $parent_branch;
+ if ($parent eq $master_branch) {
+ $parent_branch = $master_branch;
+ $parent_branch = $commit if defined $commit;
+ } else {
+ create_branch("$where/$parent.diff");
+ $parent_branch = "patch/$parent";
+ }
+
+ my $branch = "patch/$name";
+ print "\n", '=' x 64, "\nProcessing $branch ($parent_branch)\n";
+
+ if ($local_branch{$name}) {
+ system 'git', 'branch', '-D', $branch and exit 1;
+ }
+
+ system 'git', 'checkout', '-b', $branch, $parent_branch and exit 1;
+
+ open OUT, '>', "PATCH.$name" or die $!;
+ print OUT $info;
+ close OUT;
+ system 'git', 'add', "PATCH.$name" and exit 1;
+
+ open IN, '<', $patch or die "Unable to open $patch: $!\n";
+ $_ = join('', <IN>);
+ close IN;
+
+ open PIPE, '|-', 'patch -p1' or die $!;
+ print PIPE $_;
+ close PIPE;
+
+ system 'rm -f *.orig */*.orig';
+
+ while (m#\nnew file mode (\d+)\s+--- /dev/null\s+\Q+++\E b/(.*)#g) {
+ chmod oct($1), $2;
+ system 'git', 'add', $2;
+ }
+
+ while (1) {
+ system 'git status';
+ print 'Press Enter to commit, Ctrl-C to abort, or type a wild-name to add a new file: ';
+ $_ = <STDIN>;
+ last if /^$/;
+ chomp;
+ system "git add $_";
+ }
+
+ while (system 'git', 'commit', '-a', '-m', "Creating branch from $name.diff.") {
+ exit 1 if system '/bin/zsh';
+ }
+}
+
+sub usage
+{
+ die <<EOT;
+Usage branch-from-patch [OPTIONS] patches/DIFF...
+
+Options:
+-b, --branch=BRANCH Create branches relative to BRANCH if no "based-on"
+ header was found in the patch file.
+ --skip-check Skip the check that ensures starting with a clean branch.
+ --delete Delete all the local patch/* branches, not just the ones
+ that are being recreated.
+-h, --help Output this help message.
+EOT
+}