#!/bin/ksh # This script is a bit of a hack to allow us to use webrev on Mercurial # workspaces that use MQ. Specifically, this allows us to more easily see # the changes that were made over two versions of the same patch where # that patch is not at the top level of qapplied. # # Use it from the top level of a repository, and specify the name of one # or more patches you wish to see webrevs of. # # Implementation # -------------- # It works by taking a temporary clone of the parent repository, and pushes # patches up to the specified patch in the series. Then, using a list of the # files the specified patch modifies, it copies these to a new temporary repository. # # This new (probably small) repository is then cloned, and the same list of # files from the current repository are copied to this repo and committed. # # Finally, we invoke webrev on this temporary repository against it's # parent. REAL_CHILD=$(pwd) # FIXME need to make sure this version of hg has support for "qgoto" HG=/usr/bin/hg QUIET="-q" # setting this to a non-empty string makes the script emit more messages DEBUG="" # XXX hardcoding the path to webrev here is probably wrong WEBREV=/ws/onnv-tools/onbld/bin/webrev # write an awk script that can print deleted and added files # from a given patch function write_deleteadd_script { cat > /tmp/patch_webrev.$$.awk < [ ... patchn>" exit 1 } function log_debug { if [ -n "$DEBUG" ] ; then echo $@ fi } # print a list of files that given patch modifies function changed_files { ws=$1 patch=$2 if [ ! -f $ws/.hg/patches/$patch ] then echo "ERROR changed_files() unable to find $ws/.hg/patches/$patch" exit 1 fi LIST=$(/usr/xpg4/bin/grep -e +++ -e --- $ws/.hg/patches/$patch \ | awk '{print $2}' | sed -e 's,^a/,,g;s,^b/,,g' | grep -v /dev/null | sort -u) echo $LIST } # print a list of files that a given patch deletes function deleted_files { ws=$1 patch=$2 echo $(cat $ws/.hg/patches/$patch | /tmp/patch_webrev.$$.awk | grep "^deleted " | awk '{print $2}') } # print a list of files that a given patch adds function added_files { ws=$1 patch=$2 echo $(cat $ws/.hg/patches/$patch | /tmp/patch_webrev.$$.awk | grep "^added " | awk '{print $2}') } if [ $# -lt 1 ] ; then usage fi MODIFIED=$($HG status -mard) if [ -n "$MODIFIED" ] then echo "WARNING: uncommitted changes $MODIFIED appear in $REAL_CHILD" fi OUTGOING_DESC=$($HG -R ${REAL_CHILD}/.hg/patches outgoing --template '{desc}') if [ -z "${OUTGOING_DESC}" ] then echo "ERROR: no outgoing changes in ${REAL_CHILD}/.hg/patches" exit 1 fi # quick sanity check for PATCH in $@ ; do if [ ! -f .hg/patches/$PATCH ] then echo "Unable to find $REAL_CHILD/.hg/patches/$PATCH" echo "Are you running this script from the top of the workspace?" exit 1 fi done # get the parent gate PARENT=$( $HG paths | grep default | awk -F= '{print $2}' | sed -e 's/^ //g') TMP=/tmp/patch-webrev.$$ mkdir -p $TMP mkdir $TMP/parent cd $TMP/parent $HG $QUIET init log_debug "Making a qclone of $PARENT to $TMP/real-parent.hg" $HG $QUIET qclone $PARENT $TMP/real-parent.hg cd $REAL_CHILD for PATCH in $@ ; do CHANGED_FILES=$(changed_files $REAL_CHILD $PATCH) log_debug "Changed files are $CHANGED_FILES" cd $TMP/real-parent.hg log_debug "going to $PATCH in $TMP/real-parent.hg" $HG $QUIET qgoto $PATCH if [ $? -ne 0 ] ; then # XXX difficult to know what to do here, should probably # push the parent up to the patch one level beneath # where the new patch in the child is, iterating if that # patch isn't in the parent either echo "Warning $PATCH not in parent, pushing all patches instead" $HG qpush -a fi # divorcing parent from real-parent.hg by creating a new temp workspace (tar cf - $CHANGED_FILES ) | (cd $TMP/parent ; tar xf -) # now pop those patches out again so we can deal with the next patch $HG $QUIET qpop -a cd $TMP/parent $HG $QUIET add $HG $QUIET -R $TMP/parent commit -m "State of $PARENT at patch $PATCH" done write_deleteadd_script # Now clone a child from this parent log_debug "Cloning $TMP/parent to $TMP/child" $HG $QUIET clone $TMP/parent $TMP/child for PATCH in $@ do CHANGED_FILES=$(changed_files $REAL_CHILD $PATCH) log_debug "Changed files in $PATCH are $CHANGED_FILES" DELETED_FILES=$(deleted_files $REAL_CHILD $PATCH) log_debug "Deleted files in $PATCH are $DELETED_FILES" ADDED_FILES=$(added_files $REAL_CHILD $PATCH) log_debug "Added files in $PATCH are $ADDED_FILES" cd $REAL_CHILD $HG $QUIET qgoto $PATCH log_debug "Copying changes from $REAL_CHILD to $TMP/child" (tar cf - $CHANGED_FILES) | (cd $TMP/child ; tar xf - ) cd $TMP/child if [ -n "$DELETED_FILES" ] ; then $HG remove $DELETED_FILES fi if [ -n "$ADDED_FILES" ] ; then $HG add $ADDED_FILES fi $HG $QUIET -R $TMP/child commit -m "${OUTGOING_DESC}" done log_debug "At this stage, we can use webrev in $TMP/child" log_debug "For example here are outgoing changes in $TMP/child:" $HG -R $TMP/child outgoing -p log_debug "Once you're finished with $TMP, you should delete it." rm /tmp/patch_webrev.$$.awk log_debug "Generating webrev for you" THISDIR=$(pwd) cd $TMP/child $WEBREV if [ ! -e "${REAL_CHILD}/webrev" ] ; then cp -r webrev $REAL_CHILD cd $REAL_CHILD rm -rf $TMP else echo "webrev directory already exists in $REAL_CHILD - not overwriting it." echo "your webrev is at $TMP/child/webrev." fi