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
   + *