#!/usr/bin/perl -w
# (c) Eduard Bloch <blade@debian.org>, 2003
# License: GPL
# Version: $Id: svn-upgrade 2100 2005-10-13 19:29:24Z par ame ter $

use Getopt::Long qw(:config no_ignore_case bundling);
use File::Basename;
use Cwd;
#use diagnostics;
use strict;

my $startdir=getcwd;
my $scriptname="[svn-upgrade]";

sub help {
print "
Usage: svn-upgrade newsource [ OPTIONS... ]
Upgrade a source code package from an upstream revision. The source code
repository must be in the format created by svn-inject.

  -V, --upstreamversion  STRING    Forces a different upstream version string
  -c, --clean                      generic cleanup of upstream source - remove
                                   debian directory and object files
  -f, --force                      Force execution of certain operations
  -v, --verbose                    More verbose program output
  -r, --replay-conflicting         Special cleanup action: replaces all
                                   conflicting files with upstream versions
  -N, --noautodch                  Upgrade without making a new changelog entry

The new source may be a tarball compressed with gzip or bzip2 or a
directory with extraced source.
"; exit 1;
}

my $package;
my $upVersion;
my $origdir;
my $tmpdir;

my $opt_help;
my $opt_replay;
my $opt_force;
my $opt_clean;
my $upsVersion;
my $epoch;
my $verbose;
my $keep_removed;
my $quiet="-q";
my $opt_noautodch;
my $opt_noninteractive;
my $opt_ignoreerrors;
my $opt_dbgsdcommon;

my %options = (
   "h|help"                => \$opt_help,
   "V|upstreamversion=s"   => \$upsVersion,
   "upstream-version=s"    => \$upsVersion,
   "c|clean"               => \$opt_clean,
   "f|force"               => \$opt_force,
   "v|verbose"             => \$verbose,
   "r|replay-conflicting"  => \$opt_replay,
   "N|noautodch"           => \$opt_noautodch,
   "noninteractive"        => \$opt_noninteractive,
   "ignoreerrors"          => \$opt_ignoreerrors,
   "dbgsdcommon"           => \&opt_dbgsdcommon
);

my $retval = 0;

#shamelessly copied and slightly modified from svn-bp
my @CONFARGS;
for my $file ($ENV{"HOME"}."/.svn-buildpackage.conf") {

    if(open(RC, $file)) {
        SKIP: while(<RC>) {
            chomp;
            next SKIP if /^#/;
            # drop leading spaces
            s/^\s+//;
            if(/^svn-(noautodch|noninteractive|ignoreerrors|verbose|force|clean|replay-conflicting|dbgsdcommon)/) {
                # remove spaces between
                s/^(\S+)\s*=\s*/$1=/;
                #remove leading svn, since update does not have that
                s/^svn-//;
                # convert to options and push to args
                s/^/--/;
                $_=`echo -n $_` if(/[\$`~]/);
                push(@CONFARGS, $_);
            }
        }
        close(RC);
    }
}

if($#CONFARGS>=0) {
   @ARGV=(@CONFARGS, @ARGV);
   print "Imported config directives:\n\t".join("\n\t", @CONFARGS)."\n";
}



&help unless ( GetOptions(%options));
&help if ($opt_help);

my $mergemode = (`svn proplist debian` =~ /mergeWithUpstream/i);

goto SKIP_VERSION_SCAN if($opt_replay);

&help if $#ARGV < 0;
my $newsource=$ARGV[0];
&help if(!-r $newsource);
shift(@ARGV);

# don't use abs_path yet, it resolves the symlinks

#$newsource=$startdir."/".$newsource if(! $newsource=~/^\//);


if(-d $newsource) {
    basename($newsource) =~ /([^-]+)-(\d.*)/i;

    # provisoric package definition, to be overriden by the config module later
    $package = $1;
    $upsVersion = $2 if(!defined($upsVersion));
}
else {
    basename($newsource) =~/([^-]+)-(\d.*)\.(tar|tgz|tbz)/i;

    # provisoric package definition, to be overriden by the config module later
    $package = $1;
    $upsVersion = $2 if(!defined($upsVersion));

    # workaround for debianized tarballs
    if(! length($1.$2)) {
        basename($newsource) =~/(.*)_(.*)\.orig.tar.gz/i;
        # provisoric package definition, to be overriden by the config module later
        $package = $1;
        $upsVersion = $2 if(!defined($upsVersion));
    }

}

# no resolve it, we don't need the user-specified filename any longer
$newsource = long_path($newsource);

die "Unrecognized upstream version, use -V\n" if(!length($upsVersion));

SKIP_VERSION_SCAN:

undef($quiet) if ($verbose);

if (!defined $opt_dbgsdcommon) {
   use lib "/usr/share/svn-buildpackage";
} else {
   use lib ".";
};
use SDCommon;
$ENV{"SVN_BUILDPACKAGE"} = $SDCommon::version;

$SDCommon::opt_verbose=$verbose;
SDCommon::configure;
#$tagVersion=$SDCommon::tagVersion;
$upVersion=$SDCommon::upVersion;
$package  =$SDCommon::package; # XXX Overwrite the $package value!
$epoch    =$SDCommon::epoch;
$SDCommon::opt_noninteractive = 1 if $opt_noninteractive;
$SDCommon::opt_noninteractive = 1 if exists($ENV{DEBIAN_FRONTEND}) && $ENV{DEBIAN_FRONTEND} =~ /^noninteractive$/;
$SDCommon::opt_ignoreerrors = 1 if ($opt_ignoreerrors && $SDCommon::opt_noninteractive);
needs_tagsUrl;
needs_upsTagUrl if !$mergemode;
needs_upsCurrentUrl if !$mergemode;
my $c=\%SDCommon::c;

if($opt_replay) {
   for(split(/\n/,`env LC_ALL=C svn status`)) {
      if(/^C\s+(.*)/) {
         withecho("svn cat ".$$c{"upsCurrentUrl"}."/$1 > $1 && svn resolved $1");
      }
   }
   SDCommon::sd_exit 0;
}

# my private copy of the setting
if($$c{"origDir"}){$origdir=$$c{"origDir"}}else{chomp($origdir=`mktemp -d`)};

withecho("mkdir", "-p", $origdir) if (!-d $origdir);

chomp($tmpdir=`mktemp -d`);
#print "D: ".$$c{"upsTagUrl"}.join(",", values(%c));
my $upsOldVersUrl=$$c{"upsTagUrl"}."/$upVersion";
#print $upsOldVersUrl ; exit 1;
mkdir $tmpdir;

my $cat;
my $justcopy;
my $filestr = `file \"$newsource\"`;
if ($filestr =~ /gzip/){
   $cat = "zcat \"$newsource\"";
   $justcopy=1;# Always copy. Maintainer should recompress manually, if needed. if ($filestr =~ /max compression/);
}
elsif ($filestr =~ /bzip/){
   $cat = "bzcat \"$newsource\"";
}
elsif ($filestr =~ /tar archive/){
   $cat = "cat $newsource";
}
elsif ($filestr =~ /directory/){
   $cat = " ( cd \"".dirname($newsource)."\" ; tar c \"".basename($newsource)."\" ) ";
}
else {
   die "Unknown compression method!\n";
}

my $neworig="$origdir/".$package."_".$upsVersion.".orig.tar.gz";

die "$neworig exists, aborting...\n" if (-e $neworig);

# repack the new source file to tarballs/...orig.tar.gz
if(! -f $neworig) {
   # only if correct gzip format, not a directory, see above
   if($justcopy) {
      link($newsource,$neworig) || withecho("cp",$newsource,$neworig);
   }
   else {
      withecho("$cat | gzip -9 > \"$neworig\"");
   };
}

# FIXME: dircheck hier irgendwo rein
withecho("svn",$quiet,"add",$neworig) if(defined $SDCommon::c{"origUrl"});

if($mergemode) {
   print "I: Upstream source not maintained in the repository.\n";
   print "I: Assuming that it will be merged at build time.\n";
}
else
{
   if(! insvn($upsOldVersUrl)) {
         die "
         Could not find the unmodified upstream version in
         $upsOldVersUrl!
         If you think that $$c{upsCurrentUrl} is the upstream source
         which $$c{trunkUrl} is based on, run:

         svn copy $$c{upsCurrentUrl} $upsOldVersUrl

         Otherwise, please fix the headline of debian/changelog.\n";
   }
}

if($filestr =~ /directory/) {
   withecho("cp","-a", $newsource, "$tmpdir/");
}
else {
   withecho("tar", "zxf", $neworig, "-C", "$tmpdir");
}

my $srcdir="$package-$upsVersion";

# fix the FSCKING upstream source that comes without top subdirectory

chdir $tmpdir;
my @filesInside=(<*>);
if($#filesInside > 0) {
   mkdir $srcdir;
   withecho("mv",@filesInside, $srcdir);
}

# create the list of modified files if needed
my @difflist;
if($mergemode) {
   chdir $startdir;
   open(DL, "find -type f | grep -v \"\\.svn\" |") or
       die ("Failed to exec find -type f | grep -v \"\\.svn\": $!");
   while(<DL>) {
      push(@difflist, $1) if(/^[^\/]+\/(.+)\n/);
   }
   close(DL);
}

chdir $tmpdir;
system "rm -f *.gz";

# if needed, remove uninteressting files from the upstream directory,
# or move the directory to some with the proper name
if(@difflist) {
   withecho "mv * $srcdir-tmp; mkdir $srcdir";
   withecho "cd $srcdir-tmp; tar c ".join(" ", @difflist)." 2>/dev/null | tar x -C ../$srcdir";
}
else {
   # work around identical dir names
   withecho "mv * $srcdir-tmp";
   withecho "mv $srcdir-tmp $srcdir";
}

if(-d "$tmpdir/$srcdir/debian" && !$opt_force) {
   print "
Warning: it is generaly a bad idea to keep the debian directory in the
upstream source tarball unless you explicitely try to track an upstream source
including Debianisation. Consider using uclean to remove the junk there.
";
}

if($opt_clean) {
   my $dir=Cwd::getcwd;
   chdir "$tmpdir/$srcdir";
   system "make clean";
   system "rm -rf debian";
   chdir $dir;
}

# now we have the new upstream source we need in $tmpdir/$upsVersion
# allow the import to be skipped in mergeWithUpstream mode if the
# upsCurrentUrl is not known
if(defined($$c{"upsCurrentUrl"}) || !$mergemode) {
   # first look for conflicting .svn dirs
   oldSvnDirsCheck "$tmpdir/$srcdir";
   load_dirs($$c{"upsCurrentUrl"}, "$tmpdir/upsCurDir", "$tmpdir/$srcdir");
   withecho("svn", "commit", "-m", "$scriptname Integrating new upstream version, $package ($upsVersion)", "$tmpdir/upsCurDir");
   withecho("svn", "copy", "-m", "$scriptname Tagging new upstream version, $package ($upsVersion)", $$c{"upsCurrentUrl"},  $$c{"upsTagUrl"}."/$upsVersion");
   chdir $$c{"trunkDir"};
   if($mergemode) {
      # allow to fail, eg. if upstream dir is there but empty
      withechoNoPrompt("svn","merge", $upsOldVersUrl, $$c{"upsCurrentUrl"}, ".");
   }
   else {
      withecho("svn","merge", $upsOldVersUrl, $$c{"upsCurrentUrl"}, ".");
   }
   withechoNoPrompt "svn up";
}
else {
   chdir $$c{"trunkDir"};
}

open(SVN, "env LC_ALL=C svn status|") or die ("Failed tu run `svn status`: $!");
my @conflicts = grep(/^C/, <SVN>);
close SVN;
map {s/^..//} @conflicts;
if ($#conflicts != -1) {
   print "Files in conflict state:\n\n @conflicts\n";
   print "Resolve them manually and run \"svn resolved\" on each file\n.",
      "Or use \"svn-upgrade -r\" to overwrite them with new upstream verions\n";
}
withecho("rm", "-rf", $tmpdir) if(length($tmpdir));

$upsVersion = join ':', $epoch, $upsVersion if $epoch;

if ($opt_noautodch) {
    print "Done!\n";
}
else {
    withecho "debchange -D UNRELEASED -v \"$upsVersion-1\" \"(NOT RELEASED YET) New upstream release\"";
    $retval = $? >> 8;
    print "Done! Last commit pending, please execute manually.\n";
}

sub which {
    my $name=shift;
    for(split(/:/, $ENV{"PATH"})) {
        return if(-x "$_/$name");
    }
    return "";
}

sub END {
    system("rm", "-rf", $tmpdir) if ($tmpdir && -e $tmpdir);
    print STDERR "Process ended with code $retval\n";
    return $retval;
}
