r2345 - tags/scripts
Simon Horman
horms@costa.debian.org
Fri, 21 Jan 2005 11:07:04 +0100
Author: horms
Date: 2005-01-21 11:07:03 +0100 (Fri, 21 Jan 2005)
New Revision: 2345
Added:
tags/scripts/bk_get_patches
Log:
Tool for getting patches from bitkeeper
Added: tags/scripts/bk_get_patches
===================================================================
--- tags/scripts/bk_get_patches 2005-01-21 10:04:59 UTC (rev 2344)
+++ tags/scripts/bk_get_patches 2005-01-21 10:07:03 UTC (rev 2345)
@@ -0,0 +1,1001 @@
+#!/usr/bin/perl -w
+######################################################################
+# bk_get_patches November 2004
+# Horms horms@verge.net.au
+#
+# bk_get_patches
+# Retrieve Patches from bitkeeper either by changeset number of
+# by comparing files in a pristine source directory from a release
+# Copyright (C) 2004 Horms
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+# 02111-1307 USA
+#
+######################################################################
+
+use strict;
+use Date::Format;
+#use Date::Fingers;
+use Digest::MD5;
+use DB_File;
+use IPC::Open2;
+
+my $MY_VERSION="0.1.0";
+
+my $PRISTINE_DIR = undef;
+my $OUT_DIR = ".";
+my $TMP_DIR = "$ENV{HOME}/tmp/bk_get_patches";
+my $CACHE_DIR = $TMP_DIR;
+my $CACHE_DB = "$CACHE_DIR/cache.db";
+my $CACHE_VOLATILE_DB = "$CACHE_DIR/cache_volatile.db";
+
+my $VERSION = undef;
+my $PATCHLEVEL = undef;
+my $SUBLEVEL = undef;
+my $EXTRAVERSION = undef;
+
+my $PRISTINE_VERSION = undef;
+my $PRISTINE_PATCHLEVEL = undef;
+my $PRISTINE_SUBLEVEL = undef;
+my $PRISTINE_EXTRAVERSION = undef;
+
+my $CACHE_DB_HASH = {};
+my $CACHE_VOLATILE_DB_HASH = {};
+
+sub check_tree
+{
+ my ($cset, $exclude, $pfile) = (@_);
+
+ for my $file (keys %$pfile) {
+ if ( ! -f $file) {
+ print STDERR "No such file or directory \"$file\". " .
+ "Invalid command line argument?\n\n";
+ usage(-1);
+ }
+ }
+ if ( ! -f "SCCS/s.ChangeSet" or ! -f "Makefile") {
+ print STDERR "Must be run from the root of a bitkeeper tree\n";
+ return 1;
+ }
+ if ( defined $PRISTINE_DIR and !-d $PRISTINE_DIR) {
+ print STDERR "Pristine source directory, \"$PRISTINE_DIR\" "
+ ."is not a directory or does not exist\n";
+ return 1;
+ }
+ if ( defined $OUT_DIR and !-d $OUT_DIR) {
+ print STDERR "Out directory, \"$OUT_DIR\" "
+ ."is not a directory or does not exist\n";
+ return 1;
+ }
+ if ( ! -d "$TMP_DIR") {
+ print STDERR "Tempoary directory, \"$TMP_DIR\""
+ ."is not a directory or does not exist\n";
+ return 1;
+ }
+ unless ( -d "$CACHE_DIR/") {
+ mkdir "$CACHE_DIR/" || die;
+ }
+
+ open MAKEFILE, "<Makefile" || die;
+ while (<MAKEFILE>) {
+ if (/^VERSION\s*=\s*([0-9]+)\s*$/) {
+ $VERSION=$1;
+ }
+ elsif (/^PATCHLEVEL\s*=\s*([0-9]+)\s*$/) {
+ $PATCHLEVEL=$1;
+ }
+ elsif (/^SUBLEVEL\s*=\s*([0-9]+)\s*$/) {
+ $SUBLEVEL=$1;
+ }
+ elsif (/^EXTRAVERSION\s*=\s*(\S*)\s*$/) {
+ $EXTRAVERSION=$1;
+ }
+ if (defined $VERSION and defined $PATCHLEVEL and
+ defined $SUBLEVEL and defined $EXTRAVERSION) {
+ last;
+ }
+ }
+ close MAKEFILE;
+ #print "VERSION = $VERSION\n" .
+ # "PATCHLEVEL = $PATCHLEVEL\n" .
+ # "SUBLEVEL = $SUBLEVEL\n" .
+ # "EXTRAVERSION = $EXTRAVERSION\n";
+ unless (defined $VERSION and defined $PATCHLEVEL and
+ defined $SUBLEVEL and defined $EXTRAVERSION) {
+ print STDERR "Bogus Makefile in BK tree\n";
+ return 1;
+ }
+
+ if (not defined $PRISTINE_DIR) {
+ if (scalar keys %$pfile ne 0) {
+ print STDERR "--pristine-dir must be specified if "
+ . "FILES are given as arguments\n\n";
+ usage(-1);
+ }
+ return 0;
+ }
+
+ open MAKEFILE, "<$PRISTINE_DIR/Makefile" || die;
+ while (<MAKEFILE>) {
+ if (/^VERSION\s*=\s*([0-9]+)\s*$/) {
+ $PRISTINE_VERSION=$1;
+ }
+ elsif (/^PATCHLEVEL\s*=\s*([0-9]+)\s*$/) {
+ $PRISTINE_PATCHLEVEL=$1;
+ }
+ elsif (/^SUBLEVEL\s*=\s*([0-9]+)\s*$/) {
+ $PRISTINE_SUBLEVEL=$1;
+ }
+ elsif (/^EXTRAVERSION\s*=\s*(\S*)\s*$/) {
+ $PRISTINE_EXTRAVERSION=$1;
+ }
+ if (defined $PRISTINE_VERSION and
+ defined $PRISTINE_PATCHLEVEL and
+ defined $PRISTINE_SUBLEVEL and
+ defined $PRISTINE_EXTRAVERSION) {
+ last;
+ }
+ }
+ close MAKEFILE;
+ unless (defined $PRISTINE_VERSION and defined $PRISTINE_PATCHLEVEL and
+ defined $PRISTINE_SUBLEVEL and
+ defined $PRISTINE_EXTRAVERSION) {
+ print STDERR "Bogus Makefile in pristine tree\n";
+ return 1;
+ }
+
+ if ($VERSION ne $PRISTINE_VERSION or
+ $PATCHLEVEL ne $PRISTINE_PATCHLEVEL) {
+ print STDERR "Version Missmatch: " .
+ "BK tree is $VERSION.$PATCHLEVEL " .
+ "Pristine tree is " .
+ "$PRISTINE_VERSION.$PRISTINE_PATCHLEVEL\n\n";
+ return 1;
+ }
+
+ return 0;
+}
+
+sub add_cache
+{
+ my ($key, $value) = (@_);
+ $key = "${VERSION}.${PATCHLEVEL}::$key";
+ $CACHE_DB_HASH->{"$key"} = "$value";
+}
+
+sub get_cache
+{
+ my ($key) = (@_);
+ $key = "${VERSION}.${PATCHLEVEL}::$key";
+ return $CACHE_DB_HASH->{"$key"};
+}
+
+sub delete_cache
+{
+ my ($key) = (@_);
+ $key = "${VERSION}.${PATCHLEVEL}::$key";
+ delete $CACHE_DB_HASH->{"$key"};
+}
+
+sub add_cache_volatile
+{
+ my ($key, $value) = (@_);
+ $key = "${VERSION}.${PATCHLEVEL}::$key";
+ $CACHE_VOLATILE_DB_HASH->{"$key"} = "$value";
+}
+
+sub get_cache_volatile
+{
+ my ($key) = (@_);
+ $key = "${VERSION}.${PATCHLEVEL}::$key";
+ return $CACHE_VOLATILE_DB_HASH->{"$key"};
+}
+
+sub delete_cache_volatile
+{
+ my ($key) = (@_);
+ $key = "${VERSION}.${PATCHLEVEL}::$key";
+ delete $CACHE_VOLATILE_DB_HASH->{"$key"};
+}
+
+#sub gnupatch
+#{
+# my ($rset) = (@_);
+#
+# my $patch = get_cache_volatile("RSET2gnupatch::${rset}");
+# if (defined $patch) {
+# return $patch;
+# }
+#
+# $patch = "";
+# my $pid = open2(\*PATCHR, \*PATCHW, "bk gnupatch");
+# print PATCHW $rset;
+# close PATCHW;
+# while (<PATCHR>) {
+# $patch .= $_;
+# }
+# close PATCHR;
+# waitpid $pid, 0;
+#
+# add_cache_volatile("RSET2gnupatch::${rset}", $patch);
+# return $patch;
+#}
+
+sub dopatch
+{
+ my ($file, $start, $end) = (@_);
+
+ my $patch;
+ $patch = get_cache( "nrr2patch::${file}::${start}::${end}");
+ if (defined $patch) {
+ return $patch;
+ }
+
+ $patch = "";
+ open PATCH, "bk diffs -u -r$start..$end $file|";
+ while (<PATCH>) {
+ $patch .= $_;
+ }
+ close PATCH;
+
+ add_cache("nrr2patch::${file}::${start}::${end}", $patch);
+ return $patch;
+}
+
+sub __get_cset_patch
+{
+ my ($cset_key, $cset_no, $exclude, $pfile) = (@_);
+
+ my $cset_user = get_cache("MD5KEY2USER::${cset_key}");
+ if (! defined $cset_user) {
+ $cset_user = `bk prs -r$cset_key -hd :USER:` || return -1;
+ chomp $cset_user;
+ add_cache("MD5KEY2USER::${cset_key}", $cset_user)
+ }
+
+ my $cset_comment = get_cache("MD5KEY2COMMENTS::${cset_key}");
+ if (!defined $cset_comment) {
+ my @cset_comment = split /\n/,
+ `bk prs -r$cset_key -hd :COMMENTS:` ||
+ return -1;
+ $cset_comment = $cset_comment[0];
+ $cset_comment =~ s/C (.*)/$1/;
+ chomp $cset_comment;
+ add_cache("MD5KEY2COMMENTS::${cset_key}",
+ $cset_comment);
+ }
+
+ my $cset_rset = get_cache("MD5KEY2RSET::${cset_key}");
+ if (!defined $cset_rset) {
+ $cset_rset = `bk rset -hr$cset_key` || return -1;
+ add_cache("MD5KEY2RSET::${cset_key}", $cset_rset);
+ }
+
+ my $cset_rset_str;
+ my $cset_patch = "";
+ my @cset_rset = split /\n/, $cset_rset;
+
+ for my $i (@cset_rset) {
+ $i =~ m/(.*)\|.*\|(.*)\|.*\|(.*)/ || die;
+ my $file = $1;
+ my $start = $2;
+ my $end = $3;
+ my $rset_str = "$file|$start..$end";
+ my $rset_str_full = "";
+
+
+ if ($1 eq "ChangeSet") {
+ $cset_rset_str .= "# S rset: $rset_str\n";
+ next;
+ }
+
+ my $rev;
+ if (defined $pfile->{"$file"}) {
+ $rev = $pfile->{"$file"}->{"status"};
+ }
+ else {
+ $rev = $pfile->{"$file"}->{"status"} =
+ get_revision($file);
+ $pfile->{"$file"}->{"flag"} = "discovered";
+ }
+
+ if (cmp_revision($rev, $end, $file) ge 0) {
+ $cset_rset_str .= "# S rset: $rset_str\n";
+ next;
+ }
+
+ if (cmp_revision($rev, $start, $file) gt 0) {
+ $cset_rset_str .= "#(O rset: $rset_str)\n";
+ $start = $rev;
+ $rset_str = "$file|$start..$end";
+ $rset_str_full = "# U rset: $rset_str\n";
+ }
+ else {
+ $rset_str_full = "# I rset: $rset_str\n";
+ }
+
+ if (defined $exclude->{$rset_str}) {
+ $rset_str_full = "# E rset: $rset_str\n";
+ }
+ else {
+ $cset_patch .= dopatch($file, $start, $end);
+ }
+ $cset_rset_str .= $rset_str_full;
+ $pfile->{"$file"}->{"status"} = $end;
+ }
+ print "\n";
+ $cset_rset_str .=
+ "#\n" .
+ "# Key:\n" .
+ "# S: Skipped ChangeSet file only\n" .
+ "# O: Original Followed by Updated\n" .
+ "# U: Updated Included with updated range of versions\n" .
+ "# I: Included Included verbatim\n" .
+ "# E: Excluded Excluded on request from user\n" .
+ "# D: Deleted Manually deleted by subsequent user edit\n" .
+ "# R: Revised Manually revised by subsequent user edit\n" .
+ "#\n";
+
+ my $cset_export = get_cache("MD5KEY2export::${cset_key}");
+ if (! defined $cset_export) {
+ $cset_export = `bk export -tpatch -r$cset_key` || return -1;
+ add_cache("MD5KEY2export::${cset_key}", $cset_export);
+ }
+ my $cset_export_comment;
+ for my $i (split /\n/, $cset_export) {
+ if ($i !~ /^#/) {
+ last;
+ }
+ $cset_export_comment .= $i . "\n";
+ }
+
+ my $patch =
+ "# origin: $cset_user (BitKeeper)\n" .
+ "# cset: $cset_no ($VERSION.$PATCHLEVEL) " .
+ "key=$cset_key\n" .
+ "# inclusion: upstream\n" .
+ "# descrition: $cset_comment\n" .
+ "# revision date: " .
+ time2str("%a, %d %b %Y %T %z", time) . "\n" .
+ "#\n" .
+ $cset_rset_str .
+ "#\n" .
+ $cset_export_comment .
+ "#\n" .
+ $cset_patch;
+
+ return $patch;
+}
+
+#sub get_cset_patch_by_key
+#{
+# my ($cset_key, $exclude, $pfile) = (@_);
+#
+# my $cset_no = ` bk prs -r$cset_key -hnd :REV:` || return;
+# chomp $cset_no;
+#
+# return __get_cset_patch($cset_key, $cset_no, $exclude, $pfile);
+#}
+
+sub get_cset_patch_by_no
+{
+ my ($cset_no, $exclude, $pfile) = (@_);
+
+ my $cset_key = get_cache_volatile("c2MD5KEY::${cset_no}");
+ if (! defined $cset_key) {
+ $cset_key = `bk prs -r$cset_no -hd :MD5KEY:` || return;
+ chomp $cset_key;
+ add_cache_volatile("c2MD5KEY::${cset_no}", $cset_key);
+ }
+
+ return __get_cset_patch($cset_key, $cset_no, $exclude, $pfile);
+}
+sub system_wrapper
+{
+ #print "system_wrapper: @_\n";
+ system(@_);
+ if ($? == -1) {
+ die "Failed to execute";
+ }
+ elsif ($? & 127) {
+ die "Child exited with signal";
+ }
+ return $? >> 8;
+}
+
+sub test_file
+{
+ my ($file, $version) = (@_);
+
+ my $ret;
+ my $basename = $file;
+
+ $basename =~ s/.*\///;
+
+ unless ( -f "$PRISTINE_DIR/$file") {
+ return -1;
+ }
+
+ # this is nasty crap
+ system_wrapper("rm -f $TMP_DIR/$basename");
+ system_wrapper("cp $file $TMP_DIR/$basename");
+ system_wrapper("bk diffs -u -r$version $file "
+ . "| ( cd $TMP_DIR && patch -stR; )");
+ $ret = system_wrapper("diff -q $PRISTINE_DIR/$file $TMP_DIR/$basename"
+ . " > /dev/null");
+ system_wrapper("rm -f $TMP_DIR/$basename");
+
+ return $ret;
+}
+
+sub cset_get_pfile_revision
+{
+ my ($cset_no, $file) = (@_);
+
+ my $cset_key = get_cache_volatile("c2MD5KEY::${cset_no}");
+ if (! defined $cset_key) {
+ $cset_key = `bk prs -r$cset_no -hd :MD5KEY:` || return;
+ chomp $cset_key;
+ add_cache_volatile("c2MD5KEY::${cset_no}", $cset_key);
+ }
+
+ my $cset_rset = get_cache("MD5KEY2RSET::${cset_key}");
+ if (!defined $cset_rset) {
+ $cset_rset = `bk rset -hr$cset_key` || return -1;
+ add_cache("MD5KEY2RSET::${cset_key}", $cset_rset);
+ }
+ my @cset_rset = split /\n/, $cset_rset;
+ for my $i (@cset_rset) {
+ $i =~ m/(.*)\|.*\|(.*)\|.*\|.*/;
+ unless (defined $1) {
+ print "i=$i\n";
+ delete_cache("MD5KEY2RSET::${cset_key}");
+ }
+ if ($1 eq $file) {
+ return $2;
+ }
+ }
+
+ return undef;
+}
+
+
+sub get_revision_list_str
+{
+ my ($file, $nocache) = (@_);
+
+ my $rev_str;
+
+ # XXX: Is this too volatile to cache?
+ if (not defined $nocache) {
+ $rev_str = get_cache_volatile("n2REV::${file}");
+ }
+ if (! defined $rev_str) {
+ $rev_str = `bk prs -hnd:REV: $file` || return -1;
+ chomp $rev_str;
+ if (not defined $nocache) {
+ add_cache_volatile("n2REV::${file}", $rev_str);
+ }
+ }
+ return $rev_str;
+}
+
+sub get_revision_list
+{
+ my ($file, $nocache) = (@_);
+
+ my $rev_str;
+
+ return split /\n/, get_revision_list_str($file, $nocache);
+}
+
+
+sub get_revision
+{
+ my ($file) = (@_);
+
+ my @rev;
+ my $md5str;
+ my $revision;
+
+ print "O";
+
+ if (not defined $PRISTINE_DIR or not -f "$PRISTINE_DIR/$file") {
+ return "1.0";
+ }
+
+ @rev = get_revision_list($file);
+
+ {
+ my $md5 = Digest::MD5->new;
+ open PRISTINE, "<$PRISTINE_DIR/$file" or die;
+ while (<PRISTINE>) {
+ $md5->add($_);
+ }
+ close(PRISTINE);
+ $md5str = $md5->hexdigest;
+ }
+
+ $revision = get_cache("nh2r::${file}::${md5str}");
+
+ if (defined $revision) {
+ return $revision;
+ }
+
+ for my $i (@rev) {
+ print "o";
+
+ if (test_file($file, $i) != 0) {
+ next;
+ }
+ $revision = $i;
+ add_cache("nh2r::${file}::${md5str}", $i);
+ last;
+ }
+
+ return $revision;
+}
+
+sub __cmp_revision_fast
+{
+ my ($a, $b) = (@_);
+
+ if (not defined $a and not defined $b) {
+ return 0;
+ }
+ if (not defined $a) {
+ return -1;
+ }
+ if (not defined $b) {
+ return 1;
+ }
+
+ if ($a eq $b) {
+ return 0;
+ }
+
+ return undef;
+}
+
+my $REVISION_CACHE = {};
+
+sub cmp_revision_index
+{
+ my ($revision, $file) = (@_);
+
+ my $index;
+ $index = get_cache_volatile("r2index::${file}::${revision}");
+ if (defined $index) {
+ return $index;
+ }
+
+ $index = 0;
+ my @rev = get_revision_list($file);
+ for my $r (@rev) {
+ if ($r eq $revision) {
+ #if ($file eq "ChangeSet") {
+ # print "$file $a $b $index\n";
+ #}
+ $REVISION_CACHE->{$file}->{$revision} = $index;
+ add_cache_volatile("r2index::${file}::${revision}",
+ $index);
+ return $index;
+ }
+ $index++;
+ }
+
+ return undef;
+}
+
+sub cmp_revision
+{
+ my ($a, $b, $file) = (@_);
+
+ {
+ my $ret = __cmp_revision_fast($a, $b);
+ if (defined $ret) {
+ return $ret;
+ }
+ }
+
+ my $a_index = cmp_revision_index($a, $file);
+ my $b_index = cmp_revision_index($b, $file);
+
+ {
+ my $ret = __cmp_revision_fast($a_index, $b_index);
+ if (defined $ret) {
+ return $ret;
+ }
+ }
+
+ return ($b_index - $a_index);
+}
+
+#sub cmp_revision
+#{
+# my ($a, $b, $file) = (@_);
+#
+# {
+# my $ret = __cmp_revision_fast($a, $b);
+# if (defined $ret) {
+# return $ret;
+# }
+# }
+#
+# # N.B: @rev is from newest to oldest, so the test is reversed
+# my @rev = get_revision_list($file);
+# for my $r (@rev) {
+# if ($file eq "ChangeSet") {
+# print "$file $a $b $r\n";
+# }
+# if ($a eq $r) {
+# return 1;
+# }
+# elsif ($b eq $r) {
+# return -1;
+# }
+# }
+#
+# return undef;
+#}
+
+#sub cmp_revision_algorithmic
+#{
+# my ($a, $b) = (@_);
+#
+# {
+# my $ret = __cmp_revision_fast($a, $b);
+# if (defined $ret) {
+# return $ret;
+# }
+# }
+#
+# my @a_a = split /\./, $a;
+# my @b_a = split /\./, $b;
+#
+# while (1) {
+# my $x;
+# my $y;
+#
+# unless ($x = shift @a_a) { last; }
+# unless ($y = shift @b_a) { push @a_a, $x; last; }
+#
+# if ($x < $y) {
+# return -1;
+# }
+# elsif ($x > $y) {
+# return 1;
+# }
+#
+# }
+#
+# if ($#a_a ge 0) {
+# return 1;
+# }
+# elsif ($#b_a ge 0) {
+# return -1;
+# }
+#
+# return 0;
+#}
+#
+#sub cmp_cset
+#{
+# my ($a, $b) = (@_);
+#
+# # ChangeSets seem to be numerically ordered
+# # as opposed to file revisions which may be non-linear
+# # f there is a sub-revision). The ChangeSet list also turns
+# # out to be very long, so it should be both acurate
+# # and significantly faster to compare the the revision
+# # algorithmicly.
+# return cmp_revision_algorithmic($a, $b);
+#}
+
+sub cmp_cset {
+ return cmp_revision($a, $b, "ChangeSet");
+}
+
+sub sort_cmp_cset
+{
+ return cmp_cset($a, $b);
+}
+
+sub sort_cmp_cset_r
+{
+ return cmp_cset($b, $a);
+}
+
+
+sub __get_csets
+{
+ my ($cset, $exclude, $pfile, $file) = (@_);
+
+ my $md5str;
+
+ #print "file=$file\n";
+ $pfile->{$file}->{"status"} = "no patch";
+
+ my $revision = get_revision($file);
+ my @rev = get_revision_list($file);
+ $pfile->{$file}->{"status"} = $revision;
+
+ my $cset_file;
+ my $cset_no;
+ my $started;
+ for my $i (reverse @rev) {
+ if (not defined $started) {
+ if($i eq $revision) {
+ $started = 1;
+ }
+ next;
+ }
+ print ".";
+
+ $cset_no = get_cache_volatile("nr2c::${file}::${i}");
+ if (! defined $cset_no) {
+ $cset_no = `bk r2c -r$i $file` || return -1;
+ chomp $cset_no;
+ add_cache_volatile("nr2c::${file}::${i}", $cset_no);
+ }
+
+ if (defined $exclude->{$cset_no}) {
+ next;
+ }
+
+ if (defined $revision) {
+ if ($i eq $revision) {
+ last;
+ }
+ }
+ elsif (test_file($file, $i) == 0) {
+ $revision = $i;
+ add_cache("nh2r::${file}::${md5str}", $i);
+ last;
+ }
+
+ $cset_file =
+ { "start" => cset_get_pfile_revision($cset_no, $file),
+ "end" => $i,
+ "pfile" => $file,
+ "cset" => $cset_no };
+
+ if (! defined $pfile->{$file}->{"history"}) {
+ $pfile->{$file}->{"history"} = ();
+ }
+ push @{$pfile->{$file}->{"history"}}, $cset_file;
+
+ if (! defined $cset->{$cset_no}->{"history"}) {
+ $cset->{$cset_no}->{"history"} = ();
+ }
+ push @{$cset->{$cset_no}->{"history"}}, $cset_file;
+
+ unless (defined $cset->{$cset_no}->{"status"}) {
+ $cset->{$cset_no}->{"status"} = "pending";
+ }
+
+ }
+}
+
+
+sub get_csets
+{
+ my ($cset, $exclude, $pfile) = (@_);
+
+ my $count = 0;
+ for my $f (keys %$pfile) {
+ if ($pfile->{$f}->{"status"} ne "pending") {
+ next;
+ }
+ __get_csets($cset, $exclude, $pfile, $f);
+ $count++;
+ }
+ print "\n";
+
+ return $count;
+}
+
+#sub __get_pfiles
+#{
+# my ($cset, $pfile, $c) = (@_);
+#
+# my $cset_key = `bk prs -r$c -hnd :MD5KEY: ChangeSet`;
+# my @patch_f = split /\n/, `bk rset -r$cset_key`;
+#
+# #print "cset=$c\n";
+# $cset->{$c}->{"status"} = "processed";
+#
+# for my $f (@patch_f) {
+# my @tmp = split /\|/, $f;
+#
+# if (@tmp[0] eq "ChangeSet") {
+# next;
+# }
+#
+# unless (defined $pfile->{@tmp[0]}) {
+# $pfile->{@tmp[0]} = "pending";
+# }
+# #print " file=@tmp[0]\tfile_status=" . $pfile->{@tmp[0]} . "\n";
+# }
+#}
+#
+#sub get_pfiles
+#{
+# my ($cset, $pfile) = (@_);
+#
+# my $count = 0;
+# for my $c (keys %$cset) {
+# print "O";
+# if ($cset->{$c}->{"status"} ne "pending") {
+# next;
+# }
+# __get_pfiles($cset, $pfile, $c);
+# $count++;
+# }
+# print "\n";
+#
+# return $count;
+#}
+
+
+sub backport_files
+{
+ my ($cset, $exclude, $pfile) = (@_);
+
+ if (get_csets($cset, $exclude, $pfile) < 0) {
+ return -1;
+ }
+
+ print scalar(keys(%$cset)) . " change sets\n";
+ my $index = 1;
+ for my $i (sort sort_cmp_cset keys(%$cset)) {
+ printf " cset %04d $i\n", $index++;
+ for my $x (@{$cset->{$i}->{"history"}}) {
+ printf " cset=%12s %8s .. %-8s pfile=%s\n", $i,
+ $x->{"start"}, $x->{"end"}, $x->{"pfile"};
+ }
+ }
+ #print scalar(keys(%$pfile)) . " patched files\n";
+ $index=1;
+ for my $i (keys %$pfile) {
+ printf " pfile %04d $i %s\n", $index++,
+ $pfile->{$i}->{"status"};
+
+ for my $x (@{$pfile->{$i}->{"history"}}) {
+ printf " pfile=%s %8s .. %-8s cset=%s\n", $i,
+ $x->{"start"}, $x->{"end"}, $x->{"cset"};
+ }
+ }
+
+ return 0;
+}
+
+sub retrieve_changeset
+{
+ my ($cset, $exclude, $pfile) = (@_);
+
+ my $index = 0;
+ for my $cset_no (sort sort_cmp_cset keys(%$cset)) {
+ if (defined $exclude->{$cset_no}->{"status"}) {
+ print "$cset_no skip!\n";
+ next;
+ }
+
+ $index++;
+ my $cset_patch = get_cset_patch_by_no($cset_no, $exclude,
+ $pfile) ||
+ die "Could not retrieve $cset_no";
+
+ if ($OUT_DIR eq "-") {
+ print $cset_patch;
+ next;
+ }
+
+ my $file = sprintf "$OUT_DIR/%04d-%s.patch", $index, $cset_no;
+ print "$file\n";
+ open OUT, ">$file" || die;
+ print OUT $cset_patch;
+ close OUT;
+ }
+
+ return 0;
+}
+
+sub usage
+{
+ my ($exit_status) = (@_);
+
+ local *FILE = ($exit_status < 0) ? *STDERR : *STDOUT;
+
+ print FILE
+ "bk_get_patches version $MY_VERSION\n" .
+ "Copyright 2004 Horms\n" .
+ "\n" .
+ "Usage: bk_get_patches OPTIONS " .
+ "[CSET]... [FILE]...\n" .
+ " --help Display this text\n" .
+ " --pristine-dir=DIR Directory with pristine kernel\n" .
+ " source. Required if any FILEs are\n" .
+ " specified\n" .
+ " --out-dir=DIR Directory to put patch files in.\n" .
+ " - for stdout.\n".
+ " --exclude=CSET Exclude changeset/rset.\n".
+ "\n";
+
+ exit $exit_status;
+}
+
+{
+ my $pfile = {};
+ my $cset = {};
+ my $exclude = {};
+
+ umask 0077;
+
+
+ my $cmd ="";
+ for (@ARGV) {
+ if (/--help/) {
+ usage(0);
+ }
+ elsif (/--pristine[-_]dir=(.*)/) {
+ $PRISTINE_DIR = $1;
+ }
+ elsif (/--out[-_]dir=(.*)/) {
+ $OUT_DIR = $1;
+ }
+ elsif (/--exclude=(.*)/) {
+ $exclude->{$1} = "pending";
+ }
+ elsif (/^[0-9.]+$/) {
+ $cset->{$_}->{"status"} = "pending";
+ }
+ else {
+ # Assume a file
+ m/(\.\/+)*(.*)/;
+ $pfile->{$2}->{"status"} = "pending";
+ }
+ $cmd .= " \\\n\t$_";
+ }
+
+ unless (check_tree($cset, $exclude, $pfile) == 0) { exit 1; }
+ print "${0}${cmd}\n";
+
+ tie %$CACHE_DB_HASH, 'DB_File', $CACHE_DB,
+ O_CREAT|O_RDWR, 0600, $DB_HASH;
+ tie %$CACHE_VOLATILE_DB_HASH, 'DB_File', $CACHE_VOLATILE_DB,
+ O_CREAT|O_RDWR, 0600, $DB_HASH;
+
+ {
+ my $r1 = get_revision_list_str("ChangeSet", "nocache");
+ my $r2 = get_revision_list_str("ChangeSet");
+ if ($r1 ne $r2) {
+ print "Flushing volatile DB\n";
+ untie %$CACHE_VOLATILE_DB_HASH;
+ unlink $CACHE_VOLATILE_DB;
+ tie %$CACHE_VOLATILE_DB_HASH, 'DB_File',
+ $CACHE_VOLATILE_DB, O_CREAT|O_RDWR,
+ 0600, $DB_HASH;
+ }
+ }
+
+ unless (backport_files($cset, $exclude, $pfile) == 0) { die; }
+ unless (retrieve_changeset($cset, $exclude, $pfile) == 0) { die; }
+
+ untie %$CACHE_DB_HASH;
+ untie %$CACHE_VOLATILE_DB_HASH;
+}
+
+0;
Property changes on: tags/scripts/bk_get_patches
___________________________________________________________________
Name: svn:executable
+ *