]> git.neil.brown.name Git - wiggle.git/blob - p
Disable *all* backups when --no-backups used
[wiggle.git] / p
1 #!/bin/bash
2
3 # patch management
4 #
5 # Copyright (C) 2003 Neil Brown <neilb@cse.unsw.edu.au>
6 #
7 #
8 #    This program is free software; you can redistribute it and/or modify
9 #    it under the terms of the GNU General Public License as published by
10 #    the Free Software Foundation; either version 2 of the License, or
11 #    (at your option) any later version.
12 #
13 #    This program is distributed in the hope that it will be useful,
14 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
15 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 #    GNU General Public License for more details.
17 #
18 #    You should have received a copy of the GNU General Public License
19 #    along with this program; if not, write to the Free Software
20 #    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21 #
22 #    Author: Neil Brown
23 #    Email: <neilb@cse.unsw.edu.au>
24 #    Paper: Neil Brown
25 #           School of Computer Science and Engineering
26 #           The University of New South Wales
27 #           Sydney, 2052
28 #           Australia
29
30
31 # metadata is in .patches
32 # there is:
33 #   files:  list of all files checked out
34 #   name:   name of current patch
35 #   status: status of current patch
36 #   notes:  notes on current patch
37 #   applied/  patches applied nnn-name
38 #   removed/  patches removed nnn-name
39 #   included/ patches that have been included upstream
40 #   patch:  a recent copy of the 'current' patch
41 #   get-version: a script which will report the version number of the base dist
42 #   dest/   symlink to directory to publish snapshots to
43 #   mail/   composed mail messages ready for sending
44 #   maintainer  who to email patches to (Linus etc)
45 #   cc        who to CC patches to: prefix address
46 #
47 # the nnn in names in applied and removed are sequence numbers
48 # whenever we add a file we choose one more than the highest used number
49 # patch files contain then name implicitly and start with
50 #   Status: status
51 # then a blank line, normally a one line description, another blank, and more detail.
52 #
53
54 #
55 # Todo - auto bk pull:
56 #    bk pull
57 #    bk export -t patch -r DEVEL, > /tmp/apatch
58 #    bk tag DEVEL
59 #    while p open last && p discard ; do : ; done
60 #    p clean
61 #    patch -p1 -f < /tmp/apatch
62
63 find_home()
64 {
65         # walk up directory tree until a .patches directory
66         # is found.
67         # set OrigDir to name of where we were .. not dots.
68         OrigDir=
69         dir=`pwd`
70         while [ ! -d .patches -a " $dir"  != " /" ]
71         do
72                 base=${dir##*/}
73                 base=${base#/}
74                 dir=${dir%/*}
75                 case $dir in
76                         "" ) dir=/
77                 esac
78                 OrigDir=$base/$OrigDir
79                 cd ..
80         done
81         test -d .patches
82 }
83
84 get_meta()
85 {
86         name=`cat .patches/name 2> /dev/null`
87         status=`cat .patches/status 2> /dev/null`
88 }
89
90 nl='
91 '
92 get_conf()
93 {
94         _name=$1
95         _context=$2
96         _result=
97         _active=yes
98         _sep=
99         [ -f .patches/config ] || >> .patches/config
100         while read a b c
101         do
102             case $a in
103                     '[global]' ) _active=yes ;;
104                     "[$_context]") _active=yes ;;
105                     "["*"]" ) _active= ;;
106                     * ) if [ " $b" == " =" -a " $a" = " $_name" -a -n "$_active" ];
107                             then
108                             if [ -z "$c" ]; then
109                                 _result= _sep=
110                             else
111                                 _result="$_result$_sep$c"
112                                 _sep=$nl
113                             fi
114                             fi
115                             ;;
116             esac
117         done < .patches/config
118         _result=$(echo "$_result" | sed 's/^"//' )
119         eval $_name=\"\$_result\"
120 }
121
122 upgrade_one()
123 {
124         # move $1~current~ to .patches/current/$1 and same for orig
125         fl=/$1
126         for f in current orig
127         do
128             if [ -f "$1~$f~" ]
129                 then
130                     mkdir -p ".patches/$f${fl%/*}"
131                     mv "$1~$f~" ".patches/$f/$1"
132             fi
133         done
134 }
135
136
137 forget_one()
138 {
139         if true # || cmp -s "$1" ".patches/curent/$1~" && cmp -s "$1" ".patches/orgi/$1"
140         then
141             rm -f ".patches/current/$1" ".patches/orig/$1"
142             chmod -w "$1"
143         else
144             echo >&2 "ERROR $1 doesn't match original"
145         fi
146 }
147
148 rebase_one()
149 {
150     f="/$1"
151     mkdir -p .patches/orig${f%/*}
152     mkdir -p .patches/current${f%/*}
153     rm -f .patches/orig$f .patches/current$f
154     cp -p $1 .patches/orig$f
155     cp -p $1 .patches/current$f
156 }
157
158 snap_one()
159 {
160     cp "$1" "$1~snapshot~"
161 }
162
163 snap_diff()
164 {
165     diff -u "$1" "$1~snapshot~"
166 }
167 snap_back()
168 {
169     cp "$1~snapshot~" "$1"
170 }
171
172 check_out()
173 {
174         file=$1
175         file=${file#./}
176         f=/$file; f=${f%/*}
177         [ -f $file ] || >> $file
178         if [ -f $file ]
179             then
180             if [ ! -f ".patches/orig/$file" ] ; then
181                 mkdir -p .patches/orig/$f
182                 mv "$file" ".patches/orig/$file"
183                 cp ".patches/orig/$file" "$file"
184                 echo $file >> .patches/files
185                 sort -o .patches/files .patches/files
186                 chmod u+w "$file"
187             fi
188             if [ ! -f ".patches/current/$file" ] ; then
189                 mkdir -p .patches/current/$f
190                 mv "$file" ".patches/current/$file"
191                 cp ".patches/current/$file" "$file"
192             fi
193         else
194             echo >&2 Cannot checkout $file
195         fi
196 }
197
198 all_files()
199 {
200         >> .patches/files
201         while read file
202         do eval $1 $file $2
203         done < .patches/files
204 }
205
206 diff_one()
207 {
208         if cmp -s ".patches/current/$1" "$1" || [ ! -f "$1" -a ! -f ".patches/current/$1" ]
209         then :
210         else
211                 echo
212                 echo "diff .prev/$1 ./$1"
213                 if [ " $2" = " -R" ]
214                 then
215                   diff -N --show-c-function -u "./$1" "./.patches/current/$1"
216                 else
217                   diff -N --show-c-function -u "./.patches/current/$1" "./$1"
218                 fi
219         fi
220 }
221
222 diff_one_orig()
223 {
224         if cmp -s ".patches/orig/$1" "$1"
225         then :
226         else
227                 echo
228                 echo "diff ./.patches/orig/$1 ./$1"
229                 diff --show-c-function -u "./.patches/orig/$1" "./$1"
230         fi
231 }
232
233 commit_one()
234 {
235     rm -f ".patches/current/$1"
236     if [  -f "$1" ] ; then
237         mv "$1" ".patches/current/$1"
238         cp -p ".patches/current/$1" $1
239         chmod u+w $1
240     fi
241 }
242
243 discard_one()
244 {
245         cmp -s ".patches/current/$1" $1 || { rm -f "$1" ; cp ".patches/current/$1" $1; }
246         chmod u+w $1
247 }
248
249 swap_one()
250 {
251         mv "$1" "$1.tmp"
252         mv ".patches/current/$1" "$1"
253         mv "$1.tmp" ".patches/current/$1"
254 }
255
256 make_diff()
257 {
258         get_conf tagline
259         upgrade_one "$1"
260    { {
261         [ -s .patches/status ] && echo "Status: `cat .patches/status`"
262         [ -s .patches/notes ] && { echo; cat .patches/notes ; }
263         if [ -z "$tagline" ] || grep -F "$tagline" .patches/notes > /dev/null 2>&1
264         then :
265         else echo "$tagline"
266         fi
267         echo
268         all_files diff_one $1 > .patches/tmp
269         echo "### Diffstat output"
270         diffstat -p0 2> /dev/null < .patches/tmp
271         cat .patches/tmp
272         [ -s .patches/tmp ] || rm .patches/patch
273         rm .patches/tmp
274    } | sed 's,^--- ./.patches/current/,--- .prev/,' ; } > .patches/patch
275 }
276
277 save_patch()
278 {
279         dir=.patches/$1
280         name=$2
281         # move .patches/patch to $dir/nnn$name
282         #for some new nnn
283         [ -d $dir ] || mkdir $dir || exit 1
284         largest=`ls $dir | sed -n -e 's/^\([0-9][0-9][0-9]\).*/\1/p' | sort -n | tail -1`
285         if [ "0$largest" -eq 999 ]
286         then echo >&2 'ARRG - too many patches!' ; exit 1
287         fi
288         new=`expr "0$largest" + 1001`
289         new=${new#1}
290         mv .patches/patch $dir/$new$name
291 }
292
293 find_prefix()
294 {
295         # set "prefix" to number for -pn by looking at first file in given patch.
296         n=${2-1}
297         file=`lsdiff $1 | head -$n | tail -1`
298         orig=$file
299         prefix=0
300         case $file in
301             b/* ) prefix=1; return
302         esac
303         while [ \( -n "$file" -a ! -f "$file" \) -o " $file" != " ${file#/}" ]
304         do
305             file=`expr "$file" : '[^/]*/\(.*\)'`
306             prefix=`expr $prefix + 1`
307         done
308         if [ -z "$file" ]
309         then echo "Cannot find $orig" >&2
310            if [ $n -gt 4 ]
311            then exit 2;
312            else find_prefix "$1" $[n+1]
313            fi
314         fi
315         if [ " $orig" != " $file" ]
316         then
317             echo "Found $orig as $file - prefix $prefix"
318         fi
319 }
320
321 extract_notes()
322 {
323  # remove first line, Status: line, leading blanks,
324  # everything from ' *---' and trailing blanks
325  awk '
326     BEGIN { head= 1; blanks=0 ; }
327     head == 1 && ( $1 == "Status:" || $0 == "" ) {
328             next;
329     }
330     { head = 0; }
331     $0 == "" { blanks++; next; }
332     $0 ~ /^ *---/ { exit }
333     $0 ~ /^###/ { exit }
334     {   while (blanks > 0) {
335            blanks--; print "";
336         }
337         print $0;
338     }
339   ' $1
340 }
341
342
343 if [ $# -eq 0 ]
344 then
345   echo >&2 'Usage: p [help|co|make|discard|commit|status|name|...] args'
346   exit 1
347 fi
348 cmd=$1
349 shift
350
351 if [ " $cmd" = " help" ] || find_home
352 then :
353 else echo >&2 "p $cmd: cannot find .patches directory"
354      exit 1
355 fi
356
357 case $cmd in
358    co )
359         if [ $# -ne 1 ] ; then
360                 echo >&2 Usage: p co file; exit 1
361         fi
362         file=$1
363         if [ ! -f "$OrigDir$file" ]
364         then
365                 echo >&2 "p co: file $file not found"; exit 1;
366         fi
367         check_out "$OrigDir$file"
368
369         ;;
370   make | view )
371         case $1 in
372             "" )
373                 make_diff
374                 if [ -s .patches/patch ] ; then
375                     pfile=.patches/patch
376                 else
377                     echo >&2 "No current patch" ; exit 1;
378                 fi
379                 ;;
380
381             */* ) pfile=$1;;
382             * ) pfile=`echo .patches/[ra][ep][mp]*/*$1*`
383         esac
384         if [ ! -f "$pfile" ]
385         then echo >&2 "Cannot find unique patch '$1' - found: $pfile"; exit 1;
386         fi
387         ptn='^\+.*((    |[^     ].{7}){10}.|[   ]$)'
388         if grep -E -s "$ptn" $pfile > /dev/null
389         then
390             ${PAGER-less -p "$ptn"} $pfile
391         else
392             ${PAGER-less} $pfile
393         fi
394         ;;
395
396   all )
397         all_files diff_one_orig
398         ;;
399   status | name )
400         case $# in
401          1 )
402                 get_meta
403                 if [ $cmd = name ] ; then
404                   if [ -n "$name" ]; then
405                         echo "changing name from '$name' to '$1'"
406                   else
407                         echo "Setting name to '$1'"
408                   fi
409                   echo "$1" > .patches/name
410                 fi
411                 if [ $cmd = status ] ; then
412                   if [ -n "$status" ]; then
413                         echo "changing status from '$status' to '$1'"
414                   else
415                         echo "Setting status to '$1'"
416                   fi
417                   echo "$1" > .patches/status
418                 fi
419                 ;;
420           0 )
421                 get_meta
422                 echo -n "Name ($name)? " ; read name
423                 echo -n "Status ($status)?  " ; read status
424                 [ -n "$name" ] && { echo $name > .patches/name ; }
425                 [ -n "$status" ] && { echo $status > .patches/status ; }
426                 ;;
427           * )
428                 echo "Usage: p $cmd [new-$cmd]"; exit 1;
429         esac
430         ;;
431   note* )
432         >> .patches/notes
433         ${EDITOR:-vi} .patches/notes
434         ;;
435   discard|commit )
436         make_diff
437         if [ -s .patches/patch ]
438         then :
439         else echo >&2 No patch to $cmd ; exit 1
440         fi
441         if grep -s '^+.*[       ]$' .patches/patch > /dev/null
442         then
443             echo >&2 remove trailing spaces/tabs first !!
444 #           exit 1
445         fi
446         if [ $cmd == "commit" -a -f scripts/checkpatch.pl ] ; then
447             perl scripts/checkpatch.pl .patches/patch
448         fi
449         if [ -s .patches/to-resolve ]
450         then echo "Please resolve outstanding conflicts first with 'p resolve'"
451             exit 1
452         fi
453         get_meta
454         if [ -z "$name" ] ; then
455                 echo -n "Name? " ; read name
456                 if [ -z "$name" ] ; then
457                    echo >&2 "No current name, please set with 'p name'"
458                    exit 1;
459                 fi
460                 echo $name > .patches/name
461         fi
462         if [ -z "$status" ] ; then
463                 echo -n "Status? " ; read status
464                 if [ -z "$status" ] ; then
465                     echo >&2 "No current status, please set with 'p status'"
466                     exit 1;
467                 fi
468                 echo $status > .patches/status
469         fi
470         if [ -s .patches/notes ]
471         then :
472         else
473             { echo "Title...."
474               echo
475               echo "Description..."
476               echo
477               echo "====Do Not Remove===="
478               cat .patches/patch
479             } > .patches/notes
480             ${EDITOR-vi} .patches/notes
481             mv .patches/notes .patches/tmp
482             sed '/^====Do Not Remove====/,$d' .patches/tmp > .patches/notes
483             rm .patches/tmp
484         fi
485         make_diff
486         
487         if [ $cmd = commit ] ; then
488            save_patch applied "$name"
489            echo Saved as $new$name
490            all_files commit_one
491         else
492            save_patch removed "$name"
493            echo Saved as $new$name
494            all_files discard_one
495         fi
496         rm -f .patches/name .patches/status .patches/notes
497         ;;
498
499   purge )
500         make_diff
501         mv .patches/patch .patches/last-purge
502         all_files discard_one
503         rm -f .patches/name .patches/status .patches/notes
504         ;;
505   open )
506         make_diff
507         get_meta
508         if [ -s .patches/patch ]
509         then
510                 echo >&2 Patch $name already open - please commit; exit 1;
511         fi
512         if [ $# -eq 0 ]
513         then
514                 echo "Available patches are:"
515                 ls .patches/applied
516                 exit 0
517         fi
518         if [ $# -ne 1 ]
519         then echo >&2 "Usage: p open patchname" ; exit 1
520         fi
521         if [ " $1" = " last" ]
522         then
523             pfile=`ls -d .patches/applied/[0-9]* | tail -1`
524         else
525             pfile=`echo .patches/applied/*$1*`
526         fi
527         if [ ! -f "$pfile" ]
528         then echo >&2 "Cannot find unique patch '$1' - found: $pfile"; exit 1
529         fi
530         # lets see if it applies cleanly
531         if patch -s --fuzz=0 --dry-run -R -f -p0 < "$pfile"
532         then echo Ok, it seems to apply
533         else echo >&2 "Sorry, that patch doesn't apply" ; exit 1
534         fi
535         # lets go for it ...
536         patch --fuzz=0 -R -f -p0 < "$pfile"
537         all_files swap_one
538         sed -n -e '2q' -e 's/^Status: *//p' $pfile > .patches/status
539         base=${pfile##*/[0-9][0-9][0-9]}
540         [ -s .patches/name ] || echo $base > .patches/name
541         extract_notes $pfile >> .patches/notes
542         mv $pfile .patches/patch
543
544         ;;
545   included )
546         force=
547         if [ " $1" = " -f" ] ; then
548             force=yes; shift
549         fi
550         make_diff; get_meta
551         if [ -s .patches/patch ]
552         then
553             echo >&2 Patch $name already open, please commit; exit 1;
554         fi
555         if [ $# -eq 0 ]
556         then
557             echo "Unapplied patches are:"
558             ls .patches/removed
559             exit 0;
560         fi
561         if [ $# -ne 1 ]
562         then
563              echo >&2 "Usage: p included patchname"; exit 1
564         fi
565         case $1 in
566             last ) pfile=`ls -d .patches/removed/[0-9]* | tail -1` ;;
567             */* ) echo >&2 "Only local patches can have been included"; exit 1 ;;
568             *) pfile=`echo .patches/removed/*$1*`
569         esac
570         if [ ! -f "$pfile" ]
571         then echo >&2 "Cannot find unique patch '$1' - found $pfile"; exit 1
572         fi
573         echo "Using $pfile..."
574
575         # make sure patch applies in reverse
576         if patch -s --fuzz=2  -l --dry-run -f -p0 -R < "$pfile"
577         then echo "Yep, that seems to be included"
578         elif [ -n "$force" ]
579         then echo "It doesn't apply reverse-out cleanly, but you asked for it..."
580         else echo >&2 "Sorry, patch cannot be removed"; exit 1
581         fi
582         mv "$pfile" .patches/patch
583         name=${pfile##*/[0-9][0-9][0-9]}
584         save_patch included $name
585         echo "Moved to $new$name"
586         ;;
587   review )
588         # there are some patches in .removed that may be included in the current source
589         # we try to backout each one. If it backs out successfully, we move it to
590         # .reviewed and continue, else  we abort
591         # Once this has been done often enough, 'reviewed' should be run to
592         # move stuff to 'included' and to revert those patches
593         force=
594         if [ " $1" = " -f" ] ; then
595             force=yes; shift
596         fi
597         make_diff; get_meta
598         if [ -s .patches/patch ]
599         then
600             echo >&2 Patch $name already open, please deal with it; exit 1;
601         fi
602         if [ -f .patches/in-review ]
603         then :
604         else
605                 applied=`ls .patches/applied`
606                 if [ -n "$applied" ]
607                 then
608                         echo >&2 Cannot review patches while any are applied.
609                         exit 1;
610                 fi
611                 > .patches/in-review
612         fi
613         if [ $# -eq 0 ]
614         then
615             echo "Pending patches are:"
616             ls .patches/removed
617             exit 0;
618         fi
619         if [ $# -ne 1 ]
620         then
621              echo >&2 "Usage: p review patchname"; exit 1
622         fi
623         case $1 in
624             */* ) echo >&2 "Only local patches can have been included"; exit 1 ;;
625             *) pfile=`echo .patches/removed/*$1*`
626         esac
627         if [ ! -f "$pfile" ]
628         then echo >&2 "Cannot find unique patch '$1' - found $pfile"; exit 1
629         fi
630         echo "Starting from $pfile..."
631         found=
632         for fl in .patches/removed/*
633         do
634           if [ " $fl" = " $pfile" ]; then found=yes ; fi
635           if [ -n "$found" ]; then
636               echo Checking $fl
637               find_prefix "$fl"
638               lsdiff --strip=$prefix "$fl" | grep -v 'file.*changed' | while read a b
639               do check_out $a
640               done
641               if patch -s --fuzz=0 --dry-run -f -p$prefix -R < "$fl"
642               then echo Looks good..
643               elif [ -n "$force" ]
644               then echo "It doesn't backout cleanly, but you asked for it..."
645                   cp $fl .patches/last-backed
646               else echo "Patch won't back out, sorry"
647                   exit 1
648               fi
649               patch --fuzz=0 -f -p$prefix -R < "$fl" | tee .patches/tmp
650               sed -n -e '2q' -e 's/^Status: *//p' $fl > .patches/status
651               base=${fl##*/}
652               base=${base##[0-9][0-9][0-9]}
653               base=${base##patch-?-}
654               [ -s .patches/name ] || echo $base > .patches/name
655               extract_notes $fl >> .patches/notes
656               rm -f .patches/wiggled
657         sed -n -e 's/.*saving rejects to file \(.*\).rej/\1/p' .patches/tmp |
658         while read file
659         do  echo Wiggling $file.rej into place
660             rm -f $file.porig
661             > .patches/wiggled
662             wiggle --replace --merge $file $file.rej ||
663             echo $file >> .patches/to-resolve
664         done
665
666               mv $fl .patches/patch
667               save_patch reviewed $base
668               if [ -f .patches/wiggled ]
669               then echo 'Some wiggling was needed. Please review and commit'
670                   exit 0
671               fi
672               p commit || exit 1
673           fi
674         done
675         ;;
676
677   reviewed )
678               # all the currently applied patches are patches that have been
679               # reviewed as included.
680               # rip them out and stick them (reversed) into included.
681               if [ ! -f .patches/in-review ]
682                   then
683                       echo >&2 Not currently reviewing patches!
684                       exit 1;
685               fi
686               while p open last
687               do
688                 make_diff -R
689                 get_meta
690                 save_patch included "$name"
691                 echo Saved as "$new$name"
692                 all_files discard_one
693                 rm -f .patches/name .patches/status .patches/notes
694               done
695               rm .patches/in-review
696               ;;
697   list )
698             echo "Applied patches are:"
699             ls .patches/applied
700
701             echo "Unapplied patches are:"
702             ls .patches/removed
703             exit 0
704             ;;
705   lista )
706             echo "Applied patches are:"
707             ls .patches/applied
708             exit 0
709             ;;
710  apply )
711         if [ -f .patches/in-review ]
712         then
713                 echo >&2 Cannot apply patches while reviewing other - use p reviewed
714                 exit 1
715         fi
716         force= append=
717         if [ " $1" = " -f" ]; then
718             force=yes; shift
719         fi
720         if [ " $1" = " -a" ]; then
721             append=yes; shift
722         fi
723         make_diff
724         get_meta
725         if [ -s .patches/patch -a -z "$append" ]
726         then
727             echo >&2 Patch $name already open - please commit ; exit 1;
728         fi
729         if [ $# -eq 0 ]
730         then
731             echo "Unapplied patches are:"
732             ls .patches/removed
733             exit 0
734         fi
735         if [ $# -ne 1 ]
736         then echo >&2 "Usage: p apply patchname"; exit 1
737         fi
738         case $1 in
739             last ) pfile=`ls -d .patches/removed/[0-9]* | tail -1` ; echo last is "$pfile";;
740             */* ) pfile=$1 ;;
741             * ) pfile=`echo .patches/removed/*$1*`
742         esac
743         if [ ! -f "$pfile" ]
744         then echo >&2 "Cannot find unique patch '$1' - found: $pfile"; exit 1
745         fi
746         find_prefix "$pfile"
747         lsdiff --strip=$prefix "$pfile" | grep -v 'file.*changed' | while read a b
748         do check_out $a
749         done
750         # lets see if it applies cleanly
751         if patch -s --fuzz=0 --dry-run -f -p$prefix < "$pfile"
752         then echo OK, it seems to apply
753         elif [ -n "$force" ]
754         then echo "It doesn't apply cleanly, but you asked for it...."
755             echo "Saving original at .patches/last-conflict"
756             cp $pfile .patches/last-conflict
757         else echo >&2 "Sorry, patch doesn't apply"; exit 1
758         fi
759         # lets go for it ...
760         cp $pfile .patches/last-applied
761         patch --fuzz=0 -f -p$prefix < "$pfile" | tee .patches/tmp
762         sed -n -e '2q' -e 's/^Status: *//p' $pfile > .patches/status
763         base=${pfile##*/}
764         base=${base##[0-9][0-9][0-9]}
765         base=${base##patch-?-}
766         [ -s .patches/name ] || echo $base > .patches/name
767         extract_notes $pfile >> .patches/notes
768
769         sed -n -e 's/.*saving rejects to file \(.*\).rej/\1/p' .patches/tmp |
770         while read file
771         do  echo Wiggling $file.rej into place
772             rm -f $file.porig
773             wiggle --replace --merge $file $file.rej ||
774             echo $file >> .patches/to-resolve
775         done
776
777         case $pfile in
778             .patches/removed/* )
779                 mv $pfile .patches/patch
780         esac
781         ;;
782
783   unapply )
784         get_meta
785         mv .patches/last-applied .patches/patch
786         save_patch removed $name
787         echo Restored to $new$name
788         make_diff
789         mv .patches/patch .patches/last-purge
790         all_files discard_one
791         rm -f .patches/name .patches/status .patches/notes
792         ;;
793   publish )
794         name=`date -u +%Y-%m-%d-%H`
795         if [ -d .patches/dest ]
796         then : good
797         else echo >&2 No destination specified at .patches/dest ; exit 1;
798         fi
799         if [ -d .patches/dest/$name ]
800         then
801             echo >&2 $name already exists ; exit 1
802         fi
803         target=.patches/dest/$name
804         mkdir $target
805         if [ -f .patches/get-version ] ;
806         then ./.patches/get-version > $target/version
807         fi
808         [ -f .config ] && cp .config $target
809         cp .patches/applied/* $target
810         mkdir $target/misc
811         cp 2> /dev/null .patches/removed/* $target/misc || rmdir $target/misc
812         chmod -R a+rX $target
813         all_files diff_one_orig > $target/patch-all-$name
814         cd $target
815         echo Published at `/bin/pwd`
816         ;;
817   clean )
818         all_files forget_one
819         > .patches/files
820         ;;
821   openall )
822         while $0 open last && $0 discard ; do : ; done
823         ;;
824   recommit )
825         make_diff
826         get_meta
827         if [ -s .patches/patch ]
828         then
829             echo >&2 Patch $name already open - please commit ; exit 1;
830         fi
831         if [ $# -eq 0 ]
832         then
833             echo "Unapplied patches are:"
834             ls .patches/removed
835             exit 0
836         fi
837         if [ $# -ne 1 ]
838         then echo >&2 "Usage: p recommit patchname"; exit 1
839         fi
840         case $1 in
841             last ) pfile=`ls -d .patches/removed/[0-9]* | tail -1` ; echo last is "$pfile";;
842             */* ) pfile=$1 ;;
843             * ) pfile=`echo .patches/removed/*$1*`
844         esac
845         if [ ! -f "$pfile" ]
846         then echo >&2 "Cannot find unique patch '$1' - found: $pfile"; exit 1
847         fi
848         while [ -s "$pfile" ]  &&
849                 $0 apply last && $0 commit ; do : ; done
850         ;;
851   decommit )
852         make_diff
853         get_meta
854         if [ -s .patches/patch ]
855         then
856             echo >&2 Patch $name already open - please commit ; exit 1;
857         fi
858         if [ $# -eq 0 ]
859         then
860             echo "Applied patches are:"
861             ls .patches/applied
862             exit 0
863         fi
864         if [ $# -ne 1 ]
865         then echo >&2 "Usage: p decommit patchname"; exit 1
866         fi
867         case $1 in
868             last ) pfile=`ls -d .patches/applied/[0-9]* | tail -1` ; echo last is "$pfile";;
869             */* ) pfile=$1 ;;
870             * ) pfile=`echo .patches/applied/*$1*`
871         esac
872         if [ ! -f "$pfile" ]
873         then echo >&2 "Cannot find unique patch '$1' - found: $pfile"; exit 1
874         fi
875         while [ -s "$pfile" ]  &&
876              $0 open last && $0 discard ; do : ; done
877         ;;
878
879   rebase )
880         # move all applied patches to included, and
881         # copy current to orig and current
882         make_diff
883         if [ -s .patches/patch ]
884         then
885             echo >&2 Patch already open - please commit; exit 1;
886         fi
887         for p in `ls .patches/applied`
888         do
889           name=${p##[0-9][0-9][0-9]}
890           mv .patches/applied/$p .patches/patch
891           save_patch included $name
892         done
893         all_files rebase_one
894         ;;
895   snapshot )
896         all_files snap_one
897         ;;
898   snapdiff )
899         all_files snap_diff
900         ;;
901   snapback )
902         all_files snap_back
903         ;;
904   upgrade )
905         all_files upgrade_one
906         ;;
907   resolve )
908         if [ ! -s .patches/resolving ]
909         then sort -u .patches/to-resolve > .patches/resolving ; > .patches/to-resolve
910         fi
911         if [ ! -s .patches/resolving ]
912         then echo "Nothing to resolve" ; exit 0;
913         fi
914         echo "Resolving: " ; cat .patches/resolving
915         for file in `cat .patches/resolving`
916         do
917           ${EDITOR:-vi} $file
918           rm -f $file.porig
919           wiggle --replace --merge $file ||
920             echo $file >> .patches/to-resolve
921         done
922         > .patches/resolving
923         ;;
924
925   export )
926         # there must be only one patch.  We
927         # git commit, p commit, p rebase
928         if [ -n "`ls .patches/applied`" ]
929         then
930             echo 'Cannot export when there are applied patches'
931             exit 1;
932         fi
933         make_diff
934         if [ -s .patches/patch ]
935         then
936             # Ok, go for it.
937             git add `cat .patches/files`
938             author=`grep '^From:' .patches/notes | head -n 1 | sed 's/From: *//'`
939             if [ -n "$author" ]
940                 then git commit --author="$author" -a -F .patches/notes
941                 else git commit -a -F .patches/notes
942             fi
943             $0 commit
944             $0 rebase
945         fi
946         ;;
947   pull )
948         cd .patches/SOURCE && bk pull
949         ;;
950   update )
951         make_diff
952         get_meta
953         if [ -s .patches/patch ]
954         then
955                 echo >&2 Patch $name already open - please commit; exit 1;
956         fi
957         p openall && p clean &&
958           (cd .patches/SOURCE ; bk export -tpatch -rLATEST, ) > .patches/imported-patch &&
959           patch --dry-run -f -p1 < .patches/imported-patch &&
960           patch -f -p1 < .patches/imported-patch &&
961           ( rm .patches/imported-patch ; cd .patches/SOURCE ; bk tag LATEST )
962         ;;
963
964   premail )
965         # Convert some applied patches into email messages.
966         # Select patches that start with $1. Look in .patches/cc for who to Cc: to
967         rmdir .patches/mail 2>/dev/null
968         if [ -d .patches/mail ] ; then
969             echo >&2 There is already some email - run "email" or "nomail"
970             ls .patches/mail
971             exit 1;
972         fi
973         mkdir .patches/mail
974
975         get_conf author $1
976         get_conf header $1
977         if [ -n "$author" ]
978         then
979                 headers="From: $author"
980                 if [ -n "$header" ] ; then
981                     headers="$headers$nl$header"
982                 fi
983         elif [ -s .patches/owner ]; then
984                 headers=`cat .patches/owner`;
985         else
986                 echo Please add author information to .patches/config
987                 exit 1
988         fi
989         get_conf maintainer $1
990         if [ -z "$maintainer" -a -s .patches/maintainer ]
991             then
992                 maintainer=`cat .patches/maintainer`
993         fi
994
995         if [ -z "$maintainer" ] ; then
996             echo "No maintainer - please add one"
997             exit 1;
998         fi
999
1000         messid="<`date +'%Y%m%d%H%M%S'`.$$.patches@`uname -n`>"
1001         cnt=0
1002         > .patches/.tmp.cc
1003         for patch in .patches/applied/???${1}*
1004         do
1005           n=${patch##*/}
1006           n=${n:0:3}
1007           if [ -n "$2" ] && [ $2 -gt $n ] ; then continue; fi
1008           if [ -n "$3" ] && [ $3 -lt $n ] ; then continue; fi
1009           if [ -n "$4" ]; then
1010               case ,$4,  in *,$n,* ) ;; *) continue; esac
1011           fi
1012           cnt=$(expr $cnt + 1 )
1013           sed -n -e 's/^\(Signed-[Oo]ff-[Bb]y\|Acked-[Bb]y\|Cc\|From\): */Cc: /p' $patch | grep -v neilb >> .patches/.tmp.cc
1014         done
1015         get_conf cc $1
1016         get_conf tag $1
1017         this=1
1018         if [ $cnt -gt 1 ]
1019         then
1020         {
1021             echo "$headers"
1022             echo "To: $maintainer"
1023
1024             if [ -n "$cc" ]; then
1025                     echo "Cc: $cc"
1026             fi
1027             if [ -n "$tag" ]; then
1028                     sprefix="$tag: "
1029             fi
1030             if [ -s .patches/.tmp.cc ]
1031             then sort -u .patches/.tmp.cc
1032             fi
1033             if [ -s .patches/cc ] ; then
1034                 while read word prefix addr
1035                   do if [ " $word" = " $1" ] ; then
1036                         echo "Cc: $addr"
1037                         sprefix="$prefix: "
1038                     fi
1039                   done < .patches/cc
1040             fi
1041             if [ $cnt = 1 ]
1042                   then
1043                   echo "Subject: [PATCH] ${sprefix}Intro"
1044                   else
1045                   echo "Subject: [PATCH 000 of $cnt] ${sprefix}Introduction EXPLAIN PATCH SET HERE"
1046             fi
1047             echo "Message-ID: $messid"
1048             echo
1049             echo PUT COMMENTS HERE
1050         } > .patches/mail/000Intro
1051         fi
1052
1053         for patch in .patches/applied/???${1}*
1054         do
1055           n=${patch##*/}
1056           n=${n:0:3}
1057           if [ -n "$2" ] && [ $2 -gt $n ] ; then continue; fi
1058           if [ -n "$3" ] && [ $3 -lt $n ] ; then continue; fi
1059           if [ -n "$4" ]; then
1060               case ,$4,  in *,$n,* ) ;; *) continue; esac
1061           fi
1062           if [ -f ./scripts/checkpatch.pl ] 
1063           then perl ./scripts/checkpatch.pl $patch
1064           fi
1065           {
1066               sprefix=
1067               echo "$headers"
1068               echo "To: $maintainer"
1069               if [ -n "$cc" ]; then
1070                     echo "Cc: $cc"
1071               fi
1072               sed -n -e 's/^\(Signed-[Oo]ff-[Bb]y\|Acked-[Bb]y\|Cc\|From\): */Cc: /p' $patch | grep -v neilb | sort -u
1073               if [ -n "$tag" ]; then
1074                     sprefix="$tag: "
1075               fi
1076               if [ -s .patches/cc ] ; then
1077                   while read word prefix addr
1078                     do if [ " $word" = " $1" ] ; then
1079                         echo "Cc: $addr"
1080                         sprefix="$prefix: "
1081                     fi
1082                   done < .patches/cc
1083               fi
1084               head=`sed -e '/^Status/d' -e '/^$/d' -e q $patch`
1085               zerothis=$(expr $this + 1000)
1086               if [ $cnt = 1 ]
1087                   then
1088                   echo "Subject: [PATCH] $sprefix$head"
1089                   else
1090                   echo "Subject: [PATCH ${zerothis#1} of $cnt] $sprefix$head"
1091               fi
1092               echo "References: $messid"
1093               echo
1094               if [ $cnt = 1 ] ; then
1095                   echo "### Comments for Changeset"
1096               fi
1097               sed -e '1,3d' $patch
1098           } > .patches/mail/${patch#.patches/applied/}
1099           this=$(expr $this + 1)
1100         done
1101         if [ -f .patches/mail/000Intro ]; then cat .patches/mail/* | sed -n -e 's/^Subject://p'  >> .patches/mail/000Intro ; fi
1102         ls .patches/mail
1103         ;;
1104
1105     nomail )
1106           echo "Removing .patches/mail directory"
1107           rm -rf .patches/mail
1108           ;;
1109
1110      email )
1111         PATH=$HOME/bin:/usr/lib:/usr/sbin:$PATH
1112           for i in .patches/mail/*
1113           do
1114             if [ -f "$i" ]
1115                 then
1116                 echo Sending $i.
1117                 sendmail -t < $i && rm $i
1118             fi
1119           done
1120           ;;
1121
1122    test )
1123      # test all removed patches to see which ones are clearly included
1124      for p in .patches/removed/*
1125      do
1126         if patch -R --dry-run -p0 --fuzz=0 -s -f < "$p" > /dev/null 2>&1
1127         then echo $p
1128         fi
1129      done
1130      ;;
1131    help )
1132         helpfile=$0.help
1133         if [ ! -f $helpfile ]
1134         then echo >&2 $helpfile not found: no help available ; exit 2;
1135         fi
1136         if [ -z "$1" ] ; then
1137            echo
1138            sed -n -e '/^ /p' -e '/^[^ ]/q' $helpfile
1139            echo
1140            echo "Available help topics are:"
1141            sed -n '/^[^ ]/p' $helpfile | sort | column
1142         else
1143             echo
1144             awk '$0 ~ /^[^ ]/ && printed {doprint=0; printed=0}
1145                 doprint && $0 !~ /^[^ ]/ {print; printed=1;}
1146                 $0 == "'$1'" {doprint=1; found=1}
1147                 END { if (!found) print "No help available for '$1'"; }
1148                 ' $helpfile
1149             echo
1150         fi
1151         ;;
1152   * )
1153         echo >&2 "p $cmd - unknown command - try 'p help'"; exit 1;
1154 esac
1155 exit 0;