Add heuristic to take shortcut when too slow.
[wiggle/upstream.git] / p
blobb29ddcef8458a62f59e50a963f8b8128ac2eaad7
1 #!/bin/bash
3 # patch management
5 # Copyright (C) 2003 Neil Brown <neilb@cse.unsw.edu.au>
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.
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.
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
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
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
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.
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
63 find_home()
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" != " /" ]
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
84 get_meta()
86 name=`cat .patches/name 2> /dev/null`
87 status=`cat .patches/status 2> /dev/null`
90 nl='
92 get_conf()
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
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
116 esac
117 done < .patches/config
118 _result=$(echo "$_result" | sed 's/^"//' )
119 eval $_name=\"\$_result\"
122 upgrade_one()
124 # move $1~current~ to .patches/current/$1 and same for orig
125 fl=/$1
126 for f in current orig
128 if [ -f "$1~$f~" ]
129 then
130 mkdir -p ".patches/$f${fl%/*}"
131 mv "$1~$f~" ".patches/$f/$1"
133 done
137 forget_one()
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"
148 rebase_one()
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
158 snap_one()
160 cp "$1" "$1~snapshot~"
163 snap_diff()
165 diff -u "$1" "$1~snapshot~"
167 snap_back()
169 cp "$1~snapshot~" "$1"
172 check_out()
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"
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"
193 else
194 echo >&2 Cannot checkout $file
198 all_files()
200 >> .patches/files
201 while read file
202 do eval $1 $file $2
203 done < .patches/files
206 diff_one()
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"
222 diff_one_orig()
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"
233 commit_one()
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
243 discard_one()
245 cmp -s ".patches/current/$1" $1 || { rm -f "$1" ; cp ".patches/current/$1" $1; }
246 chmod u+w $1
249 swap_one()
251 mv "$1" "$1.tmp"
252 mv ".patches/current/$1" "$1"
253 mv "$1.tmp" ".patches/current/$1"
256 make_diff()
258 get_conf tagline
259 upgrade_one "$1"
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"
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
277 save_patch()
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
288 new=`expr "0$largest" + 1001`
289 new=${new#1}
290 mv .patches/patch $dir/$new$name
293 find_prefix()
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#/}" ]
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]
315 if [ " $orig" != " $file" ]
316 then
317 echo "Found $orig as $file - prefix $prefix"
321 extract_notes()
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;
330 { head = 0; }
331 $0 == "" { blanks++; next; }
332 $0 ~ /^ *---/ { exit }
333 $0 ~ /^###/ { exit }
334 { while (blanks > 0) {
335 blanks--; print "";
337 print $0;
339 ' $1
343 if [ $# -eq 0 ]
344 then
345 echo >&2 'Usage: p [help|co|make|discard|commit|status|name|...] args'
346 exit 1
348 cmd=$1
349 shift
351 if [ " $cmd" = " help" ] || find_home
352 then :
353 else echo >&2 "p $cmd: cannot find .patches directory"
354 exit 1
357 case $cmd in
358 co )
359 if [ $# -ne 1 ] ; then
360 echo >&2 Usage: p co file; exit 1
362 file=$1
363 if [ ! -f "$OrigDir$file" ]
364 then
365 echo >&2 "p co: file $file not found"; exit 1;
367 check_out "$OrigDir$file"
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;
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;
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
396 all )
397 all_files diff_one_orig
399 status | name )
400 case $# in
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'"
409 echo "$1" > .patches/name
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'"
417 echo "$1" > .patches/status
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 ; }
428 echo "Usage: p $cmd [new-$cmd]"; exit 1;
429 esac
431 note* )
432 >> .patches/notes
433 ${EDITOR:-vi} .patches/notes
435 discard|commit )
436 make_diff
437 if [ -s .patches/patch ]
438 then :
439 else echo >&2 No patch to $cmd ; exit 1
441 if grep -s '^+.*[ ]$' .patches/patch > /dev/null
442 then
443 echo >&2 remove trailing spaces/tabs first !!
444 # exit 1
446 if [ $cmd == "commit" -a -f scripts/checkpatch.pl ] ; then
447 perl scripts/checkpatch.pl .patches/patch
449 if [ -s .patches/to-resolve ]
450 then echo "Please resolve outstanding conflicts first with 'p resolve'"
451 exit 1
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;
460 echo $name > .patches/name
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;
468 echo $status > .patches/status
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
485 make_diff
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
496 rm -f .patches/name .patches/status .patches/notes
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
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;
512 if [ $# -eq 0 ]
513 then
514 echo "Available patches are:"
515 ls .patches/applied
516 exit 0
518 if [ $# -ne 1 ]
519 then echo >&2 "Usage: p open patchname" ; exit 1
521 if [ " $1" = " last" ]
522 then
523 pfile=`ls -d .patches/applied/[0-9]* | tail -1`
524 else
525 pfile=`echo .patches/applied/*$1*`
527 if [ ! -f "$pfile" ]
528 then echo >&2 "Cannot find unique patch '$1' - found: $pfile"; exit 1
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
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
545 included )
546 force=
547 if [ " $1" = " -f" ] ; then
548 force=yes; shift
550 make_diff; get_meta
551 if [ -s .patches/patch ]
552 then
553 echo >&2 Patch $name already open, please commit; exit 1;
555 if [ $# -eq 0 ]
556 then
557 echo "Unapplied patches are:"
558 ls .patches/removed
559 exit 0;
561 if [ $# -ne 1 ]
562 then
563 echo >&2 "Usage: p included patchname"; exit 1
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
573 echo "Using $pfile..."
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
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"
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
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;
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;
611 > .patches/in-review
613 if [ $# -eq 0 ]
614 then
615 echo "Pending patches are:"
616 ls .patches/removed
617 exit 0;
619 if [ $# -ne 1 ]
620 then
621 echo >&2 "Usage: p review patchname"; exit 1
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
630 echo "Starting from $pfile..."
631 found=
632 for fl in .patches/removed/*
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
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
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
672 p commit || exit 1
674 done
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;
686 while p open last
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
697 list )
698 echo "Applied patches are:"
699 ls .patches/applied
701 echo "Unapplied patches are:"
702 ls .patches/removed
703 exit 0
705 lista )
706 echo "Applied patches are:"
707 ls .patches/applied
708 exit 0
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
716 force= append=
717 if [ " $1" = " -f" ]; then
718 force=yes; shift
720 if [ " $1" = " -a" ]; then
721 append=yes; shift
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;
729 if [ $# -eq 0 ]
730 then
731 echo "Unapplied patches are:"
732 ls .patches/removed
733 exit 0
735 if [ $# -ne 1 ]
736 then echo >&2 "Usage: p apply patchname"; exit 1
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
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
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
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
777 case $pfile in
778 .patches/removed/* )
779 mv $pfile .patches/patch
780 esac
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
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;
799 if [ -d .patches/dest/$name ]
800 then
801 echo >&2 $name already exists ; exit 1
803 target=.patches/dest/$name
804 mkdir $target
805 if [ -f .patches/get-version ] ;
806 then ./.patches/get-version > $target/version
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`
817 clean )
818 all_files forget_one
819 > .patches/files
821 openall )
822 while $0 open last && $0 discard ; do : ; done
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;
831 if [ $# -eq 0 ]
832 then
833 echo "Unapplied patches are:"
834 ls .patches/removed
835 exit 0
837 if [ $# -ne 1 ]
838 then echo >&2 "Usage: p recommit patchname"; exit 1
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
848 while [ -s "$pfile" ] &&
849 $0 apply last && $0 commit ; do : ; done
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;
858 if [ $# -eq 0 ]
859 then
860 echo "Applied patches are:"
861 ls .patches/applied
862 exit 0
864 if [ $# -ne 1 ]
865 then echo >&2 "Usage: p decommit patchname"; exit 1
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
875 while [ -s "$pfile" ] &&
876 $0 open last && $0 discard ; do : ; done
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;
887 for p in `ls .patches/applied`
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
895 snapshot )
896 all_files snap_one
898 snapdiff )
899 all_files snap_diff
901 snapback )
902 all_files snap_back
904 upgrade )
905 all_files upgrade_one
907 resolve )
908 if [ ! -s .patches/resolving ]
909 then sort -u .patches/to-resolve > .patches/resolving ; > .patches/to-resolve
911 if [ ! -s .patches/resolving ]
912 then echo "Nothing to resolve" ; exit 0;
914 echo "Resolving: " ; cat .patches/resolving
915 for file in `cat .patches/resolving`
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
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;
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
943 $0 commit
944 $0 rebase
947 pull )
948 cd .patches/SOURCE && bk pull
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;
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 )
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;
973 mkdir .patches/mail
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"
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
989 get_conf maintainer $1
990 if [ -z "$maintainer" -a -s .patches/maintainer ]
991 then
992 maintainer=`cat .patches/maintainer`
995 if [ -z "$maintainer" ] ; then
996 echo "No maintainer - please add one"
997 exit 1;
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}*
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
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
1021 echo "$headers"
1022 echo "To: $maintainer"
1024 if [ -n "$cc" ]; then
1025 echo "Cc: $cc"
1027 if [ -n "$tag" ]; then
1028 sprefix="$tag: "
1030 if [ -s .patches/.tmp.cc ]
1031 then sort -u .patches/.tmp.cc
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: "
1039 done < .patches/cc
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"
1047 echo "Message-ID: $messid"
1048 echo
1049 echo PUT COMMENTS HERE
1050 } > .patches/mail/000Intro
1053 for patch in .patches/applied/???${1}*
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
1062 if [ -f ./scripts/checkpatch.pl ]
1063 then perl ./scripts/checkpatch.pl $patch
1066 sprefix=
1067 echo "$headers"
1068 echo "To: $maintainer"
1069 if [ -n "$cc" ]; then
1070 echo "Cc: $cc"
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: "
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: "
1082 done < .patches/cc
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"
1092 echo "References: $messid"
1093 echo
1094 if [ $cnt = 1 ] ; then
1095 echo "### Comments for Changeset"
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
1105 nomail )
1106 echo "Removing .patches/mail directory"
1107 rm -rf .patches/mail
1110 email )
1111 PATH=$HOME/bin:/usr/lib:/usr/sbin:$PATH
1112 for i in .patches/mail/*
1114 if [ -f "$i" ]
1115 then
1116 echo Sending $i.
1117 sendmail -t < $i && rm $i
1119 done
1122 test )
1123 # test all removed patches to see which ones are clearly included
1124 for p in .patches/removed/*
1126 if patch -R --dry-run -p0 --fuzz=0 -s -f < "$p" > /dev/null 2>&1
1127 then echo $p
1129 done
1131 help )
1132 helpfile=$0.help
1133 if [ ! -f $helpfile ]
1134 then echo >&2 $helpfile not found: no help available ; exit 2;
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
1153 echo >&2 "p $cmd - unknown command - try 'p help'"; exit 1;
1154 esac
1155 exit 0;