#!/usr/bin/perl -w use strict; use Error qw(:try); use File::Copy qq(move); use Git; use Storable qq(retrieve); use Term::ANSIColor qw(colored); my $tmpdir = $ENV{TMPDIR} || "/tmp"; my $repo = Git->repository; if (scalar @ARGV < 1) { print "Usage: $0 patches_dir [nonzero_to_force_removal]\n"; exit 1; } open(SERIES, "+; } my $patchdir = $ARGV[0]; my $idsfile = "$patchdir/ids"; my $force_removal = $ARGV[1] || 0; my $destdir = "patches.kernel.org"; mkdir $destdir if (! -d $destdir); my $ids = retrieve($idsfile) or die "cannot read $idsfile"; my $regexp = join "|", map @$_, values %$ids; my @candidates = (); if ($regexp eq "") { print STDERR colored("empty regexp computed? Skipping patches removal...\n", 'yellow'); } else { try { @candidates = $repo->command('grep', '-El', $regexp, '--', 'patches.*') or die "cannot execute git grep"; } catch Git::Error::Command with { # not found is OK }; } sub output_refs($@) { my ($fh, @refs) = @_; my %uniq = map { s/fate/FATE/i; s/bnc/bnc/i; $_, 1 } @refs; print $fh "References: ", join(' ', sort keys %uniq), "\n"; } sub push_refs($@) { my ($dest, @refs) = @_; open(DEST, "<$dest") || die "cannot open $dest for reading"; my @dest = ; close DEST; open(DEST, ">$dest") || die "cannot open $dest for writing"; my $had_git_commit = 0; foreach my $line (@dest) { if (!$had_git_commit && $line =~ /^Git-commit: /) { output_refs(\*DEST, @refs); $had_git_commit = 1; } elsif ($line =~ /^References: (.*)$/) { chomp $1; push @refs, (split /[\s,]+/, $1); next; } print DEST $line; } close DEST; } my %files; my $tags = qr/(?:Git-[Cc]ommit: |Patch-[Mm]ainline: |From )([0-9a-f]{40})/; sub handle_removal($$) { my ($line, $dest) = @_; $line =~ /^([^:]+):($tags)?/; my $file = $1; my $match = $2; $files{$file} = 1; # weird git-commit tag or file may be deleted already return unless (defined $match && -f $file); print colored("\tRemoving $file\n", "yellow"); open(PATCH, "<$file") or die "cannot open $file"; my %shas = (); my @refs = (); while (my $line = ) { chomp $line; $shas{$1} = 1 if ($line =~ /^$tags/); if ($line =~ /^References: (.*)$/) { push @refs, (split /[\s,]+/, $1); } } close PATCH; return unless ($force_removal || scalar(keys %shas) == 1); try { $repo->command('rm', '--', $file); } catch Git::Error::Command with { # sometimes, they are dirty }; $series =~ s/ (?: # empty or non-comment line (^(?:$|[ \t]*[^ \t#].*)\n) # comment to be deleted [ \t]*\#.*\n )? # file to be deleted [ \t]+\Q$file\E[ \t]*\n /$1 || ""/mex; $series_changed = 1; if (scalar @refs) { if (-f $dest) { push_refs($dest, @refs); } else { print STDERR colored("\tmissed references:\n\t", 'red'); output_refs(\*STDERR, @refs); } } } foreach my $patch (sort keys %$ids) { my $src = "$patchdir/$patch"; my $dest = "$destdir/$patch"; print "Handling $patch\n"; if (-f $src && ! -f $dest) { move($src, $dest) || die "cannot move $src to $dest"; print "\tMoved to $destdir\n"; $repo->command('add', $dest); print "\tAdded to GIT\n"; unless ($series =~ s/(latest standard kernel patches(?:\n[^\n]+)+\n)\n/$1\t$dest\n\n/) { die "cannot find a place in series.conf to add a patch"; } $series_changed = 1; } my $re = join("|", @{$$ids{$patch}}); if (scalar @candidates > 0 && $re ne "") { my @found; try { @found = $repo->command('grep', '-E', $re, '--', @candidates) or die "cannot execute git grep"; } catch Git::Error::Command with { # not found is OK }; foreach (@found) { next if (/patches\.kernel\.org\//); handle_removal($_, $dest); } } } if ($series_changed) { seek(SERIES, 0, 0) || die "cannot seek series.conf"; truncate(SERIES, 0) || die "cannot truncate series.conf"; print SERIES $series; } close SERIES; foreach my $file (keys %files) { next unless (-e $file); try { $repo->command_noisy('grep', '-E', $regexp, '--', $file); } catch Git::Error::Command with { # not found is OK }; } 0;