📄 patch-update
字号:
#!/usr/bin/perl -w# This script is used to turn one or more of the "patch/*" branches# into one or more diffs in the "patches" directory. Pass the option# --gen if you want generated files in the diffs. Pass the name of# one or more diffs if you want to just update a subset of all the# diffs.use strict;use Getopt::Long;my $patches_dir = 'patches';my $tmp_dir = "patches.$$";&Getopt::Long::Configure('bundling');&usage if !&GetOptions( 'branch|b=s' => \( my $master_branch = 'master' ), 'skip-check' => \( my $skip_branch_check ), 'shell|s' => \( my $launch_shell ), 'gen:s' => \( my $incl_generated_files ), 'help|h' => \( my $help_opt ),);&usage if $help_opt;if (defined $incl_generated_files) { $patches_dir = $incl_generated_files if $incl_generated_files ne ''; $incl_generated_files = 1;}die "No '$patches_dir' directory was found.\n" unless -d $patches_dir;die "No '.git' directory present in the current dir.\n" unless -d '.git';my($status, $is_clean, $starting_branch) = &check_git_status;if (!$skip_branch_check && !$is_clean) { die "The checkout is not clean:\n", $status;}my @extra_files;open(IN, '<', 'Makefile.in') or die "Couldn't open Makefile.in: $!\n";while (<IN>) { if (s/^GENFILES=//) { while (s/\\$//) { $_ .= <IN>; } @extra_files = split(' ', $_); last; }}close IN;if ($incl_generated_files) { die "'$tmp_dir' must not exist in the current directory.\n" if -e $tmp_dir; mkdir($tmp_dir, 0700) or die "Unable to mkdir($tmp_dir): $!\n"; system "./config.status Makefile && make gen && rsync -a @extra_files $tmp_dir/master/" and exit 1;}our $last_touch = time;my %patches;# Start by finding all patches so that we can load all possible parents.open(PIPE, '-|', 'git', 'branch', '-l') or die $!;while (<PIPE>) { if (m# patch/(.*)#) { $patches{$1} = 1; }}close PIPE;my @patches = sort keys %patches;my(%parent, %description);foreach my $patch (@patches) { my $branch = "patch/$patch"; my $desc = ''; open(PIPE, '-|', 'git', 'diff', '-U1000', "$master_branch...$branch", '--', "PATCH.$patch") or die $!; while (<PIPE>) { last if /^@@ /; } while (<PIPE>) { next unless s/^[ +]//; if (m#patch -p1 <patches/(\S+)\.diff# && $1 ne $patch) { my $parent = $parent{$patch} = $1; if (!$patches{$parent}) { die "Parent of $patch is not a local branch: $parent\n"; } } $desc .= $_; } close PIPE; $description{$patch} = $desc;}if (@ARGV) { # Limit the list of patches to actually process based on @ARGV. @patches = ( ); foreach (@ARGV) { s{^patch(es)?/} {}; s{\.diff$} {}; if (!$patches{$_}) { die "Local branch not available for patch: $_\n"; } push(@patches, $_); }}my %completed;foreach my $patch (@patches) { next if $completed{$patch}++; last unless update_patch($patch);}if ($incl_generated_files) { system "rm -rf $tmp_dir";}sleep 1 while $last_touch >= time;system "git checkout $starting_branch" and exit 1;exit;sub update_patch{ my($patch) = @_; my $parent = $parent{$patch}; if (defined $parent) { unless ($completed{$parent}++) { update_patch($parent); } $parent = "patch/$parent"; } else { $parent = $master_branch; } print "======== $patch ========\n"; sleep 1 while $incl_generated_files && $last_touch >= time; system "git checkout patch/$patch" and return 0; my $ok = system("git merge $parent") == 0; if (!$ok || $launch_shell) { print qq|"git merge $parent" incomplete -- please fix.\n| if !$ok; $ENV{PS1} = "[$parent] patch/$patch: "; while (1) { if (system($ENV{SHELL}) != 0) { print "Abort? [n/y] "; $_ = <STDIN>; next unless /^y/i; return 0; } ($status, $is_clean) = &check_git_status; last if $is_clean; print $status; } } open(OUT, '>', "$patches_dir/$patch.diff") or die $!; print OUT $description{$patch}, "\n"; if ($incl_generated_files) { system "./config.status Makefile && make gen && rsync -a @extra_files $tmp_dir/$patch/" and exit 1; } $last_touch = time; open(PIPE, '-|', 'git', 'diff', $parent) or die $!; DIFF: while (<PIPE>) { while (m{^diff --git a/PATCH}) { while (<PIPE>) { last if m{^diff --git a/}; } last DIFF if !defined $_; } next if /^index /; print OUT $_; } close PIPE; if ($incl_generated_files) { my $parent_dir; if ($parent eq $master_branch) { $parent_dir = 'master'; } else { ($parent_dir) = $parent =~ m{([^/]+)$}; } open(PIPE, '-|', 'diff', '-up', "$tmp_dir/$parent_dir", "$tmp_dir/$patch") or die $!; while (<PIPE>) { s#^(diff -up) $tmp_dir/[^/]+/(.*?) $tmp_dir/[^/]+/(.*)#$1 a/$2 b/$3#o; s#^\Q---\E $tmp_dir/[^/]+/([^\t]+)\t.*#--- a/$1#o; s#^\Q+++\E $tmp_dir/[^/]+/([^\t]+)\t.*#+++ b/$1#o; print OUT $_; } close PIPE; } close OUT; 1;}exit;sub check_git_status{ open(IN, '-|', 'git status') or die $!; my $status = join('', <IN>); close IN; my $is_clean = $status =~ /\nnothing to commit \(working directory clean\)/; my($starting_branch) = $status =~ /^# On branch (.+)\n/; ($status, $is_clean, $starting_branch);}sub usage{ die <<EOT;Usage: patch-update [OPTIONS]--gen[=DIR] Include generated files. Optional dest DIR overrides "patches".--skip-check Skip the check that ensures starting with a clean branch.EOT}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -