# HG changeset patch # User Alexis S. L. Carvalho # Date 1194650665 7200 # Node ID 6ffca2bf23da2b051d82301514495a629a2df189 # Parent b11c855cde96364fc57f29dcac1f9fa46d56fe13# Parent 0b3f910dfd17ea18eea017a51e5685742771986c merge with crew-stable diff -r 0b3f910dfd17 -r 6ffca2bf23da CONTRIBUTORS --- a/CONTRIBUTORS Fri Nov 09 20:21:35 2007 -0200 +++ b/CONTRIBUTORS Fri Nov 09 21:24:25 2007 -0200 @@ -1,4 +1,7 @@ -Andrea Arcangeli +[This file is here for historical purposes, all recent contributors +should appear in the changelog directly] + +Andrea Arcangeli Thomas Arendsen Hein Goffredo Baroncelli Muli Ben-Yehuda @@ -36,5 +39,3 @@ Rafael Villar Burke Tristan Wibberley Mark Williamson - -If you are a contributor and don't see your name here, please let me know. diff -r 0b3f910dfd17 -r 6ffca2bf23da contrib/bash_completion --- a/contrib/bash_completion Fri Nov 09 20:21:35 2007 -0200 +++ b/contrib/bash_completion Fri Nov 09 21:24:25 2007 -0200 @@ -305,6 +305,15 @@ _hg_ext_mq_patchlist qunapplied } +_hg_cmd_qgoto() +{ + if [[ "$prev" = @(-n|--name) ]]; then + _hg_ext_mq_queues + return + fi + _hg_ext_mq_patchlist qseries +} + _hg_cmd_qdelete() { local qcmd=qunapplied diff -r 0b3f910dfd17 -r 6ffca2bf23da contrib/hgk --- a/contrib/hgk Fri Nov 09 20:21:35 2007 -0200 +++ b/contrib/hgk Fri Nov 09 21:24:25 2007 -0200 @@ -649,7 +649,7 @@ if {$stuffsaved} return if {![winfo viewable .]} return catch { - set f [open "~/.gitk-new" w] + set f [open "~/.hgk-new" w] puts $f [list set mainfont $mainfont] puts $f [list set curidfont $curidfont] puts $f [list set textfont $textfont] @@ -687,7 +687,7 @@ puts $f "#" puts $f "set authorcolors {$authorcolors}" close $f - file rename -force "~/.gitk-new" "~/.gitk" + file rename -force "~/.hgk-new" "~/.hgk" } set stuffsaved 1 } @@ -3847,7 +3847,7 @@ deeppink mediumorchid blue burlywood4 goldenrod slateblue red2 navy dimgrey } -catch {source ~/.gitk} +catch {source ~/.hgk} if {$curidfont == ""} { # initialize late based on current mainfont set curidfont "$mainfont bold italic underline" diff -r 0b3f910dfd17 -r 6ffca2bf23da hgext/convert/__init__.py --- a/hgext/convert/__init__.py Fri Nov 09 20:21:35 2007 -0200 +++ b/hgext/convert/__init__.py Fri Nov 09 21:24:25 2007 -0200 @@ -5,12 +5,12 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -from common import NoRepo, SKIPREV, converter_source, converter_sink +from common import NoRepo, SKIPREV, converter_source, converter_sink, mapfile from cvs import convert_cvs from darcs import darcs_source from git import convert_git from hg import mercurial_source, mercurial_sink -from subversion import svn_source, debugsvnlog +from subversion import debugsvnlog, svn_source, svn_sink import filemap import os, shutil @@ -29,6 +29,7 @@ sink_converters = [ ('hg', mercurial_sink), + ('svn', svn_sink), ] def convertsource(ui, path, type, rev): @@ -61,23 +62,10 @@ self.ui = ui self.opts = opts self.commitcache = {} - self.revmapfile = revmapfile - self.revmapfilefd = None self.authors = {} self.authorfile = None - self.maporder = [] - self.map = {} - try: - origrevmapfile = open(self.revmapfile, 'r') - for l in origrevmapfile: - sv, dv = l[:-1].split() - if sv not in self.map: - self.maporder.append(sv) - self.map[sv] = dv - origrevmapfile.close() - except IOError: - pass + self.map = mapfile(ui, revmapfile) # Read first the dst author map if any authorfile = self.dest.authorfile() @@ -165,16 +153,6 @@ return s - def mapentry(self, src, dst): - if self.revmapfilefd is None: - try: - self.revmapfilefd = open(self.revmapfile, "a") - except IOError, (errno, strerror): - raise util.Abort("Could not open map file %s: %s, %s\n" % (self.revmapfile, errno, strerror)) - self.map[src] = dst - self.revmapfilefd.write("%s %s\n" % (src, dst)) - self.revmapfilefd.flush() - def writeauthormap(self): authorfile = self.authorfile if authorfile: @@ -221,7 +199,7 @@ dest = SKIPREV else: dest = self.map[changes] - self.mapentry(rev, dest) + self.map[rev] = dest return files, copies = changes parents = [self.map[r] for r in commit.parents] @@ -249,13 +227,13 @@ self.dest.copyfile(copyf, f) newnode = self.dest.putcommit(filenames, parents, commit) - self.mapentry(rev, newnode) + self.map[rev] = newnode def convert(self): try: self.source.before() self.dest.before() - self.source.setrevmap(self.map, self.maporder) + self.source.setrevmap(self.map) self.ui.status("scanning source...\n") heads = self.source.getheads() parents = self.walktree(heads) @@ -285,7 +263,7 @@ # write another hash correspondence to override the previous # one so we don't end up with extra tag heads if nrev: - self.mapentry(c, nrev) + self.map[c] = nrev self.writeauthormap() finally: @@ -296,8 +274,7 @@ self.dest.after() finally: self.source.after() - if self.revmapfilefd: - self.revmapfilefd.close() + self.map.close() def convert(ui, src, dest=None, revmapfile=None, **opts): """Convert a foreign SCM repository to a Mercurial one. @@ -311,6 +288,7 @@ Accepted destination formats: - Mercurial + - Subversion (history on branches is not preserved) If no revision is given, all revisions will be converted. Otherwise, convert will only import up to the named revision (given in a format diff -r 0b3f910dfd17 -r 6ffca2bf23da hgext/convert/common.py --- a/hgext/convert/common.py Fri Nov 09 20:21:35 2007 -0200 +++ b/hgext/convert/common.py Fri Nov 09 21:24:25 2007 -0200 @@ -1,6 +1,8 @@ # common code for the convert extension -import base64 +import base64, errno import cPickle as pickle +from mercurial import util +from mercurial.i18n import _ def encodeargs(args): def encodearg(s): @@ -15,6 +17,11 @@ s = base64.decodestring(s) return pickle.loads(s) +def checktool(exe, name=None): + name = name or exe + if not util.find_exe(exe): + raise util.Abort('cannot find required "%s" tool' % name) + class NoRepo(Exception): pass SKIPREV = 'SKIP' @@ -48,11 +55,8 @@ def after(self): pass - def setrevmap(self, revmap, order): - """set the map of already-converted revisions - - order is a list with the keys from revmap in the order they - appear in the revision map file.""" + def setrevmap(self, revmap): + """set the map of already-converted revisions""" pass def getheads(self): @@ -184,3 +188,105 @@ filter empty revisions. """ pass + + def before(self): + pass + + def after(self): + pass + + +class commandline(object): + def __init__(self, ui, command): + self.ui = ui + self.command = command + + def prerun(self): + pass + + def postrun(self): + pass + + def _run(self, cmd, *args, **kwargs): + cmdline = [self.command, cmd] + list(args) + for k, v in kwargs.iteritems(): + if len(k) == 1: + cmdline.append('-' + k) + else: + cmdline.append('--' + k.replace('_', '-')) + try: + if len(k) == 1: + cmdline.append('' + v) + else: + cmdline[-1] += '=' + v + except TypeError: + pass + cmdline = [util.shellquote(arg) for arg in cmdline] + cmdline += ['<', util.nulldev] + cmdline = util.quotecommand(' '.join(cmdline)) + self.ui.debug(cmdline, '\n') + + self.prerun() + try: + return util.popen(cmdline) + finally: + self.postrun() + + def run(self, cmd, *args, **kwargs): + fp = self._run(cmd, *args, **kwargs) + output = fp.read() + self.ui.debug(output) + return output, fp.close() + + def checkexit(self, status, output=''): + if status: + if output: + self.ui.warn(_('%s error:\n') % self.command) + self.ui.warn(output) + msg = util.explain_exit(status)[0] + raise util.Abort(_('%s %s') % (self.command, msg)) + + def run0(self, cmd, *args, **kwargs): + output, status = self.run(cmd, *args, **kwargs) + self.checkexit(status, output) + return output + + +class mapfile(dict): + def __init__(self, ui, path): + super(mapfile, self).__init__() + self.ui = ui + self.path = path + self.fp = None + self.order = [] + self._read() + + def _read(self): + try: + fp = open(self.path, 'r') + except IOError, err: + if err.errno != errno.ENOENT: + raise + return + for line in fp: + key, value = line[:-1].split(' ', 1) + if key not in self: + self.order.append(key) + super(mapfile, self).__setitem__(key, value) + fp.close() + + def __setitem__(self, key, value): + if self.fp is None: + try: + self.fp = open(self.path, 'a') + except IOError, err: + raise util.Abort(_('could not open map file %r: %s') % + (self.path, err.strerror)) + self.fp.write('%s %s\n' % (key, value)) + self.fp.flush() + super(mapfile, self).__setitem__(key, value) + + def close(self): + if self.fp: + self.fp.close() + self.fp = None diff -r 0b3f910dfd17 -r 6ffca2bf23da hgext/convert/cvs.py --- a/hgext/convert/cvs.py Fri Nov 09 20:21:35 2007 -0200 +++ b/hgext/convert/cvs.py Fri Nov 09 21:24:25 2007 -0200 @@ -3,7 +3,7 @@ import os, locale, re, socket from mercurial import util -from common import NoRepo, commit, converter_source +from common import NoRepo, commit, converter_source, checktool class convert_cvs(converter_source): def __init__(self, ui, path, rev=None): @@ -13,6 +13,9 @@ if not os.path.exists(cvs): raise NoRepo("%s does not look like a CVS checkout" % path) + for tool in ('cvsps', 'cvs'): + checktool(tool) + self.changeset = {} self.files = {} self.tags = {} diff -r 0b3f910dfd17 -r 6ffca2bf23da hgext/convert/darcs.py --- a/hgext/convert/darcs.py Fri Nov 09 20:21:35 2007 -0200 +++ b/hgext/convert/darcs.py Fri Nov 09 21:24:25 2007 -0200 @@ -1,6 +1,6 @@ # darcs support for the convert extension -from common import NoRepo, commit, converter_source +from common import NoRepo, checktool, commandline, commit, converter_source from mercurial.i18n import _ from mercurial import util import os, shutil, tempfile @@ -17,15 +17,18 @@ except ImportError: ElementTree = None -class darcs_source(converter_source): +class darcs_source(converter_source, commandline): def __init__(self, ui, path, rev=None): - super(darcs_source, self).__init__(ui, path, rev=rev) + converter_source.__init__(self, ui, path, rev=rev) + commandline.__init__(self, ui, 'darcs') # check for _darcs, ElementTree, _darcs/inventory so that we can # easily skip test-convert-darcs if ElementTree is not around if not os.path.exists(os.path.join(path, '_darcs')): raise NoRepo("%s does not look like a darcs repo" % path) + checktool('darcs') + if ElementTree is None: raise util.Abort(_("Python ElementTree module is not available")) @@ -45,7 +48,8 @@ output, status = self.run('init', repodir=self.tmppath) self.checkexit(status) - tree = self.xml('changes', '--xml-output', '--summary') + tree = self.xml('changes', xml_output=True, summary=True, + repodir=self.path) tagname = None child = None for elt in tree.findall('patch'): @@ -65,31 +69,9 @@ self.ui.debug('cleaning up %s\n' % self.tmppath) shutil.rmtree(self.tmppath, ignore_errors=True) - def _run(self, cmd, *args, **kwargs): - cmdline = ['darcs', cmd, '--repodir', kwargs.get('repodir', self.path)] - cmdline += args - cmdline = [util.shellquote(arg) for arg in cmdline] - cmdline += ['<', util.nulldev] - cmdline = ' '.join(cmdline) - self.ui.debug(cmdline, '\n') - return util.popen(cmdline) - - def run(self, cmd, *args, **kwargs): - fp = self._run(cmd, *args, **kwargs) - output = fp.read() - return output, fp.close() - - def checkexit(self, status, output=''): - if status: - if output: - self.ui.warn(_('darcs error:\n')) - self.ui.warn(output) - msg = util.explain_exit(status)[0] - raise util.Abort(_('darcs %s') % msg) - - def xml(self, cmd, *opts): + def xml(self, cmd, **kwargs): etree = ElementTree() - fp = self._run(cmd, *opts) + fp = self._run(cmd, **kwargs) etree.parse(fp) self.checkexit(fp.close()) return etree.getroot() @@ -105,15 +87,15 @@ desc=desc.strip(), parents=self.parents[rev]) def pull(self, rev): - output, status = self.run('pull', self.path, '--all', - '--match', 'hash %s' % rev, - '--no-test', '--no-posthook', - '--external-merge', '/bin/false', + output, status = self.run('pull', self.path, all=True, + match='hash %s' % rev, + no_test=True, no_posthook=True, + external_merge='/bin/false', repodir=self.tmppath) if status: if output.find('We have conflicts in') == -1: self.checkexit(status, output) - output, status = self.run('revert', '--all', repodir=self.tmppath) + output, status = self.run('revert', all=True, repodir=self.tmppath) self.checkexit(status, output) def getchanges(self, rev): diff -r 0b3f910dfd17 -r 6ffca2bf23da hgext/convert/filemap.py --- a/hgext/convert/filemap.py Fri Nov 09 20:21:35 2007 -0200 +++ b/hgext/convert/filemap.py Fri Nov 09 21:24:25 2007 -0200 @@ -128,7 +128,7 @@ self.children = {} self.seenchildren = {} - def setrevmap(self, revmap, order): + def setrevmap(self, revmap): # rebuild our state to make things restartable # # To avoid calling getcommit for every revision that has already @@ -143,7 +143,7 @@ seen = {SKIPREV: SKIPREV} dummyset = util.set() converted = [] - for rev in order: + for rev in revmap.order: mapped = revmap[rev] wanted = mapped not in seen if wanted: @@ -157,7 +157,7 @@ arg = None converted.append((rev, wanted, arg)) self.convertedorder = converted - return self.base.setrevmap(revmap, order) + return self.base.setrevmap(revmap) def rebuild(self): if self._rebuilt: diff -r 0b3f910dfd17 -r 6ffca2bf23da hgext/convert/git.py --- a/hgext/convert/git.py Fri Nov 09 20:21:35 2007 -0200 +++ b/hgext/convert/git.py Fri Nov 09 21:24:25 2007 -0200 @@ -3,7 +3,7 @@ import os from mercurial import util -from common import NoRepo, commit, converter_source +from common import NoRepo, commit, converter_source, checktool class convert_git(converter_source): # Windows does not support GIT_DIR= construct while other systems @@ -31,6 +31,9 @@ path += "/.git" if not os.path.exists(path + "/objects"): raise NoRepo("%s does not look like a Git repo" % path) + + checktool('git-rev-parse', 'git') + self.path = path def getheads(self): diff -r 0b3f910dfd17 -r 6ffca2bf23da hgext/convert/subversion.py --- a/hgext/convert/subversion.py Fri Nov 09 20:21:35 2007 -0200 +++ b/hgext/convert/subversion.py Fri Nov 09 21:24:25 2007 -0200 @@ -17,9 +17,13 @@ import locale import os +import re import sys import cPickle as pickle -from mercurial import util +import tempfile + +from mercurial import strutil, util +from mercurial.i18n import _ # Subversion stuff. Works best with very recent Python SVN bindings # e.g. SVN 1.5 or backports. Thanks to the bzr folks for enhancing @@ -28,6 +32,7 @@ from cStringIO import StringIO from common import NoRepo, commit, converter_source, encodeargs, decodeargs +from common import commandline, converter_sink, mapfile try: from svn.core import SubversionException, Pool @@ -149,9 +154,9 @@ self.head = self.revid(self.last_changed) self._changescache = None - def setrevmap(self, revmap, order): + def setrevmap(self, revmap): lastrevs = {} - for revid in revmap.keys(): + for revid in revmap.iterkeys(): uuid, module, revnum = self.revsplit(revid) lastrevnum = lastrevs.setdefault(module, revnum) if revnum > lastrevnum: @@ -664,3 +669,198 @@ pool = Pool() rpath = '/'.join([self.base, path]).strip('/') return ['%s/%s' % (path, x) for x in svn.client.ls(rpath, optrev(revnum), True, self.ctx, pool).keys()] + +pre_revprop_change = '''#!/bin/sh + +REPOS="$1" +REV="$2" +USER="$3" +PROPNAME="$4" +ACTION="$5" + +if [ "$ACTION" = "M" -a "$PROPNAME" = "svn:log" ]; then exit 0; fi +if [ "$ACTION" = "A" -a "$PROPNAME" = "hg:convert-branch" ]; then exit 0; fi +if [ "$ACTION" = "A" -a "$PROPNAME" = "hg:convert-rev" ]; then exit 0; fi + +echo "Changing prohibited revision property" >&2 +exit 1 +''' + +class svn_sink(converter_sink, commandline): + commit_re = re.compile(r'Committed revision (\d+).', re.M) + + def prerun(self): + if self.wc: + os.chdir(self.wc) + + def postrun(self): + if self.wc: + os.chdir(self.cwd) + + def join(self, name): + return os.path.join(self.wc, '.svn', name) + + def revmapfile(self): + return self.join('hg-shamap') + + def authorfile(self): + return self.join('hg-authormap') + + def __init__(self, ui, path): + converter_sink.__init__(self, ui, path) + commandline.__init__(self, ui, 'svn') + self.delete = [] + self.wc = None + self.cwd = os.getcwd() + + path = os.path.realpath(path) + + created = False + if os.path.isfile(os.path.join(path, '.svn', 'entries')): + self.wc = path + self.run0('update') + else: + if os.path.isdir(os.path.dirname(path)): + if not os.path.exists(os.path.join(path, 'db', 'fs-type')): + ui.status(_('initializing svn repo %r\n') % + os.path.basename(path)) + commandline(ui, 'svnadmin').run0('create', path) + created = path + path = 'file://' + path + wcpath = os.path.join(os.getcwd(), os.path.basename(path) + '-wc') + ui.status(_('initializing svn wc %r\n') % os.path.basename(wcpath)) + self.run0('checkout', path, wcpath) + + self.wc = wcpath + self.opener = util.opener(self.wc) + self.wopener = util.opener(self.wc) + self.childmap = mapfile(ui, self.join('hg-childmap')) + + if created: + hook = os.path.join(created, 'hooks', 'pre-revprop-change') + fp = open(hook, 'w') + fp.write(pre_revprop_change) + fp.close() + util.set_exec(hook, True) + + def wjoin(self, *names): + return os.path.join(self.wc, *names) + + def putfile(self, filename, flags, data): + if 'l' in flags: + self.wopener.symlink(data, filename) + else: + try: + if os.path.islink(self.wjoin(filename)): + os.unlink(filename) + except OSError: + pass + self.wopener(filename, 'w').write(data) + was_exec = util.is_exec(self.wjoin(filename)) + util.set_exec(self.wjoin(filename), 'x' in flags) + if was_exec: + if 'x' not in flags: + self.run0('propdel', 'svn:executable', filename) + else: + if 'x' in flags: + self.run0('propset', 'svn:executable', '*', filename) + + def delfile(self, name): + self.delete.append(name) + + def copyfile(self, source, dest): + # SVN's copy command pukes if the destination file exists, but + # our copyfile method expects to record a copy that has + # already occurred. Cross the semantic gap. + wdest = self.wjoin(dest) + exists = os.path.exists(wdest) + if exists: + fd, tempname = tempfile.mkstemp( + prefix='hg-copy-', dir=os.path.dirname(wdest)) + os.close(fd) + os.unlink(tempname) + os.rename(wdest, tempname) + try: + self.run0('copy', source, dest) + finally: + if exists: + try: + os.unlink(wdest) + except OSError: + pass + os.rename(tempname, wdest) + + def dirs_of(self, files): + dirs = set() + for f in files: + if os.path.isdir(self.wjoin(f)): + dirs.add(f) + for i in strutil.rfindall(f, '/'): + dirs.add(f[:i]) + return dirs + + def add_files(self, files): + add_dirs = [d for d in self.dirs_of(files) + if not os.path.exists(self.wjoin(d, '.svn', 'entries'))] + if add_dirs: + self.run('add', non_recursive=True, quiet=True, *add) + if files: + self.run('add', quiet=True, *files) + return files.union(add_dirs) + + def tidy_dirs(self, names): + dirs = list(self.dirs_of(names)) + dirs.sort(reverse=True) + deleted = [] + for d in dirs: + wd = self.wjoin(d) + if os.listdir(wd) == '.svn': + self.run0('delete', d) + deleted.append(d) + return deleted + + def addchild(self, parent, child): + self.childmap[parent] = child + + def putcommit(self, files, parents, commit): + for parent in parents: + try: + return self.childmap[parent] + except KeyError: + pass + entries = set(self.delete) + if self.delete: + self.run0('delete', *self.delete) + self.delete = [] + files = util.frozenset(files) + entries.update(self.add_files(files.difference(entries))) + entries.update(self.tidy_dirs(entries)) + fd, messagefile = tempfile.mkstemp(prefix='hg-convert-') + fp = os.fdopen(fd, 'w') + fp.write(commit.desc) + fp.close() + try: + output = self.run0('commit', + username=util.shortuser(commit.author), + file=messagefile, + *list(entries)) + try: + rev = self.commit_re.search(output).group(1) + except AttributeError: + self.ui.warn(_('unexpected svn output:\n')) + self.ui.warn(output) + raise util.Abort(_('unable to cope with svn output')) + if commit.rev: + self.run('propset', 'hg:convert-rev', commit.rev, + revprop=True, revision=rev) + if commit.branch and commit.branch != 'default': + self.run('propset', 'hg:convert-branch', commit.branch, + revprop=True, revision=rev) + for parent in parents: + self.addchild(parent, rev) + return rev + finally: + os.unlink(messagefile) + + def puttags(self, tags): + self.ui.warn(_('XXX TAGS NOT IMPLEMENTED YET\n')) diff -r 0b3f910dfd17 -r 6ffca2bf23da hgext/gpg.py --- a/hgext/gpg.py Fri Nov 09 20:21:35 2007 -0200 +++ b/hgext/gpg.py Fri Nov 09 21:24:25 2007 -0200 @@ -249,7 +249,7 @@ message = opts['message'] if not message: message = "\n".join([_("Added signature for changeset %s") - % hgnode.hex(n) + % hgnode.short(n) for n in nodes]) try: repo.commit([".hgsigs"], message, opts['user'], opts['date']) diff -r 0b3f910dfd17 -r 6ffca2bf23da tests/test-convert-svn --- a/tests/test-convert-svn Fri Nov 09 20:21:35 2007 -0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,115 +0,0 @@ -#!/bin/sh - -"$TESTDIR/hghave" svn svn-bindings || exit 80 - -fix_path() -{ - tr '\\' / -} - -echo "[extensions]" >> $HGRCPATH -echo "convert = " >> $HGRCPATH - -svnadmin create svn-repo - -echo % initial svn import -mkdir t -cd t -echo a > a -cd .. - -svnpath=`pwd | fix_path` -# SVN wants all paths to start with a slash. Unfortunately, -# Windows ones don't. Handle that. -expr $svnpath : "\/" > /dev/null -if [ $? -ne 0 ]; then - svnpath='/'$svnpath -fi - -svnurl=file://$svnpath/svn-repo/trunk -svn import -m init t $svnurl | fix_path - -echo % update svn repository -svn co $svnurl t2 | fix_path -cd t2 -echo b >> a -echo b > b -svn add b -svn ci -m changea -cd .. - -echo % convert to hg once -hg convert $svnurl - -echo % update svn repository again -cd t2 -echo c >> a -echo c >> b -svn ci -m changeb -cd .. - -echo % test incremental conversion -hg convert $svnurl - -echo % test filemap -echo 'include b' > filemap -hg convert --filemap filemap $svnurl fmap -echo '[extensions]' >> $HGRCPATH -echo 'hgext.graphlog =' >> $HGRCPATH -hg glog -R fmap --template '#rev# #desc|firstline# files: #files#\n' - -######################################## - -echo "# now tests that it works with trunk/branches/tags layout" -echo -echo % initial svn import -mkdir projA -cd projA -mkdir trunk -mkdir branches -mkdir tags -cd .. - -svnurl=file://$svnpath/svn-repo/projA -svn import -m "init projA" projA $svnurl | fix_path - - -echo % update svn repository -svn co $svnurl/trunk A | fix_path -cd A -echo hello > letter.txt -svn add letter.txt -svn ci -m hello - -echo world >> letter.txt -svn ci -m world - -svn copy -m "tag v0.1" $svnurl/trunk $svnurl/tags/v0.1 - -echo 'nice day today!' >> letter.txt -svn ci -m "nice day" -cd .. - -echo % convert to hg once -hg convert $svnurl A-hg - -echo % update svn repository again -cd A -echo "see second letter" >> letter.txt -echo "nice to meet you" > letter2.txt -svn add letter2.txt -svn ci -m "second letter" - -svn copy -m "tag v0.2" $svnurl/trunk $svnurl/tags/v0.2 - -echo "blah-blah-blah" >> letter2.txt -svn ci -m "work in progress" -cd .. - -echo % test incremental conversion -hg convert $svnurl A-hg - -cd A-hg -hg glog --template '#rev# #desc|firstline# files: #files#\n' -hg tags -q -cd .. diff -r 0b3f910dfd17 -r 6ffca2bf23da tests/test-convert-svn-sink --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-convert-svn-sink Fri Nov 09 21:24:25 2007 -0200 @@ -0,0 +1,91 @@ +#!/bin/sh + +"$TESTDIR/hghave" svn svn-bindings || exit 80 + +echo "[extensions]" >> $HGRCPATH +echo "convert = " >> $HGRCPATH + +hg init a + +echo a > a/a +echo % add +hg --cwd a ci -d '0 0' -A -m 'add a file' + +echo a >> a/a +echo % modify +hg --cwd a ci -d '1 0' -m 'modify a file' +hg --cwd a tip -q + +hg convert -d svn a +(cd a-hg-wc; svn up; svn st -v; svn log --xml -v --limit=2 | sed 's,.*,,') +ls a a-hg-wc +cmp a/a a-hg-wc/a && echo same || echo different + +hg --cwd a mv a b +echo % rename +hg --cwd a ci -d '2 0' -m 'rename a file' +hg --cwd a tip -q + +hg convert -d svn a +(cd a-hg-wc; svn up; svn st -v; svn log --xml -v --limit=1 | sed 's,.*,,') +ls a a-hg-wc + +hg --cwd a cp b c +echo % copy +hg --cwd a ci -d '3 0' -m 'copy a file' +hg --cwd a tip -q + +hg convert -d svn a +(cd a-hg-wc; svn up; svn st -v; svn log --xml -v --limit=1 | sed 's,.*,,') +ls a a-hg-wc + +hg --cwd a rm b +echo % remove +hg --cwd a ci -d '4 0' -m 'remove a file' +hg --cwd a tip -q + +hg convert -d svn a +(cd a-hg-wc; svn up; svn st -v; svn log --xml -v --limit=1 | sed 's,.*,,') +ls a a-hg-wc + +chmod +x a/c +echo % executable +hg --cwd a ci -d '5 0' -m 'make a file executable' +hg --cwd a tip -q + +hg convert -d svn a +(cd a-hg-wc; svn up; svn st -v; svn log --xml -v --limit=1 | sed 's,.*,,') +test -x a-hg-wc/c && echo executable || echo not executable + +echo % branchy history + +hg init b +echo base > b/b +hg --cwd b ci -d '0 0' -Ambase + +echo left-1 >> b/b +echo left-1 > b/left-1 +hg --cwd b ci -d '1 0' -Amleft-1 + +echo left-2 >> b/b +echo left-2 > b/left-2 +hg --cwd b ci -d '2 0' -Amleft-2 + +hg --cwd b up 0 + +echo right-1 >> b/b +echo right-1 > b/right-1 +hg --cwd b ci -d '3 0' -Amright-1 + +echo right-2 >> b/b +echo right-2 > b/right-2 +hg --cwd b ci -d '4 0' -Amright-2 + +hg --cwd b up -C 2 +hg --cwd b merge +hg --cwd b revert -r 2 b +hg --cwd b ci -d '5 0' -m 'merge' + +hg convert -d svn b +echo % expect 4 changes +(cd b-hg-wc; svn up; svn st -v; svn log --xml -v | sed 's,.*,,') diff -r 0b3f910dfd17 -r 6ffca2bf23da tests/test-convert-svn-sink.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-convert-svn-sink.out Fri Nov 09 21:24:25 2007 -0200 @@ -0,0 +1,251 @@ +% add +adding a +% modify +1:10307c220ed9 +assuming destination a-hg +initializing svn repo 'a-hg' +initializing svn wc 'a-hg-wc' +scanning source... +sorting... +converting... +1 add a file +0 modify a file +At revision 2. + 2 2 test . + 2 2 test a + + + +test + + +/a + +modify a file + + +test + + +/a + +add a file + + +a: +a + +a-hg-wc: +a +same +% rename +2:6e45a219686e +assuming destination a-hg +initializing svn wc 'a-hg-wc' +scanning source... +sorting... +converting... +0 rename a file +At revision 3. + 3 3 test . + 3 3 test b + + + +test + + +/a +/b + +rename a file + + +a: +b + +a-hg-wc: +b +% copy +3:d811dc81efbb +assuming destination a-hg +initializing svn wc 'a-hg-wc' +scanning source... +sorting... +converting... +0 copy a file +At revision 4. + 4 4 test . + 4 3 test b + 4 4 test c + + + +test + + +/c + +copy a file + + +a: +b +c + +a-hg-wc: +b +c +% remove +4:045e93063aca +assuming destination a-hg +initializing svn wc 'a-hg-wc' +scanning source... +sorting... +converting... +0 remove a file +At revision 5. + 5 5 test . + 5 4 test c + + + +test + + +/b + +remove a file + + +a: +c + +a-hg-wc: +c +% executable +5:7eda3f4b5331 +svn: Path 'b' does not exist +assuming destination a-hg +initializing svn wc 'a-hg-wc' +scanning source... +sorting... +converting... +0 make a file executable +abort: svn exited with status 1 +At revision 5. + 5 5 test . + M 5 4 test c + + + +test + + +/b + +remove a file + + +executable +% branchy history +adding b +adding left-1 +adding left-2 +1 files updated, 0 files merged, 2 files removed, 0 files unresolved +adding right-1 +adding right-2 +3 files updated, 0 files merged, 2 files removed, 0 files unresolved +warning: conflicts during merge. +merging b +merging b failed! +2 files updated, 0 files merged, 0 files removed, 1 files unresolved +There are unresolved merges, you can redo the full merge using: + hg update -C 2 + hg merge 4 +assuming destination b-hg +initializing svn repo 'b-hg' +initializing svn wc 'b-hg-wc' +scanning source... +sorting... +converting... +5 base +4 left-1 +3 left-2 +2 right-1 +1 right-2 +0 merge +% expect 4 changes +At revision 4. + 4 4 test . + 4 3 test b + 4 2 test left-1 + 4 3 test left-2 + 4 4 test right-1 + 4 4 test right-2 + + + +test + + +/right-1 +/right-2 + +merge + + +test + + +/b +/left-2 + +left-2 + + +test + + +/b +/left-1 + +left-1 + + +test + + +/b + +base + + diff -r 0b3f910dfd17 -r 6ffca2bf23da tests/test-convert-svn-source --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-convert-svn-source Fri Nov 09 21:24:25 2007 -0200 @@ -0,0 +1,115 @@ +#!/bin/sh + +"$TESTDIR/hghave" svn svn-bindings || exit 80 + +fix_path() +{ + tr '\\' / +} + +echo "[extensions]" >> $HGRCPATH +echo "convert = " >> $HGRCPATH + +svnadmin create svn-repo + +echo % initial svn import +mkdir t +cd t +echo a > a +cd .. + +svnpath=`pwd | fix_path` +# SVN wants all paths to start with a slash. Unfortunately, +# Windows ones don't. Handle that. +expr $svnpath : "\/" > /dev/null +if [ $? -ne 0 ]; then + svnpath='/'$svnpath +fi + +svnurl=file://$svnpath/svn-repo/trunk +svn import -m init t $svnurl | fix_path + +echo % update svn repository +svn co $svnurl t2 | fix_path +cd t2 +echo b >> a +echo b > b +svn add b +svn ci -m changea +cd .. + +echo % convert to hg once +hg convert $svnurl + +echo % update svn repository again +cd t2 +echo c >> a +echo c >> b +svn ci -m changeb +cd .. + +echo % test incremental conversion +hg convert $svnurl + +echo % test filemap +echo 'include b' > filemap +hg convert --filemap filemap $svnurl fmap +echo '[extensions]' >> $HGRCPATH +echo 'hgext.graphlog =' >> $HGRCPATH +hg glog -R fmap --template '#rev# #desc|firstline# files: #files#\n' + +######################################## + +echo "# now tests that it works with trunk/branches/tags layout" +echo +echo % initial svn import +mkdir projA +cd projA +mkdir trunk +mkdir branches +mkdir tags +cd .. + +svnurl=file://$svnpath/svn-repo/projA +svn import -m "init projA" projA $svnurl | fix_path + + +echo % update svn repository +svn co $svnurl/trunk A | fix_path +cd A +echo hello > letter.txt +svn add letter.txt +svn ci -m hello + +echo world >> letter.txt +svn ci -m world + +svn copy -m "tag v0.1" $svnurl/trunk $svnurl/tags/v0.1 + +echo 'nice day today!' >> letter.txt +svn ci -m "nice day" +cd .. + +echo % convert to hg once +hg convert $svnurl A-hg + +echo % update svn repository again +cd A +echo "see second letter" >> letter.txt +echo "nice to meet you" > letter2.txt +svn add letter2.txt +svn ci -m "second letter" + +svn copy -m "tag v0.2" $svnurl/trunk $svnurl/tags/v0.2 + +echo "blah-blah-blah" >> letter2.txt +svn ci -m "work in progress" +cd .. + +echo % test incremental conversion +hg convert $svnurl A-hg + +cd A-hg +hg glog --template '#rev# #desc|firstline# files: #files#\n' +hg tags -q +cd .. diff -r 0b3f910dfd17 -r 6ffca2bf23da tests/test-convert-svn-source.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-convert-svn-source.out Fri Nov 09 21:24:25 2007 -0200 @@ -0,0 +1,114 @@ +% initial svn import +Adding t/a + +Committed revision 1. +% update svn repository +A t2/a +Checked out revision 1. +A b +Sending a +Adding b +Transmitting file data .. +Committed revision 2. +% convert to hg once +assuming destination trunk-hg +initializing destination trunk-hg repository +scanning source... +sorting... +converting... +1 init +0 changea +% update svn repository again +Sending a +Sending b +Transmitting file data .. +Committed revision 3. +% test incremental conversion +assuming destination trunk-hg +destination trunk-hg is a Mercurial repository +scanning source... +sorting... +converting... +0 changeb +% test filemap +initializing destination fmap repository +scanning source... +sorting... +converting... +2 init +1 changea +0 changeb +o 1 changeb files: b +| +o 0 changea files: b + +# now tests that it works with trunk/branches/tags layout + +% initial svn import +Adding projA/trunk +Adding projA/branches +Adding projA/tags + +Committed revision 4. +% update svn repository +Checked out revision 4. +A letter.txt +Adding letter.txt +Transmitting file data . +Committed revision 5. +Sending letter.txt +Transmitting file data . +Committed revision 6. + +Committed revision 7. +Sending letter.txt +Transmitting file data . +Committed revision 8. +% convert to hg once +initializing destination A-hg repository +scanning source... +sorting... +converting... +3 init projA +2 hello +1 world +0 nice day +updating tags +% update svn repository again +A letter2.txt +Sending letter.txt +Adding letter2.txt +Transmitting file data .. +Committed revision 9. + +Committed revision 10. +Sending letter2.txt +Transmitting file data . +Committed revision 11. +% test incremental conversion +destination A-hg is a Mercurial repository +scanning source... +sorting... +converting... +1 second letter +0 work in progress +updating tags +o 7 update tags files: .hgtags +| +o 6 work in progress files: letter2.txt +| +o 5 second letter files: letter.txt letter2.txt +| +o 4 update tags files: .hgtags +| +o 3 nice day files: letter.txt +| +o 2 world files: letter.txt +| +o 1 hello files: letter.txt +| +o 0 init projA files: + +tip +v0.2 +v0.1 diff -r 0b3f910dfd17 -r 6ffca2bf23da tests/test-convert-svn.out --- a/tests/test-convert-svn.out Fri Nov 09 20:21:35 2007 -0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,114 +0,0 @@ -% initial svn import -Adding t/a - -Committed revision 1. -% update svn repository -A t2/a -Checked out revision 1. -A b -Sending a -Adding b -Transmitting file data .. -Committed revision 2. -% convert to hg once -assuming destination trunk-hg -initializing destination trunk-hg repository -scanning source... -sorting... -converting... -1 init -0 changea -% update svn repository again -Sending a -Sending b -Transmitting file data .. -Committed revision 3. -% test incremental conversion -assuming destination trunk-hg -destination trunk-hg is a Mercurial repository -scanning source... -sorting... -converting... -0 changeb -% test filemap -initializing destination fmap repository -scanning source... -sorting... -converting... -2 init -1 changea -0 changeb -o 1 changeb files: b -| -o 0 changea files: b - -# now tests that it works with trunk/branches/tags layout - -% initial svn import -Adding projA/trunk -Adding projA/branches -Adding projA/tags - -Committed revision 4. -% update svn repository -Checked out revision 4. -A letter.txt -Adding letter.txt -Transmitting file data . -Committed revision 5. -Sending letter.txt -Transmitting file data . -Committed revision 6. - -Committed revision 7. -Sending letter.txt -Transmitting file data . -Committed revision 8. -% convert to hg once -initializing destination A-hg repository -scanning source... -sorting... -converting... -3 init projA -2 hello -1 world -0 nice day -updating tags -% update svn repository again -A letter2.txt -Sending letter.txt -Adding letter2.txt -Transmitting file data .. -Committed revision 9. - -Committed revision 10. -Sending letter2.txt -Transmitting file data . -Committed revision 11. -% test incremental conversion -destination A-hg is a Mercurial repository -scanning source... -sorting... -converting... -1 second letter -0 work in progress -updating tags -o 7 update tags files: .hgtags -| -o 6 work in progress files: letter2.txt -| -o 5 second letter files: letter.txt letter2.txt -| -o 4 update tags files: .hgtags -| -o 3 nice day files: letter.txt -| -o 2 world files: letter.txt -| -o 1 hello files: letter.txt -| -o 0 init projA files: - -tip -v0.2 -v0.1 diff -r 0b3f910dfd17 -r 6ffca2bf23da tests/test-convert.out --- a/tests/test-convert.out Fri Nov 09 20:21:35 2007 -0200 +++ b/tests/test-convert.out Fri Nov 09 21:24:25 2007 -0200 @@ -11,6 +11,7 @@ Accepted destination formats: - Mercurial + - Subversion (history on branches is not preserved) If no revision is given, all revisions will be converted. Otherwise, convert will only import up to the named revision (given in a format diff -r 0b3f910dfd17 -r 6ffca2bf23da tests/test-execute-bit --- a/tests/test-execute-bit Fri Nov 09 20:21:35 2007 -0200 +++ b/tests/test-execute-bit Fri Nov 09 21:24:25 2007 -0200 @@ -1,5 +1,7 @@ #!/bin/sh +"$TESTDIR/hghave" execbit || exit 80 + hg init echo a > a hg ci -d'0 0' -Am'not executable' diff -r 0b3f910dfd17 -r 6ffca2bf23da tests/test-merge-types --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-merge-types Fri Nov 09 21:24:25 2007 -0200 @@ -0,0 +1,36 @@ +#!/bin/sh + +hg init +echo a > a +hg ci -Amadd + +chmod +x a +hg ci -mexecutable + +hg up 0 +rm a +ln -s symlink a +hg ci -msymlink + +hg merge + +echo % symlink is left parent, executable is right + +if [ -L a ]; then + echo a is a symlink + readlink a +elif [ -x a ]; then + echo a is executable +fi + +hg update -C 1 +hg merge + +echo % symlink is right parent, executable is left + +if [ -L a ]; then + echo a is a symlink + readlink a +elif [ -x a ]; then + echo a is executable +fi diff -r 0b3f910dfd17 -r 6ffca2bf23da tests/test-merge-types.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-merge-types.out Fri Nov 09 21:24:25 2007 -0200 @@ -0,0 +1,1 @@ +### This test is for a known, unfixed bug ###