#!/usr/bin/perl -w use strict; use File::Basename qq(basename); use Git; use Storable qw(store); use Term::ANSIColor qw(colored); if (@ARGV < 2) { print "Usage: $0 suse_machine|-d stable_version\n"; exit 1; } my $machine = shift; my $dump_only = $machine eq '-d'; my $stable_ver = shift; my $old_version; my $new_version; my $bsc; my %bsc_map = ( 'other' => '1012628', ); if ($stable_ver =~ /^v?(([3-9]\.[0-9]+)\.([0-9]+))$/) { $new_version = $1; $old_version = $2; $bsc = $bsc_map{$old_version}; if ($3 ne 1) { $old_version .= '.' . ($3 - 1); } } else { die "cannot understand stable version $stable_ver"; } if (!defined $bsc) { print colored("Kernel version not found in the map, assuming Tumbleweed\n", 'red'); $bsc = $bsc_map{'other'}; } my $patchpar = '/dev/shm'; my $patchdir = "patches-$new_version"; my $patchpath = "$patchpar/$patchdir"; my $idsfile = "$patchpath/ids"; if (!$dump_only && !mkdir $patchpath) { die "$patchpath already exists"; } my $range = "v$old_version..v$new_version"; my $repo = Git->repository(); my @revs = $repo->command('rev-list', '--reverse', $range); my %ids; my $digits = length scalar @revs; my $counter = 1; my @to_delete; my $sha_re = qr/[0-9a-f]{40}/; $digits = 3 if ($digits < 3); print "References: bsc#$bsc $new_version\n" if ($dump_only); foreach my $rev (@revs) { my ($filename, @commit_log) = $repo->command('show', '--no-patch', '--format=%f%n%B', $rev); my $cont = 0; my @shas; my @unmatched_shas; foreach (@commit_log) { if ($cont) { if (/^\s+($sha_re)\s*[\])]$/) { push @shas, $1; $cont = 0; next; } } if (/^[Cc]ommit ($sha_re) upstream\.?$/ || /^[\[(]\s*[Uu]pstream commit ($sha_re)\s*[\])]$/ || /^[uU]pstream commit ($sha_re)\.$/ || /^This is a backport of ($sha_re)$/) { push @shas, $1; } elsif (/^\(cherry picked from commit ($sha_re)\)$/) { # if this is not the first SHA, it's likely DRM crap push(@shas, $1) if (!scalar @shas); } elsif (/^[\[(]\s*[Uu]pstream commits ($sha_re)\s+and\s*$/) { push @shas, $1; $cont = 1; } elsif (/^(Fixes:|This reverts commit) $sha_re(?: \(".*)?\.?$/ || /^Link: .*lkml/) { # ignore } elsif (/\b$sha_re\b/) { push @unmatched_shas, $_; } } if ($dump_only) { print "$rev"; if (scalar @shas) { print "=", join ' ', @shas; } else { print ' STABLE-ONLY patch: "', $commit_log[0], '"'; } print "\n"; next; } # better than nothing if (!scalar @shas) { push @shas, $rev; } my @patch = $repo->command('format-patch', '--stdout', '--signoff', '--no-renames', '-1', $rev, '--add-header', "References: bsc#$bsc", '--add-header', "Patch-mainline: $new_version", (map { ('--add-header', "Git-commit: $_") } @shas)); # drop From shift(@patch) =~ /^From/ or die "From line is not the first one?"; my $newname = sprintf("${new_version}-%0${digits}d-%s", $counter, $filename); # 57 is what git-format-patch uses $newname =~ s/^(.{1,57}).*$/$1.patch/; my $newpath = "$patchpath/$newname"; open(PATCH, ">$newpath") or die "cannot output to $newpath"; print PATCH join "\n", @patch; print PATCH "\n"; close PATCH; $ids{$newname} = [ @shas ]; $rev =~ /.{12}/; print colored($&, "yellow"), " -> $newname\n"; foreach (@shas) { /.{12}/; print "\tUpstream SHA: ", colored("$&\n", "yellow"); } foreach (@unmatched_shas) { print colored("\tUNMATCHED SHA:", 'bold red'), " $_\n"; } push @to_delete, $newpath; $counter++; } exit 0 if ($dump_only); store(\%ids, $idsfile) or die "cannot write $idsfile"; push @to_delete, $idsfile; if ($machine ne 'localhost') { system("tar -cC $patchpar $patchdir|ssh -C $machine -o StrictHostKeyChecking=no 'tar -xC $patchpar'") == 0 || die "ssh didn't start"; unlink(@to_delete) or print STDERR "cannot delete some temp files\n"; rmdir("$patchpath") or print STDERR "cannot remove $patchpath\n"; } print "Written patches and ids to $machine:$patchpath\n"; 0;