File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / rsync / packaging / patch-update
Revision 1.1: download - view: text, annotated - select for diffs - revision graph
Fri Feb 17 15:09:30 2012 UTC (12 years, 4 months ago) by misho
CVS tags: MAIN, HEAD
Initial revision

    1: #!/usr/bin/perl
    2: # This script is used to turn one or more of the "patch/BASE/*" branches
    3: # into one or more diffs in the "patches" directory.  Pass the option
    4: # --gen if you want generated files in the diffs.  Pass the name of
    5: # one or more diffs if you want to just update a subset of all the
    6: # diffs.
    7: 
    8: use strict;
    9: use warnings;
   10: use Getopt::Long;
   11: 
   12: my $patches_dir = 'patches';
   13: my $tmp_dir = "patches.$$";
   14: my $make_gen_cmd = 'make -f prepare-source.mak conf && ./config.status && make gen';
   15: 
   16: &Getopt::Long::Configure('bundling');
   17: &usage if !&GetOptions(
   18:     'branch|b=s' => \( my $master_branch = 'master' ),
   19:     'skip-check' => \( my $skip_branch_check ),
   20:     'shell|s' => \( my $launch_shell ),
   21:     'gen:s' => \( my $incl_generated_files ),
   22:     'help|h' => \( my $help_opt ),
   23: );
   24: &usage if $help_opt;
   25: 
   26: if (defined $incl_generated_files) {
   27:     $patches_dir = $incl_generated_files if $incl_generated_files ne '';
   28:     $incl_generated_files = 1;
   29: }
   30: 
   31: die "No '$patches_dir' directory was found.\n" unless -d $patches_dir;
   32: die "No '.git' directory present in the current dir.\n" unless -d '.git';
   33: 
   34: require 'packaging/git-status.pl';
   35: check_git_state($master_branch, !$skip_branch_check, 1);
   36: 
   37: my $master_commit;
   38: open PIPE, '-|', "git log -1 --no-color $master_branch" or die $!;
   39: while (<PIPE>) {
   40:     if (/^commit (\S+)/) {
   41: 	$master_commit = $1;
   42: 	last;
   43:     }
   44: }
   45: close PIPE;
   46: die "Unable to determine commit hash for master branch: $master_branch\n" unless defined $master_commit;
   47: 
   48: my @extra_files;
   49: open(IN, '<', 'Makefile.in') or die "Couldn't open Makefile.in: $!\n";
   50: while (<IN>) {
   51:     if (s/^GENFILES=//) {
   52: 	while (s/\\$//) {
   53: 	    $_ .= <IN>;
   54: 	}
   55: 	@extra_files = split(' ', $_);
   56: 	last;
   57:     }
   58: }
   59: close IN;
   60: 
   61: if ($incl_generated_files) {
   62:     die "'$tmp_dir' must not exist in the current directory.\n" if -e $tmp_dir;
   63:     mkdir($tmp_dir, 0700) or die "Unable to mkdir($tmp_dir): $!\n";
   64:     system "$make_gen_cmd && rsync -a @extra_files $tmp_dir/master/" and exit 1;
   65: }
   66: our $last_touch = time;
   67: 
   68: my %patches;
   69: 
   70: # Start by finding all patches so that we can load all possible parents.
   71: open(PIPE, '-|', 'git', 'branch', '-l') or die $!;
   72: while (<PIPE>) {
   73:     if (m# patch/\Q$master_branch\E/(.*)#o) {
   74: 	$patches{$1} = 1;
   75:     }
   76: }
   77: close PIPE;
   78: 
   79: my @patches = sort keys %patches;
   80: 
   81: my(%parent, %description);
   82: foreach my $patch (@patches) {
   83:     my $branch = "patch/$master_branch/$patch";
   84:     my $desc = '';
   85:     open(PIPE, '-|', 'git', 'diff', '-U1000', "$master_branch...$branch", '--', "PATCH.$patch") or die $!;
   86:     while (<PIPE>) {
   87: 	last if /^@@ /;
   88:     }
   89:     while (<PIPE>) {
   90: 	next unless s/^[ +]//;
   91: 	if (m#patch -p1 <patches/(\S+)\.diff# && $1 ne $patch) {
   92: 	    my $parent = $parent{$patch} = $1;
   93: 	    if (!$patches{$parent}) {
   94: 		die "Parent of $patch is not a local branch: $parent\n";
   95: 	    }
   96: 	}
   97: 	$desc .= $_;
   98:     }
   99:     close PIPE;
  100:     $description{$patch} = $desc;
  101: }
  102: 
  103: if (@ARGV) {
  104:     # Limit the list of patches to actually process based on @ARGV.
  105:     @patches = ( );
  106:     foreach (@ARGV) {
  107: 	s{^patch(es)?/} {};
  108: 	s{\.diff$} {};
  109: 	if (!$patches{$_}) {
  110: 	    die "Local branch not available for patch: $_\n";
  111: 	}
  112: 	push(@patches, $_);
  113:     }
  114: }
  115: 
  116: my %completed;
  117: foreach my $patch (@patches) {
  118:     next if $completed{$patch}++;
  119:     last unless update_patch($patch);
  120: }
  121: 
  122: if ($incl_generated_files) {
  123:     system "rm -rf $tmp_dir";
  124: }
  125: 
  126: sleep 1 while $last_touch >= time;
  127: system "git checkout $master_branch" and exit 1;
  128: 
  129: exit;
  130: 
  131: 
  132: sub update_patch
  133: {
  134:     my($patch) = @_;
  135: 
  136:     my $parent = $parent{$patch};
  137:     my $based_on;
  138:     if (defined $parent) {
  139: 	unless ($completed{$parent}++) {
  140: 	    update_patch($parent);
  141: 	}
  142: 	$based_on = $parent = "patch/$master_branch/$parent";
  143:     } else {
  144: 	$parent = $master_branch;
  145: 	$based_on = $master_commit;
  146:     }
  147: 
  148:     print "======== $patch ========\n";
  149: 
  150:     sleep 1 while $incl_generated_files && $last_touch >= time;
  151:     system "git checkout patch/$master_branch/$patch" and return 0;
  152: 
  153:     my $ok = system("git merge $based_on") == 0;
  154:     if (!$ok || $launch_shell) {
  155: 	my($parent_dir) = $parent =~ m{([^/]+)$};
  156: 	print qq|"git merge $based_on" incomplete -- please fix.\n| if !$ok;
  157: 	$ENV{PS1} = "[$parent_dir] $patch: ";
  158: 	while (1) {
  159: 	    if (system($ENV{SHELL}) != 0) {
  160: 		print "Abort? [n/y] ";
  161: 		$_ = <STDIN>;
  162: 		next unless /^y/i;
  163: 		return 0;
  164: 	    }
  165: 	    my($cur_branch, $is_clean, $status) = check_git_status(0);
  166: 	    last if $is_clean;
  167: 	    print $status;
  168: 	}
  169:     }
  170: 
  171:     open(OUT, '>', "$patches_dir/$patch.diff") or die $!;
  172:     print OUT $description{$patch}, "\nbased-on: $based_on\n";
  173: 
  174:     if ($incl_generated_files) {
  175: 	system "$make_gen_cmd && rsync -a @extra_files $tmp_dir/$patch/" and exit 1;
  176:     }
  177:     $last_touch = time;
  178: 
  179:     open(PIPE, '-|', 'git', 'diff', $based_on) or die $!;
  180:     DIFF: while (<PIPE>) {
  181: 	while (m{^diff --git a/PATCH}) {
  182: 	    while (<PIPE>) {
  183: 		last if m{^diff --git a/};
  184: 	    }
  185: 	    last DIFF if !defined $_;
  186: 	}
  187: 	next if /^index /;
  188: 	print OUT $_;
  189:     }
  190:     close PIPE;
  191: 
  192:     if ($incl_generated_files) {
  193: 	my $parent_dir;
  194: 	if ($parent eq $master_branch) {
  195: 	    $parent_dir = 'master';
  196: 	} else {
  197: 	    ($parent_dir) = $parent =~ m{([^/]+)$};
  198: 	}
  199: 	open(PIPE, '-|', 'diff', '-up', "$tmp_dir/$parent_dir", "$tmp_dir/$patch") or die $!;
  200: 	while (<PIPE>) {
  201: 	    s#^(diff -up) $tmp_dir/[^/]+/(.*?) $tmp_dir/[^/]+/(.*)#$1 a/$2 b/$3#o;
  202: 	    s#^\Q---\E $tmp_dir/[^/]+/([^\t]+)\t.*#--- a/$1#o;
  203: 	    s#^\Q+++\E $tmp_dir/[^/]+/([^\t]+)\t.*#+++ b/$1#o;
  204: 	    print OUT $_;
  205: 	}
  206: 	close PIPE;
  207:     }
  208: 
  209:     close OUT;
  210: 
  211:     1;
  212: }
  213: 
  214: exit;
  215: 
  216: sub usage
  217: {
  218:     die <<EOT;
  219: Usage: patch-update [OPTIONS] [patches/DIFF...]
  220: 
  221: Options:
  222: -b, --branch=BRANCH  The master branch to merge into the patch/BASE/* branches.
  223:     --gen[=DIR]      Include generated files.  Optional destination DIR
  224:                      arg overrides the default of using the "patches" dir.
  225:     --skip-check     Skip the check that ensures starting with a clean branch.
  226: -s, --shell          Launch a shell for every patch/BASE/* branch updated, not
  227:                      just when a conflict occurs.
  228: -h, --help           Output this help message.
  229: EOT
  230: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>