#!/bin/sh # # p4 # # Wrapper for Perforce "p4" command to extend it to support the # more popular CVS update/commit model. # # Stick this in your $PATH before /usr/bin/p4. # # Author: Rick Richardson # http://http://home.mn.rr.com/richardsons/ # # TODO import: # Handle repository vendortag releasetag args # TODO rename: # Theres a problem if you do this: # p4 rename xxx yyy; p4 commit; p4 rename yyy xxx; p4 commit # The file will end up deleted. # P4=/usr/bin/p4 TMP=/tmp/p4blather TMP=/tmp/p4blather$$ # # Save the original value of P4CONFIG, so we can warn the user in # our "p4 ws" command. Then set P4CONFIG if it wasn't already set. # saveP4CONFIG="$P4CONFIG" if [ "$P4CONFIG" = "" ]; then export P4CONFIG=.p4config fi # # Report an error and exit # PROGNAME="$0" error() { echo "`basename $PROGNAME`: $1" >&2 exit 1 } # # Version of "p4" command that eats the blather and returns just # a valid exit status code. Yes, p4 needs an option to do this. # p4rc() { $P4 -s "$@" >$TMP 2>&1 while read tag blather do case "$tag" in error:) rm -f $TMP return 1 ;; esac done < $TMP rm -f $TMP return 0 } case "$1" in annotate) shift for file in "$@" do if [ -d "$file" ]; then p4 files $file/... | while read pfile blather do p4pr.perl $pfile done else p4pr.perl $file fi done ;; commit) shift if [ $# = 0 ]; then # Finger out which files need to be committed... # ... ala CVS, current dir and lower only. $P4 diff -se ./... 2>/dev/null | while read file do p4rc edit $file || error "Can't p4 edit $file" done $P4 submit ./... else # User supplied list of files/dirs to commit pfiles= for file in "$@" do if [ -d "$file" ]; then $P4 diff -se $file/... 2>/dev/null | while read dirfile do p4rc edit $dirfile || error "Can't p4 edit $dirfile" done pfiles="$pfiles $file/..." else p4rc edit $file || error "Can't p4 edit $file" pfiles="$pfiles $file" fi done $P4 submit $pfiles fi ;; import) # TODO: handle repository vendortag releasetag args find . -type f -print | $P4 -x - add $P4 submit ./... ;; rename) shift if [ $# != 2 ]; then error "Usage: p4 rename from to" fi FROM="$1" TO="$2" $P4 integrate $FROM $TO $P4 delete $FROM # TODO: theres a problem if you do this: # p4 rename xxx yyy; p4 commit; p4 rename yyy xxx; p4 commit # The file will end up deleted. ;; stat) shift # Convert directory names to p4 syntax pfiles= for file in "$@" do if [ -d "$file" ]; then pfiles="$pfiles $file/..." else pfiles="$pfiles $file" fi done if [ "$pfiles" = "" ]; then pfiles=./... fi echo "Files needing an update/sync..." $P4 diff -sd $pfiles | sed -e 's/^/ /' $P4 sync -n $pfiles 2>&1 | sed -e 's/^/ /' -e 's/File(s) up-to-date./None/' \ -e '/file(s) up-to-date./d' echo echo "Files needing a commit/submit..." $P4 diff -se $pfiles | sed -e 's/^/ /' ;; update) # Do a sync and then a resolve shift if [ $# = 0 ]; then # ala CVS, sync/resolve current dir and lower only. # Pick up files that are missing... $P4 diff -sd ./... | $P4 -x- sync -f # Pick up files that have changed... $P4 sync ./... $P4 resolve ./... else # Convert directory names to p4 syntax pfiles= for file in "$@" do if [ -d "$file" ]; then pfiles="$pfiles $file/..." else pfiles="$pfiles $file" fi done # Pick up files that are missing... $P4 diff -sd $pfiles | $P4 -x- sync -f # Pick up files that have changed... $P4 sync $pfiles $P4 resolve $pfiles fi ;; ws) shift if [ $# = 0 ]; then error "Usage: p4 ws [serv:port [user [passwd]]]" fi if [ -f "$P4CONFIG" ]; then error "$P4CONFIG exists. Remove it first" fi WSNAME="$1" PORT="$2" USER="$3" PASSWD="$4" if [ "$P4PORT" = "" ]; then if [ "$PORT" = "" ]; then echo "P4PORT=perforce:1666" > $P4CONFIG else echo "P4PORT=$PORT" > $P4CONFIG fi else echo "P4PORT=$P4PORT" > $P4CONFIG fi echo "P4CLIENT=$WSNAME" >> $P4CONFIG if [ "$USER" != "" ]; then echo "P4USER=$USER" >> $P4CONFIG fi if [ "$PASSWD" != "" ]; then echo "P4PASSWD=$PASSWD" >> $P4CONFIG fi $P4 client -o | sed 's/noallwrite/allwrite/' | $P4 client -i $P4 client if [ "$saveP4CONFIG" = "" ]; then echo "Warning: Add 'export P4CONFIG=.p4config' to your .profile" echo "Warning: for safety in case you stop using this wrapper" fi ;; help) if [ $# = 1 ]; then $P4 | sed '/p4 help env.*/a\ p4 help ext list extended commands provided by p4 wrapper' elif [ $2 = "ext" ]; then cat <<-EOF Most extended subcommands take file and/or directory arguments, recursing on the directories. If no arguments are supplied to such a command, it will recurse on the current directory (inclusive) by default. This is the surprise-free behavior CVS users are used to. Extended Perforce client commands: p4 annotate file|dir ... Make a listing of who changed what line. p4 commit [file|dir] ... Submit changed files to the depot. p4 import Add all files in current directory and below to the depot. p4 rename from to Rename the file "from" as "to". p4 stat [file|dir] ... Report all files needing an update/sync and/or a commit/submit. p4 update [file|dir] ... Get latest files from depot, doing a resolve if necessary. p4 ws [server:port [user [passwd]]] Create a new workspace in the current directory. EOF else $P4 "$@" fi ;; *) # Hand off all other commands to p4 exec $P4 "$@" ;; esac