hg transplant, rebasing, and merging

Gilles Moris gilles.moris at free.fr
Mon Feb 25 07:05:39 CST 2008


On Sat February 23 2008 01:15:15 Benjamin Smedberg wrote:
> I have used mq to perform these tasks, but it is pretty painful. To rebase a
> branch:
> 
> hg qsave -e -c
> hg up -C tip
> hg qpush -m... repeat Npatches
> hg qpop -a -n patches.N

I also find that the qsave merge mechanism expose too much to the mq internals.
This is not very usable in that state.

I came up with a script that is basically what I would like a qrebase command
to do:
- remember the previous qparent (here passed as argument)
- apply the patch up to the first conflict
- perform a graphical merge on the conflicting files
- refresh the patch and continue until the whole stack has been applied

Note the clear disclaimer here:
- this is just the state of my current reflexion. I have never tested it in
  production for the simple reason that have not yet jumped to mercurial. This
  is just a prototype. Sorry if it is not very clear nor well documented.
- It is particularly suboptimal. I am not at the point looking for performances.
- too many output, but I am still debugging it
- I am not yet very confortable with the merge mechanism. I don't know if the choice
  of the base (last known qparent) is very appropriate. This should be working when
  rebasing upstream. I don't know if it could work well across branches or backward.
- There will for sure be some problems when it comes to conflicts due to removing
  renaming or moving files. The standard mercurial merge mechanism would help.
- this is just a bash script: it would be better to have it in the mq extension.

Anyway, here it is:
#!/bin/bash

# check inputs: hg root, argv, qseries
if [ $# -ne 1 ]; then
  echo "Usage: $0 <base rev from which to merge>"
  exit 1
fi

base="$1"
root="`hg root`"
if [ $? -ne 0 ]; then
  echo "ERROR: no mercurial tree detected in $PWD."
  exit 1
fi

olddir="$PWD"
cd "$root"

if [ -z "`hg qseries`" ]; then
  echo "ERROR: no mq patch detected"
  exit 1
fi

printf "This script will clean up all .rej and .orig files. OK y/n? "
read ans

if [ "$ans" != y ]; then
  echo "ERROR: cannot purge patch files"
  exit
fi

find . -name '*.orig' -o -name '*.rej' | xargs rm

# pop all patches if needed
hg qtop > /dev/null 2>&1
if [ $? -eq 0 ]; then
  hg qpop -a
  if [ $? -ne 0 ]; then
    echo "ERROR: cannot qpop -a"
    exit 1
  fi
fi

# remember the original tip
source=`hg parent --template '{node}\n'`

# find the first failing patch
while ! hg qpush -a;
do

# find patch all rejected files
rejected=`find . -name '*.rej'`
patchlevel="`hg qtop`"

# go back to the tip without patch
hg qpop -a

tmpdir=qmerge.$$
mkdir $tmpdir

# record the files from the pull: other
for file in $rejected
do
  dirname=`dirname $file`
  basename=`basename $file .rej`
  mkdir -p $tmpdir/other/$dirname
  cp -a $dirname/$basename $tmpdir/other/$dirname
done

# and go to the last known release where patch applied
hg update -C $base
if [ $? -ne 0 ]; then
  echo "ERROR: could not checkout $base."
  exit 1
fi

# record the rejected files at that node: base
for file in $rejected
do
  dirname=`dirname $file`
  basename=`basename $file .rej`
  mkdir -p $tmpdir/base/$dirname
  cp -a $dirname/$basename $tmpdir/base/$dirname
done

# go to the same patch level from the base
hg qgoto "$patchlevel"
if [ $? -ne 0 ]; then
  echo "ERROR: could not apply $patchlevel from base. Going back to node $source"
  hg up -C $source
  rm -rf $tmpdir
  exit 1
fi

# record those files as mine
for file in $rejected
do
  dirname=`dirname $file`
  basename=`basename $file .rej`
  mkdir -p $tmpdir/mine/$dirname
  cp -a $dirname/$basename $tmpdir/mine/$dirname
done

# go back to the tip with mq patches
hg qpop -a
hg up -C $source
hg qpush -a

# and perform 3-way merge
kdiff3 -m -o . $tmpdir/base $tmpdir/mine $tmpdir/other

# final save and qrefresh
printf "Save the merge: y/n? "
read ans

if [ "$ans" != y ]; then
  echo "Merge not saved"
  exit 2
fi

hg qrefresh

rm -rf $tmpdir

hg qpop -a

done



More information about the Mercurial mailing list