- #!/bin/sh
- MD5SUM="534841181cec9b82373185b088619dc0 -"
- VERSION="Sat Jul 14 15:16:10 CDT 2001 <rick@ipcroe.rkkda.com>"
-
- #
- # 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 <rickr@mn.rr.com>
- # http://home.mn.rr.com/richardsons/
- #
- # TODO (many):
- # Handle CVS style options.
- # TODO import:
- # Handle repository vendortag releasetag args
- # TODO rename:
- # There is a Perforce problem if you do this:
- # p4 rename xxx yyy; p4 commit; p4 rename yyy xxx; p4 commit
- # The file will end up deleted. The only workaround I know of
- # is to rename it three times (xxx->yyy->zzz->xxx).
- # TODO add:
- # There is a bug with stock Perforce "p4 add". If you p4 add
- # a file that doesn't yet exist, then create the file and
- # try to do a submit/commit, the submit will fail.
- # At this point, the only thing you can do is to issue
- # a p4 revert and then a new p4 add command. This wrapper
- # should be changed to protect against that.
- #
-
- 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
- }
-
- #
- # Run the users favorite editor
- #
- editor() {
- editor=vi
- if [ "$P4EDITOR" != "" ]; then
- editor="$P4EDITOR"
- fi
-
- $editor "$@"
- }
-
- #
- # 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
- }
-
- #
- # Augmented "submit" command that allows a log message (with -m)
- # and which allows more than one file argument. Apparently, users
- # have asked Perforce for this going back to 1999, but they have
- # been unresponsive. Another argument for open source software.
- #
- # N.B. the file/dir arguments are Perforce syntax.
- #
- p4submit() {
- #
- # Parse p4 submit options
- #
- IOPT=0
- MOPT=0
- COPT=
- SOPT=
- LOGMSG="enter_description_here"
- LOGFILE=
- OPTIND=0
- while getopts "c:isF:m:" opt
- do
- case $opt in
- c) COPT="$OPTARG";;
- i) IOPT=1;;
- s) SOPT="-s";;
- F) LOGFILE="$OPTARG"; MOPT=1;;
- m) LOGMSG="$OPTARG"; MOPT=1;;
- esac
- done
- shift `expr $OPTIND - 1`
-
- #
- # handle p4 submit -c
- #
- if [ "$COPT" != "" ]; then
- $P4 submit -c $COPT
- return
- fi
-
- #
- # For all other cases, prepare a changelog file...
- #
- TMP2=/tmp/p4files.$$
- $P4 opened "$@" > $TMP2 2>/dev/null
- if [ ! -s $TMP2 ]; then
- rm -f $TMP2
- echo "No files to submit from the default changelist."
- return
- fi
-
- (
- # Hey, this is great, we can put the log message up top
- # where its quicker to get to in order to change.
- echo "Description:"
- if [ "$LOGFILE" = "" ]; then
- echo " $LOGMSG"
- else
- cat $LOGFILE | sed 's/^/ /'
- fi
- $P4 change -o $SOPT 2>&1 | sed '/^Description/,$d'
- echo "Files:"
- sed -e 's/^/ /' -e 's/#/ #/' < $TMP2 | sort -u
- rm -f $TMP2
- ) > $TMP
-
- #
- # If we are interactive, fire up the editor
- #
- if [ $MOPT = 0 -a $IOPT = 0 -a "$LOGFILE" = "" ]; then
- sum=`md5sum $TMP`
- editor $TMP
- sum2=`md5sum $TMP`
- if [ "$sum" = "$sum2" ]; then
- echo "Error in submit specification."
- echo "Change description missing. You must enter one."
- echo -n "Hit return to continue..."
- read answer
- editor $TMP
- sum2=`md5sum $TMP`
- if [ "$sum" = "$sum2" ]; then
- echo "Specification not corrected -- giving up."
- rm -f $TMP
- return
- fi
- fi
- fi
-
- #
- # Feed the changelog file to the real p4 submit command
- #
- $P4 submit -i $SOPT < $TMP
- rm -f $TMP
- }
-
- #
- # Parse the global p4 options
- #
- P4ONLY=
- OPTIND=0
- while getopts "c:d:H:p:P:su:v:Vx:?h" opt
- do
- case $opt in
- c) P4="$P4 -c $OPTARG";;
- d) P4="$P4 -d $OPTARG";;
- H) P4="$P4 -H $OPTARG";;
- p) P4="$P4 -p $OPTARG";;
- P) P4="$P4 -P $OPTARG";;
- s) P4ONLY="$P4ONLY -s";;
- u) P4="$P4 -u $OPTARG";;
- v) P4="$P4 -v $OPTARG";;
- x) P4ONLY="$P4ONLY -x $OPTARG";;
- V)
- echo "p4 wrapper: version $VERSION"
- exec $P4 -V
- ;;
- h|\?) $P4 -h
- set -- help
- OPTIND=1
- break;
- # echo " Run \"p4 help\" for further information"
- # echo
- # exit 0
- ;;
- esac
- done
- shift `expr $OPTIND - 1`
-
- #
- # Main program
- #
- case "$1" in
- annotate)
- shift
-
- #
- # Get CVS style args
- #
- REV=
- RECURSE=1
- OPTIND=0
- while getopts "lRfr:D:h?" opt
- do
- case $opt in
- # TODO: handle the rest of the CVS options
- l) # Local directory only, no recursion.
- RECURSE=0
- ;;
- R) # Process directories recursively.
- RECURSE=1
- ;;
- f) # Use head revision if tag/date not found.
- error "option not implemented yet"
- ;;
- r) # rev; Annotate using specified revision/tag
- case "$OPTARG" in
- [0-9]*) REV="#$OPTARG";;
- "#"*) REV="$OPTARG";;
- @*) REV="$OPTARG";;
- [A-Za-z]*) REV="@$OPTARG";;
- *) error "Unknown revision syntax";;
- esac
- ;;
- D) # date; Set date to annoate on from
- # Perforce has a very limited date syntax.
- # Use the more flexible "date" command to parse
- # the date we are given, then turn that into
- # Perforce syntax.
- REV=`date -d "$OPTARG" "+@%Y/%m/%d:%H:%M:%S"`
- if [ $? != 0 ]; then
- exit 1
- fi
- error "p4pr.perl too stupid to handle -D right now"
- ;;
- h|\?)
- echo "Usage:"
- echo -n " p4 annotate"
- echo " [-lRf] [-r rev|-D date] [file|dir] ..."
- exit 0
- ;;
- esac
- done
- shift `expr $OPTIND - 1`
-
- if [ $RECURSE = 1 ]; then
- glob=...
- else
- glob="*"
- fi
-
- if [ $# = 0 ]; then
- $P4 files ./$glob | sed 's/#.*//' |
- while read pfile blather
- do
- p4pr.perl $pfile$REV
- done
- else
- for file in "$@"
- do
- if [ -d "$file" ]; then
- $P4 files $file/"$glob" | sed 's/#.*//' |
- while read pfile blather
- do
- p4pr.perl $pfile$REV
- done
- else
- p4pr.perl $file$REV
- fi
- done
- fi
- ;;
-
- commit)
- shift
- #
- # Get CVS style args
- #
- LOGMSG=
- LOGFILE=
- RECURSE=1
- OPTIND=0
- while getopts "F:lm:Rh?" opt
- do
- case $opt in
- # TODO: handle the rest of the CVS options
- F) LOGFILE="$OPTARG";;
- f) RECURSE=0;;
- l) RECURSE=0;;
- m) LOGMSG="$OPTARG";;
- R) RECURSE=1;;
- r) # rev; Commit to this branch or trunk revision.
- error "option -r not implemented yet"
- ;;
- h|\?)
- echo -n "Usage: p4 commit [-nRlf] [-m msg|-F logfile]"
- echo " [-r rev] [file|dir]..."
- echo -n " -R Process directories"
- echo " recursively. (Default)"
- echo " -l Local directory only (not recursive)."
- echo -n " -f Force the file to be committed;"
- echo " disables recursion."
- echo " -F file Read the log message from file."
- echo " -m msg Log message."
- echo " -r rev Commit to this branch/trunk revision."
- exit 0
- ;;
- esac
- done
- shift `expr $OPTIND - 1`
-
- 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
- if [ $RECURSE = 1 ]; then
- pfiles=./...
- else
- pfiles=./*
- fi
- 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
- fi
-
- #
- # Use our augmented version of p4 submit to do the dirty work
- #
- if [ "$LOGMSG" != "" ]; then
- p4submit -m "$LOGMSG" $pfiles
- elif [ "$LOGFILE" != "" ]; then
- p4submit -F "$LOGFILE" $pfiles
- else
- p4submit $pfiles
- fi
- ;;
-
- import)
- shift
-
- OPTIND=0
- while getopts "h?" opt
- do
- case $opt in
- h|\?)
- echo "Usage:"
- echo " p4 import"
- exit 0
- ;;
- esac
- done
- shift `expr $OPTIND - 1`
-
- # TODO: handle repository vendortag releasetag args
-
- find . -type f -print | $P4 -x - add
- $P4 submit ./...
- ;;
-
- log)
- shift
-
- # TODO: handle CVS options
-
- OPTIND=0
- while getopts "h?" opt
- do
- case $opt in
- h|\?)
- echo "Usage:"
- echo " p4 log [file|dir] ..."
- exit 0
- ;;
- esac
- done
- shift `expr $OPTIND - 1`
-
- # 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
- $P4 filelog -l $pfiles
- ;;
-
- rename)
- shift
-
- OPTIND=0
- while getopts "h?" opt
- do
- case $opt in
- h|\?)
- echo "Usage: p4 rename from to"
- exit 0
- ;;
- esac
- done
- shift `expr $OPTIND - 1`
-
- 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
-
- OPTIND=0
- while getopts "h?" opt
- do
- case $opt in
- h|\?)
- echo "Usage: p4 stat [file|dir] ..."
- exit 0
- ;;
- esac
- done
- shift `expr $OPTIND - 1`
-
- # 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/^/ /'
- ;;
-
- submit)
- #
- # As a safety net, use unadorned $P4 submit if we can,
- # otherwise, use our augmented version.
- #
- if [ $# -le 1 ]; then
- exec $P4 "$@"
- else
- shift
- p4submit "$@"
- fi
- ;;
-
- update)
- # Do a sync and then a resolve
- shift
-
- #
- # Get CVS style args
- #
- REV=
- FFLAG=
- OPTIND=0
- while getopts "APCdflRpk:r:D:j:I:W:h?" opt
- do
- case $opt in
- # TODO: handle the rest of the CVS options
- A) # Reset sticky
- error "option not implemented yet"
- ;;
- P) # Prune empty directories
- error "option not implemented yet"
- ;;
- C) # Overwrite locally modified files with clean copies
- FFLAG=-f
- ;;
- d) # Build directories like checkout does
- error "option not implemented yet"
- ;;
- f) # Force a head revision match if tag/date not found.
- error "option not implemented yet"
- ;;
- l) # Local directory only, no recursion.
- error "option not implemented yet"
- ;;
- R) # Process directories recursively.
- error "option not implemented yet"
- ;;
- p) # Send updates to standard output (avoids stickiness).
- error "option not implemented yet"
- ;;
- k) # kopt; Use RCS kopt -k option on checkout.
- error "option not implemented yet"
- ;;
- r) # rev; Update using specified revision/tag (is sticky).
- case "$OPTARG" in
- [0-9]*) REV="#$OPTARG";;
- "#"*) REV="$OPTARG";;
- @*) REV="$OPTARG";;
- [A-Za-z]*) REV="@$OPTARG";;
- *) error "Unknown revision syntax";;
- esac
- ;;
- D) # date; Set date to update from (in CVS, is sticky).
- # Perforce has a very limited date syntax.
- # Use the more flexible "date" command to parse
- # the date we are given, then turn that into
- # Perforce syntax.
- REV=`date -d "$OPTARG" "+@%Y/%m/%d:%H:%M:%S"`
- if [ $? != 0 ]; then
- exit 1
- fi
- ;;
- j) # rev; Merge changes between current revision and rev.
- error "option not implemented yet"
- ;;
- I) # ign; More files to ignore (! to reset).
- error "option not implemented yet"
- ;;
- W) # spec; Wrappers specification line.
- error "option not implemented yet"
- ;;
- h|\?)
- echo "Usage:"
- echo " p4 update [options] [file|dir] ..."
- exit 0
- ;;
- esac
- done
- shift `expr $OPTIND - 1`
-
- if [ $# = 0 ]; then
- # ala CVS, sync/resolve current dir and lower only.
-
- # Pick up files that are missing...
- $P4 diff -sd ./...$REV | $P4 -x- sync -f
-
- # Pick up files that have changed...
- $P4 sync $FFLAG ./...$REV
- $P4 resolve ./...
- else
- # Convert directory names to p4 syntax
- pfiles=
- pdfiles=
- for file in "$@"
- do
- if [ -d "$file" ]; then
- pfiles="$pfiles $file/..."
- pdfiles="$pfiles $file/...$REV"
- else
- pfiles="$pfiles $file"
- pdfiles="$pfiles $file$REV"
- fi
- done
-
- # Pick up files that are missing...
- $P4 diff -sd $pfiles | $P4 -x- sync -f
-
- # Pick up files that have changed...
- $P4 sync $FFLAG $pdfiles
- $P4 resolve $pfiles
- fi
- ;;
-
- checkout)
- shift
-
- # TODO: handle CVS options
-
- OPTIND=0
- while getopts "h?" opt
- do
- case $opt in
- h|\?)
- echo "Usage: p4 checkout dir ..."
- exit 0
- ;;
- esac
- done
- shift `expr $OPTIND - 1`
-
- for module in "$@"
- do
- $P4 sync $module/...
- done
- ;;
-
- ws)
- #
- # Perform the "dirty secret" business of setting up a workspace.
- #
- # This basically means creating a .p4config file in the current
- # directory to hold the workspace name and server settings, and
- # then running the p4 client program.
- #
- # The business of setting all those environment variables in
- # your .profile is a crock. If you do it that way, you can only
- # have one workspace per login. The dotfile is the only way to go.
- #
- shift
-
- OPTIND=0
- while getopts "h?" opt
- do
- case $opt in
- h|\?)
- echo "Usage: p4 ws <wsname> [serv:port [user [passwd]]]"
- exit 0
- ;;
- esac
- done
- shift `expr $OPTIND - 1`
-
- if [ $# = 0 ]; then
- error "Usage: p4 ws <wsname> [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
- else
- echo "P4USER=$LOGNAME" >> $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 views.*/a\
- p4 help wrapper list extended commands provided by p4 wrapper'
- elif [ $2 = "wrapper" ]; 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 checkout dir ...
- Checkout everything under the directory "dir".
- p4 commit [file|dir] ...
- Submit changed files to the depot.
- p4 import
- Add all files in current directory and below to
- the depot.
- p4 log file ...
- List revision history of files.
- 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 submit [-m logmsg] [p4filespec] ...
- Augmented version of p4 submit that allows more than
- one P4-style file specification. It also allows the
- changelog description to be specified with -m.
- p4 update [file|dir] ...
- Get latest files from depot, doing a resolve
- if necessary.
- p4 ws <wsname> [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 $P4ONLY "$@"
- ;;
- esac