diff options
-rw-r--r-- | AUTHORS | 35 | ||||
-rw-r--r-- | Makefile | 133 | ||||
-rw-r--r-- | README.in | 7 | ||||
-rw-r--r-- | TODO | 96 | ||||
-rwxr-xr-x | bin/guards | 246 | ||||
-rw-r--r-- | bin/quilt.in | 90 | ||||
-rw-r--r-- | docco.txt | 717 | ||||
-rwxr-xr-x | inpatch.in | 143 | ||||
-rwxr-xr-x | lib/apatch.in | 114 | ||||
-rw-r--r-- | lib/backup-files.c | 228 | ||||
-rwxr-xr-x | lib/parse-patch | 46 | ||||
-rw-r--r-- | lib/patchfns.in | 434 | ||||
-rwxr-xr-x | lib/rpatch.in | 148 | ||||
-rwxr-xr-x | lib/spec2series | 223 | ||||
-rwxr-xr-x | lib/touched-by-patch | 69 | ||||
-rw-r--r-- | patch-scripts.changes | 11 | ||||
-rw-r--r-- | quilt.changes | 36 | ||||
-rw-r--r-- | quilt.spec (renamed from patch-scripts.spec) | 43 | ||||
-rw-r--r--[-rwxr-xr-x] | quilt/add.in (renamed from patchadd.in) | 61 | ||||
-rw-r--r-- | quilt/applied.in | 71 | ||||
-rw-r--r-- | quilt/delete.in | 92 | ||||
-rw-r--r-- | quilt/diff.in | 244 | ||||
-rw-r--r-- | quilt/files.in | 112 | ||||
-rw-r--r--[-rwxr-xr-x] | quilt/import.in (renamed from importpatch.in) | 44 | ||||
-rw-r--r--[-rwxr-xr-x] | quilt/new.in (renamed from newpatch.in) | 42 | ||||
-rw-r--r-- | quilt/next.in | 72 | ||||
-rw-r--r-- | quilt/patches.in | 93 | ||||
-rw-r--r--[-rwxr-xr-x] | quilt/pop.in (renamed from poppatch.in) | 29 | ||||
-rw-r--r-- | quilt/previous.in | 70 | ||||
-rw-r--r--[-rwxr-xr-x] | quilt/push.in (renamed from pushpatch.in) | 26 | ||||
-rw-r--r-- | quilt/refresh.in | 201 | ||||
-rw-r--r-- | quilt/remove.in | 119 | ||||
-rwxr-xr-x | quilt/rest.in | 72 | ||||
-rw-r--r-- | quilt/series.in | 87 | ||||
-rw-r--r-- | quilt/setup.in | 172 | ||||
-rw-r--r-- | quilt/top.in | 62 | ||||
-rwxr-xr-x | refpatch.in | 335 | ||||
-rwxr-xr-x | toppatch.in | 138 |
38 files changed, 3795 insertions, 1166 deletions
@@ -1,11 +1,32 @@ -At the begining was the void. Then Andrew Morton <akpm@digeo.com> begun a -collection of small scripts for patch management. The contributions came of -Stephen Cameron <steve.cameron@hp.com>, Matt Reppert <arashi@arashi.yi.org> -and Jeremy Fitzhardinge <jeremy@digeo.com>. +At the begining was the void. Then Andrew Morton <akpm@digeo.com> begun +a collection of small scripts for patch management. The contributions +came from Stephen Cameron <steve.cameron@hp.com>, Matt Reppert +<arashi@arashi.yi.org> and Jeremy Fitzhardinge <jeremy@digeo.com>. Then, Andreas Gruenbacher <agruen@suse.de> came to cleanup, reorganize, -document and speed up the code. He did also package it to rpm +document and speed up the code. He did also package it to rpm. -Finally, Martin Quinson <Martin.Quinson@tuxfamily.org> did a debian package -and came with several little patches. +Finally, Martin Quinson <Martin.Quinson@tuxfamily.org> did a debian +package and came with several little patches. + +Andrew Morton <akpm@digeo.com> + Collection of scripts for patch management (patch-scripts). + +Stephen Cameron <steve.cameron@hp.com> + Contributed pstatus. + +Matt Reppert <arashi@arashi.yi.org> +Jeremy Fitzhardinge <jeremy@digeo.com> + Contributions to Andrew's scripts. + +Andreas Gruenbacher <agruen@suse.de> + Clean up, reorganize, speedups, documentation. + Package up as RPM. + +Gerd Knorr <kraxel@suse.de> + Contributed spec2series. + +Martin Quinson <Martin.Quinson@tuxfamily.org> + Several little patches. + Package up for Debian. @@ -1,67 +1,110 @@ -SCRIPTS_IN := inpatch newpatch patchadd poppatch pushpatch refpatch toppatch \ - importpatch -LIB_SCRIPTS_IN := apatch rpatch patchfns backup-files -LIB_SCRIPTS := touched-by-patch parse-patch -#LIB_LIB := patchfns +PACKAGE := quilt +VERSION := 0.21 -VERSION := 0.11.1 +prefix := /usr/local +bindir := $(prefix)/bin +datadir := $(prefix)/share +mandir := $(datadir)/man +docdir := $(datadir)/doc/packages -prefix := /usr/local -bindir := $(prefix)/bin -datadir := $(prefix)/share -LIB := $(datadir)/patch-scripts -docdir := $(datadir)/doc/packages +QUILT_DIR = $(datadir)/$(PACKAGE) +LIB_DIR = $(datadir)/$(PACKAGE)/lib -CFLAGS = -Wall +CFLAGS = -g -Wall -% : %.in - @echo "$< -> $@" - @sed -e "s:@LIB@:$(LIB):g" $< > $@ - @chmod --reference=$< $@ +#----------------------------------------------------------------------- +SRC += COPYING AUTHORS TODO BUGS Makefile \ + quilt.spec quilt.changes \ + misc/akmp-patch-scripts-0.9.tar.gz \ + misc/jr-quilt-0.1.0.tar.bz2 \ + misc/test.tar.gz + +BIN_IN := quilt +BIN_SRC := $(BIN_IN:%=%.in) guards +BIN := $(BIN_IN) guards +SRC += $(BIN_SRC:%=bin/%) +DIRT += $(BIN_IN:%=bin/%) + +QUILT_IN := add applied delete diff files import new next patches \ + pop previous push refresh remove rest series setup top -all : scripts README +QUILT_SRC := $(QUILT_IN:%=%.in) +QUILT := $(QUILT_IN) +SRC += $(QUILT_SRC:%=quilt/%) +DIRT += $(QUILT_IN:%=quilt/%) -scripts : $(SCRIPTS_IN) $(LIB_SCRIPTS_IN:%=lib/%) +LIB_IN := apatch rpatch patchfns +LIB_SRC := $(LIB_IN:%=%.in) parse-patch spec2series \ + backup-files.c +LIB := $(LIB_IN) parse-patch spec2series backup-files +SRC += $(LIB_SRC:%=lib/%) +DIRT += $(LIB_IN:%=lib/%) lib/backup-files{,.o} + +DOC_IN := README +DOC_SRC := $(DOC_IN:%=%.in) +DOC := $(DOC_IN) docco.txt +SRC += $(DOC_SRC) docco.txt +DIRT += $(DOC_IN) + +MAN1 := bin/guards.1 +#----------------------------------------------------------------------- + +all : scripts + +scripts : $(BIN:%=bin/%) $(QUILT:%=quilt/%) $(LIB:%=lib/%) \ + $(DOC) $(MAN1) README : README.in - @echo "$< -> $@" @awk '/@REFERENCE@/ { system("$(MAKE) -s reference") ; next }'$$'\n'' \ - { print }' $< > $@ + { print }' 2>&1 $< > $@ -reference : $(SCRIPTS_IN) - @for i in $(SCRIPTS_IN); \ +.PHONY :: reference +reference : $(QUILT:%=quilt/%) + @for i in $+; \ do \ + echo "$$i >> README" >&2; \ echo; \ - ./$$i -h; \ + (bash -c ". lib/patchfns ; . $$i -h"); \ done | \ awk '/Usage:/ { sub(/Usage: ?/, "") ; print ; next } '$$'\n'' \ { printf " %s\n", $$0 }' -dist : distclean - rm -f patch-scripts-$(VERSION) - ln -s . patch-scripts-$(VERSION) - tar cvfz patch-scripts-$(VERSION).tar.gz \ - --exclude=patch-scripts-$(VERSION)/patch-scripts-* \ - --exclude=CVS \ - patch-scripts-$(VERSION)/* - rm -f patch-scripts-$(VERSION) +bin/guards.1 : bin/guards + mkdir -p $$(dirname $@) + pod2man $< > $@ -install : all - install -d $(BUILD_ROOT)$(LIB) - install -m 755 $(LIB_SCRIPTS_IN:%=lib/%) \ - $(BUILD_ROOT)$(LIB) - install -m 755 $(LIB_SCRIPTS:%=lib/%) \ - $(BUILD_ROOT)$(LIB) - #install -m 644 $(LIB_LIB:%=lib/%) \ - # $(BUILD_ROOT)$(LIB) +dist : + rm -f $(PACKAGE)-$(VERSION) + ln -s . $(PACKAGE)-$(VERSION) + tar cvfz $(PACKAGE)-$(VERSION).tar.gz \ + $(SRC:%=$(PACKAGE)-$(VERSION)/%) + rm -f $(PACKAGE)-$(VERSION) +install : all install -d $(BUILD_ROOT)$(bindir) - install -m 755 $(SCRIPTS_IN) \ - $(BUILD_ROOT)$(bindir) + install -m 755 $(BIN:%=bin/%) $(BUILD_ROOT)$(bindir)/ + + install -d $(BUILD_ROOT)$(QUILT_DIR) + install -m 755 $(QUILT:%=quilt/%) $(BUILD_ROOT)$(QUILT_DIR)/ + + install -d $(BUILD_ROOT)$(LIB_DIR) + install -m 755 $(filter-out lib/patchfns lib/backup-files, \ + $(LIB:%=lib/%)) $(BUILD_ROOT)$(LIB_DIR)/ + install -m 644 lib/patchfns $(BUILD_ROOT)$(LIB_DIR)/ + install -m 755 -s lib/backup-files $(BUILD_ROOT)$(LIB_DIR)/ + + install -d $(BUILD_ROOT)$(docdir)/$(PACKAGE) + install -m 644 README $(BUILD_ROOT)$(docdir)/$(PACKAGE)/ - install -d $(BUILD_ROOT)$(docdir)/patch-scripts - install -m 644 README needs-checking/docco.txt \ - $(BUILD_ROOT)$(docdir)/patch-scripts + install -d $(BUILD_ROOT)$(mandir)/man1 + install -m 644 $(MAN1) $(BUILD_ROOT)$(mandir)/man1/ clean distclean : - rm -f $(SCRIPTS_IN) $(LIB_SCRIPTS_IN:%=lib/%) README + rm -f $(DIRT) + +% : %.in + @echo "$< -> $@" + @sed -e "s:@LIB@:$(LIB_DIR):g" \ + -e "s:@QUILT@:$(QUILT_DIR):g" \ + $< > $@ + @chmod --reference=$< $@ @@ -83,6 +83,13 @@ Command reference ================= @REFERENCE@ +guards [--prefix=dir] [--path=dir1:dir2:...] [--default=0|1] + [--check] [--config=file] symbol ... + + Convert a series file with conditional statements into a series + file as expected; see the guards(1) manual page. + + Helper files/scripts ==================== @@ -1,13 +1,93 @@ -Keep -p0 and -p1 patches, and convert -pN (N>1) to -p1 in refpatch +This is my current list of open issues with the patch scripts. I would +like to do the most intrusive changes before syncing up with the +Savannah CVS. Of course, contributions are very welcome. It wouldn't +hurt to know if you want to work on some of the issues in advance to +avoid duplicate work, though :) -Test Suite + -- Andreas Gruenbacher <agruen@suse.de> -Support compressed files in .pc/? -Use proper temporary files in parse-patch, so that parse-patch can be used -multiple times in pipes! +General: -check if things like reading the patch file list should be done globally -in some scripts. + - Test if patches/ can be moved with environment variable as + planned. + + - Abstract backup operations to/from the .pc/ directory, so that + optionally something like rcs can be used instead of + lib/backup-files? + + - Add regression test suite; the scripts were broken often enough + already... + + - Add a ~/.quiltrc that contains default options for individual + commands, similar to ~/.cvsrc. Also requires some more + negative options so that the effects of ~/.quiltrc can be + reversed. + + - Add something similar to cvs diff, which scans all files for + changes that have not been folded back into their patches, + similar to: + `for p in $(quilt series); do quilt diff -z $p; done'? + + - Allow to add a directory? Then we could detect also new files + in the directory, without having to add them individually. + + - Add option for creating hard links. +Documentation: + + - How to rediff with pushpatch -f / poppatch -f? + + - How to import a new version of a patch? + + - How to import a complete directory, before doing + wild changes? (This will also cause new files to end up in the + patch.) + +parse-patch: + + - Handle SIGINT in parse-patch -u (in the part that moves the + temp file to the patch)? + +backup-files: + + - Extend so that it can also copy files instead of hard linking, + and when hard links are not possible. Also add option to + disallow hard links on the original file (for patchadd). + + - SIGINT handling? + +quilt import: + + - Add option to replace the currently applied patch with a new + one, by backing out the topmost patch first. + + - Diff -u the documentation of the old and new file, unless one + of them is empty. Let the user decide whether to keep the left + or the right documentation, or to merge them both. (-d{ona}?) + +quilt add: + + - Add option for creating (or rather, leaving) hard links. + + +quilt setup: + + - spec2series also prints -p1; omit. + +rpatch: + + - If not removing the topmost patch, add checks if any files are + hidden by later patches. If so, refuse to remove patch! (Note + that poppatch takes care of that currently.) + +apatch: + + - Allow to add a patch in the middle of the applied series, and + inject the patch in its proper position in applied-patches. + Needs to check if any of the files in the patch are touched by + later patches. + +touched_by_patch: + + - Implement more of patch's filename heuristic? -Clean up temp file handling diff --git a/bin/guards b/bin/guards new file mode 100755 index 0000000..9681186 --- /dev/null +++ b/bin/guards @@ -0,0 +1,246 @@ +#!/usr/bin/perl -w + +# This script is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# See the COPYING and AUTHORS files for more details. + +use FileHandle; +use Getopt::Long; +use strict; + +# Prototypes +sub files_in($$); +sub parse($$); +sub help(); + +sub slashme($) { + my ($dir) = @_; + $dir =~ s#([^/])$#$&/#; # append a slash if necessary + if ($dir eq './') { + return ''; + } else { + return $dir; + } +} + +# Generate a list of files in a directory +# +sub files_in($$) { + my ($dir, $path) = @_; + my $dh = new FileHandle; + my (@files, $file); + + + opendir $dh, length("$dir$path") ? "$dir$path" : '.' + or die "$dir$path: $!\n"; + while ($file = readdir($dh)) { + next if $file =~ /^(\.|\.\.|\.#.*|CVS)$/; + if (-d "$dir$path$file") { + @files = (@files, files_in($dir, "$path$file/")); + } else { + #print "[$path$file]\n"; + push @files, "$path$file"; + } + } + closedir $dh; + return @files; +} + +# Parse a configuration file +# Callback called with ($patch, @guards) arguments +# +sub parse($$) { + my ($fh, $callback) = @_; + + my $mode = 0; + + while (<$fh>) { + my @guards = (); + s/(^|\s+)#.*//; + foreach my $token (split) { + if ($token =~ /^[-+]/) { + if ($mode == 1) { + @guards = (); + $mode = 0; + } + push @guards, $token; + } else { + $mode = 1; + #print "[" . join(",", @guards) . "] $patch\n"; + &$callback($token, @guards); + } + } + } +} + +# Command line options +# +my ($dir, $config, $default, $check) = ('', '-', 1, 0); +my @path; + +# Help text +# +sub help() { + print "$0 - select from a list of files guarded by conditions\n"; + print "SYNOPSIS: $0 [--prefix=dir] [--path=dir1:dir2:...]\n" . + " [--default=0|1] [--check] [--config=file] symbol ...\n\n" . + " (Default values: --path='" . join(':', @path) . "', " . + "--default=$default)\n"; + exit 0; +} + +# Parse command line options +# +Getopt::Long::Configure ("bundling"); +eval { + unless (GetOptions ( + 'd|prefix=s' => \$dir, + 'c|config=s' => \$config, + 'C|check' => \$check, + 'p|path=s' => \@path, + 'D|default=i' => \$default, + 'h|help' => sub { help(); exit 0; })) { + help(); + exit 1; + } +}; +if ($@) { + print "$@"; + help(); + exit 1; +} + +@path = ('.') + unless (@path); +@path = split(/:/, join(':', @path)); + +my $fh = ($config eq '-') ? \*STDIN : new FileHandle($config) + or die "$config: $!\n"; + +$dir = slashme($dir); + +if ($check) { + # Check for duplicate files, or for files that are not referenced by + # the specification. + + my $problems = 0; + my @files; + + foreach (@path) { + @files = (@files, files_in($dir, slashme($_))); + } + my %files = map { $_ => 0 } @files; + + parse($fh, sub { + my ($patch, @guards) = @_; + if (exists $files{$patch}) { + $files{$patch}++; + } else { + print "Not found: $dir$patch\n"; + $problems++; + }}); + + $fh->close(); + + my ($file, $ref); + while (($file, $ref) = each %files) { + next if $ref == 1; + + print "Unused: $file\n" if $ref == 0; + print "Multiple uses: $file\n" if $ref > 1; + $problems++; + } + exit $problems ? 1 : 0; + +} else { + # Generate a list of patches to apply. + + my %symbols = map { $_ => 1 } @ARGV; + + parse($fh, sub { + my ($patch, @guards) = @_; + + my $selected; + if (@guards) { + # If the first guard is -xxx, the patch is included by default; + # if it is -xxx, the patch is excluded by default. + $selected = ($guards[0] =~ /^-/); + + foreach (@guards) { + /^([-+])(!?)(.*)?/ + or die "Bad guard '$_'\n"; + + # Check if the guard matches + if (($2 eq '!' && !exists $symbols{$3}) || + ($2 eq '' && ( $3 eq '' || exists $symbols{$3}))) { + # Include or exclude + $selected = ($1 eq '+'); + } + } + } else { + # If there are no guards, use the specified default result. + $selected = $default; + } + + print "$dir$patch\n" + if $selected; + }); + + $fh->close(); + + exit 0; +} + +__END__ + +=head1 NAME + +guards - select from a list of files guarded by conditions + +=head1 SYNOPSIS + +F<guards> [--prefix=F<dir>] [--path=F<dir2:dir2:...>] + [--default=I<0>|I<1>] [--check] [--config=F<file>] + I<symbol> ... + +=head1 DESCRIPTION + +The script reads a configuration file that may contain so-called guards, file +names, and comments, and writes those file names that satisfy all guards to +standard output. The script takes a list of symbols as its arguments. Each line +in the ocnfiguration file is processed separately. Lines may start with a +number of guards. The following guards are defined: + +=over + ++I<xxx> Include the file(s) on this line if the symbol I<xxx> is defined. + +-I<xxx> Exclude the file(s) on this line if the symbol I<xxx> is defined. + ++!I<xxx> Include the file(s) on this line if the symbol I<xxx> is not defined. + +-!I<xxx> Exclude the file(s) on this line if the symbol I<xxx> is not defined. + +- Exclude this file. Used to avoid spurious I<--check> messages. + +=back + +The guards are processed left to right. The last guard that matches determines +if the file is included. If no guard is specified, the I<--default> +setting determines if the file is included. + +If no configuration file is specified, the script reads from standard input. + +The I<--check> option is used to compare the specification file against the +file system. If files are referenced in the specification that do not exist, or +if files are not enlisted in the specification file warnings are printed. The +I<--path> option can be used to specify which directory or directories to scan. +Multiple directories are eparated by a colon (C<:>) character. The +I<--prefix> option specifies the location of the files. + +=head1 AUTHOR + +Andreas Gruenbacher <agruen@suse.de> (SuSE Linux AG) + diff --git a/bin/quilt.in b/bin/quilt.in new file mode 100644 index 0000000..d1a6867 --- /dev/null +++ b/bin/quilt.in @@ -0,0 +1,90 @@ +#!/bin/bash + +# This script is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# See the COPYING and AUTHORS files for more details. + +usage() +{ + + echo "Usage: quilt command [-h] ..." >&2 + #echo "Commands are:" $( + # local command + # for command in @QUILT@/* + # do + # if [ -f "$command" -a -x "$command" ] + # then + # echo "${command#@QUILT@/}" + # fi + # done \ + # | sort + #) + echo "Commands are:" + quilt_commands \ + | sort \ + | column | column -t \ + | sed -e 's/^/\t/' + exit 1 +} + +quilt_commands() +{ + local command + for command in @QUILT@/* + do + if [ -f "$command" -a -x "$command" ] + then + echo ${command##@QUILT@/} + fi + done +} + +for arg in "$@" +do + case $arg in + [^-]*) + if [ -z "$command" ] + then + command="$arg" + else + args[${#args[@]}]="$arg" + fi ;; + *) + args[${#args[@]}]="$arg" ;; + esac +done + +if ! [ -f "@QUILT@/$command" -a -x "@QUILT@/$command" ] +then + if [ -n "$command" ] + then + for arg in $(quilt_commands) + do + case "$arg" in + $command*) + commands[${#commands[@]}]="$arg" + ;; + esac + done + fi + + if [ ${#commands[@]} -eq 0 ] + then + usage + elif [ ${#commands[@]} -eq 1 ] + then + command="${commands[0]}" + unset commands + else + echo "$command:" "${commands[@]}" >&2 + exit 1 + fi +fi + +set -- "${args[@]}" +unset arg args + +#. @QUILT@/$command +bash -c ". @QUILT@/$command" "quilt ${command##*/}" "$@" diff --git a/docco.txt b/docco.txt new file mode 100644 index 0000000..1860f13 --- /dev/null +++ b/docco.txt @@ -0,0 +1,717 @@ +Patch management scripts +Andrew Morton <akpm@digeo.com> +18 October 2002 + +This is a description of a bunch of shell scripts which I use for +managing kernel patches. They are quite powerful. They can be used on +projects other than the linux kernel. They are easy to use, and fast. + +You end up doing a ton of recompiling with these scripts, because +you're pushing and popping all the time. ccache takes away the pain of +all that. http://ccache.samba.org/ - be sure to put the cache +directory on the same fs as where you're working so that ccache can use +hardlinks. + +The key philosophical concept is that your primary output is patches. +Not ".c" files, not ".h" files. But patches. So patches are the +first-class object here. + +Installation +============ + +You place all the scripts somewhere in your path, or in +/usr/lib/patch-scripts. + +Terminology +=========== + +The patch scripts require three special directories called "pc", +"patches" and "txt". + +If the environment variable PATCHSCRIPTS is set, it is taken to to be +the directory in which those three directories reside. Typically, it +would be a relative pathname. So + + setenv PATCHSCRIPTS ./i-put-them-here + +would tell the patch scripts to look in ./i-put-them-here/pc, etc. + +If PATCHSCRIPTS is not set, and the directory ./patch-scripts is +present then the patch scripts will us ./patch-scripts/pc/, +./patch-scripts/patches/ and ./patch-scripts/txt/. + +Otherwise, the patch scripts use ./pc, ./patches and ./txt. + +In this document, the symbol $P is used to describe the directory which +holds the pc/, patches/ and txt/ directories, as determined by the +above search. + +It is expected that $P will always expand to a relative path. + +Concepts +======== + +All work occurs with a single directory tree. All commands are invoked +within the root of that tree. The scripts manage a "stack" of patches. + +Each patch is a changeset against the base tree plus the preceding patches. + +All patches are listed, in order, in the file ./series. You manage the +series file. Lines in the series file which start with `#' are ignored. + +Any currently-applied patches are described in the file +./applied-patches. The patch scripts manage this file. + +Each patch affects a number of files in the tree. These files are +listed in a "patch control" file. These .pc files live in the +directory $P/pc/ + +Patches are placed in the directory $P/patches/ + +Documentation for the patches is placed in $P/txt/ + +So for a particular patch "my-first-patch" the following will exist: + +- An entry "my-first-patch.patch" in ./series + +- An entry "my-first-patch" in ./applied-patches (if it's currently applied) + +- A file $P/pc/my-first-patch.pc which contains the names of the + files which my-first-patch modifies, adds or removes + +- A file $P/txt/my-first-patch.txt which contains the patch's + changelog. + +- A file $P/patches/my-first-patch.patch, which is the output of the + patch scripts. + +Operation +========= + +When a patch "my-patch" is applied with apatch, or with pushpatch +(which calls apatch), all the affected files (from $P/pc/my-patch.pc) +are copied to files with ~my-patch appended. So if $P/pc/my-patch.pc +contained + + kernel/sched.c + fs/inode.c + +then apatch will copy those files into kernel/sched.c~my-patch and +fs/inode.c~my-patch. It will then apply the patch to kernel/sched.c +and fs/inode.c + +When a diff is regenerated by refpatch (which calls mpatch), the diff +is made between kernel/sched.c and kernel/sched.c~my-patch. How do the +scripts know to use "~my-patch"? Because my-patch is the current +topmost patch. It's the last line in ./applied-patches. + +In this way, the whole thing is stackable. If you have four patches +applied, say "patch-1", "patch-2", "patch-3" and "patch-4", and if +patch-2 and patch-4 both touch kernel/sched.c then you will have: + + kernel/sched.c~patch-2 Original copy, before patch-2 + kernel/sched.c~patch-4 Copy before patch-4. Contains changes + from patch-2 + kernel/sched.c Current working copy. Contains changes + from patch-4. + +This means that your diff headers contain "~patch-name" in them, which +is convenient documentation. + +Walkthrough +=========== + +Let's start. + +Go into /usr/src/linux (or wherever) + + mkdir pc patches txt + +Now let's generate a patch + + fpatch my-patch kernel/sched.c + +OK, we've copied kernel/sched.c to kernel/sched.c~my-patch. We've +appended "my-patch" to ./applied-patches and we've put "kernel/sched.c" +into the patch control file, pc/my-patch.pc. + + Now edit kernel/sched.c a bit. + +Now we're ready to document the patch + + Now write txt/my-patch.txt + +Now generate the patch + + refpatch + +This will generate patches/my-patch.patch. Take a look. + +Now remove the patch + + poppatch + +applied-patches is now empty, and the patch is removed. + +Now let's add a file to my-patch and then generate my-second-patch: + + Add "my-patch.patch" to ./series (no blank lines in that file please) + + pushpatch + +OK, the patch is applied again. Let's add another file + + fpatch kernel/printk.c + +Note that here we gave fpatch a single argument. So rather than +opening a new patch, it adds kernel/printk.c to the existing topmost +patch. That's my-patch. + + Edit kernel/printk.c + +Refresh my-patch (you end up running refpatch a lot) + + refpatch + +Now start a second patch: + + fpatch my-second-patch kernel/sched.c + +Now take a look at applied-patches. Also do an `ls kernel/sched*'. + + Edit kernel/sched.c, to make some changes for my-second-patch + +Generate my-second-patch: + + refpatch + +Take a look in patches/my-second-patch.patch + +Don't forget to add "my-second-patch.patch" to the series file. + +And remove both patches: + + poppatch + poppatch + + +That's pretty much it, really. + + +Command reference +================= + +Generally, where any of these commands take a "patch-name", that can be +of the form txt/patch-name.txt, patch-name.pc, just patch-name or +whatever. The scripts will strip off a leading "txt/", "patches/" or +"pc/" and any trailing extension. This is so you can do + + apatch patches/a<tab> + +to conveniently use shell tabbing to select patch names. + + + +added-by-patch + + Some internal thing. + +apatch [-f] patch-name + + This is the low-level function which adds patches. It does the + copying into ~-files and updates the applied-patches file. It + applies the actual patch. + + apatch will do a patch --dry-run first and will refuse to apply the + patch if the dryrun fails. + + So when you are getting rejects you do this: + + pushpatch # This fails, due to rejects. Drat. + apatch -f patch-name # Force the patch + (or) pushpatch -f # Force the patch + + OK, you've now applied patch-name, but you have rejects. Go fix + those up and do + + refpatch + + And you're ready to move on. + +combine-series output-file + + It incrementally combinediffs all the patches in series to make a + complete patch for the series. Requires combinediff frmo patchutils. + + See http://cyberelk.net/tim/patchutils/ (Don't download the + "experimental" patchutils - it seems to only have half of the + commands in it. Go for "stable") + +cvs-take-patch + + I forget. + +export_patch + + export the patches listed in ./series to a set of files which + are named in such a way that the sort order is the same as the + order of the series file. + + Usage: export_patch directory [prefix] + + Example: + + Suppose ./series contains + + mango.patch + orange.patch + banana.patch + apple.patch + pear.patch + + export_patch ../mypatches fruit + + The patches would be copied to + + ../mypatches/p00001_fruit_mango.patch + ../mypatches/p00002_fruit_orange.patch + ../mypatches/p00003_fruit_banana.patch + ../mypatches/p00003_fruit_banana.patch + ../mypatches/p00003_fruit_banana.patch + + Named in this way, someone may easily apply them: + + cat mypatches/p*fruit* | patch -p1 + + If prefix is omitted, the patchnames will be transformed + such that "original.patch" becomes "pXXXXX_original.patch". + +fpatch [patch-name] foo.c + + If patch-name is given, fpatch will start a new patch which + modifies (or adds, or removes) the single file foo.c. It updates + ./applied-patches and creates pc/patch-name.pc. fpatch will copy + foo.c to foo.c~patch-name in preparation for edits of foo.c. + + If patch-name is not given then fpatch will add foo.c to the + current topmost patch. It will add "foo.c" to $P/pc/$(toppatch).pc. + It will copy foo.c to foo.c~$(toppatch). + +import_patch + + Imports a set of patch files, creating $P/pc, $P/txt, $P/patches and + ./series as necessary. It also creates $P/txt/*.txt by stripping + off the top of the patches (and removes any diffstat output it finds, + so that it can eat refpatch output and export_patch output.) The + imported patch names are appended to the series file. + + In creating the $P/txt/*.txt files, mail headers are stripped with + formail, preserving the "From:" and "Subject:" lines. "DESC" and + "EDESC" markers are added if they are not already present, using the + "From:" and "Subject:" lines for the DESC portion, if they are present. + (See "patchdesc" command, below, for more on these markers.) + + Also, it can rename the patch file as it is imported by stripping out + a pattern. This is useful if, as often is the case, you have patch + sets with filenames designed to help sort the patches into the correct + order, such as "p001_xxx_funky_stuff.patch" you can have it automatically + renamed to funky_stuff.patch on import, and let the series file manage + the ordering. + + Import_patch will uncompress patches (*.Z, *.bz2, *.gz) as necessary. + + Usage: + + import_patch [-p pattern] patchfile ... + + Example: + + % ls ../fruit/p*patch + ../fruit/p00001_northern_apple.patch + ../fruit/p00001_tropical_mango.patch + ../fruit/p00002_northern_pear.patch + ../fruit/p00002_tropical_orange.patch + ../fruit/p00003_tropical_banana.patch + % import_patch -p 'p[0-9]*_tropical_' ../fruit/p*tropical* + Recreated pc/mango.pc + Recreated pc/orange.pc + Recreated pc/banana.pc + % import_patch -p 'p[0-9]*_northern_' ../fruit/p*northern* + Recreated pc/apple.pc + Recreated pc/pear.pc + + Then you can "pushpatch; refpatch" 5 times. + +inpatch + + List the names of ths files which are affected by the current + topmost patch. + + This is basically + + cat pc/$(toppatch).pc + +join-patch patchname + + "joins" the named patch to the current topmost patch. + + Use this when you want to merge two patches into one. All the + files which `patchname' affects are added to pc/$(toppatch).pc (if + they are not already there) and patch `patchname' is applied. The + top patch remains unchanged. You'll need to run refpatch afterwards. + +mpatch + + A low-level thing to generate patches + +new-kernel + + Some thing I use for importing a new kernel from kernel.org + +p0-2-p1 + + Internal thing to convert patch -p0 form into patch -p1 + +patchdesc + + Generates a single-line description of a patch. + + The txt/my-patch.txt files have the following format: + + <start of file> + DESC + some short description + EDESC + + The long description + <end of file> + + I use + + patchdesc $(cat series) + + to generate short-form summaries of the patch series. + +patchfns + + Internal utilities + +pcpatch + + Standalone tool to generate a .pc file from a patch. + + Say someone sends you "his-patch.diff". What you do is: + + cp ~/his-patch.diff patches/his-patch.patch + pcpatch his-patch + + This generates $P/pc/his-patch.pc and you're all set. Add + "his-patch.patch" to ./series in the right place and start pushing. + +p_diff + + I forget + +poppatch + + Remove one or more patches from the current stack. This command + does *not* use the series file. It works purely against + applied-patches. + + Usage: + + poppatch + Remove the topmost patch + poppatch 10 + Remove ten patches + poppatch some-patch-name[.patch] + Remove patches until "some-patch-name" is top patch + +pstatus + + Shows status of patches + + Usage: + pstatus [patchfile ...] + + One line per patch is output showing: + 1: Patch number in the series file + 2: Whether the patch is currently applied + 3: Name of patch + 4: Status of the patch (needs pcpatch, changelog, refpatch) + + If no patchfiles are specified, $P/patches/*.patch + are assumed. + + Caveats: + A patch set which contains separate patches to add a file + and modify that same file may give spurious "Needs refpatch" + status for the patch which adds the file or the topmost patch. + +ptkdiff + + Two modes: + + ptkdiff - + + Run tkdiff against all the file affected + by $(toppatch). The diff is only for the changes made + by the top patch! ie: it's between "filename" and + "filename~toppatch-name". + + ptkdiff filename + + Just run tkdiff against that file, + showing the changes which are due to toppatch. + +pushpatch [-f] + + Apply the next patch, from the series file. + + This consults ./applied-patches to find out the top patch, then + consults ./series to find the next patch. And pushes it. + + pushpatch + + Apply the next patch + + pushpatch 10 + + Apply the next ten patches + + pushpatch some-patch-name + + Keep pushing patches until "some-patch-name" is toppatch + + pushpatch -f + + Push the next patch, ignoring rejects. + +refpatch + + regnerates the topmost patch. Reads all the affected files + from pc/$(toppatch).pc and diffs them against their tilde-files. + + Also pastes into the patch your patch documentation and + generates a diffstat summary. + +removed-by-patch + + Some thing. + +rename-patch + + CVS rename for patches. + +rolled-up-patch + + Bit of a hack. Is designed to generate a rolled-up diff of all + currently-applied patches. But it requires a ../linux-2.x.y tree to + diff against. Needs to be redone. + +rpatch + + Internal command + +split-patch + + Some thing someone write to split patches up. I don't use it. + +tag-series + + Assuming you keep pc/*, patches/* and txt/* under CVS revision + control, tag-series allows you to tag a patchset's individual + components. I use + + tag-series s2_5_44-mm3 pc/2.5.44-mm3-series + + which will attach the cvs tag "s2_5_44-mm3" to every .pc, .patch + and .txt file which is mentioned in the series file + "pc/2.5.44-mm3-series". + + It will also tag pc/2.5.44-mm3-series, which is a bit redundant + given that I use a different series file for each patchset release.. + + +toppatch + + Print the name of the topmost patch. From ./applied-patches + +touched-by-patch patch-filename + + List the names of files which are affected by a diff. + +unitdiff.py + + Rasmus Andersen's script to convert a diff into minimum-context + form. This form has a better chance of applying if you're getting + nasty rejects. But patch can and will make mistakes when fed + small-context input. + + +Work Practices +============== + +I keep the kernel tree, the $P/pc/, $P/patches/ and $P/txt/ contents under +CVS control. This is important... + +I have several "series" files. I keep these in $P/pc/foo-series and use + + ln -s pc/foo-series series + +when I'm working on foo. + +If someone sends me a patch I'll do: + + cp ~/whatever patches/his-patch.patch + pcpatch his-patch + apatch his-patch + + If apatch fails then run `apatch -f his-patch' and fix the rejects. + + refpatch + + to clean up any fuzz. + + poppatch + cvs add pc/his-patch.pc patches/his-patch.patch + cvs commit pc patches + + Now edit ./series and place "his-patch.patch" in the appropriate place. + + +If you're working on a particular patch (say, "dud-patch") and you +balls something up, just run: + + refpatch # Generate the crap patch + poppatch # Remove it all + rm patches/dud-patch.patch + cvs up patches/dud-patch.patch + +and all is well. + + +Getting updates from Linus +========================== + +What I do is to grab the latest -bk diff from +http://www.kernel.org/pub/linux/kernel/people/dwmw2/bk-2.5/ +and do: + + gzip -d < cs<tab> > patches/linus.patch + pcpatch linus + apatch linus | grep diff + + Now fix up all the files which got deleted, + because there's something wrong with bitkeeper diffs: + + cvs up -ko <missing files from the above diff> + + apatch linus + $EDITOR linus/linus.txt + + Add the changeset number to txt/linus.txt + + refpatch + poppatch + + Now add "linus.patch" as the first entry in your ./series file and + start pushing your other patches on top of that. + +BUGS +==== + +Tons and tons. The scripts are fragile, the error handling is ungraceful and +if you do something silly you can end up in a pickle. + +Generally the scripts are very careful to not wreck your files or your +patches. But they can get the ./applied-patches and ~-files into an +awkward state. + +Usually you can sort it out by copying the ~-files back onto the originals +and removing the last line from ./applied-patches. Or do a "refpatch ; +poppatch ; rm patches/troublesome-patch.patch ; cvs up patches". + +If it's really bad, just blow away the entire tree and do a new CVS checkout. + + +Working on non-kernel projects +============================== + +Well it's the same thing. Say you've downloaded a copy of util-linux +and you want to make a change: + + cd /usr/src + tar xvfz ~/util-linux.tar.gz + cd util-linux + mkdir pc patches txt + fpatch my-patch sys-utils/rdev.c + fpatch sys-utils/ipcs.8 + <edit, edit> + refpatch + <ship patches/my-patch.patch> + +How to balls things up +====================== + +Well here's one way. Suppose you have 20 patches applied, and three of +them (say, "p1", "p6" and "p11") all modify "foo.c". + +Now you go and change foo.c. + +Well, to which patch does that change belong? You need to decide. +Let's say you decide "p6". + +If you run `refpatch' when "p11" is toppatch then you lose. The diff +went into p11. + +What you can do is: + +1: + poppatch p6 + <edit> + refpatch + pushpatch p11 + <test> + + (See why ccache is looking good?) + +or + +2: + <edit> + <test> + poppatch p6 <hope like hell that the other patches remove cleanly> + refpatch + + +Another good way of ballsing up is to cheat. Say "oh I just want to make +this one-line change". And "oh, and this one". + +Now you're getting in a mess. It's much, much better to just use the system: + + fpatch junk file1 + fpatch file2 + <edit> + <play> + refpatch + poppatch + rm pc/junk.pc patches/junk.patch + +Merging with -mm kernels +======================== + +Haven't tried this, but it should work: + +- Grab all the patches from broken-out/, place them in your $P/patches/ + +- Copy my series file into ./series (or $P/pc/akpm-series and symlink it) + +- pushpatch 99 + +And you're off and running. The nice thing about this is that you can +send me incremental diffs to diffs which I already have. + +Or whatever. I'm fairly handy with diffs nowadays. Rejects are +expected. I just prefer to have "one concept per diff". + diff --git a/inpatch.in b/inpatch.in deleted file mode 100755 index f3c3526..0000000 --- a/inpatch.in +++ /dev/null @@ -1,143 +0,0 @@ -#! /bin/bash - -# This script is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# See the COPYING and AUTHORS files for more details. - -# Read in library functions -if ! [ -r @LIB@/patchfns ] -then - echo "Cannot read library @LIB@/patchfns" >&2 - exit 1 -fi -. @LIB@/patchfns - -usage() -{ - echo "Usage: inpatch [patchname]" - echo " inpatch -f filename" - if [ x$1 = x-h ] - then - cat <<EOF - -Print the list of files that the topmost or specified patch -changes. - --f Print which patches change the specified file. - -EOF - exit 0 - else - exit 1 - fi -} - -options=`getopt -o f:h -- "$@"` - -if [ $? -ne 0 ] -then - usage -fi - -eval set -- "$options" - -while true -do - case "$1" in - -f) - opt_file=$2 - shift 2 ;; - -h) - usage -h ;; - --) - shift - break ;; - esac -done - -if [ $# -gt 1 ] -then - usage -fi -opt_patch=$1 - -scan_patches() -{ - local patches=$* patch - for patch in $patches - do - refresh_file_list $patch - status=$? - if [ $status -eq 2 ] - then - if [ -z "$opt_quiet" ] - then - echo "Recreated file list for $patch" >&2 - fi - elif [ $status -ne 0 ] - then - echo "Could not create file list for patch $patch" >&2 - return 1 - fi - done - - grep -l -e "^$(quote_re $opt_file)\$" $(pc_file_name $patches) \ - | sed -e 's:^\.pc/::' -e 's:/\.pc$::' -} - -if [ -n "$opt_patch" ] -then - patch=$(stripit "$opt_patch") -else - patch=$(top_patch) - if [ -z "$patch" ] - then - echo "No patches applied" >&2 - exit 1 - fi -fi - -if [ -z "$opt_file" ] -then - if ! is_applied $patch - then - if ! patch_in_series - then - echo "Patch $patch not in series file" >&2 - else - echo "Patch is not applied (no old/new status)" >&2 - no_status=1 - fi - fi - - for file in $(files_in_patch $patch) - do - if [ -n "$no_status" -o -s $(backup_file_name $patch $file) ] - then - echo "$file" - else - echo "$file (new)" - fi - done -else - #if [ -n "$opt_patch" ] - #then - # scan_patches $(patches_before $opt_patch) | sed -e 's/^/> /' - # scan_patches $opt_patch | sed -e 's/^/| /' - # scan_patches $(patches_after $opt_patch) | sed -e 's/^/< /' - #else - # series=$(cat_series) - # if [ -n "$series" ] - # then - # scan_patches $series | sed -e 's/^/< /' - # fi - #fi - series=$(cat_series) - if [ -n "$series" ] - then - scan_patches $series - #| sed -e 's/^'"$(quote_re $patch)"'$/\0 (here)/' - fi -fi diff --git a/lib/apatch.in b/lib/apatch.in index 0e0c366..76d9dc7 100755 --- a/lib/apatch.in +++ b/lib/apatch.in @@ -1,4 +1,4 @@ -#! /bin/bash +#!/bin/bash # This script is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as @@ -6,48 +6,85 @@ # # See the COPYING and AUTHORS files for more details. -if ! [ -r @LIB@/patchfns ] +# Read in library functions +if [ "$(type -t patch_file_name)" != function ] then - echo "Cannot read library @LIB@/patchfns" >&2 - exit 1 + if ! [ -r @LIB@/patchfns ] + then + echo "Cannot read library @LIB@/patchfns" >&2 + exit 1 + fi + . @LIB@/patchfns fi -. @LIB@/patchfns usage() { - echo "Usage: apatch [-fq] patchname" + echo "Usage: $0 [-fqv] patchname" exit 1 } +rollback_patch() +{ + local patch=$1 pc_file=$(pc_file_name $patch) + @LIB@/backup-files $silent_unless_verbose \ + -f $pc_file -B .pc/$patch/ -r + rm -f $(files_in_patch $patch | sed -e 's/$/\.rej/') +} + interrupt() { - local pc_file=$(pc_file_name $1) - @LIB@/backup-files -s -f $pc_file -B .pc/$patch/ -r - echo "apatch interrupted by user" + rollback_patch $1 + echo "Interrupted by user; patch $patch was not applied." exit 1 } apply_patch() { + local patch=$1 + local patch_file=$(patch_file_name $patch) + + if ! [ -s $patch_file ] + then + echo "Patch file $patch_file appears to be empty" + return 0 + fi + + if [ "x${patch_file:(-3)}" = "x.gz" ] + then + gzip -cd $patch_file \ + | patch $(patch_args $patch) --no-backup-if-mismatch \ + -E $silent + elif [ "x${patch_file:(-4)}" = "x.bz2" ] + then + bzip2 -cd $patch_file \ + | patch $(patch_args $patch) --no-backup-if-mismatch \ + -E $silent + else + patch $(patch_args $patch) --no-backup-if-mismatch \ + -E $silent -i $patch_file + fi +} + +apatch() +{ local patch=$(stripit $1) local pc_file=$(pc_file_name $patch) - local patch_file=$(patch_file_name $patch) local file status - if ! [ -e "$patch_file" ] + trap "" SIGINT + if ! refresh_file_list $patch then - echo "No patch named $patch found." + echo "refresh_file_list failed" return 1 fi - #if is_applied "$patch" - #then - # echo "$patch" is already applied - # return 1 - #fi - - trap "" SIGINT - refresh_file_list $patch + if ! [ -e $pc_file ] + then + echo "Patch $patch appears to be empty, applied" + add_to_db $patch + return 0 + fi + status=$? if [ $status -eq 2 ] then @@ -57,21 +94,20 @@ apply_patch() return 1 fi - if ! @LIB@/backup-files -s -f $pc_file -B .pc/$patch/ + if ! @LIB@/backup-files $silent_unless_verbose \ + -f $pc_file -B .pc/$patch/ then exit 1 fi trap "interrupt $patch" SIGINT - # (patch -b fails if a file appears more than once in a patch!) - #patch $(patch_args $patch) --no-backup-if-mismatch \ - # -E $silent -i $patch_file - patch $(patch_args $patch) --no-backup-if-mismatch \ - -E $silent -i $patch_file + + apply_patch $patch status=$? + trap "" SIGINT - # Remember date/time of applying so that poppatch can + # Remember date/time of applying so that pop can # avoid reverse applying the patch in the usual cases. touch $pc_file @@ -81,20 +117,20 @@ apply_patch() if [ $status -eq 0 ] then echo "Applied $patch" - rm -f $(pc_file_name $patch)~forced + rm -f $pc_file~refresh else - touch $(pc_file_name $patch)~forced - echo "Applied $patch (forced; needs refpatch)" + touch $pc_file~refresh + echo "Applied $patch (forced; needs refresh)" fi else - @LIB@/backup-files -s -f $pc_file -B .pc/$patch/ -r - echo "Patch $patch does not apply" + rollback_patch $patch + echo "Patch $patch does not apply (enforce with -f)" fi trap - SIGINT return $status } -options=`getopt -o fqh -- "$@"` +options=`getopt -o fqvh -- "$@"` if [ $? -ne 0 ] then @@ -112,6 +148,9 @@ do -q) opt_quiet=1 shift ;; + -v) + opt_verbose=1 + shift ;; -h) usage -h ;; --) @@ -126,16 +165,15 @@ then fi [ -n "$opt_quiet" ] && silent=-s +[ -z "$opt_verbose" ] && silent_unless_verbose=-s patch=$(stripit $1) top=$(top_patch) -if [ -n "$top" -a -e $(pc_file_name $top)~forced ] +if [ -n "$top" -a -e $(pc_file_name $top)~refresh ] then - echo "The topmost patch $top was force applied. Please run" \ - "refpatch before applying other patches." + echo "The topmost patch $top needs to be refreshed first." exit 1 fi -apply_patch $patch - +apatch $patch diff --git a/lib/backup-files.c b/lib/backup-files.c index b9f1b21..c6e0c6d 100644 --- a/lib/backup-files.c +++ b/lib/backup-files.c @@ -1,14 +1,29 @@ -/* backup-files.c - Andreas Gruenbacher, 18 January 2003 +/* + File: backup-files.c + + Copyright (C) 2003 Andreas Gruenbacher <agruen@suse.de> + SuSE Labs, SuSE Linux AG + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Library 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 + Library General Public License for more details. - `patch -b' fails to back up files correctly if a file occurs - more than once in a patch, so we use this utility instead. + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* - - catch SIGINT - - remove backup files and directories -*/ + * Create backup files of a list of files similar to GNU patch. A path + * name prefix and suffix for the backup file can be specified with the + * -B and -Z options. + */ #include <sys/types.h> #include <sys/stat.h> @@ -20,10 +35,18 @@ const char *progname; +enum { what_backup, what_restore, what_remove }; + +const char *opt_prefix="", *opt_suffix="", *opt_file=NULL; +int opt_silent=0, opt_what=what_backup; + +#define LINE_LENGTH 1024 + + void usage(void) { - printf("Usage: %s [-B prefix] [-z suffix] [-f filelist] [-s] [-r|-x]\n" + printf("Usage: %s [-B prefix] [-z suffix] [-f {filelist|-}] [-s] [-r|-x] filename ...\n" "\n" "\tCreate hard linked backup copies of a list of files\n" "\tread from standard input.\n" @@ -60,26 +83,96 @@ create_parents(char *filename) void remove_parents(char *filename) { - char *f; + char *f, *g = NULL; + f = strrchr(filename, '/'); while ((f = strrchr(filename, '/')) != NULL) { - *f = '\0'; + if (g != NULL) + *g = '/'; + g = f; + *f= '\0'; + rmdir(filename); } + if (g != NULL) + *g = '/'; } -enum { what_backup, what_restore, what_remove }; +int +process_file(char *file) +{ + char backup[LINE_LENGTH]; -#define LINE_LENGTH 1024 + if (strlen(opt_prefix) + strlen(file) + + strlen(opt_suffix) >= sizeof(backup)) { + perror("Line buffer too small\n"); + return 1; + } + + snprintf(backup, sizeof(backup), "%s%s%s", + opt_prefix, file, opt_suffix); + + if (opt_what == what_backup) { + int fd; + create_parents(backup); + + unlink(backup); + if (link(file, backup) == 0) { + if (!opt_silent) + printf("Copying %s\n", file); + } else if ((fd = creat(backup, 0666)) != -1) { + close(fd); + if (!opt_silent) + printf("New file %s\n", file); + } else { + perror(backup); + return 1; + } + return 0; + } else if (opt_what == what_restore) { + struct stat st; + create_parents(file); + + if (stat(backup, &st) != 0) { + perror(backup); + return 1; + } + if (st.st_size == 0) { + if (unlink(file) == 0 || errno == ENOENT) { + if (!opt_silent) + printf("Removing %s\n", file); + unlink(backup); + remove_parents(backup); + } else { + perror(file); + return 1; + } + } else { + unlink(file); + if (link(backup, file) == 0) { + if (!opt_silent) + printf("Restoring %s\n", file); + unlink(backup); + remove_parents(backup); + } else { + fprintf(stderr, "Could not restore " + "file `%s' to `%s': %s\n", + backup, file, strerror(errno)); + return 1; + } + } + return 0; + } else if (opt_what == what_remove) { + unlink(backup); + remove_parents(backup); + return 0; + } else + return 1; +} int main(int argc, char *argv[]) { - char orig[LINE_LENGTH], *o, backup[LINE_LENGTH]; - FILE *file; - - const char *opt_prefix="", *opt_suffix="", *opt_file=NULL; - int opt_silent=0, opt_what=what_backup; int opt, status=0; progname = argv[0]; @@ -117,95 +210,44 @@ main(int argc, char *argv[]) } } - if ((*opt_prefix == '\0' && *opt_suffix == '\0') || optind != argc) { + if ((*opt_prefix == '\0' && *opt_suffix == '\0') || + (opt_file == NULL && optind == argc)) { usage(); return 1; } if (opt_file != NULL) { - if ((file = fopen(opt_file, "r")) == NULL) { - perror(opt_file); - return 1; - } - } else { - file = stdin; - } - - while (fgets(orig, sizeof(orig), file)) { - if (strlen(opt_prefix) + strlen(orig) + strlen(opt_suffix) >= - sizeof(backup)) { - perror("Line buffer too small\n"); - return 1; - } - - o = strchr(orig, '\0'); - if (o > orig && *(o-1) == '\n') - *(o-1) = '\0'; - if (*orig == '\0') - continue; - - snprintf(backup, sizeof(backup), "%s%s%s", - opt_prefix, orig, opt_suffix); + FILE *file; + char line[LINE_LENGTH]; - if (opt_what == what_backup) { - int fd; - create_parents(backup); - - if (link(orig, backup) == 0) { - if (!opt_silent) - printf("Copying %s\n", orig); - } else if ((fd = creat(backup, 0666)) != -1) { - close(fd); - if (!opt_silent) - printf("New file %s\n", orig); - } else { - perror(backup); + if (!strcmp(opt_file, "-")) { + file = stdin; + } else { + if ((file = fopen(opt_file, "r")) == NULL) { + perror(opt_file); return 1; } - } else if (opt_what == what_restore) { - struct stat st; + } - create_parents(orig); + while (fgets(line, sizeof(line), file)) { + char *l = strchr(line, '\0'); - if (stat(backup, &st) != 0) { - perror(backup); - status=1; + if (l > line && *(l-1) == '\n') + *(l-1) = '\0'; + if (*line == '\0') continue; - } - if (st.st_size == 0) { - if (unlink(orig) == 0 || errno == ENOENT) { - if (!opt_silent) - printf("Removing %s\n", orig); - unlink(backup); - remove_parents(backup); - } else { - if (errno != ENOENT) { - perror(orig); - status=1; - } - } - } else { - unlink(orig); - if (link(backup, orig) == 0) { - if (!opt_silent) - printf("Restoring %s\n", orig); - unlink(backup); - remove_parents(backup); - } else { - fprintf(stderr, "Could not restore " - "file `%s' to `%s': %s\n", - backup, orig, strerror(errno)); - status=1; - } - } - } else { - unlink(backup); - remove_parents(backup); + + if ((status = process_file(line)) != 0) + return status; } - } - if (file != stdin) { - fclose(file); + if (file != stdin) { + fclose(file); + } + } + for (; optind < argc; optind++) { + if ((status = process_file(argv[optind])) != 0) + return status; } return status; diff --git a/lib/parse-patch b/lib/parse-patch index b4dded2..9d38e36 100755 --- a/lib/parse-patch +++ b/lib/parse-patch @@ -1,10 +1,17 @@ #!/usr/bin/perl -w +# This script is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# See the COPYING and AUTHORS files for more details. + # Extract or update a section from a combined patch + documentation + # meta information file. use FileHandle; use Getopt::Long; +use File::Temp qw(tempfile); use strict; my $select; @@ -20,7 +27,9 @@ if (!GetOptions("s|select=s" => \$select, foreach my $arg (@ARGV) { my $fh; - if ($arg =~ /\.gz$/) { + if (! -e $arg) { + $fh = new FileHandle("/dev/null"); + } elsif ($arg =~ /\.gz$/) { $fh = new FileHandle("gzip -cd $arg |"); } elsif ($arg =~ /\.bz2$/) { $fh = new FileHandle("bzip2 -cd $arg |"); @@ -53,31 +62,43 @@ foreach my $arg (@ARGV) { } } } elsif (defined $update) { - my $fh2; + my ($fh2, $tempname) = tempfile("$arg.XXXXXX"); if ($arg =~ /\.gz$/) { - $fh2 = new FileHandle("| gzip -c > $arg.parse"); + $fh2->close(); + if (! -e $tempname) { + die "File $tempname disappeared!\n"; + } + $fh2 = new FileHandle("| gzip -c > $tempname"); } elsif ($arg =~ /\.bz2$/) { - $fh2 = new FileHandle("| bzip2 -c > $arg.parse"); - } else { - $fh2 = new FileHandle("$arg.parse", O_CREAT|O_WRONLY); + $fh2->close(); + if (! -e $tempname) { + die "File $tempname disappeared!\n"; + } + $fh2 = new FileHandle("| bzip2 -c > $tempname"); } unless ($fh2) { - die "$arg.parse: $!\n"; + die "$tempname: $!\n"; } # Copy things before updated section + my $last_was_newline=1; # start first section in first line while (<$fh>) { if (/^%(.*)/ && $1 eq $update) { last; } + $last_was_newline = ($_ eq "\n"); print $fh2 $_; } + print $fh2 "\n" + unless ($last_was_newline); + # Create/replace updated section print $fh2 "%$update\n"; while (<STDIN>) { print $fh2 $_; } print $fh2 "\n"; + # Skip obsolete section while (<$fh>) { if (/^%(.*)/) { @@ -93,11 +114,14 @@ foreach my $arg (@ARGV) { die "$arg.patch: $!\n"; } - unlink "$arg~"; - unless (rename $arg, "$arg~") { - die "Failed to rename $arg to $arg~: $!\n"; + if (-e $arg) { + unlink "$arg~"; + unless (rename $arg, "$arg~") { + die "Failed to rename $arg to $arg~: $!\n"; + } } - unless (rename "$arg.parse", $arg) { + unless (rename $tempname, $arg) { + rename("$arg~", $arg); die "Failed to rename $arg.parse to $arg: $!\n"; } } diff --git a/lib/patchfns.in b/lib/patchfns.in index 63fae45..17c5b56 100644 --- a/lib/patchfns.in +++ b/lib/patchfns.in @@ -1,8 +1,10 @@ -debug() -{ - #echo "$@" >&2 - true -} +#!/bin/bash + +# This script is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# See the COPYING and AUTHORS files for more details. if [ -n "$PATCHSCRIPTS" ] then @@ -23,20 +25,36 @@ fi DB=".pc/applied-patches" +# Define where diffstat lives (may be missing) +DIFFSTAT=/usr/bin/diffstat + quote_re() { echo "$1" | sed -e 's:\([/.+*\[\\]\):\\\1:g' } +#basename() +#{ +# local path=${1%/} +# echo "${path/*\/}" +#} + +#dirname() +#{ +# local path=${1%/} +#} + patch_file_name() { local patch=$1 if [ -e $SERIES ] then - awk '/^'"$(quote_re $patch)"'(|\.patch|\.diff?)([ \t]|$)/ \ - { printf "'"$P"'patches/%s\n", $1; exit }' \ - $SERIES + awk '/^'"$(quote_re $patch)"'(|\.patch|\.diff?)(|\.gz|\.bz2)([ \t]|$)/ \ + { printf "'"$P"'patches/%s\n", $1 + exit + } + ' $SERIES fi } @@ -47,13 +65,16 @@ patch_args() if [ -e $SERIES ] then - awk '/^'"$(quote_re $patch)"'(|\.patch|\.diff?)([ \t]|$)/ \ + awk ' + /^'"$(quote_re $patch)"'(|\.patch|\.diff?)(|\.gz|\.bz2)([ \t]|$)/ \ { if (NF >= 2) for (i=2; i <= NF; i++) - print $i ; - else print "-p1" ; - exit }' \ - $SERIES + print $i + else + print "-p1" ; + exit + } + ' $SERIES fi } @@ -87,20 +108,24 @@ change_db_strip_level() if [ -e $SERIES ] then - local tmp=$(mktemp /tmp/patch-scripts.XXXXXX) - awk '/^'"$(quote_re $patch)"'(\.patch|\.diff?)([ \t]|$)/ \ - {for(i=2; i<=NF; i++) - if ($i ~ /^-p/) - {$i="'"$level"'" ; break} - if (i > NF) - $i="'"$level"'"} - {print}' \ - $SERIES > $tmp - if cmp $SERIES $tmp >/dev/null 2>/dev/null + local tmpfile=$(mktemp /tmp/patch-scripts.XXXXXX) + awk ' + /^'"$(quote_re $patch)"'(\.patch|\.diff?)(|\.gz|\.bz2)([ \t]|$)/ \ + { for(i=2; i<=NF; i++) + if ($i ~ /^-p/) { + $i="'"$level"'" + break + } + if (i > NF) + $i="'"$level"'" + } + { print } + ' $SERIES > $tmpfile + if cmp $SERIES $tmpfile >/dev/null 2>/dev/null then - rm -f $tmp + rm -f $tmpfile else - mv -f $tmp $SERIES + mv -f $tmpfile $SERIES fi else return 1 @@ -115,7 +140,7 @@ patch_in_series() then return 1 else - grep -q '^'"$(quote_re $patch)"'\(\|\.patch\|.diff\?\)\([ \t]\|$\)' $SERIES + grep -q '^'"$(quote_re $patch)"'\(\|\.patch\|.diff\?\)\(\|\.gz\|\.bz2\)\([ \t]\|$\)' $SERIES fi } @@ -123,31 +148,54 @@ patch_in_series() insert_in_series() { local patch=$1 patch_args=$2 - local top=$(top_patch) tmp + local top=$(top_patch) tmpfile if [ -n "$patch_args" ] then patch_args=" $patch_args" fi - tmp=$(mktemp /tmp/patch-scripts.XXXXXX) || return 1 + tmpfile=$(mktemp /tmp/patch-scripts.XXXXXX) || return 1 + mkdir -p $(dirname $SERIES) if [ -n "$top" ] then - awk ' { print } - /^'"$(quote_re $top)"'(|\.patch|\.diff?)([ \t]|$)/ \ - { print "'"$patch$patch_args"'" }' \ - < $SERIES \ - > $tmp \ - || return 1 + awk ' + { print } + /^'"$(quote_re $top)"'(|\.patch|\.diff?)(|\.gz|\.bz2)([ \t]|$)/ \ + { print "'"$patch$patch_args"'" } + ' $SERIES > $tmpfile + status=$? + if [ $status -ne 0 ] + then + rm -f $tmpfile + return 1 + fi else - echo $patch$patch_args > $tmp + echo $patch$patch_args > $tmpfile if [ -e $SERIES ] then - cat $SERIES >> $tmp + cat $SERIES >> $tmpfile fi fi - mkdir -p $(dirname $SERIES) - mv -f $tmp $SERIES + mv -f $tmpfile $SERIES +} + +remove_from_series() +{ + local patch=$1 + + tmpfile=$(mktemp /tmp/patch-scripts.XXXXXX) || return 1 + awk ' + ! /^'"$(quote_re $patch)"'(|\.patch|\.diff?)(|\.gz|\.bz2)([ \t]|$)/ \ + { print } + ' $SERIES > $tmpfile + if [ $? -eq 0 ] + then + mv -f $tmpfile $SERIES + else + rm -f $tmpfile + return 1 + fi } pc_file_name() @@ -170,34 +218,23 @@ backup_file_name() done } -__cat_series() +cat_series() { - local series=$1 - if [ -e $series ] + if [ -e $SERIES ] then sed -e '/^#/d' -e 's/^[ \t]*//' -e 's/[ \t].*//' \ - -e 's/\.patch$//' -e 's/\.diff\?//' $series + -e 's/\.gz$//' -e 's/\.bz2$//' \ + -e 's/\.patch$//' -e 's/\.diff\?$//' $SERIES else return 1 fi } -cat_series() -{ - __cat_series $SERIES -} - top_patch() { [ -e $DB ] && tail -1 $DB } -die() -{ - echo "error: $*" - exit 1 -} - is_numeric() { echo $1 | grep -q -e '^[0-9]*$' @@ -219,17 +256,30 @@ is_applied() patches_before() { local patch=$1 - cat_series \ - | awk '$0 == "'"$patch"'" { exit } - { print }' + + if [ -n "$patch" ] + then + cat_series \ + | awk ' + $0 == "'"$patch"'" { exit } + { print } + ' + fi } patches_after() { local patch=$1 - cat_series \ - | awk 'seen { print } - $0 == "'"$patch"'" { seen=1 }' + if [ -n "$patch" ] + then + cat_series \ + | awk ' + seen { print } + $0 == "'"$patch"'" { seen=1 } + ' + else + cat_series + fi } # List all patches that have been applied on top of patch $1 @@ -237,8 +287,10 @@ patches_on_top_of() { local patch=$1 [ -e $DB ] || return - awk '$0 == "'"$patch"'" { seen=1 ; next } - seen { print }' $DB + awk ' + $0 == "'"$patch"'" { seen=1 ; next } + seen { print } + ' $DB } # Print the name of the patch that modified the file $2 next after @@ -255,30 +307,106 @@ next_patch_for_file() | head -1 \ | sed -e 's:^\.pc/::' -e 's:/\.pc$::' fi + + #modified_files $file -- $patches_on_top \ + #| cut -d $'\t' -f2 \ + #| cut -d ' ' -f1 } -can_apply() +# Create a list of files and the patches that modify them. +refresh_patches_per_file() { - local silent - if [ "x$1" == "x-s" ] + local pc_files=$(pc_file_name $(cat_series)) + local ex_pc_files pc_file + + if [ -e .pc/patches-per-file ] then - silent=-s - shift + local needs_refresh + for pc_file in pc_files + do + if [ .pc/patches-per-file -ot $pc_file ] + then + needs_refresh=1 + break + fi + done + if [ -z "$needs_refresh" ] + then + return 0 + fi fi - local patch=$1 - patch -p1 --dry-run -f $silent -i $(patch_file_name $patch) < /dev/null -} -can_remove() -{ - local silent - if [ "x$1" == "x-s" ] + for pc_file in $pc_files + do + if [ -e $pc_file ] + then + ex_pc_files[${#ex_pc_files[@]}]=$pc_file + fi + done + + if [ ${#ex_pc_files[@]} -eq 0 ] then - silent=-s - shift + rm -f .pc/patches-per-file + return 0 fi - local patch=$1 - patch -R -p1 --dry-run -f $silent -i $(patch_file_name $patch) < /dev/null + + awk ' + ARGIND!=saved { sub(/^.pc\//, "", FILENAME) + sub(/\/\.pc/, "", FILENAME) + saved=ARGIND + } + { if (files[$0]) + files[$0]=files[$0] " " FILENAME + else + files[$0]=FILENAME + } + END { for (file in files) + printf "%s\t%s\n", file, files[file] + } + ' "${ex_pc_files[@]}" > .pc/patches-per-file +} + +# For a lists of patches and a list of files, compute which patches +# modify which files. Invoked as +# modified_files file ... [-- patch ...] +# +modified_files() +{ + if ! refresh_patches_per_file + then + return 1 + fi + + awk ' + BEGIN { no_files=1 + no_patches=1 + for (i=1; i<ARGC; i++) { + if (ARGV[i]=="--") + break + files[ARGV[i]]=1 + no_files=0 + } + for (i++; i<ARGC; i++) { + patches[ARGV[i]]=1 + no_patches=0 + } + split("", ARGV) # read from standard input + } + no_files || files[$1] { + if (no_patches) { + print + next + } + for (i=2; i<=NF; i++) + if ($i in patches) { + printf "%s\t%s", $1, $i + for (i++; i<=NF; i++) + if ($i in patches) + printf " %s", $i + printf "\n" + } + } + ' "$@" < .pc/patches-per-file } add_to_db() @@ -290,10 +418,13 @@ remove_from_db() { local patch=$1 local tmpfile - tmpfile=$(mktemp /tmp/patch-scripts.XXXXXX) - grep -v "^$patch\$" $DB > $tmpfile - mv -f $tmpfile $DB - [ -s $DB ] || rm -f $DB + if tmpfile=$(mktemp /tmp/patch-scripts.XXXXXX) + then + grep -v "^$(quote_re $patch)\$" $DB > $tmpfile + mv -f $tmpfile $DB + rm -f $tmpfile + [ -s $DB ] || rm -f $DB + fi } stripit() @@ -303,6 +434,7 @@ stripit() echo $1 | sed -e 's/^\(\.\/\)*//' \ -e 's/^'"$P"'patches\///' -e 's/^\.pc\///' \ + -e 's/\.gz$//' -e 's/\.bz2$//' \ -e 's/\.patch$//' -e 's/\.diff\?$//' fi } @@ -322,15 +454,19 @@ files_in_patch() fi } -install_file_in_patch() +touched_by_patch() { - local file=$1 patch=$2 bup_file=$(backup_file_name $patch $file) - - if ! echo $file @LIB@/backup-file -s -B .pc/$patch/ || \ - ! echo $file >> $(pc_file_name $patch) - then - return 1 - fi + local strip=$1 patch=$2 + cat_file $(patch_file_name $patch) \ + | awk ' + /^\+\+\+[ \t]/ { + sub(/^\+\+\+[ \t]/, "") + sub(/[ \t].*/, "") + sub(/^\/dev\/null/, "") + for (i=0; i<'$strip'; i++) + sub(/^[^\/]*\//, "") + print + }' } refresh_file_list() @@ -341,40 +477,103 @@ refresh_file_list() if ! [ -e "$patch_file" ] then - echo "Patch file $patch_file does not exist" - return 1 + return 0 fi - if [ ! -e $pc_file -o $pc_file -ot $patch_file ] + if [ ! -e $pc_file -o \ + $pc_file -ot $patch_file -o \ + $pc_file -ot $SERIES ] then - mkdir -p $(dirname $pc_file) - echo "touched-by-patch $(patch_strip_level $patch) $patch_file > $pc_file" >&2 - @LIB@/touched-by-patch -p$(patch_strip_level $patch) \ - $patch_file > $pc_file - if [ $? -ne 0 ] + local tmpfile status + if ! mkdir -p $(dirname $pc_file) || \ + ! tmpfile=$(mktemp /tmp/patch-scripts.XXXXXX) + then + return 1 + fi + + if [ -e $pc_file ] + then + cat $pc_file >> $tmpfile + fi + if ! touched_by_patch $(patch_strip_level $patch) \ + $patch >> $tmpfile then return 1 fi - return 2 + sort $tmpfile | uniq > $pc_file + rm $tmpfile + return 0 fi } -directory_names() +diff_file() +{ + local file=$1 suffix=$2 old_file=$3 new_file=$4 + local old_file new_file + + if [ ! -e "$old_file" -a ! -e "$new_file" ] + then + echo "File $file does not exist" >&2 + return 0 + fi + local old_hdr new_hdr + if [ $opt_strip_level -eq 0 ] + then + old_hdr=$file$suffix + new_hdr=$file + else + local dir=$(basename $PWD) + old_hdr=$dir$suffix/$file + new_hdr=$dir/$file + fi + + echo Index: $new_hdr + diff -Nu $DIFF_OPTS $old_file $new_file | + fix_diff_header $old_hdr $new_hdr +} + +fix_diff_header() { - echo "$@" \ - | awk 'BEGIN {RS=" "} - {sub(/\/?[^\/]*$/, "") ; printf "[%s]\n", $0} - {if (last != $0) - print last - last = $0} - END {print last}' + local from=$1 to=$2 + sed -e 's:^--- [^ \t]*:--- '"$from"':' \ + -e 's:^+++ [^ \t]*:+++ '"$to"':' } -need_file_there() +cat_file() { - if [ ! -e $1 ] + local filename + + for filename in "$@" + do + if [ -e "$filename" ] + then + case "$filename" in + *.gz|*.tgz) + gzip -cd "$filename" ;; + *.bz2) + bzip2 -cd "$filename" ;; + *) + cat "$filename" ;; + esac + fi + done +} + +cat_to_file() +{ + local filename="$1" + + if [ -z "$filename" ] then - echo "File $1 does not exist" - exit 1 + cat + else + case "$filename" in + *.gz|*.tgz) + gzip -c > "$filename" ;; + *.bz2) + bzip2 -c > "$filename" ;; + *) + cat > "$filename" ;; + esac fi } @@ -384,10 +583,15 @@ patch_description() if [ -e "$patch_file" ] then - awk '/^--- / {exit} - diff {print diff ; diff=""} - /^diff / {diff=$0 ; next} - {print}' \ - $patch_file + awk ' + /^--- / { exit } + diff_line { print diff_line + diff_line="" + } + /^diff / { diff_line=$0 + next + } + { print } + ' $patch_file fi } diff --git a/lib/rpatch.in b/lib/rpatch.in index 3c05c0a..85e8408 100755 --- a/lib/rpatch.in +++ b/lib/rpatch.in @@ -1,4 +1,4 @@ -#! /bin/bash +#!/bin/bash # This script is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as @@ -6,16 +6,20 @@ # # See the COPYING and AUTHORS files for more details. -if ! [ -r @LIB@/patchfns ] +# Read in library functions +if [ "$(type -t patch_file_name)" != function ] then - echo "Cannot read library @LIB@/patchfns" >&2 - exit 1 + if ! [ -r @LIB@/patchfns ] + then + echo "Cannot read library @LIB@/patchfns" >&2 + exit 1 + fi + . @LIB@/patchfns fi -. @LIB@/patchfns usage() { - echo "Usage: rpatch [-fRq] patchname" + echo "Usage: $0 [-fRq] patchname" exit 1 } @@ -52,10 +56,17 @@ verify_removal() files_may_have_changed() { local patch=$1 file + local patch_file=$(patch_file_name $patch) local pc_file=$(pc_file_name $patch) + + if ! [ -e $pc_file ] + then + return 1 + fi + local apply_ts=$(date -r $pc_file '+%s') ts - if [ $pc_file -ot $(patch_file_name $patch) ] + if [ -e "$patch_file" -a $pc_file -ot "$patch_file" ] then return 0 fi @@ -76,25 +87,60 @@ files_may_have_changed() return 1 } -abort_patch() +rollback_patch() { - local pc_file=$(pc_file_name $1) - @LIB@/backup-files -s -f $pc_file -z ~rpatch -r - exit 1 + local patch=$1 pc_file=$(pc_file_name $patch) + @LIB@/backup-files $silent_unless_verbose \ + -f $pc_file -z ~rpatch -r + rm -f $(files_in_patch $patch | sed -e 's/$/\.rej/') } interrupt() { local patch=$1 - abort_patch $patch - echo "rpatch interrupted by user" + rollback_patch $patch + echo "Interrupted by user; patch $patch was not removed." exit 1 } -do_remove() +reverse_patch() +{ + local patch=$1 + local patch_file=$(patch_file_name $patch) + + if ! [ -s $patch_file ] + then + echo "Patch file $patch_file appears to be empty" + return 0 + fi + + if [ "x${patch_file:(-3)}" = "x.gz" ] + then + gzip -cd $patch_file \ + | patch $(patch_args $patch) --no-backup-if-mismatch \ + -R -E $silent + elif [ "x${patch_file:(-4)}" = "x.bz2" ] + then + bzip2 -cd $patch_file \ + | patch $(patch_args $patch) --no-backup-if-mismatch \ + -R -E $silent + else + patch $(patch_args $patch) --no-backup-if-mismatch \ + -R -E $silent -i $patch_file + fi +} + +rpatch() { local patch=$1 pc_file=$(pc_file_name $patch) + if ! [ -e $pc_file ] + then + echo "Patch $patch appears to be empty, removed" + remove_from_db $patch + return 0 + fi + trap "" SIGINT if [ -n "$opt_force" ] || \ ( [ -z "$opt_remove" ] && ! files_may_have_changed $patch ) @@ -106,44 +152,53 @@ do_remove() @LIB@/backup-files $silent -f $pc_file -B .pc/$patch/ -r status=$? remove_from_db $patch - rm -f $(pc_file_name $patch)~forced - return $status + rm -f $pc_file~refresh + if [ $status != 0 ] + then + exit $status + fi else - local patch_file=$(patch_file_name $patch) - if ! [ -e "$patch_file" ] + if ! @LIB@/backup-files $silent_unless_verbose \ + -f $pc_file -z ~rpatch then - echo "No patch named $patch found." + echo "Failed to create temporary files" >&2 return 1 fi trap "interrupt $patch" SIGINT - #if [ ! -e $patch_file ] || grep -q '^%patch$' $patch_file - #then - # @LIB@/parse-patch -s patch $patch_file \ - # | patch $(patch_args $patch) --no-backup-if-mismatch \ - # -R -E $silent - #else - patch $(patch_args $patch) --no-backup-if-mismatch \ - -R -E $silent -i $patch_file - #fi + + reverse_patch $patch status=$? + trap "" SIGINT + if [ $status -eq 0 ] && verify_removal $patch then - @LIB@/backup-files -s -f $pc_file -z ~rpatch -x - @LIB@/backup-files -s -f $pc_file -B .pc/$patch/ -x + @LIB@/backup-files $silent_unless_verbose \ + -f $pc_file -z ~rpatch -x + @LIB@/backup-files $silent_unless_verbose \ + -f $pc_file -B .pc/$patch/ -x remove_from_db $patch else - @LIB@/backup-files -s -f $pc_file -z ~rpatch -r - echo "Patch $patch does not remove cleanly" + rollback_patch $patch + echo "Patch $patch does not remove (enforce with -f)" return 1 fi fi trap - SIGINT + + local top=$(top_patch) where + if [ -z "$top" ] + then + where="no patches applied" + else + where="now at $top" + fi + echo "Removed $patch, $where" } -options=`getopt -o fRqh -- "$@"` +options=`getopt -o fRqvh -- "$@"` if [ $? -ne 0 ] then @@ -165,6 +220,9 @@ do -q) opt_quiet=1 shift ;; + -v) + opt_verbose=1 + shift ;; -h) usage -h ;; --) @@ -180,29 +238,13 @@ fi patch=$(stripit $1) [ -n "$opt_quiet" ] && silent=-s +[ -z "$opt_verbose" ] && silent_unless_verbose=-s top=$(top_patch) -if [ -n "$top" -a -e $(pc_file_name $top)~forced -a -z "$opt_force" ] +if [ -n "$top" -a -e $(pc_file_name $top)~refresh -a -z "$opt_force" ] then - echo "The topmost patch $top was force applied. Please run" \ - "refpatch before removing it." + echo "The topmost patch $top needs to be refreshed first." exit 1 fi -#if is_applied "$patch" -#then - do_remove "$patch" || exit 1 -#else -# echo "Patch $patch is not applied" -# exit 1 -#fi - -top=$(top_patch) -if [ -z "$top" ] -then - where="no patches applied" -else - where="now at $top" -fi -echo "Removed $patch, $where" - +rpatch "$patch" || exit 1 diff --git a/lib/spec2series b/lib/spec2series new file mode 100755 index 0000000..685f8e0 --- /dev/null +++ b/lib/spec2series @@ -0,0 +1,223 @@ +#!/bin/bash + +# This script is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# See the COPYING and AUTHORS files for more details. + +# defaults +debug=0 +specfile="" +outfile="" + +function usage() { +cat <<EOF +Usage: quilt spec2series [ options ] specfile + +-h print this text +-d debug mode (prints lots additional info + as comments into the output file) +-o <file> + specify output file, stdout if unspecified + +EOF +} + +# parse args +while test "$1" != ""; do + case "$1" in + -h | --help) + usage; exit 0 + ;; + -d | --debug) + debug=1; shift + ;; + -o | --outfile) + outfile="$2"; shift; shift + ;; + *) + specfile="$1" + break; + ;; + esac +done + +if test ! -f "$specfile"; then + usage + exit 1 +fi + +# get absolute patch for specfile location +specdir=`dirname $specfile` +specfile=`basename $specfile` +case "$specdir" in + .) specdir="`pwd`" + ;; + /*) # nothing + ;; + *) specdir="`pwd`/$specdir" + ;; +esac + +# create tmp work dir +WORK="${TMPDIR-/tmp}/rpmlog-$$" +mkdir -p "$WORK" || exit 1 +trap 'rm -rf "$WORK"' EXIT +mkdir -p "$WORK/build" +mkdir -p "$WORK/bin" + +# create md5 sums, also for uncompressed files +echo -n "### md5: " >&2 +(cd $specdir; for file in /dev/null *; do + case "$file" in + ready | bigpack | MD5SUMS | MD5SUMS.meta | *.spec | *.changes) + continue + ;; + esac + type="`file -b $file | cut -d" " -f1`" + case "$type" in + compress*) + echo -n "z" >&2 + set -- `zcat $file | md5sum` + echo "$1 zcat ${file}" + ;; + gzip) + echo -n "g" >&2 + set -- `zcat $file | md5sum` + echo "$1 zcat ${file}" + ;; + bzip2) + echo -n "b" >&2 + set -- `bzcat $file | md5sum` + echo "$1 bzcat ${file}" + ;; + esac + echo -n "." >&2 + set -- `md5sum < $file` + echo "$1 cat ${file}" +done) > $WORK/md5sum +echo " done" >&2 + +# prepare rpm dir fixups and hooks +RPM="rpm --rcfile=/usr/lib/rpm/rpmrc:$WORK/rpmrc" +PATH="$WORK/bin:$PATH" +grep ^macrofiles /usr/lib/rpm/rpmrc \ + | sed -e "/macrofiles/s|$|:$WORK/rpmmacros|" \ + > $WORK/rpmrc +cat <<-EOF > "$WORK/rpmmacros" + %_sourcedir $specdir + %_specdir $specdir + %_builddir $WORK/build +EOF + +# wrapper script for patch and tar +cat <<-'EOF' > "$WORK/bin/patch" + #!/bin/sh + + # save stuff for log + unpackcmd=`basename $0` + unpackdir=`pwd` + unpackargs="$*" + uncompress="false" + unpackfile="[oops]" + + # sort of progress bar + case $unpackcmd in + tar) echo -n "t" >&2;; + patch) echo -n "p" >&2;; + *) echo -n "?" >&2;; + esac + + # find real binary + realcmd="" + test -x "/bin/$unpackcmd" && realcmd="/bin/$unpackcmd" + test -x "/usr/bin/$unpackcmd" && realcmd="/usr/bin/$unpackcmd" + test "$realcmd" = "" && exit 1 + + # put data into tmpfile, exec real cmd, return on failure + WORK=`dirname $RPM_BUILD_DIR` + cat > "$WORK/data" + if test -x /bin/$unpackcmd; then + cmddir="/bin" + fi + $realcmd $* < "$WORK/data" + retval="$?" + test "$retval" != "0" && exit $retval + + # find original data file by md5sum + set -- `md5sum < "$WORK/data"` + unpackfile="[$1]" + set -- `cat "$WORK/md5sum"` + while test "$1" != ""; do + if test "[$1]" = "$unpackfile"; then + uncompress="$2" + unpackfile="$3" + break + fi + shift + done + + # print results + unpackdir=`echo $unpackdir | sed -e "s|$RPM_BUILD_DIR|BUILD|"` + echo -n "# log: [$unpackdir] $uncompress $unpackfile" >>$WORK/cmdlog + echo " | $unpackcmd $unpackargs" >>$WORK/cmdlog + if test "$unpackcmd" = "patch" -a \ + -f "$RPM_SOURCE_DIR/$unpackfile"; then + patchdir="${unpackdir#BUILD/}" + if test ! -f "$WORK/patchdir"; then + echo -n $patchdir > $WORK/patchdir + fi + if test "`cat $WORK/patchdir`" = "$patchdir"; then + level=`echo $unpackargs | tr " " "\n" | grep ^-p` + echo "$unpackfile $level" >> $WORK/patchlog + fi + fi + if test "$unpackcmd" = "tar" -a \ + -f "$RPM_SOURCE_DIR/$unpackfile"; then + echo -n "# Source: $unpackfile" >>$WORK/tarlog + if test "$unpackdir" != "BUILD"; then + echo -n " -C ${unpackdir#BUILD/}" >>$WORK/tarlog + fi + echo "" >>$WORK/tarlog + fi +EOF +chmod 755 "$WORK/bin/patch" +ln -s patch "$WORK/bin/tar" + +# let rpm do all the dirty specfile stuff ... +echo -n "### rpm: " >&2 +touch $WORK/patchlog +$RPM --nodeps --quiet -bp "$specdir/$specfile" </dev/null +echo " done" >&2 + +# print results saved by the wrapper script +( + # header + echo "# Patch series file for quilt, created by $0" + echo "#" + echo "# Sourcedir: $specdir" + echo "# Specfile: $specfile" + if test -f $WORK/patchdir; then + echo "# Patchdir: `cat $WORK/patchdir`" + fi + echo "#" + + # additional info for trouble shooting + if test "$debug" = "1"; then + cat $WORK/md5sum | sed -e 's/^/# md5: /' + echo "#" + + test -f $WORK/cmdlog && cat $WORK/cmdlog && echo "#" + fi + + # list tarballs + patches + test -f $WORK/tarlog && cat $WORK/tarlog && echo "#" + test -f $WORK/patchlog && cat $WORK/patchlog +)|( + if test "$outfile" != ""; then + cat > "$outfile" + else + cat + fi +) diff --git a/lib/touched-by-patch b/lib/touched-by-patch deleted file mode 100755 index bdfc296..0000000 --- a/lib/touched-by-patch +++ /dev/null @@ -1,69 +0,0 @@ -#! /bin/bash -# Extract names of new files from a patch, print them out - -# This script is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# See the COPYING and AUTHORS files for more details. - -usage() -{ - echo "Usage: touched-by-patch [-p num] patchname" - exit 1 -} - -options=`getopt -o p:h -- "$@"` - -if [ $? -ne 0 ] -then - usage -fi - -eval set -- "$options" - -while true -do - case "$1" in - -p) - opt_p=$2 - shift 2 ;; - -h) - usage ;; - --) - shift - break ;; - esac -done - -if [ $# -ne 1 ] -then - usage -fi - -patch_file=$1 -[ -z "$opt_p" ] && opt_p=1 - -case "$patch_file" in -*.bz2) - command="bzip2 -cd $patch_file";; -*.gz) - command="gzip -cd $patch_file";; -*) - command="cat $patch_file";; -esac - -# Neither `+++' nor `---' works for all patches; patch looks at the -# file system to determine which file name to use. - -eval $command \ -| awk '/^\+\+\+[ \t]/ { - sub(/^\+\+\+[ \t]/, "") - sub(/[ \t].*/, "") - sub(/^\/dev\/null/, "") - for (i=0; i<'$opt_p'; i++) - sub(/^[^\/]*\//, "") - print - }' \ -| sort \ -| uniq diff --git a/patch-scripts.changes b/patch-scripts.changes deleted file mode 100644 index f78a580..0000000 --- a/patch-scripts.changes +++ /dev/null @@ -1,11 +0,0 @@ -------------------------------------------------------------------- -Tue Jan 14 10:02:50 CET 2003 - agruen@suse.de - -- Move library files to /usr/share/lib/patch-scripts -- Try to recover from keyboard interrupt (^C) - -------------------------------------------------------------------- -Mon Jan 13 13:04:27 CET 2003 - agruen@suse.de - -- Initial package - diff --git a/quilt.changes b/quilt.changes new file mode 100644 index 0000000..584d212 --- /dev/null +++ b/quilt.changes @@ -0,0 +1,36 @@ +------------------------------------------------------------------- +Tue Jan 21 16:49:18 CET 2003 - agruen@suse.de + +- NOTE: The scripts are in so much a state of flux that keeping + a real changelog at this moment doesn't make sense... + +------------------------------------------------------------------- +Mon Jan 20 20:12:58 CET 2003 - agruen@suse.de + +- Add Gerd Knorr's scripts for generating series files from RPM + sources to contrib/ +- Write an initial initseries script that sets up a source tree + from a spec file. This version still sucks. + +------------------------------------------------------------------- +Mon Jan 20 02:34:40 CET 2003 - agruen@suse.de + +- Add -r options to newpatch and patchadd. +- Improve backup-files slightly. + +------------------------------------------------------------------- +Sat Jan 18 21:11:21 CET 2003 - agruen@suse.de + +- Initial release 0.11 +- Fix a few bugs; 0.12 + +------------------------------------------------------------------- +Tue Jan 14 10:02:50 CET 2003 - agruen@suse.de + +- Move library files to /usr/share/lib/patch-scripts +- Try to recover from keyboard interrupt (^C) + +------------------------------------------------------------------- +Mon Jan 13 13:04:27 CET 2003 - agruen@suse.de + +- Initial package diff --git a/patch-scripts.spec b/quilt.spec index a5eda6c..d260730 100644 --- a/patch-scripts.spec +++ b/quilt.spec @@ -1,27 +1,26 @@ # -# spec file for patch scripts +# spec file for quilt - patch management scripts # -# Copyright (c) 2002 SuSE Linux AG, Nuernberg, Germany. +# Copyright (c) 2003 SuSE Linux AG, Nuernberg, Germany. # This file and all modifications and additions to the pristine # package are under the same license as the package itself. # # Please submit bugfixes or comments via http://www.suse.de/feedback/ # -# neededforbuild fileutils make sed +# neededforbuild sh-utils # usedforbuild -Name: patch-scripts +Name: quilt Summary: Scripts for working with series of patches License: GPL Group: Productivity/Text/Utilities -Version: 0.11 +Version: 0.21 Release: 1 Requires: textutils diffutils patch gzip bzip2 perl mktemp Autoreqprov: off -Source: patch-scripts-%{version}.tar.gz +Source: quilt-%{version}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-build -BuildArchitectures: noarch %description The scripts allow to manage a series of patches by keeping @@ -37,6 +36,9 @@ Authors: Andreas Gruenbacher <agruen@suse.de> %prep +if [ "${RPM_BUILD_ROOT%/}" != "" ]; then + rm -rf $RPM_BUILD_ROOT +fi %setup %build @@ -47,25 +49,14 @@ make install prefix=/usr BUILD_ROOT=$RPM_BUILD_ROOT %files %defattr(-, root, root) -/usr/bin/newpatch -/usr/bin/patchadd -/usr/bin/pushpatch -/usr/bin/poppatch -/usr/bin/refpatch -/usr/bin/importpatch -/usr/bin/toppatch -/usr/bin/inpatch +/usr/bin/guards +/usr/bin/quilt -/usr/share/patch-scripts/patchfns -/usr/share/patch-scripts/apatch -/usr/share/patch-scripts/rpatch -/usr/share/patch-scripts/touched-by-patch -/usr/share/patch-scripts/parse-patch -/usr/share/patch-scripts/backup-files +/usr/share/quilt/ -%doc /usr/share/doc/packages/patch-scripts/README -%doc /usr/share/doc/packages/patch-scripts/docco.txt +%doc /usr/share/man/man1/guards.1.gz +%doc README + +%changelog +# The changelog is kept in %{name}.changes -%changelog -n star -* Mon Jan 13 2002 - agruen -- Initial package diff --git a/patchadd.in b/quilt/add.in index de2d6b2..39e1a36 100755..100644 --- a/patchadd.in +++ b/quilt/add.in @@ -1,4 +1,4 @@ -#! /bin/bash +#!/bin/bash # This script is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as @@ -7,16 +7,19 @@ # See the COPYING and AUTHORS files for more details. # Read in library functions -if ! [ -r @LIB@/patchfns ] +if [ "$(type -t patch_file_name)" != function ] then - echo "Cannot read library @LIB@/patchfns" >&2 - exit 1 + if ! [ -r @LIB@/patchfns ] + then + echo "Cannot read library @LIB@/patchfns" >&2 + exit 1 + fi + . @LIB@/patchfns fi -. @LIB@/patchfns usage() { - echo "Usage: patchadd [-p patchname] filename ..." + echo "Usage: quilt add [-p patch] {file} ..." if [ x$1 = x-h ] then cat <<EOF @@ -26,7 +29,7 @@ Files must be added to the patch before being modified. Files that are modified by patches on top of the specified patch cannot be added. --p patchname +-p patch Patch to add files to. EOF @@ -71,7 +74,7 @@ then fi if [ -z "$patch" ] then - echo "No patches seem to be applied" + echo "No patches applied" fi if ! is_applied $patch @@ -80,22 +83,46 @@ then exit 1 fi +status=0 for file in $* do if file_in_patch $file $patch then echo "File $file is already in patch $patch" - else - next_patch=$(next_patch_for_file $patch $file) - if [ -n "$next_patch" ] - then - echo "File $file shadowed by patch $next_patch" - exit 1 - fi + status=1 + continue + fi + next_patch=$(next_patch_for_file $patch $file) + if [ -n "$next_patch" ] + then + echo "File $file modified by patch $next_patch" + status=1 + continue + fi - if install_file_in_patch $file $patch + if ! @LIB@/backup-files -s -B .pc/$patch/ $file || \ + ! echo $file >> $(pc_file_name $patch) + then + echo "Failed to back up file $file" >&2 + status=1 + continue + fi + + if [ -e $file ] + then + if [ "$(ls -dl $file | awk '{print $2}')" -gt 1 ] then - echo "File $file added to patch $patch" + # We have a file with several hard links. + # As the file may likely be modified by hand + # now, create a copy to make sure nothing + # happens to the original file. + tmpfile=$(mktemp $file.XXXXXX) && + cp -f $file $tmpfile && + mv -f $tmpfile $file + rm -f $tmpfile fi fi + + echo "File $file added to patch $patch" done +exit $status diff --git a/quilt/applied.in b/quilt/applied.in new file mode 100644 index 0000000..cbefdca --- /dev/null +++ b/quilt/applied.in @@ -0,0 +1,71 @@ +#!/bin/bash + +# This script is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# See the COPYING and AUTHORS files for more details. + +# Read in library functions +if [ "$(type -t patch_file_name)" != function ] +then + if ! [ -r @LIB@/patchfns ] + then + echo "Cannot read library @LIB@/patchfns" >&2 + exit 1 + fi + . @LIB@/patchfns +fi + +usage() +{ + echo "Usage: quilt applied [patch]" + if [ x$1 = x-h ] + then + cat <<EOF + +Print a list of applied patches, or all patches up to and including the +specified patch in the file series. + +EOF + exit 0 + else + exit 1 + fi +} + +options=`getopt -o h -- "$@"` + +if [ $? -ne 0 ] +then + usage +fi + +eval set -- "$options" + +while true +do + case "$1" in + -h) + usage -h ;; + --) + shift + break ;; + esac +done + +if [ $# -gt 1 ] +then + usage +elif [ $# -eq 1 ] +then + patch=$(stripit $1) +else + patch=$(top_patch) +fi + +if [ -n "$patch" ] +then + patches_before $patch + echo $patch +fi diff --git a/quilt/delete.in b/quilt/delete.in new file mode 100644 index 0000000..3c5cb91 --- /dev/null +++ b/quilt/delete.in @@ -0,0 +1,92 @@ +#!/bin/bash + +# This script is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# See the COPYING and AUTHORS files for more details. + +# Read in library functions +if [ "$(type -t patch_file_name)" != function ] +then + if ! [ -r @LIB@/patchfns ] + then + echo "Cannot read library @LIB@/patchfns" >&2 + exit 1 + fi + . @LIB@/patchfns +fi + +usage() +{ + echo "Usage: quilt delete [patch]" + if [ x$1 = x-h ] + then + cat <<EOF + +Remove the specified or topmost patchfrom the series file. If the patch +is applied, it is first tried to remove it. (Only the topmost patch can +be removed right now.) + +EOF + exit 0 + else + exit 1 + fi +} + +options=`getopt -o h -- "$@"` + +if [ $? -ne 0 ] +then + usage +fi + +eval set -- "$options" + +while true +do + case "$1" in + -h) + usage -h ;; + --) + shift + break ;; + esac +done + +if [ $# -gt 1 ] +then + usage +fi + +patch=$(stripit $1) + +if [ -z "$patch" ] +then + patch="$(top_patch)" + if [ -z "$patch" ] + then + echo "No patches applied" + exit 1 + fi +else + if ! patch_in_series $patch + then + echo "Patch $patch does not exist" + exit 1 + fi +fi +if is_applied $patch +then + if [ "$patch" != "$(top_patch)" ] || \ + ! @LIB@/rpatch -fq "$patch" + then + echo "Patch $patch is currently applied" + exit 1 + fi +fi +if ! remove_from_series $patch +then + echo "Failed to remove patch $patch" +fi diff --git a/quilt/diff.in b/quilt/diff.in new file mode 100644 index 0000000..a114f8a --- /dev/null +++ b/quilt/diff.in @@ -0,0 +1,244 @@ +#!/bin/bash + +# This script is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# See the COPYING and AUTHORS files for more details. + +# Read in library functions +if [ "$(type -t patch_file_name)" != function ] +then + if ! [ -r @LIB@/patchfns ] + then + echo "Cannot read library @LIB@/patchfns" >&2 + exit 1 + fi + . @LIB@/patchfns +fi + +usage() +{ + local redirect + if [ x$1 != x-h ] + then + redirect='>&2' + fi + echo "Usage: quilt diff [-p n] [-c patch|-z] [patch]" $redirect + + if [ x$1 = x-h ] + then + cat <<EOF + +Produce a diff of the specified patch, or the topmost patch +by default. + +-p n Create a -p n style patch (-p0 or -p1 supported). + +-c patch + Create a combined diff for all patches between this + patch and the specified or topmost patch. + +-z Write to standard output the changes that have been + made relative to the topmost or specified patch. + +EOF + exit 0 + else + exit 1 + fi +} + +die () +{ + local status=$1 + [ -n "$workdir" ] && rm -rf $workdir + exit $status +} + +options=`getopt -o c:p:zh -- "$@"` + +if [ $? -ne 0 ] +then + usage +fi + +eval set -- "$options" + +while true +do + case "$1" in + -p) + opt_strip_level=$2 + shift 2 ;; + -c) + opt_combine=$2 + shift 2 ;; + -z) + opt_relative=1 + shift ;; + -h) + usage -h ;; + --) + shift + break ;; + esac +done + +if [ -n "$opt_combine" -a -n "$opt_relative" ] +then + echo "Options \`-c patch' and \`-z' cannot be combined." + die 1 +fi + +if [ $# -eq 1 ] +then + last_patch=$(stripit $1) +elif [ $# -gt 1 ] +then + usage +fi + +if [ -z "$last_patch" ] +then + last_patch=$(top_patch) + if [ -z "$last_patch" ] + then + echo "No patches seem to be applied" >&2 + die 1 + fi +fi + +if ! is_applied "$last_patch" +then + echo "Patch $last_patch is not applied" >&2 + die 1 +fi + +if [ -z "$opt_strip_level" ] +then + opt_strip_level=$(patch_strip_level $last_patch) +fi +if [ $opt_strip_level -lt 0 -o $opt_strip_level -gt 1 ] +then + echo "Cannot diff patches with -p$opt_strip_level," \ + "please specify -p0 or -p1 instead" >&2 + die 1 +fi + +if [ -n "$opt_combine" ] +then + if ! refresh_patches_per_file + then + echo "refresh_patches_per_file failed." + die 1 + fi + + set -- $(patches_before $last_patch) $last_patch + if [ "x$opt_combine" != "x-" ] + then + while [ $# -ge 1 -a "$1" != "$opt_combine" ] + do + shift + done + if [ $# -eq 0 ] + then + echo "Patch $opt_combine not applied" \ + "before $last_patch." + die 1 + fi + fi + + while read file first garbage + do + patches[${#patches[@]}]="$first" + files[${#files[@]}]="$file" + done \ + < <(modified_files -- "$@") +else + files=( $(files_in_patch $last_patch) ) +fi + +trap "die 1" SIGTERM + +if [ -n "$opt_relative" ] +then + patch_file=$(patch_file_name $last_patch) + patch_args=$(patch_args $last_patch) + workdir=$(mktemp -d patch-scripts.XXXXXX) + pwd=$PWD + + if ! cd .pc/$last_patch + then + echo "Cannot change into .pc/$last_patch" + die 1 + fi + if ! cp -l --parents "${files[@]}" $pwd/$workdir/ + then + echo "Failed to copy files to temporary directory" + die 1 + fi + if ! cd $pwd/$workdir + then + echo "Cannot change to temporary directory" + die 1 + fi + + if [ -s $pwd/$patch_file ] + then + if ! cat_file $pwd/$patch_file \ + | patch $patch_args --no-backup-if-mismatch \ + -E >/dev/null 2>/dev/null + then + echo "Failed to patch temporary files" + die 1 + fi + fi + if ! cd $pwd + then + echo "Cannot change to source directory" + die 1 + fi +fi + +for ((i=0; i<${#files[@]}; i++)) +do + file="${files[$i]}" + first_patch=${patches[$i]:-$last_patch} + + next_patch=$(next_patch_for_file $last_patch $file) + if [ -z "$next_patch" ] + then + new_file="$file" + else + new_file="$(backup_file_name $next_patch $file)" + files_were_shadowed=1 + fi + + if [ -z "$opt_relative" ] + then + if [ -z "$opt_combine" ] + then + suffix="~${last_patch//\//_}" + else + suffix=".orig" + fi + old_file=$(backup_file_name $first_patch $file) + diff_file $file $suffix $old_file $new_file + else + diff_file $file ".orig" "$workdir/$file" $new_file + fi + + if [ $? -ne 0 ] + then + echo "Diff failed, aborting." >&2 + die 1 + fi +done + +if [ -n "$files_were_shadowed" ] +then + echo "More recent patches modify the same files." + die 1 +fi +die 0 diff --git a/quilt/files.in b/quilt/files.in new file mode 100644 index 0000000..6ea95d2 --- /dev/null +++ b/quilt/files.in @@ -0,0 +1,112 @@ +#!/bin/bash + +# This script is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# See the COPYING and AUTHORS files for more details. + +# Read in library functions +if [ "$(type -t patch_file_name)" != function ] +then + if ! [ -r @LIB@/patchfns ] + then + echo "Cannot read library @LIB@/patchfns" >&2 + exit 1 + fi + . @LIB@/patchfns +fi + +usage() +{ + echo "Usage: quilt files [-v] [patch]" + if [ x$1 = x-h ] + then + cat <<EOF + +Print the list of files that the topmost or specified patch +changes. + +-v Verbose, more user friendly output. + +EOF + exit 0 + else + exit 1 + fi +} + +options=`getopt -o vh -- "$@"` + +if [ $? -ne 0 ] +then + usage +fi + +eval set -- "$options" + +while true +do + case "$1" in + -v) + opt_verbose=1 + shift ;; + -h) + usage -h ;; + --) + shift + break ;; + esac +done + +if [ $# -gt 1 ] +then + usage +fi +opt_patch=$1 + +if [ -n "$opt_patch" ] +then + patch=$(stripit "$opt_patch") +else + patch=$(top_patch) + if [ -z "$patch" ] + then + echo "No patches applied" >&2 + exit 1 + fi +fi + +if ! is_applied $patch +then + if [ -n "$opt_verbose" ] + then + echo "Patch is not applied (no verbose output)" >&2 + opt_verbose=0 + else + echo "Patch is not applied" >&2 + fi +fi + +if [ -z "$opt_verbose" ] +then + files_in_patch $patch +else + for file in $(files_in_patch $patch) + do + status=" " + if [ -s $(backup_file_name $patch $file) ] + then + if ! [ -s $file ] + then + status="-" + fi + else + if [ -s $file ] + then + status="+" + fi + fi + echo "$status $file" + done +fi diff --git a/importpatch.in b/quilt/import.in index 2e0c8cf..0f3df93 100755..100644 --- a/importpatch.in +++ b/quilt/import.in @@ -1,4 +1,4 @@ -#! /bin/bash +#!/bin/bash # This script is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as @@ -7,16 +7,19 @@ # See the COPYING and AUTHORS files for more details. # Read in library functions -if ! [ -r @LIB@/patchfns ] +if [ "$(type -t patch_file_name)" != function ] then - echo "Cannot read library @LIB@/patchfns" >&2 - exit 1 + if ! [ -r @LIB@/patchfns ] + then + echo "Cannot read library @LIB@/patchfns" >&2 + exit 1 + fi + . @LIB@/patchfns fi -. @LIB@/patchfns usage() { - echo "Usage: importpatch [-f] [-p num] [-n patchname] [patchfile]" + echo "Usage: quilt import [-f] [-p num] [-n patch] [patchfile]" if [ x$1 = x-h ] then cat <<EOF @@ -29,7 +32,7 @@ used as the patch name. -p num Number of directory levels to strip when aplying (default=1) --n patchfile +-n patch File name relative to patches/ to use. -f Overwite/update existing patches. @@ -111,29 +114,32 @@ fi case "$input_file" in '') - tmp=$(mktemp /tmp/patch-scripts.XXXXXX) - if ! cat > $tmp + tmpfile=$(mktemp /tmp/patch-scripts.XXXXXX) + if ! cat > $tmpfile then echo "Cannot read from standard input." + rm -f $tmpfile exit 1 fi - input_file=$tmp ;; + input_file=$tmpfile ;; *.gz) - tmp=$(mktemp /tmp/patch-scripts.XXXXXX) - if ! gzip -cd "$input_file" > $tmp + tmpfile=$(mktemp /tmp/patch-scripts.XXXXXX) + if ! gzip -cd "$input_file" > $tmpfile then echo "Cannot decompress file $input_file" + rm -f $tmpfile exit 1 fi - input_file=$tmp ;; + input_file=$tmpfile ;; *.bz2) - tmp=$(mktemp /tmp/patch-scripts.XXXXXX) - if ! bzip2 -cd "$input_file" > $tmp + tmpfile=$(mktemp /tmp/patch-scripts.XXXXXX) + if ! bzip2 -cd "$input_file" > $tmpfile then echo "Cannot decompress file $input_file" + rm -f $tmpfile exit fi - input_file=$tmp ;; + input_file=$tmpfile ;; *) if ! [ -r "$input_file" ] then @@ -154,7 +160,7 @@ then ! grep -q '^%patch$' "$input_file" then echo "Updating %patch section of patch $patch" - if ! @LIB@/parse-patch -u patch $patch_file< "$input_file" + if ! @LIB@/parse-patch -u patch $patch_file < "$input_file" then echo "Failed to update %patch section of patch $patch" exit 1 @@ -188,7 +194,7 @@ then echo "Failed to insert $patch in file series." fi -if [ -n "$tmp" ] +if [ -n "$tmpfile" ] then - rm -f $tmp + rm -f $tmpfile fi diff --git a/newpatch.in b/quilt/new.in index 34242f9..f04c47e 100755..100644 --- a/newpatch.in +++ b/quilt/new.in @@ -1,4 +1,4 @@ -#! /bin/bash +#!/bin/bash # This script is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as @@ -7,22 +7,25 @@ # See the COPYING and AUTHORS files for more details. # Read in library functions -if ! [ -r @LIB@/patchfns ] +if [ "$(type -t patch_file_name)" != function ] then - echo "Cannot read library @LIB@/patchfns" >&2 - exit 1 + if ! [ -r @LIB@/patchfns ] + then + echo "Cannot read library @LIB@/patchfns" >&2 + exit 1 + fi + . @LIB@/patchfns fi -. @LIB@/patchfns usage() { - echo "Usage: newpatch patchname" + echo "Usage: quilt new {patchname}" if [ x$1 = x-h ] then cat <<EOF - Create a new patch, and insert it after the topmost patch - in the patch series file. +Create a new patch with the specified file name, and insert +it after the topmost patch in the patch series file. EOF exit 0 @@ -56,12 +59,8 @@ then usage fi -patch_file=$(echo $1 | sed -e 's:^'"$P"'patches/::' -e 's:^\.pc/::') +patch_file=$(echo $1 | sed -e 's:^'"$P"'patches/::') patch=$(stripit $patch_file) -if [ -z "$patch" ] -then - usage -fi if patch_in_series $patch then @@ -69,11 +68,14 @@ then exit 1 fi -pc_file=$(pc_file_name $patch) -mkdir -p $(dirname $pc_file) -touch $pc_file - -insert_in_series $patch_file || exit 1 -add_to_db $patch || exit 1 +mkdir -p $(dirname $(pc_file_name $patch)) +rm -f $(pc_file_name $patch) -echo "Patch $patch is now on top" +if ! insert_in_series $patch_file || \ + ! add_to_db $patch +then + echo "Failed to create patch $patch" + exit 1 +else + echo "Patch $patch is now on top" +fi diff --git a/quilt/next.in b/quilt/next.in new file mode 100644 index 0000000..74ba4d2 --- /dev/null +++ b/quilt/next.in @@ -0,0 +1,72 @@ +#!/bin/bash + +# This script is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# See the COPYING and AUTHORS files for more details. + +# Read in library functions +if [ "$(type -t patch_file_name)" != function ] +then + if ! [ -r @LIB@/patchfns ] + then + echo "Cannot read library @LIB@/patchfns" >&2 + exit 1 + fi + . @LIB@/patchfns +fi + +usage() +{ + echo "Usage: quilt next [patch]" + if [ x$1 = x-h ] + then + cat <<EOF + +Print the name of the next patch after the specified or topmost patch in +the series file. + +EOF + exit 0 + else + exit 1 + fi +} + +options=`getopt -o h -- "$@"` + +if [ $? -ne 0 ] +then + usage +fi + +eval set -- "$options" + +while true +do + case "$1" in + -h) + usage -h ;; + --) + shift + break ;; + esac +done + +if [ $# -gt 1 ] +then + usage +elif [ $# -eq 1 ] +then + patch=$(stripit $1) +else + patch=$(top_patch) +fi + +if [ -z "$patch" ] +then + cat_series | head -1 +else + patches_after $patch | head -1 +fi diff --git a/quilt/patches.in b/quilt/patches.in new file mode 100644 index 0000000..6e1834b --- /dev/null +++ b/quilt/patches.in @@ -0,0 +1,93 @@ +#!/bin/bash + +# This script is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# See the COPYING and AUTHORS files for more details. + +# Read in library functions +if [ "$(type -t patch_file_name)" != function ] +then + if ! [ -r @LIB@/patchfns ] + then + echo "Cannot read library @LIB@/patchfns" >&2 + exit 1 + fi + . @LIB@/patchfns +fi + +usage() +{ + echo "Usage: quilt contains {file}" + if [ x$1 = x-h ] + then + cat <<EOF + +Print the list of patches that modify the specified file. + +-v Verbose, more user friendly output. + +EOF + exit 0 + else + exit 1 + fi +} + +scan_patches() +{ + local prefix=$1 file=$2 + shift 2 + local patch + + for patch in $(modified_files $file -- "$@" \ + | cut -d $'\t' -f2) + do + echo "$prefix$patch" + done +} + +options=`getopt -o vh -- "$@"` + +if [ $? -ne 0 ] +then + usage +fi + +eval set -- "$options" + +while true +do + case "$1" in + -v) + opt_verbose=1 + shift ;; + -h) + usage -h ;; + --) + shift + break ;; + esac +done + +if [ $# -ne 1 ] +then + usage +fi +opt_file=$1 + +if ! refresh_patches_per_file +then + exit 1 +fi + +if [ -n "$opt_verbose" ] +then + top=$(top_patch) + scan_patches "+ " $opt_file $(patches_before $top) + scan_patches "= " $opt_file $top + scan_patches " " $opt_file $(patches_after $top) +else + scan_patches "" $opt_file +fi diff --git a/poppatch.in b/quilt/pop.in index ff0b563..3ab2fac 100755..100644 --- a/poppatch.in +++ b/quilt/pop.in @@ -1,4 +1,4 @@ -#! /bin/bash +#!/bin/bash # This script is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as @@ -7,16 +7,19 @@ # See the COPYING and AUTHORS files for more details. # Read in library functions -if ! [ -r @LIB@/patchfns ] +if [ "$(type -t patch_file_name)" != function ] then - echo "Cannot read library @LIB@/patchfns" >&2 - exit 1 + if ! [ -r @LIB@/patchfns ] + then + echo "Cannot read library @LIB@/patchfns" >&2 + exit 1 + fi + . @LIB@/patchfns fi -. @LIB@/patchfns usage() { - echo "Usage: poppatch [-afRq] [npatches|patchname]" + echo "Usage: quilt pop [-afRqv] [num|patch]" if [ x$1 = x-h ] then cat <<EOF @@ -37,6 +40,8 @@ name is specified, remove the next patch. -q Quiet operation. +-v Verbose operation. + EOF exit 0 else @@ -68,11 +73,11 @@ list_patches() fi n=$[$n+1] fi - echo $patch if [ $patch = "$stop_at_patch" ] then break fi + echo $patch done if [ -n "$stop_at_patch" -a "$patch" != "$stop_at_patch" ] then @@ -82,7 +87,7 @@ list_patches() fi } -options=`getopt -o fRqah -- "$@"` +options=`getopt -o fRqvah -- "$@"` if [ $? -ne 0 ] then @@ -105,6 +110,9 @@ do -q) opt_quiet=1 shift ;; + -v) + opt_verbose=1 + shift ;; -a) opt_all=1 shift ;; @@ -139,6 +147,8 @@ fi rpatch_options="$rpatch_options -R" [ -n "$opt_quiet" ] && rpatch_options="$rpatch_options -q" +[ -n "$opt_verbose" ] && + rpatch_options="$rpatch_options -v" if [ -n "$stop_at_patch" ] then @@ -164,12 +174,11 @@ for patch in $patches do if ! @LIB@/rpatch $rpatch_options $patch then - echo Still at patch $patch exit 1 fi if [ -n "$interrupted" ] then - echo "poppatch interrupted by user" + echo "Interrupted by user" exit 1 fi [ -z "$opt_quiet" ] && echo diff --git a/quilt/previous.in b/quilt/previous.in new file mode 100644 index 0000000..45d4ce8 --- /dev/null +++ b/quilt/previous.in @@ -0,0 +1,70 @@ +#!/bin/bash + +# This script is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# See the COPYING and AUTHORS files for more details. + +# Read in library functions +if [ "$(type -t patch_file_name)" != function ] +then + if ! [ -r @LIB@/patchfns ] + then + echo "Cannot read library @LIB@/patchfns" >&2 + exit 1 + fi + . @LIB@/patchfns +fi + +usage() +{ + echo "Usage: quilt previous [patch]" + if [ x$1 = x-h ] + then + cat <<EOF + +Print the name of the previous patch before the specified or topmost +patch in the series file. + +EOF + exit 0 + else + exit 1 + fi +} + +options=`getopt -o h -- "$@"` + +if [ $? -ne 0 ] +then + usage +fi + +eval set -- "$options" + +while true +do + case "$1" in + -h) + usage -h ;; + --) + shift + break ;; + esac +done + +if [ $# -gt 1 ] +then + usage +elif [ $# -eq 1 ] +then + patch=$(stripit $1) +else + patch=$(top_patch) +fi + +if [ -n "$patch" ] +then + patches_before $patch | tail -1 +fi diff --git a/pushpatch.in b/quilt/push.in index d8fcab9..d488c7e 100755..100644 --- a/pushpatch.in +++ b/quilt/push.in @@ -1,4 +1,4 @@ -#! /bin/bash +#!/bin/bash # This script is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as @@ -7,16 +7,19 @@ # See the COPYING and AUTHORS files for more details. # Read in library functions -if ! [ -r @LIB@/patchfns ] +if [ "$(type -t patch_file_name)" != function ] then - echo "Cannot read library @LIB@/patchfns" >&2 - exit 1 + if ! [ -r @LIB@/patchfns ] + then + echo "Cannot read library @LIB@/patchfns" >&2 + exit 1 + fi + . @LIB@/patchfns fi -. @LIB@/patchfns usage() { - echo "Usage: pushpatch [-afq] [npatches|patchname]" + echo "Usage: quilt push [-afqv] [num|patch]" if [ x$1 = x-h ] then cat <<EOF @@ -33,6 +36,8 @@ specified, apply the next patch. -q Quiet operation. +-v Verbose operation. + EOF exit 0 else @@ -77,7 +82,7 @@ list_patches() fi } -options=`getopt -o fqah -- "$@"` +options=`getopt -o fqvah -- "$@"` if [ $? -ne 0 ] then @@ -95,6 +100,9 @@ do -q) opt_quiet=1 shift ;; + -v) + opt_verbose=1 + shift ;; -a) opt_all=1 shift ;; @@ -127,6 +135,8 @@ fi apatch_options="$apatch_options -f" [ -n "$opt_quiet" ] && apatch_options="$apatch_options -q" +[ -n "$opt_verbose" ] && + apatch_options="$apatch_options -v" if [ -n "$stop_at_patch" ] then @@ -162,7 +172,7 @@ do fi if [ -n "$interrupted" ] then - echo "pushpatch interrupted by user" + echo "Interrupted by user" exit 1 fi [ -z "$opt_quiet" ] && echo diff --git a/quilt/refresh.in b/quilt/refresh.in new file mode 100644 index 0000000..a7e5064 --- /dev/null +++ b/quilt/refresh.in @@ -0,0 +1,201 @@ +#!/bin/bash + +# This script is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# See the COPYING and AUTHORS files for more details. + +# Read in library functions +if [ "$(type -t patch_file_name)" != function ] +then + if ! [ -r @LIB@/patchfns ] + then + echo "Cannot read library @LIB@/patchfns" >&2 + exit 1 + fi + . @LIB@/patchfns +fi + +usage() +{ + local redirect + if [ x$1 != x-h ] + then + redirect='>&2' + fi + echo "Usage: quilt refresh [-p n] [-f] [patch]" $redirect + + if [ x$1 = x-h ] + then + cat <<EOF + +Refresh an applied patch. Refreshes the specified patch, or +the topmost patch by default. Documentation on top of the +actual patch is retained. + +It is possible to refresh patches that are not on top. If +any patches on top of the patch to refresh modify the same +files, this script prints out the file and patch names. If +there are any such conflicts, patches can still be refreshed +with -f. In that case this script will print a warning for +each shadowed file, changes by more recent patches will be +ignored, and only changes in files that have not been +modified by any more recent patches will end up in the +specified patch. + +-p n Create a -pn style patch (-p0 or -p1 supported). + +-f Force refresh, even if more recent patches modify + some of the same files. + +EOF + exit 0 + else + exit 1 + fi +} + +die () +{ + local status=$1 + [ -n "$tmpfile" ] && rm -f $tmpfile + [ -n "$tmpfile2" ] && rm -f $tmpfile2 + exit $status +} + +options=`getopt -o p:fh -- "$@"` + +if [ $? -ne 0 ] +then + usage +fi + +eval set -- "$options" + +while true +do + case "$1" in + -p) + opt_strip_level=$2 + shift 2 ;; + -f) + opt_force=1 + shift ;; + -h) + usage -h ;; + --) + shift + break ;; + esac +done + +if [ $# -eq 1 ] +then + opt_patch=$1 +elif [ $# -gt 1 ] +then + usage +fi + +if [ -n "$opt_patch" ] +then + patch=$(stripit $opt_patch) +else + patch=$(top_patch) + if [ -z "$patch" ] + then + echo "No patches seem to be applied" >&2 + exit 1 + fi +fi + +if ! is_applied "$patch" +then + echo "Patch $patch is not applied" >&2 + exit 1 +fi + +if [ -z "$opt_strip_level" ] +then + opt_strip_level=$(patch_strip_level $patch) +fi +if [ $opt_strip_level -gt 1 ] +then + echo "Cannot refresh patches with -p$opt_strip_level," \ + "please specify -p0 or -p1 instead" >&2 + exit 1 +fi + +trap "die 1" SIGTERM + +tmpfile=$(mktemp /tmp/patch-scripts.XXXXXX) + +for file in $(files_in_patch $patch) +do + old_file=$(backup_file_name $patch $file) + next_patch=$(next_patch_for_file $patch $file) + if [ -z "$next_patch" ] + then + new_file=$file + else + new_file=$(backup_file_name $next_patch $file) + files_were_shadowed=1 + fi + if ! diff_file $file "~${patch//\//_}" $old_file $new_file >> $tmpfile + then + echo "Diff failed, aborting." >&2 + die 1 + fi + + if [ -n "$files_were_shadowed" -a -z "$opt_force" ] + then + echo "More recent patches modify the same files." \ + "Enforce refresh with -f." >&2 + die 1 + fi +done + +if ! [ -s $tmpfile ] +then + echo "Nothing in patch $patch" >&2 + die 1 +fi + +patch_file=$(patch_file_name $patch) + +trap "" SIGINT + +if [ -e $patch_file ] && grep -q '^%patch$' $patch_file +then + if [ -x "$DIFFSTAT" ] + then + $DIFFSTAT $tmpfile 2>/dev/null \ + | @LIB@/parse-patch -u diffstat $patch_file + fi + cat $tmpfile \ + | @LIB@/parse-patch -u patch $patch_file +else + if ! tmpfile2=$(mktemp /tmp/patch-scripts.XXXXXX) + then + die 1 + fi + + if ! cat_file $patch_file \ + | patch_description > $tmpfile2 || \ + ! cat $tmpfile >> $tmpfile2 || \ + ! cat $tmpfile2 \ + | cat_to_file $patch_file + then + die 1 + fi +fi +status=$? + +rm -f $(pc_file_name $patch)~refresh +echo "Refreshed patch $patch" +if ! change_db_strip_level -p$opt_strip_level $patch +then + die 1 +fi +die 0 diff --git a/quilt/remove.in b/quilt/remove.in new file mode 100644 index 0000000..bea9baf --- /dev/null +++ b/quilt/remove.in @@ -0,0 +1,119 @@ +#!/bin/bash + +# This script is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# See the COPYING and AUTHORS files for more details. + +# Read in library functions +if [ "$(type -t patch_file_name)" != function ] +then + if ! [ -r @LIB@/patchfns ] + then + echo "Cannot read library @LIB@/patchfns" >&2 + exit 1 + fi + . @LIB@/patchfns +fi + +usage() +{ + echo "Usage: quilt remove [-p patch] {file} ..." + if [ x$1 = x-h ] + then + cat <<EOF + +Remove one or more files to the topmost or named patch. +Files that are modified by patches on top of the specified +patch cannot be removed. + +-p patch + Patch to add files to. + +EOF + exit 0 + else + exit 1 + fi +} + +options=`getopt -o p:h -- "$@"` + +if [ $? -ne 0 ] +then + usage +fi + +eval set -- "$options" + +while true +do + case "$1" in + -p) + opt_patch=$2 + shift 2 ;; + -h) + usage -h ;; + --) + shift + break ;; + esac +done + +if [ $# -lt 1 ] +then + usage +fi + +patch=$(stripit $opt_patch) +if [ -z "$patch" ] +then + patch=$(top_patch) +fi +if [ -z "$patch" ] +then + echo "No patches applied" +fi + +if ! is_applied $patch +then + echo "Patch $patch is not applied" + exit 1 +fi + +status=0 +for file in $* +do + if ! file_in_patch $file $patch + then + echo "File $file is not in patch $patch" + status=1 + continue + fi + + next_patch=$(next_patch_for_file $patch $file) + if [ -n "$next_patch" ] + then + echo "File $file modified by patch $next_patch" + status=1 + continue + fi + + # Restore file from backup + if ! @LIB@/backup-files -s -B .pc/$patch/ -r $file + then + echo "Failed to remove file $file from patch $patch" + status=1 + continue + fi + + pc_file=$(pc_file_name $patch) + tmpfile=$(mktemp /tmp/patch-scripts.XXXXXX) && + grep -v '^'"$(quote_re $file)"'$' $pc_file > $tmpfile && + mv -f $tmpfile $pc_file + rm -f $tmpfile + + echo "File $file removed from patch $patch" +done +exit $status diff --git a/quilt/rest.in b/quilt/rest.in new file mode 100755 index 0000000..6642fbb --- /dev/null +++ b/quilt/rest.in @@ -0,0 +1,72 @@ +#!/bin/bash + +# This script is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# See the COPYING and AUTHORS files for more details. + +# Read in library functions +if [ "$(type -t patch_file_name)" != function ] +then + if ! [ -r @LIB@/patchfns ] + then + echo "Cannot read library @LIB@/patchfns" >&2 + exit 1 + fi + . @LIB@/patchfns +fi + +usage() +{ + echo "Usage: quilt rest [patch]" + if [ x$1 = x-h ] + then + cat <<EOF + +Print a list of patches that are not applied, or all patches that follow +the specified patch in the series file. + +EOF + exit 0 + else + exit 1 + fi +} + +options=`getopt -o h -- "$@"` + +if [ $? -ne 0 ] +then + usage +fi + +eval set -- "$options" + +while true +do + case "$1" in + -h) + usage -h ;; + --) + shift + break ;; + esac +done + +if [ $# -gt 1 ] +then + usage +elif [ $# -eq 1 ] +then + patch=$(stripit $1) +else + patch=$(top_patch) +fi + +if [ -z "$patch" ] +then + cat_series +else + patches_after $patch +fi diff --git a/quilt/series.in b/quilt/series.in new file mode 100644 index 0000000..7c10fad --- /dev/null +++ b/quilt/series.in @@ -0,0 +1,87 @@ +#!/bin/bash + +# This script is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# See the COPYING and AUTHORS files for more details. + +# Read in library functions +if [ "$(type -t patch_file_name)" != function ] +then + if ! [ -r @LIB@/patchfns ] + then + echo "Cannot read library @LIB@/patchfns" >&2 + exit 1 + fi + . @LIB@/patchfns +fi + +usage() +{ + echo "Usage: quilt series [-v]" + if [ x$1 = x-h ] + then + cat <<EOF + +Print the names of all patches in the file series. + +-v Verbose, more user friendly output +EOF + exit 0 + else + exit 1 + fi +} + +cat_patches() +{ + local prefix=$1 + shift + local patch + + for patch in "$@" + do + echo "$prefix$patch" + done +} + +options=`getopt -o vh -- "$@"` + +if [ $? -ne 0 ] +then + usage +fi + +eval set -- "$options" + +opt_what=here + +while true +do + case "$1" in + -v) + opt_verbose=1 + shift ;; + -h) + usage -h ;; + --) + shift + break ;; + esac +done + +if [ $# -ne 0 ] +then + usage +fi + +if [ -n "$opt_verbose" ] +then + top=$(top_patch) + cat_patches "+ " $(patches_before $top) + [ -n "$top" ] && cat_patches "= " $top + cat_patches " " $(patches_after $top) +else + cat_series +fi diff --git a/quilt/setup.in b/quilt/setup.in new file mode 100644 index 0000000..3d2bee0 --- /dev/null +++ b/quilt/setup.in @@ -0,0 +1,172 @@ +#!/bin/bash + +# This script is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# See the COPYING and AUTHORS files for more details. + +# Read in library functions +if [ "$(type -t patch_file_name)" != function ] +then + if ! [ -r @LIB@/patchfns ] + then + echo "Cannot read library @LIB@/patchfns" >&2 + exit 1 + fi + . @LIB@/patchfns +fi + +usage() +{ + echo "Usage: quilt setup [-d sourcedir] {seriesfile|specfile}" + if [ x$1 = x-h ] + then + cat <<EOF + +Initializes a source tree from a patch series file. The +patch series file must contain the name of the relevant +tar archive, in addition to the list of patches. + +EOF + exit 0 + else + exit 1 + fi +} + +parse_series() +{ + local series="$1" + + perl -e ' + while(<>) { + if (/^#\s*[Ss]ource:\s*(\S+)\s*(-C\s*(\S+))?/) { + print "S $1 ", ($3 ? $3 : "."), "\n"; + } elsif (/^#\s*[Pp]atchdir:\s*(\S+)/) { + print "D $1\n"; + } elsif (/^([^#\s]+)/) { + print "P $1\n"; + } + } + ' $series + echo "I $series" +} + +options=`getopt -o d:h -- "$@"` + +if [ $? -ne 0 ] +then + usage +fi + +eval set -- "$options" + +while true +do + case "$1" in + -d) + opt_source=$2 + shift 2 ;; + -h) + usage -h ;; + --) + shift + break ;; + esac +done + +if [ $# -ne 1 ] +then + usage +fi + +case "$1" in +*.spec) + spec_file="$1" + tmpfile=$(mktemp /tmp/patch-scripts.XXXXXX) + series_file=$tmpfile + if ! @LIB@/spec2series "$spec_file" > $tmpfile + then + exit 1 + fi + ;; +*) + series_file=$1 + if ! [ -e "$series_file" ] + then + echo "Series file $series_file not found" + exit 1 + fi + ;; +esac + +if [ -n "$opt_source" ] +then + source="$opt_source" +elif [ -n "$spec_file" ] +then + source="$(dirname "$spec_file")" +else + source="$(dirname "$series_file")" +fi + +status=0 +packagedir=. +while read cmd arg arg2 && \ + [ $status -eq 0 ] +do + case $cmd in + D) + # Base directory for package + packagedir="$arg" + if [ -d "$packagedir" ] + then + echo "Directory $packagedir exists already." + status=1 + break + elif [ -e "$packagedir" ] + then + echo "File $packagedir exists." + status=1 + break + fi + ;; + S) + echo "Unpacking archive $source/$arg" + mkdir -p "$arg2" && \ + cat_file "$source/$arg" \ + | tar xf - -C "${arg2:-.}" + ;; + P) + echo "Copying patch $source/$arg" + mkdir -p "$packagedir/patches/" && \ + cp "$source/$arg" "$packagedir/patches/" \ + ;; + I) + echo "Copying series file" + cp "$arg" "$packagedir/patches/series" + ;; + *) + status=1 + break + ;; + esac + status=$? +done \ +< <(parse_series "$series_file") + +if [ -n "$tmpfile" ] +then + rm -f $tmpfile +fi + +if [ $status -ne 0 ] +then + exit 1 +fi + +if [ "$packagedir" != "." ] +then + echo "Directory $packagedir set up." +fi diff --git a/quilt/top.in b/quilt/top.in new file mode 100644 index 0000000..84f6ba7 --- /dev/null +++ b/quilt/top.in @@ -0,0 +1,62 @@ +#!/bin/bash + +# This script is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# See the COPYING and AUTHORS files for more details. + +# Read in library functions +if [ "$(type -t patch_file_name)" != function ] +then + if ! [ -r @LIB@/patchfns ] + then + echo "Cannot read library @LIB@/patchfns" >&2 + exit 1 + fi + . @LIB@/patchfns +fi + +usage() +{ + echo "Usage: quilt top" + if [ x$1 = x-h ] + then + cat <<EOF + +Print the name of the topmost patch on the current stack of applied +patches. + +EOF + exit 0 + else + exit 1 + fi +} + +options=`getopt -o h -- "$@"` + +if [ $? -ne 0 ] +then + usage +fi + +eval set -- "$options" + +while true +do + case "$1" in + -h) + usage -h ;; + --) + shift + break ;; + esac +done + +if [ $# -ne 0 ] +then + usage +fi + +top_patch diff --git a/refpatch.in b/refpatch.in deleted file mode 100755 index f1a95b3..0000000 --- a/refpatch.in +++ /dev/null @@ -1,335 +0,0 @@ -#! /bin/bash - -# This script is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# See the COPYING and AUTHORS files for more details. - -# Read in library functions -if ! [ -r @LIB@/patchfns ] -then - echo "Cannot read library @LIB@/patchfns" >&2 - exit 1 -fi -. @LIB@/patchfns - -usage() -{ - local redirect - if [ x$1 != x-h ] - then - redirect='>&2' - fi - echo "Usage: refpatch [-cfz] [patchname]" $redirect - - if [ x$1 = x-h ] - then - cat <<EOF - -Refresh an applied patch. Refreshes the specified patch, or -the topmost patch by default. Documentation on top of the -actual patch is retained. - -It is possible to refresh patches that are not on top. -If any patches on top of the patch to refresh modify the -same files, refpatch prints out the file and patch names. -If there are any such conflicts, patches can still be -refreshed with -f. In that case refpatch will print a -warning for each thus shadowed file, changes by more -recent patches will be ignored, and only changes in files -that have not been modified by any more recent patches -will end up in the specified patch. - --c Write to standard output only; don't update the patch. - --f Force refresh, even if more recent patches modify - some of the same files. - --z Write to standard output the changes that have been - made relative to the topmost or specified patch. - -EOF - exit 0 - else - exit 1 - fi -} - -fix_diff_header() -{ - local from=$1 to=$2 - sed -e 's:^--- [^ \t]*:--- '"$from"':' \ - -e 's:^+++ [^ \t]*:+++ '"$to"':' -} - -diff_file() -{ - local file=$1 patch=$2 status=0 - local next_patch=$(next_patch_for_file $patch $file) - local old_file=$(backup_file_name $patch $file) - local new_file file_was_shadowed - if [ -n "$next_patch" ] - then - new_file=$(backup_file_name $next_patch $file) - file_was_shadowed=1 - else - new_file=$file - fi - - if [ ! -e $old_file -a ! -e $new_file ] - then - echo "File $new_file does not exist" >&2 - return 0 - fi - local dir=$(basename $PWD) suffix=${patch//\//_} old_hdr new_hdr - if [ $opt_strip -eq 0 ] - then - old_hdr=$file~$suffix - new_hdr=$file - else - old_hdr=$dir~$suffix/$file - new_hdr=$dir/$file - fi - - if [ -z "$opt_post" ] - then - echo diff -Nu $DIFF_OPTS $old_file $new_file >&2 - diff -Nu $DIFF_OPTS $old_file $new_file | - fix_diff_header $old_hdr $new_hdr - status=$? - - if [ $status -eq 0 -a -n "$file_was_shadowed" ] - then - status=2 - fi - elif [ -n "$file_was_shadowed" ] - then - return 0 - else - local tmp=$(mktemp /tmp/patch-scripts.XXXXXX) - if ! filterdiff $(patch_args $patch) -i $file \ - $(patch_file_name $patch) \ - | patch -s -o $tmp $old_file --no-backup-if-mismatch - then - echo "Failed to patch temporary file" >&2 - return 1 - fi - - echo diff -Nu $DIFF_OPTS $new_file.orig $new_file >&2 - diff -Nu $DIFF_OPTS $tmp $new_file | - fix_diff_header $new_file.orig $new_hdr - status=$? - rm -f $tmp - fi - return $status -} - -die () -{ - local status=$1 - [ -n "$tmp" ] && rm -f $tmp - [ -n "$tmp2" ] && rm -f $tmp2 - [ -n "$workdir" ] && rm -rf $workdir - exit $status -} - -options=`getopt -o p:fczh -- "$@"` - -if [ $? -ne 0 ] -then - usage -fi - -eval set -- "$options" - -while true -do - case "$1" in - -p) - opt_strip=$2 - shift 2 ;; - -c) - opt_stdout=1 - shift ;; - -f) - opt_force=1 - shift ;; - -z) - opt_post=1 - opt_stdout=1 - shift ;; - -h) - usage -h ;; - --) - shift - break ;; - esac -done - -if [ $# -eq 1 ] -then - opt_patch=$1 -elif [ $# -gt 1 ] -then - usage -fi - -if [ -n "$opt_patch" ] -then - patch=$(stripit $opt_patch) -else - patch=$(top_patch) - if [ -z "$patch" ] - then - echo "No patches seem to be applied" >&2 - exit 1 - fi -fi - -if ! is_applied "$patch" -then - echo "Patch $patch is not applied" >&2 - exit 1 -fi - -if [ -z "$opt_strip" ] -then - opt_strip=$(patch_strip_level $patch) -fi -if [ $opt_strip -gt 1 ] -then - echo "Cannot refresh patches with -p$opt_strip," \ - "please specify -p0 or -p1 instead" >&2 - exit 1 -fi - -trap "die 1" SIGTERM - -if [ -z "$opt_stdout" ] -then - tmp=$(mktemp /tmp/patch-scripts.XXXXXX) -fi - -if [ -n "$opt_post" ] -then - patch_file=$(patch_file_name $patch) - files=$(files_in_patch $patch) - workdir=$(mktemp -d patch-scripts.XXXXXX) - pwd=$PWD - - if ! cd .pc/$patch - then - echo "Cannot change into .pc/$patch" - die 1 - fi - if ! cp -l --parents $files $pwd/$workdir/ - then - echo "Failed to copy files to temporary directory" - die 1 - fi - if ! cd $pwd/$workdir - then - echo "Cannot change to temporary directory" - die 1 - fi - if ! patch $(patch_args $patch) --no-backup-if-mismatch \ - -E -i $pwd/$patch_file >/dev/null 2>/dev/null - then - echo "Failed to patch temporary files" - die 1 - fi - if ! cd $pwd - then - echo "Cannot change to source directory" - die 1 - fi - - for file in $(files_in_patch $patch) - do - suffix=${patch//\//_} - if [ $opt_strip -eq 0 ] - then - old_hdr=$file.orig - new_hdr=$file - else - dir=$(basename $PWD) - old_hdr=$dir.orig/$file - new_hdr=$dir/$file - fi - echo diff -Nu $DIFF_OPTS $new_hdr.orig $new_hdr >&2 - diff -Nu $DIFF_OPTS $workdir/$file $file \ - | fix_diff_header $old_hdr $new_hdr - if [ $? -ne 0 ] - then - status=1 - fi - done - die $status -else - for file in $(files_in_patch $patch) - do - diff_file $file $patch - status=$? - if [ $status -eq 2 ] - then - files_were_shadowed=1 - elif [ $status -ne 0 ] - then - echo "Diff failed, aborting." >&2 - die 1 - fi - - if [ -n "$files_were_shadowed" -a -z "$opt_force" ] - then - echo "More recent patches modify the same files." \ - "Enforce refresh with -f." >&2 - die 1 - fi - done -fi \ -| if [ -z "$opt_stdout" ] -then - cat > $tmp -else - cat -fi - -if [ -z "$opt_stdout" ] -then - if ! [ -s $tmp ] - then - echo "Nothing in patch $patch" >&2 - die 1 - fi - - patch_file=$(patch_file_name $patch) - - trap "" SIGINT - - if [ ! -e $patch_file ] || grep -q '^%patch$' $patch_file - then - touch $patch_file - cat $tmp \ - | @LIB@/parse-patch -u patch $patch_file - else - tmp2=$(mktemp /tmp/patch-scripts.XXXXXX) && - patch_description $patch_file > $tmp2 && - cat $tmp >> $tmp2 && - mv $tmp2 $patch_file - fi - status=$? - - rm -f $tmp $tmp2 - if [ $status -ne 0 ] - then - die 1 - fi - - rm -f $(pc_file_name $patch)~forced - echo "Refreshed patch $patch" - if ! change_db_strip_level -p$opt_strip $patch - then - die 1 - fi -fi diff --git a/toppatch.in b/toppatch.in deleted file mode 100755 index e65dc9d..0000000 --- a/toppatch.in +++ /dev/null @@ -1,138 +0,0 @@ -#! /bin/bash - -# This script is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# See the COPYING and AUTHORS files for more details. - -# Read in library functions -if ! [ -r @LIB@/patchfns ] -then - echo "Cannot read library @LIB@/patchfns" >&2 - exit 1 -fi -. @LIB@/patchfns - -usage() -{ - echo "Usage: toppatch [-bpnfa] [patchname]" - if [ x$1 = x-h ] - then - cat <<EOF - -Print the name of the topmost patch on the current stack -of applied patches, or the names of one or more patches -relative to this patch. - --b Print the names of all patches before the - topmost or specified patch. - --p Print the name of the previous patch in the - series. - --n Print the name of the next patch in the series. - --f Print the names of all patches following the - topmost or specified patch. - --a Print the names of all patches in the file series. - -EOF - exit 0 - else - exit 1 - fi -} - -options=`getopt -o bpnfah -- "$@"` - -if [ $? -ne 0 ] -then - usage -fi - -eval set -- "$options" - -opt_what=here - -while true -do - case "$1" in - -b) - opt_what=before - shift ;; - -p) - opt_what=previous - shift ;; - -n) - opt_what=next - shift ;; - -f) - opt_what=follow - shift ;; - -a) - opt_what=all - shift ;; - -h) - usage -h ;; - --) - shift - break ;; - esac -done - -if [ $# -gt 1 ] -then - usage -elif [ $# -eq 1 ] -then - opt_patch=$1 -fi - -if [ -n "$opt_patch" ] -then - patch=$(stripit $opt_patch) -else - patch=$(top_patch) -fi - -case $opt_what in -before) - if [ -n "$patch" ] - then - patches_before $patch - fi - ;; -previous) - if [ -n "$patch" ] - then - patches_before $patch | tail -1 - fi - ;; -here) - if [ -n "$patch" ] - then - echo $patch - fi - ;; -next) - if [ -z "$patch" ] - then - cat_series | head -1 - else - patches_after $patch | head -1 - fi - ;; -follow) - if [ -z "$patch" ] - then - cat_series - else - patches_after $patch - fi - ;; -all) - cat_series - ;; -esac |