changeset: 215:b5ef9a404f5d
user: Augie Fackler <durin42@gmail.com>
date: Fri Mar 27 16:56:07 2009 -0500
summary: Stopped idiotic filtering of revisions that did not edit something branches/tags/trunk.
diff --git a/rebuildmeta.py b/rebuildmeta.py
--- a/rebuildmeta.py
+++ b/rebuildmeta.py
@@ -85,6 +85,8 @@
branchinfofile = open(os.path.join(svnmetadir, 'branch_info'), 'w')
pickle.dump(branchinfo, branchinfofile)
branchinfofile.close()
+
+ # now handle tags
tagsinfo = {}
realtags = svn.tags
tagsleft = realtags.items()
@@ -103,10 +105,11 @@
older_tags = svn.tags_at_rev(rev)
newsrc, newrev = older_tags[src]
tagsleft.append((tag, (newsrc, newrev)))
- if source.startswith('branches/') or source == 'trunk':
+ continue
+ else:
source = determinebranch(source)
- if rev <= last_rev:
- tagsinfo[tag] = source, rev
+ if rev <= last_rev:
+ tagsinfo[tag] = source, rev
tagsinfofile = open(os.path.join(svnmetadir, 'tag_info'), 'w')
pickle.dump(tagsinfo, tagsinfofile)
tagsinfofile.close()
@@ -114,10 +117,11 @@
rebuildmeta = util.command_needs_no_url(rebuildmeta)
def determinebranch(branch):
- if branch.startswith('branches'):
+ assert not branch.startswith('tags/'), "Tags can't be tags of other tags."
+ if branch.startswith('branches/'):
branch = branch[len('branches/'):]
elif branch == 'trunk':
branch = None
else:
- assert False, 'Unhandled case while regenerating metadata.'
+ branch = '../' + branch
return branch
diff --git a/svnwrap/svn_swig_wrapper.py b/svnwrap/svn_swig_wrapper.py
--- a/svnwrap/svn_swig_wrapper.py
+++ b/svnwrap/svn_swig_wrapper.py
@@ -211,6 +211,8 @@
source = hist.paths[path].copyfrom_path
source_rev = 0
for p in hist.paths:
+ if not p.startswith(path):
+ continue
if hist.paths[p].copyfrom_rev:
# We assume that the revision of the source tree as it was
# copied was actually the revision of the highest revision
@@ -247,26 +249,8 @@
The reason this is lazy is so that you can use the same repo object
to perform RA calls to get deltas.
"""
- # NB: you'd think this would work, but you'd be wrong. I'm pretty
- # convinced there must be some kind of svn bug here.
- #return self.fetch_history_at_paths(['tags', 'trunk', 'branches'],
- # start=start)
- # this does the same thing, but at the repo root + filtering. It's
- # kind of tough cookies, sadly.
- for r in self.fetch_history_at_paths([''], start=start,
- chunk_size=chunk_size):
- should_yield = False
- i = 0
- paths = list(r.paths.keys())
- while i < len(paths) and not should_yield:
- p = paths[i]
- if (p.startswith('trunk') or p.startswith('tags')
- or p.startswith('branches')):
- should_yield = True
- i += 1
- if should_yield:
- yield r
-
+ return self.fetch_history_at_paths([''], start=start,
+ chunk_size=chunk_size)
def fetch_history_at_paths(self, paths, start=None, stop=None,
chunk_size=1000):
changeset: 216:3c87de482011
user: Augie Fackler <durin42@gmail.com>
date: Sat Mar 28 14:26:35 2009 -0500
summary: tools: fixes for wc outputting with spaces.
diff --git a/tools/common.sh b/tools/common.sh
--- a/tools/common.sh
+++ b/tools/common.sh
@@ -9,7 +9,7 @@
echo 'export failed!'
return 255
fi
- if [[ "`hg st | wc -l`" == "0" ]] ; then
+ if [[ "`hg st | wc -l | python -c 'import sys; print sys.stdin.read().strip()'`" == "0" ]] ; then
return 0
else
if [[ $1 != "keep" ]] ; then
diff --git a/tools/verify-all-heads.sh b/tools/verify-all-heads.sh
--- a/tools/verify-all-heads.sh
+++ b/tools/verify-all-heads.sh
@@ -1,7 +1,7 @@
#!/bin/sh
. $(dirname $0)/common.sh
-for b in `hg branches | cut -f 1 -d ' '` ; do
+for b in `hg branches -a | cut -f 1 -d ' ' | grep -v closed-branches` ; do
hg co $b || break
echo Verifying $b
verify_current_revision keep > /dev/null || break
changeset: 217:6eb691a163cd
user: Augie Fackler <durin42@gmail.com>
date: Sat Mar 28 14:27:27 2009 -0500
summary: hg_delta_editor: stop using foobaz as a bogus filename.
diff --git a/hg_delta_editor.py b/hg_delta_editor.py
--- a/hg_delta_editor.py
+++ b/hg_delta_editor.py
@@ -346,6 +346,8 @@
return True
def _is_path_valid(self, path):
+ if path is None:
+ return False
subpath = self._split_branch_path(path)[0]
if subpath is None:
return False
@@ -825,7 +827,7 @@
delete_entry = stash_exception_on_self(delete_entry)
def open_file(self, path, parent_baton, base_revision, p=None):
- self.current_file = 'foobaz'
+ self.current_file = None
fpath, branch = self._path_and_branch_for_path(path)
if fpath:
self.current_file = path
@@ -868,7 +870,7 @@
def add_file(self, path, parent_baton, copyfrom_path,
copyfrom_revision, file_pool=None):
- self.current_file = 'foobaz'
+ self.current_file = None
self.base_revision = None
if path in self.deleted_files:
del self.deleted_files[path]
@@ -930,6 +932,7 @@
source_rev = copyfrom_revision
cp_f, source_branch = self._path_and_branch_for_path(copyfrom_path)
if cp_f == '' and br_path == '':
+ assert br_path is not None
self.branches[branch] = source_branch, source_rev, self.current_rev.revnum
new_hash = self.get_parent_revision(source_rev + 1,
source_branch)
changeset: 218:a360ddc97719
user: Augie Fackler <durin42@gmail.com>
date: Sat Mar 28 16:38:57 2009 -0500
summary: branches: change handling again, but this time a little less magic.
diff --git a/fetch_command.py b/fetch_command.py
--- a/fetch_command.py
+++ b/fetch_command.py
@@ -115,6 +115,7 @@
if count % 50 == 0:
svn.init_ra_and_client()
+
def replay_convert_rev(hg_editor, svn, r):
hg_editor.set_current_rev(r)
svn.get_replay(r.revnum, hg_editor)
diff --git a/hg_delta_editor.py b/hg_delta_editor.py
--- a/hg_delta_editor.py
+++ b/hg_delta_editor.py
@@ -225,6 +225,8 @@
if branchpath.startswith('trunk/'):
branches[self._localname('trunk')] = 'trunk'
continue
+ if branchname and branchname.startswith('../'):
+ continue
branches[branchname] = branchpath
return branches
@@ -276,10 +278,17 @@
if path.startswith('trunk/'):
path = test.split('/')[1:]
test = 'trunk'
+ elif path.startswith('branches/'):
+ elts = path.split('/')
+ test = '/'.join(elts[:2])
+ path = '/'.join(elts[2:])
else:
path = test.split('/')[-1]
test = '/'.join(test.split('/')[:-1])
- return path, self._localname(test), test
+ ln = self._localname(test)
+ if ln and ln.startswith('../'):
+ return None, None, None
+ return path, ln, test
def set_current_rev(self, rev):
"""Set the revision we're currently converting.
diff --git a/rebuildmeta.py b/rebuildmeta.py
--- a/rebuildmeta.py
+++ b/rebuildmeta.py
@@ -108,7 +108,7 @@
continue
else:
source = determinebranch(source)
- if rev <= last_rev:
+ if rev <= last_rev and (source or 'default') in repo.branchtags():
tagsinfo[tag] = source, rev
tagsinfofile = open(os.path.join(svnmetadir, 'tag_info'), 'w')
pickle.dump(tagsinfo, tagsinfofile)
changeset: 219:794f473b9b49
user: Augie Fackler <durin42@gmail.com>
date: Sat Mar 28 17:38:48 2009 -0500
summary: info: stop failing if the rev is not a child of an svn rev.
diff --git a/utility_commands.py b/utility_commands.py
--- a/utility_commands.py
+++ b/utility_commands.py
@@ -72,7 +72,11 @@
svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
hge.revmap.iterkeys()))
parent = find_wc_parent_rev(ui, repo, hge, svn_commit_hashes)
- r, br = svn_commit_hashes[parent.node()]
+ pn = parent.node()
+ if pn not in svn_commit_hashes:
+ ui.status('Not a child of an svn revision.\n')
+ return 0
+ r, br = svn_commit_hashes[pn]
subdir = parent.extra()['convert_revision'][40:].split('@')[0]
if br == None:
branchpath = '/trunk'
changeset: 220:06eb60f9a026
user: Augie Fackler <durin42@gmail.com>
date: Sat Mar 28 17:50:00 2009 -0500
summary: push: Do not attempt to push empty revisions.
diff --git a/push_cmd.py b/push_cmd.py
--- a/push_cmd.py
+++ b/push_cmd.py
@@ -11,6 +11,15 @@
import utility_commands
+class BaseException(Exception):
+ pass
+
+
+class NoFilesException(BaseException):
+ """Exception raised when you try and commit without files.
+ """
+
+
def push_revisions_to_subversion(ui, repo, hg_repo_path, svn_url,
stupid=False, **opts):
"""push revisions starting at a specified head back to Subversion.
@@ -50,7 +59,12 @@
and c.node() in svn_commit_hashes]
# 2. Commit oldest revision that needs to be pushed
base_revision = svn_commit_hashes[base_n][0]
- commit_from_rev(ui, repo, old_ctx, hge, svn_url, base_revision)
+ try:
+ commit_from_rev(ui, repo, old_ctx, hge, svn_url, base_revision)
+ except NoFilesException:
+ ui.warn("Could not push revision %s because it had no changes in svn.\n" %
+ old_ctx)
+ return 1
# 3. Fetch revisions from svn
r = fetch_command.fetch_revisions(ui, svn_url, hg_repo_path,
stupid=stupid)
@@ -268,6 +282,8 @@
addeddirs = [svnpath(d) for d in addeddirs]
deleteddirs = [svnpath(d) for d in deleteddirs]
new_target_files += addeddirs + deleteddirs + changeddirs
+ if not new_target_files:
+ raise NoFilesException()
try:
svn.commit(new_target_files, rev_ctx.description(), file_data,
base_revision, set(addeddirs), set(deleteddirs),
changeset: 221:ced45b753ba7
user: Augie Fackler <durin42@gmail.com>
date: Sat Mar 28 17:59:55 2009 -0500
summary: util: better error messages when finding svn parent of a revision.
diff --git a/util.py b/util.py
--- a/util.py
+++ b/util.py
@@ -3,6 +3,7 @@
from mercurial import hg
from mercurial import node
+from mercurial import util
svn_subcommands = { }
def register_subcommand(name):
@@ -94,7 +95,8 @@
and sourcerev.node() != node.nullid):
outgoing_rev_hashes.append(sourcerev.node())
sourcerev = sourcerev.parents()
- assert len(sourcerev) == 1
+ if len(sourcerev) != 1:
+ raise util.Abort("Sorry, can't find svn parent of a merge revision.")
sourcerev = sourcerev[0]
if sourcerev.node() != node.nullid:
return outgoing_rev_hashes
changeset: 222:f2c65dd3d5c0
user: Luke Opperman <luke@loppear.com>
date: Tue Mar 31 15:23:38 2009 -0500
summary: Avoid problems in _localname for commits to tags by ignoring them
diff --git a/hg_delta_editor.py b/hg_delta_editor.py
--- a/hg_delta_editor.py
+++ b/hg_delta_editor.py
@@ -221,6 +221,8 @@
parentdir = '/'.join(path[:-1])
filepaths = [p for p in filepaths if not '/'.join(p).startswith(parentdir)]
branchpath = self._normalize_path(parentdir)
+ if branchpath.startswith('tags/'):
+ continue
branchname = self._localname(branchpath)
if branchpath.startswith('trunk/'):
branches[self._localname('trunk')] = 'trunk'
changeset: 223:330f0b15d417
user: Luke Opperman <luke@loppear.com>
date: Tue Apr 07 13:30:05 2009 -0500
summary: issue67: mercurial 1.3 util incompatibility with encoding swap
diff --git a/fetch_command.py b/fetch_command.py
--- a/fetch_command.py
+++ b/fetch_command.py
@@ -29,8 +29,7 @@
"""pull new revisions from Subversion
"""
svn_url = util.normalize_url(svn_url)
- old_encoding = merc_util._encoding
- merc_util._encoding = 'UTF-8'
+ old_encoding = util.swap_out_encoding()
skipto_rev=int(skipto_rev)
have_replay = not stupid
if have_replay and not callable(
@@ -107,7 +106,8 @@
ui.status('Got a 502, retrying (%s)\n' % tries)
else:
raise
- merc_util._encoding = old_encoding
+ util.swap_out_encoding(old_encoding)
+
fetch_revisions = util.register_subcommand('pull')(fetch_revisions)
diff --git a/push_cmd.py b/push_cmd.py
--- a/push_cmd.py
+++ b/push_cmd.py
@@ -24,8 +24,7 @@
stupid=False, **opts):
"""push revisions starting at a specified head back to Subversion.
"""
- oldencoding = merc_util._encoding
- merc_util._encoding = 'UTF-8'
+ old_encoding = util.swap_out_encoding()
hge = hg_delta_editor.HgChangeReceiver(hg_repo_path,
ui_=ui)
svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
@@ -101,7 +100,7 @@
rebasesrc = node.bin(child.extra().get('rebase_source', node.hex(node.nullid)))
hge = hg_delta_editor.HgChangeReceiver(hg_repo_path, ui_=ui)
svn_commit_hashes = dict(zip(hge.revmap.itervalues(), hge.revmap.iterkeys()))
- merc_util._encoding = oldencoding
+ util.swap_out_encoding(old_encoding)
return 0
push_revisions_to_subversion = util.register_subcommand('push')(push_revisions_to_subversion)
# for git expats
diff --git a/util.py b/util.py
--- a/util.py
+++ b/util.py
@@ -4,6 +4,10 @@
from mercurial import hg
from mercurial import node
from mercurial import util
+try:
+ from mercurial import encoding
+except ImportError:
+ encoding = None
svn_subcommands = { }
def register_subcommand(name):
@@ -135,3 +139,14 @@
def describe_commit(ui, h, b):
ui.note(' committed to "%s" as %s\n' % ((b or 'default'), node.short(h)))
+
+
+def swap_out_encoding(new_encoding="UTF-8"):
+ """ Utility for mercurial incompatibility changes, can be removed after 1.3"""
+ if encoding is None:
+ old = util._encoding
+ util._encoding = new_encoding
+ else:
+ old = encoding.encoding
+ encoding.encoding = new_encoding
+ return old
changeset: 227:d79843a3d42c
user: Augie Fackler <durin42@gmail.com>
date: Mon Mar 30 10:24:27 2009 -0500
summary: Fixing syntax highlighting in emacs.
diff --git a/hg_delta_editor.py b/hg_delta_editor.py
--- a/hg_delta_editor.py
+++ b/hg_delta_editor.py
@@ -60,12 +60,12 @@
self.revmap[revnum, branch] = node_hash
def last_known_revision(self):
- ''' Obtain the highest numbered -- i.e. latest -- revision known.
+ """Obtain the highest numbered -- i.e. latest -- revision known.
Currently, this function just iterates over the entire revision map
using the max() builtin. This may be slow for extremely large
repositories, but for now, it's fast enough.
- '''
+ """
try:
return max(k[0] for k in self.revmap.iterkeys())
except ValueError:
changeset: 228:f71af18c4379
parent: 223:330f0b15d417
parent: 227:d79843a3d42c
user: Augie Fackler <durin42@gmail.com>
date: Tue Apr 07 13:48:58 2009 -0500
summary: Merge with crew.
diff --git a/__init__.py b/__init__.py
--- a/__init__.py
+++ b/__init__.py
@@ -37,7 +37,7 @@
try:
return svncommand.svncmd(ui, repo, subcommand, *args, **opts)
except core.SubversionException, e:
- if e.apr_err == 230001:
+ if e.apr_err == core.SVN_ERR_RA_SERF_SSL_CERT_UNTRUSTED:
raise mutil.Abort('It appears svn does not trust the ssl cert for this site.\n'
'Please try running svn ls on that url first.')
raise
@@ -60,7 +60,7 @@
try:
res = fetch_command.fetch_revisions(ui, svn_url, hg_repo_path, **opts)
except core.SubversionException, e:
- if e.apr_err == 230001:
+ if e.apr_err == core.SVN_ERR_RA_SERF_SSL_CERT_UNTRUSTED:
raise mutil.Abort('It appears svn does not trust the ssl cert for this site.\n'
'Please try running svn ls on that url first.')
raise
diff --git a/fetch_command.py b/fetch_command.py
--- a/fetch_command.py
+++ b/fetch_command.py
@@ -54,11 +54,10 @@
if os.path.exists(hg_editor.uuid_file):
uuid = open(hg_editor.uuid_file).read()
assert uuid == svn.uuid
- start = int(open(hg_editor.last_revision_handled_file, 'r').read())
+ start = hg_editor.last_known_revision()
else:
open(hg_editor.uuid_file, 'w').write(svn.uuid)
open(hg_editor.svn_url_file, 'w').write(svn_url)
- open(hg_editor.last_revision_handled_file, 'w').write(str(0))
initializing_repo = True
start = skipto_rev
@@ -92,20 +91,12 @@
else:
stupid_svn_server_pull_rev(ui, svn, hg_editor, r)
converted = True
- tmpfile = '%s_tmp' % hg_editor.last_revision_handled_file
- fp = open(tmpfile, 'w')
- fp.write(str(r.revnum))
- fp.close()
- merc_util.rename(tmpfile,
- hg_editor.last_revision_handled_file)
except core.SubversionException, e: #pragma: no cover
- if hasattr(e, 'message') and (
- 'Server sent unexpected return value (502 Bad Gateway)'
- ' in response to PROPFIND') in e.message:
+ if e.apr_err == core.SVN_ERR_RA_DAV_REQUEST_FAILED:
tries += 1
ui.status('Got a 502, retrying (%s)\n' % tries)
else:
- raise
+ raise merc_util.Abort(*e.args)
util.swap_out_encoding(old_encoding)
fetch_revisions = util.register_subcommand('pull')(fetch_revisions)
@@ -249,7 +240,7 @@
except svnwrap.SubversionRepoCanNotDiff:
raise BadPatchApply('subversion diffing code is not supported')
except core.SubversionException, e:
- if (hasattr(e, 'apr_err') and e.apr_err != 160013):
+ if (hasattr(e, 'apr_err') and e.apr_err != core.SVN_ERR_FS_NOT_FOUND):
raise
raise BadPatchApply('previous revision does not exist')
if '\0' in d:
@@ -610,7 +601,7 @@
ui, svn, hg_editor, b, r, parentctx)
except BadPatchApply, e:
# Either this revision or the previous one does not exist.
- ui.status("fetching entire rev: %s.\n" % e.message)
+ ui.status("Fetching entire revision: %s.\n" % e.args[0])
files_touched, filectxfn2 = stupid_fetch_branchrev(
svn, hg_editor, b, branches[b], r, parentctx)
diff --git a/hg_delta_editor.py b/hg_delta_editor.py
--- a/hg_delta_editor.py
+++ b/hg_delta_editor.py
@@ -59,6 +59,18 @@
f.close()
self.revmap[revnum, branch] = node_hash
+ def last_known_revision(self):
+ """Obtain the highest numbered -- i.e. latest -- revision known.
+
+ Currently, this function just iterates over the entire revision map
+ using the max() builtin. This may be slow for extremely large
+ repositories, but for now, it's fast enough.
+ """
+ try:
+ return max(k[0] for k in self.revmap.iterkeys())
+ except ValueError:
+ return 0
+
def __init__(self, path=None, repo=None, ui_=None,
subdir='', author_host='',
tag_locations=['tags'],
@@ -133,7 +145,6 @@
assert os.path.isfile(self.revmap_file)
assert os.path.isfile(self.svn_url_file)
assert os.path.isfile(self.uuid_file)
- assert os.path.isfile(self.last_revision_handled_file)
else:
self.repo = hg.repository(self.ui, repo_path, create=True)
os.makedirs(os.path.dirname(self.uuid_file))
@@ -523,6 +534,7 @@
branches = {}
for path, entry in self.externals.iteritems():
if not self._is_path_valid(path):
+ self.ui.warn('WARNING: Invalid path %s in externals\n' % path)
continue
p, b, bp = self._split_branch_path(path)
if bp not in branches:
@@ -692,6 +704,14 @@
return self.authors[author]
return '%s%s' %(author, self.author_host)
+ def svnauthorforauthor(self, author):
+ for svnauthor, hgauthor in self.authors.iteritems():
+ if author == hgauthor:
+ return svnauthor
+ else:
+ # return the original svn-side author
+ return author.rsplit('@', 1)[0]
+
def readauthors(self, authorfile):
self.ui.note(('Reading authormap from %s\n') % authorfile)
f = open(authorfile, 'r')
@@ -777,10 +797,6 @@
return self.meta_file_named('uuid')
uuid_file = property(uuid_file)
- def last_revision_handled_file(self):
- return self.meta_file_named('last_rev')
- last_revision_handled_file = property(last_revision_handled_file)
-
def branch_info_file(self):
return self.meta_file_named('branch_info')
branch_info_file = property(branch_info_file)
@@ -853,6 +869,8 @@
baserev = self.current_rev.revnum - 1
parent = self.get_parent_revision(baserev + 1, branch)
self.load_base_from_ctx(path, fpath, self.repo.changectx(parent))
+ else:
+ self.ui.warn('WARNING: Opening non-existant file %s\n' % path)
open_file = stash_exception_on_self(open_file)
def aresamefiles(self, parentctx, childctx, files):
@@ -1038,11 +1056,10 @@
if not window:
self.current_files[self.current_file] = target.getvalue()
except core.SubversionException, e: #pragma: no cover
- if e.message == 'Delta source ended unexpectedly':
+ if e.apr_err == core.SVN_ERR_INCOMPLETE_DATA:
self.missing_plaintexts.add(self.current_file)
else: #pragma: no cover
- self._exception_info = sys.exc_info()
- raise
+ raise util.Abort(*e.args)
except: #pragma: no cover
print len(base), self.current_file
self._exception_info = sys.exc_info()
diff --git a/rebuildmeta.py b/rebuildmeta.py
--- a/rebuildmeta.py
+++ b/rebuildmeta.py
@@ -79,9 +79,6 @@
if c.branch() == 'closed-branches':
if branch in branchinfo:
del branchinfo[branch]
- lastrevfile = open(os.path.join(svnmetadir, 'last_rev'), 'w')
- lastrevfile.write(str(last_rev))
- lastrevfile.close()
branchinfofile = open(os.path.join(svnmetadir, 'branch_info'), 'w')
pickle.dump(branchinfo, branchinfofile)
branchinfofile.close()
diff --git a/svncommand.py b/svncommand.py
--- a/svncommand.py
+++ b/svncommand.py
@@ -5,7 +5,6 @@
from mercurial import hg
from mercurial import node
-from mercurial import util as merc_util
import svnwrap
import util
diff --git a/svnwrap/svn_swig_wrapper.py b/svnwrap/svn_swig_wrapper.py
--- a/svnwrap/svn_swig_wrapper.py
+++ b/svnwrap/svn_swig_wrapper.py
@@ -189,7 +189,7 @@
try:
tags = self.list_dir('tags', revision=revision).keys()
except core.SubversionException, e:
- if e.apr_err == 160013:
+ if e.apr_err == core.SVN_ERR_FS_NOT_FOUND:
return {}
raise
tag_info = {}
@@ -370,10 +370,8 @@
e_baton, self.pool)
except core.SubversionException, e: #pragma: no cover
# can I depend on this number being constant?
- if (e.message == "Server doesn't support the replay command"
- or e.apr_err == 170003
- or e.message == 'The requested report is unknown.'
- or e.apr_err == 200007):
+ if (e.apr_err == core.SVN_ERR_RA_NOT_IMPLEMENTED or
+ e.apr_err == core.SVN_ERR_UNSUPPORTED_FEATURE):
raise SubversionRepoCanNotReplay, ('This Subversion server '
'is older than 1.4.0, and cannot satisfy replay requests.')
else:
diff --git a/utility_commands.py b/utility_commands.py
--- a/utility_commands.py
+++ b/utility_commands.py
@@ -89,7 +89,7 @@
if url[-1] == '/':
url = url[:-1]
url = '%s%s' % (url, branchpath)
- author = '@'.join(parent.user().split('@')[:-1])
+ author = hge.svnauthorforauthor(parent.user())
# cleverly figure out repo root w/o actually contacting the server
reporoot = url[:len(url)-len(subdir)]
ui.status('''URL: %(url)s
changeset: 229:7f20914e52e8
user: Augie Fackler <durin42@gmail.com>
date: Tue Mar 31 15:53:41 2009 -0500
summary: Better 502 logic that does not catch non-502 errors.
diff --git a/fetch_command.py b/fetch_command.py
--- a/fetch_command.py
+++ b/fetch_command.py
@@ -77,7 +77,7 @@
# got a 502? Try more than once!
tries = 0
converted = False
- while not converted and tries < 3:
+ while not converted:
try:
util.describe_revision(ui, r)
if have_replay:
@@ -92,7 +92,9 @@
stupid_svn_server_pull_rev(ui, svn, hg_editor, r)
converted = True
except core.SubversionException, e: #pragma: no cover
- if e.apr_err == core.SVN_ERR_RA_DAV_REQUEST_FAILED:
+ if (e.apr_err == core.SVN_ERR_RA_DAV_REQUEST_FAILED
+ and '502' in str(e)
+ and tries < 3):
tries += 1
ui.status('Got a 502, retrying (%s)\n' % tries)
else:
changeset: 230:4c3bad24f950
user: Augie Fackler <durin42@gmail.com>
date: Mon Mar 30 16:11:45 2009 -0500
summary: rebase: cleanup slightly.
diff --git a/push_cmd.py b/push_cmd.py
--- a/push_cmd.py
+++ b/push_cmd.py
@@ -82,7 +82,7 @@
if ctx.node() == oldest:
return
extra['branch'] = ctx.branch()
- utility_commands.rebase_commits(ui, repo, hg_repo_path,
+ utility_commands.rebase_commits(ui, repo,
extrafn=extrafn,
sourcerev=needs_transplant,
**opts)
diff --git a/utility_commands.py b/utility_commands.py
--- a/utility_commands.py
+++ b/utility_commands.py
@@ -130,7 +130,7 @@
print_parent_revision = util.register_subcommand('parent')(print_parent_revision)
-def rebase_commits(ui, repo, hg_repo_path, extrafn=None, sourcerev=None, **opts):
+def rebase_commits(ui, repo, extrafn=None, sourcerev=None, **opts):
"""rebase current unpushed revisions onto the Subversion head
This moves a line of development from making its own head to the top of
@@ -146,8 +146,7 @@
extrafn = extrafn2
if sourcerev is None:
sourcerev = repo.parents()[0].node()
- hge = hg_delta_editor.HgChangeReceiver(hg_repo_path,
- ui_=ui)
+ hge = hg_delta_editor.HgChangeReceiver(repo=repo)
svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
hge.revmap.iterkeys()))
o_r = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes, sourcerev=sourcerev)
changeset: 232:c0063328587f
user: Augie Fackler <durin42@gmail.com>
date: Tue Apr 07 17:38:13 2009 -0500
summary: Fix and test for directory deletes during branch creation.
diff --git a/hg_delta_editor.py b/hg_delta_editor.py
--- a/hg_delta_editor.py
+++ b/hg_delta_editor.py
@@ -845,7 +845,7 @@
# assuming it is a directory
self.externals[path] = None
map(self.delete_file, [pat for pat in self.current_files.iterkeys()
- if pat.startswith(path)])
+ if pat.startswith(path+'/')])
for f in ctx.walk(our_util.PrefixMatch(br_path2)):
f_p = '%s/%s' % (path, f[len(br_path2):])
if f_p not in self.current_files:
changeset: 233:80a700398995
user: Daniel Tang <dytang@cs.purdue.edu>
date: Mon Apr 06 02:50:22 2009 -0400
summary: Fix SubversionRepo to actually use provided username
diff --git a/svnwrap/svn_swig_wrapper.py b/svnwrap/svn_swig_wrapper.py
--- a/svnwrap/svn_swig_wrapper.py
+++ b/svnwrap/svn_swig_wrapper.py
@@ -120,7 +120,7 @@
"""
def __init__(self, url='', username=''):
self.svn_url = url
- self.uname = username
+ self.username = username
self.auth_baton_pool = core.Pool()
self.auth_baton = _create_auth_baton(self.auth_baton_pool)
@@ -139,6 +139,10 @@
"""
# while we're in here we'll recreate our pool
self.pool = core.Pool()
+ if self.username:
+ core.svn_auth_set_parameter(self.auth_baton,
+ core.SVN_AUTH_PARAM_DEFAULT_USERNAME,
+ self.username)
self.client_context = client.create_context()
self.client_context.auth_baton = self.auth_baton
changeset: 234:33e885f5f86a
user: Daniel Tang <dytang@cs.purdue.edu>
date: Mon Apr 06 02:52:14 2009 -0400
summary: Add --username and --password options to all commands
diff --git a/__init__.py b/__init__.py
--- a/__init__.py
+++ b/__init__.py
@@ -79,6 +79,8 @@
('', 'filemap', '',
'remap file to exclude paths or include only certain paths'),
('', 'force', False, 'force an operation to happen'),
+ ('', 'username', '', 'username for authentication'),
+ ('', 'password', '', 'password for authentication'),
],
svncommand.generate_help(),
),
@@ -90,6 +92,8 @@
('A', 'authors', '', 'username mapping filename'),
('', 'filemap', '',
'remap file to exclude paths or include only certain paths'),
+ ('', 'username', '', 'username for authentication'),
+ ('', 'password', '', 'password for authentication'),
],
'hg svnclone source [dest]'),
}
diff --git a/fetch_command.py b/fetch_command.py
--- a/fetch_command.py
+++ b/fetch_command.py
@@ -41,7 +41,9 @@
' of SWIG.\n')
have_replay = False
initializing_repo = False
- svn = svnwrap.SubversionRepo(svn_url, username=merc_util.getuser())
+ user = opts.get('username', merc_util.getuser())
+ passwd = opts.get('password', '')
+ svn = svnwrap.SubversionRepo(svn_url, user, passwd)
author_host = "@%s" % svn.uuid
tag_locations = tag_locations.split(',')
hg_editor = hg_delta_editor.HgChangeReceiver(hg_repo_path,
diff --git a/push_cmd.py b/push_cmd.py
--- a/push_cmd.py
+++ b/push_cmd.py
@@ -29,6 +29,8 @@
ui_=ui)
svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
hge.revmap.iterkeys()))
+ user = opts.get('username', merc_util.getuser())
+ passwd = opts.get('password', '')
# Strategy:
# 1. Find all outgoing commits from this head
if len(repo.parents()) != 1:
@@ -59,14 +61,16 @@
# 2. Commit oldest revision that needs to be pushed
base_revision = svn_commit_hashes[base_n][0]
try:
- commit_from_rev(ui, repo, old_ctx, hge, svn_url, base_revision)
+ commit_from_rev(ui, repo, old_ctx, hge, svn_url, base_revision,
+ user, passwd)
except NoFilesException:
ui.warn("Could not push revision %s because it had no changes in svn.\n" %
old_ctx)
return 1
# 3. Fetch revisions from svn
r = fetch_command.fetch_revisions(ui, svn_url, hg_repo_path,
- stupid=stupid)
+ stupid=stupid, username=user,
+ password=passwd)
assert not r or r == 0
# 4. Find the new head of the target branch
repo = hg.repository(ui, hge.path)
@@ -182,11 +186,12 @@
ext.read(ctx['.hgsvnexternals'].data())
return ext
-def commit_from_rev(ui, repo, rev_ctx, hg_editor, svn_url, base_revision):
+def commit_from_rev(ui, repo, rev_ctx, hg_editor, svn_url, base_revision,
+ username, password):
"""Build and send a commit from Mercurial to Subversion.
"""
file_data = {}
- svn = svnwrap.SubversionRepo(svn_url, username=merc_util.getuser())
+ svn = svnwrap.SubversionRepo(svn_url, username, password)
parent = rev_ctx.parents()[0]
parent_branch = rev_ctx.parents()[0].branch()
branch_path = 'trunk'
diff --git a/rebuildmeta.py b/rebuildmeta.py
--- a/rebuildmeta.py
+++ b/rebuildmeta.py
@@ -14,7 +14,9 @@
raise mutil.Abort('You must pass the svn URI used to create this repo.')
uuid = None
url = args[0].rstrip('/')
- svn = svnwrap.SubversionRepo(url=url)
+ user = opts.get('username', mutil.getuser())
+ passwd = opts.get('password', '')
+ svn = svnwrap.SubversionRepo(url, user, passwd)
subdir = svn.subdir
svnmetadir = os.path.join(repo.path, 'svn')
if not os.path.exists(svnmetadir):
diff --git a/svnwrap/svn_swig_wrapper.py b/svnwrap/svn_swig_wrapper.py
--- a/svnwrap/svn_swig_wrapper.py
+++ b/svnwrap/svn_swig_wrapper.py
@@ -118,9 +118,10 @@
This uses the SWIG Python bindings, and will only work on svn >= 1.4.
It takes a required param, the URL.
"""
- def __init__(self, url='', username=''):
+ def __init__(self, url='', username='', password=''):
self.svn_url = url
self.username = username
+ self.password = password
self.auth_baton_pool = core.Pool()
self.auth_baton = _create_auth_baton(self.auth_baton_pool)
@@ -143,6 +144,10 @@
core.svn_auth_set_parameter(self.auth_baton,
core.SVN_AUTH_PARAM_DEFAULT_USERNAME,
self.username)
+ if self.password:
+ core.svn_auth_set_parameter(self.auth_baton,
+ core.SVN_AUTH_PARAM_DEFAULT_PASSWORD,
+ self.password)
self.client_context = client.create_context()
self.client_context.auth_baton = self.auth_baton
diff --git a/utility_commands.py b/utility_commands.py
--- a/utility_commands.py
+++ b/utility_commands.py
@@ -50,7 +50,9 @@
url = hge.url
if url[-1] == '/':
url = url[:-1]
- svn = svnwrap.SubversionRepo(url)
+ user = opts.get('username', mutil.getuser())
+ passwd = opts.get('passwd', '')
+ svn = svnwrap.SubversionRepo(url, user, passwd)
dirs = [''] + [d[0] for d in svn.list_files(branchpath, r) if d[1] == 'd']
for dir in dirs:
props = svn.list_props('%s/%s/' % (branchpath,dir), r)
changeset: 235:2969a20e0eef
user: Daniel Tang <dytang@cs.purdue.edu>
date: Mon Apr 06 11:19:51 2009 -0400
summary: Add support for user:pass@url repositories to be hg-like
diff --git a/svnwrap/svn_swig_wrapper.py b/svnwrap/svn_swig_wrapper.py
--- a/svnwrap/svn_swig_wrapper.py
+++ b/svnwrap/svn_swig_wrapper.py
@@ -5,6 +5,7 @@
import sys
import tempfile
import hashlib
+import urlparse
from svn import client
from svn import core
@@ -91,6 +92,20 @@
return core.svn_auth_open(providers, pool)
+def parse_url(url):
+ """Parse a URL and return a tuple (username, password, url)
+ """
+ scheme, netloc, path, params, query, fragment = urlparse.urlparse(url)
+ user, passwd = None, None
+ if '@' in netloc:
+ userpass, netloc = netloc.split('@')
+ if ':' in userpass:
+ user, passwd = userpass.split(':')
+ user, passwd = urlparse.unquote(user) or None, urlparse.unquote(passwd) or None
+ else:
+ user = urlparse.unquote(userpass) or None
+ url = urlparse.urlunparse((scheme, netloc, path, params, query, fragment))
+ return (user, passwd, url)
class Revision(object):
"""Wrapper for a Subversion revision.
@@ -119,9 +134,11 @@
It takes a required param, the URL.
"""
def __init__(self, url='', username='', password=''):
- self.svn_url = url
- self.username = username
- self.password = password
+ parsed = parse_url(url)
+ # --username and --password override URL credentials
+ self.username = username or parsed[0]
+ self.password = password or parsed[1]
+ self.svn_url = parsed[2]
self.auth_baton_pool = core.Pool()
self.auth_baton = _create_auth_baton(self.auth_baton_pool)
changeset: 236:c34abd2448b7
user: Daniel Tang <dytang@cs.purdue.edu>
date: Tue Apr 07 22:15:22 2009 -0400
summary: Issue #60: Add a svn sub-command to list all authors in a Subversion repository
diff --git a/utility_commands.py b/utility_commands.py
--- a/utility_commands.py
+++ b/utility_commands.py
@@ -198,6 +198,26 @@
show_outgoing_to_svn = util.register_subcommand('outgoing')(show_outgoing_to_svn)
+def list_authors(ui, args, authors=None, **opts):
+ """list all authors in a Subversion repository
+ """
+ if not len(args):
+ ui.status('No repository specified.\n')
+ return
+ svn = svnwrap.SubversionRepo(util.normalize_url(args[0]))
+ author_set = set()
+ for rev in svn.revisions():
+ author_set.add(str(rev.author)) # So None becomes 'None'
+ if authors:
+ authorfile = open(authors, 'w')
+ authorfile.write('%s=\n' % '=\n'.join(sorted(author_set)))
+ authorfile.close()
+ else:
+ ui.status('%s\n' % '\n'.join(sorted(author_set)))
+list_authors = util.register_subcommand('listauthors')(list_authors)
+list_authors = util.command_needs_no_url(list_authors)
+
+
def version(ui, **opts):
"""Show current version of hg and hgsubversion.
"""
changeset: 237:c90cfa665b81
user: Dirkjan Ochtman <dirkjan@ochtman.nl>
date: Wed Apr 08 13:07:23 2009 +0200
summary: Cope with date-less revisions.
diff --git a/fetch_command.py b/fetch_command.py
--- a/fetch_command.py
+++ b/fetch_command.py
@@ -586,8 +586,8 @@
break
if not is_closed:
deleted_branches[branch] = branchtip
- date = r.date.replace('T', ' ').replace('Z', '').split('.')[0]
- date += ' -0000'
+
+ date = hg_editor.fixdate(r.date)
check_deleted_branches = set()
for b in branches:
parentctx = hg_editor.repo[hg_editor.get_parent_revision(r.revnum, b)]
diff --git a/hg_delta_editor.py b/hg_delta_editor.py
--- a/hg_delta_editor.py
+++ b/hg_delta_editor.py
@@ -129,11 +129,22 @@
self.readauthors(authors)
if self.authors:
self.writeauthors()
+
+ self.lastdate = '1970-01-01 00:00:00'
self.includepaths = {}
self.excludepaths = {}
if filemap and os.path.exists(filemap):
self.readfilemap(filemap)
+ def fixdate(self, date):
+ if date is not None:
+ date = date.replace('T', ' ').replace('Z', '').split('.')[0]
+ date += ' -0000'
+ self.lastdate = date
+ else:
+ date = self.lastdate
+ return date
+
def __setup_repo(self, repo_path):
"""Verify the repo is going to work out for us.
@@ -573,8 +584,7 @@
files_to_commit.sort()
branch_batches = {}
rev = self.current_rev
- date = rev.date.replace('T', ' ').replace('Z', '').split('.')[0]
- date += ' -0000'
+ date = self.fixdate(rev.date)
# build up the branches that have files on them
for f in files_to_commit:
changeset: 238:e8b3ca865f93
user: Augie Fackler <durin42@gmail.com>
date: Wed Apr 08 10:28:56 2009 -0500
summary: Fix a boneheaded mistake I made when I touched up a previous patch.
diff --git a/svnwrap/svn_swig_wrapper.py b/svnwrap/svn_swig_wrapper.py
--- a/svnwrap/svn_swig_wrapper.py
+++ b/svnwrap/svn_swig_wrapper.py
@@ -4,8 +4,8 @@
import shutil
import sys
import tempfile
-import hashlib
import urlparse
+import urllib
from svn import client
from svn import core
@@ -101,9 +101,9 @@
userpass, netloc = netloc.split('@')
if ':' in userpass:
user, passwd = userpass.split(':')
- user, passwd = urlparse.unquote(user) or None, urlparse.unquote(passwd) or None
+ user, passwd = urllib.unquote(user) or None, urllib.unquote(passwd) or None
else:
- user = urlparse.unquote(userpass) or None
+ user = urllib.unquote(userpass) or None
url = urlparse.urlunparse((scheme, netloc, path, params, query, fragment))
return (user, passwd, url)
changeset: 239:e2214c8fc91f
user: Dirkjan Ochtman <dirkjan@ochtman.nl>
date: Wed Apr 08 17:34:01 2009 +0200
summary: Put all stupid stuff in it's own module (separate from fetch-command).
diff --git a/fetch_command.py b/fetch_command.py
--- a/fetch_command.py
+++ b/fetch_command.py
@@ -1,26 +1,15 @@
-import cStringIO
-import re
import os
-from mercurial import patch
-from mercurial import node
-from mercurial import context
-from mercurial import revlog
from mercurial import util as merc_util
from svn import core
from svn import delta
import hg_delta_editor
import svnwrap
-import svnexternals
+import stupid as stupidmod
import util
-def print_your_svn_is_old_message(ui): #pragma: no cover
- ui.status("In light of that, I'll fall back and do diffs, but it won't do "
- "as good a job. You should really upgrade your server.\n")
-
-
def fetch_revisions(ui, svn_url, hg_repo_path, skipto_rev=0, stupid=None,
tag_locations='tags',
authors=None,
@@ -87,11 +76,11 @@
replay_convert_rev(hg_editor, svn, r)
except svnwrap.SubversionRepoCanNotReplay, e: #pragma: no cover
ui.status('%s\n' % e.message)
- print_your_svn_is_old_message(ui)
+ stupidmod.print_your_svn_is_old_message(ui)
have_replay = False
- stupid_svn_server_pull_rev(ui, svn, hg_editor, r)
+ stupidmod.svn_server_pull_rev(ui, svn, hg_editor, r)
else:
- stupid_svn_server_pull_rev(ui, svn, hg_editor, r)
+ stupidmod.svn_server_pull_rev(ui, svn, hg_editor, r)
converted = True
except core.SubversionException, e: #pragma: no cover
if (e.apr_err == core.SVN_ERR_RA_DAV_REQUEST_FAILED
@@ -141,552 +130,3 @@
hg_editor.missing_plaintexts = set()
hg_editor.ui.note('\n')
hg_editor.commit_current_delta()
-
-
-binary_file_re = re.compile(r'''Index: ([^\n]*)
-=*
-Cannot display: file marked as a binary type.''')
-
-property_exec_set_re = re.compile(r'''Property changes on: ([^\n]*)
-_*
-(?:Added|Name): svn:executable
- \+''')
-
-property_exec_removed_re = re.compile(r'''Property changes on: ([^\n]*)
-_*
-(?:Deleted|Name): svn:executable
- -''')
-
-empty_file_patch_wont_make_re = re.compile(r'''Index: ([^\n]*)\n=*\n(?=Index:)''')
-
-any_file_re = re.compile(r'''^Index: ([^\n]*)\n=*\n''', re.MULTILINE)
-
-property_special_set_re = re.compile(r'''Property changes on: ([^\n]*)
-_*
-(?:Added|Name): svn:special
- \+''')
-
-property_special_removed_re = re.compile(r'''Property changes on: ([^\n]*)
-_*
-(?:Deleted|Name): svn:special
- \-''')
-
-def mempatchproxy(parentctx, files):
- # Avoid circular references patch.patchfile -> mempatch
- patchfile = patch.patchfile
-
- class mempatch(patchfile):
- def __init__(self, ui, fname, opener, missing=False):
- patchfile.__init__(self, ui, fname, None, False)
-
- def readlines(self, fname):
- if fname not in parentctx:
- raise IOError('Cannot find %r to patch' % fname)
- fctx = parentctx[fname]
- data = fctx.data()
- if 'l' in fctx.flags():
- data = 'link ' + data
- return cStringIO.StringIO(data).readlines()
-
- def writelines(self, fname, lines):
- files[fname] = ''.join(lines)
-
- def unlink(self, fname):
- files[fname] = None
-
- return mempatch
-
-
-def filteriterhunks(hg_editor):
- iterhunks = patch.iterhunks
- def filterhunks(ui, fp, sourcefile=None):
- applycurrent = False
- for data in iterhunks(ui, fp, sourcefile):
- if data[0] == 'file':
- if hg_editor._is_file_included(data[1][1]):
- applycurrent = True
- else:
- applycurrent = False
- assert data[0] != 'git', 'Filtering git hunks not supported.'
- if applycurrent:
- yield data
- return filterhunks
-
-def stupid_diff_branchrev(ui, svn, hg_editor, branch, r, parentctx):
- """Extract all 'branch' content at a given revision.
-
- Return a tuple (files, filectxfn) where 'files' is the list of all files
- in the branch at the given revision, and 'filectxfn' is a memctx compatible
- callable to retrieve individual file information. Raise BadPatchApply upon
- error.
- """
- def make_diff_path(branch):
- if branch == 'trunk' or branch is None:
- return 'trunk'
- elif branch.startswith('../'):
- return branch[3:]
- return 'branches/%s' % branch
- parent_rev, br_p = hg_editor.get_parent_svn_branch_and_rev(r.revnum, branch)
- diff_path = make_diff_path(branch)
- try:
- if br_p == branch:
- # letting patch handle binaries sounded
- # cool, but it breaks patch in sad ways
- d = svn.get_unified_diff(diff_path, r.revnum, deleted=False,
- ignore_type=False)
- else:
- d = svn.get_unified_diff(diff_path, r.revnum,
- other_path=make_diff_path(br_p),
- other_rev=parent_rev,
- deleted=True, ignore_type=True)
- if d:
- raise BadPatchApply('branch creation with mods')
- except svnwrap.SubversionRepoCanNotDiff:
- raise BadPatchApply('subversion diffing code is not supported')
- except core.SubversionException, e:
- if (hasattr(e, 'apr_err') and e.apr_err != core.SVN_ERR_FS_NOT_FOUND):
- raise
- raise BadPatchApply('previous revision does not exist')
- if '\0' in d:
- raise BadPatchApply('binary diffs are not supported')
- files_data = {}
- binary_files = {}
- touched_files = {}
- for m in binary_file_re.findall(d):
- # we have to pull each binary file by hand as a fulltext,
- # which sucks but we've got no choice
- binary_files[m] = 1
- touched_files[m] = 1
- d2 = empty_file_patch_wont_make_re.sub('', d)
- d2 = property_exec_set_re.sub('', d2)
- d2 = property_exec_removed_re.sub('', d2)
- for f in any_file_re.findall(d):
- # Here we ensure that all files, including the new empty ones
- # are marked as touched. Content is loaded on demand.
- touched_files[f] = 1
- if d2.strip() and len(re.findall('\n[-+]', d2.strip())) > 0:
- try:
- oldpatchfile = patch.patchfile
- olditerhunks = patch.iterhunks
- patch.patchfile = mempatchproxy(parentctx, files_data)
- patch.iterhunks = filteriterhunks(hg_editor)
- try:
- # We can safely ignore the changed list since we are
- # handling non-git patches. Touched files are known
- # by our memory patcher.
- patch_st = patch.applydiff(ui, cStringIO.StringIO(d2),
- {}, strip=0)
- finally:
- patch.patchfile = oldpatchfile
- patch.iterhunks = olditerhunks
- except patch.PatchError:
- # TODO: this happens if the svn server has the wrong mime
- # type stored and doesn't know a file is binary. It would
- # be better to do one file at a time and only do a
- # full fetch on files that had problems.
- raise BadPatchApply('patching failed')
- for x in files_data.iterkeys():
- ui.note('M %s\n' % x)
- # if this patch didn't apply right, fall back to exporting the
- # entire rev.
- if patch_st == -1:
- assert False, ('This should only happen on case-insensitive'
- ' volumes.')
- elif patch_st == 1:
- # When converting Django, I saw fuzz on .po files that was
- # causing revisions to end up failing verification. If that
- # can be fixed, maybe this won't ever be reached.
- raise BadPatchApply('patching succeeded with fuzz')
- else:
- ui.status('Not using patch for %s, diff had no hunks.\n' %
- r.revnum)
-
- exec_files = {}
- for m in property_exec_removed_re.findall(d):
- exec_files[m] = False
- for m in property_exec_set_re.findall(d):
- exec_files[m] = True
- for m in exec_files:
- touched_files[m] = 1
- link_files = {}
- for m in property_special_set_re.findall(d):
- # TODO(augie) when a symlink is removed, patching will fail.
- # We're seeing that above - there's gotta be a better
- # workaround than just bailing like that.
- assert m in files_data
- link_files[m] = True
- for m in property_special_removed_re.findall(d):
- assert m in files_data
- link_files[m] = False
-
- for p in r.paths:
- if p.startswith(diff_path) and r.paths[p].action == 'D':
- p2 = p[len(diff_path)+1:].strip('/')
- if p2 in parentctx:
- files_data[p2] = None
- continue
- # If this isn't in the parent ctx, it must've been a dir
- files_data.update([(f, None) for f in parentctx if f.startswith(p2 + '/')])
-
- for f in files_data:
- touched_files[f] = 1
-
- copies = getcopies(svn, hg_editor, branch, diff_path, r, touched_files,
- parentctx)
-
- def filectxfn(repo, memctx, path):
- if path in files_data and files_data[path] is None:
- raise IOError()
-
- if path in binary_files:
- data, mode = svn.get_file(diff_path + '/' + path, r.revnum)
- isexe = 'x' in mode
- islink = 'l' in mode
- else:
- isexe = exec_files.get(path, 'x' in parentctx.flags(path))
- islink = link_files.get(path, 'l' in parentctx.flags(path))
- data = ''
- if path in files_data:
- data = files_data[path]
- if islink:
- data = data[len('link '):]
- elif path in parentctx:
- data = parentctx[path].data()
-
- copied = copies.get(path)
- return context.memfilectx(path=path, data=data, islink=islink,
- isexec=isexe, copied=copied)
-
- return list(touched_files), filectxfn
-
-def makecopyfinder(r, branchpath, rootdir):
- """Return a function detecting copies.
-
- Returned copyfinder(path) returns None if no copy information can
- be found or ((source, sourcerev), sourcepath) where "sourcepath" is the
- copy source path, "sourcerev" the source svn revision and "source" is the
- copy record path causing the copy to occur. If a single file was copied
- "sourcepath" and "source" are the same, while file copies dectected from
- directory copies return the copied source directory in "source".
- """
- # filter copy information for current branch
- branchpath = branchpath + '/'
- fullbranchpath = rootdir + branchpath
- copies = []
- for path, e in r.paths.iteritems():
- if not e.copyfrom_path:
- continue
- if not path.startswith(branchpath):
- continue
- if not e.copyfrom_path.startswith(fullbranchpath):
- # ignore cross branch copies
- continue
- dest = path[len(branchpath):]
- source = e.copyfrom_path[len(fullbranchpath):]
- copies.append((dest, (source, e.copyfrom_rev)))
-
- copies.sort(reverse=True)
- exactcopies = dict(copies)
-
- def finder(path):
- if path in exactcopies:
- return exactcopies[path], exactcopies[path][0]
- # look for parent directory copy, longest first
- for dest, (source, sourcerev) in copies:
- dest = dest + '/'
- if not path.startswith(dest):
- continue
- sourcepath = source + '/' + path[len(dest):]
- return (source, sourcerev), sourcepath
- return None
-
- return finder
-
-def getcopies(svn, hg_editor, branch, branchpath, r, files, parentctx):
- """Return a mapping {dest: source} for every file copied into r.
- """
- if parentctx.node() == revlog.nullid:
- return {}
-
- # Extract svn copy information, group them by copy source.
- # The idea is to duplicate the replay behaviour where copies are
- # evaluated per copy event (one event for all files in a directory copy,
- # one event for single file copy). We assume that copy events match
- # copy sources in revision info.
- svncopies = {}
- finder = makecopyfinder(r, branchpath, svn.subdir)
- for f in files:
- copy = finder(f)
- if copy:
- svncopies.setdefault(copy[0], []).append((f, copy[1]))
- if not svncopies:
- return {}
-
- # cache changeset contexts and map them to source svn revisions
- ctxs = {}
- def getctx(svnrev):
- if svnrev in ctxs:
- return ctxs[svnrev]
- changeid = hg_editor.get_parent_revision(svnrev + 1, branch)
- ctx = None
- if changeid != revlog.nullid:
- ctx = hg_editor.repo.changectx(changeid)
- ctxs[svnrev] = ctx
- return ctx
-
- # check svn copies really make sense in mercurial
- hgcopies = {}
- for (sourcepath, rev), copies in svncopies.iteritems():
- sourcectx = getctx(rev)
- if sourcectx is None:
- continue
- sources = [s[1] for s in copies]
- if not hg_editor.aresamefiles(sourcectx, parentctx, sources):
- continue
- hgcopies.update(copies)
- return hgcopies
-
-def stupid_fetch_externals(svn, branchpath, r, parentctx):
- """Extract svn:externals for the current revision and branch
-
- Return an externalsfile instance or None if there are no externals
- to convert and never were.
- """
- externals = svnexternals.externalsfile()
- if '.hgsvnexternals' in parentctx:
- externals.read(parentctx['.hgsvnexternals'].data())
- # Detect property additions only, changes are handled by checking
- # existing entries individually. Projects are unlikely to store
- # externals on many different root directories, so we trade code
- # duplication and complexity for a constant lookup price at every
- # revision in the common case.
- dirs = set(externals)
- if parentctx.node() == revlog.nullid:
- dirs.update([p for p,k in svn.list_files(branchpath, r.revnum) if k == 'd'])
- dirs.add('')
- else:
- branchprefix = branchpath + '/'
- for path, e in r.paths.iteritems():
- if e.action == 'D':
- continue
- if not path.startswith(branchprefix) and path != branchpath:
- continue
- kind = svn.checkpath(path, r.revnum)
- if kind != 'd':
- continue
- path = path[len(branchprefix):]
- dirs.add(path)
- if e.action == 'M' or (e.action == 'A' and e.copyfrom_path):
- # Do not recurse in copied directories, changes are marked
- # as 'M', except for the copied one.
- continue
- for child, k in svn.list_files(branchprefix + path, r.revnum):
- if k == 'd':
- dirs.add((path + '/' + child).strip('/'))
-
- # Retrieve new or updated values
- for dir in dirs:
- try:
- values = svn.list_props(branchpath + '/' + dir, r.revnum)
- externals[dir] = values.get('svn:externals', '')
- except IOError:
- externals[dir] = ''
-
- if not externals and '.hgsvnexternals' not in parentctx:
- # Do not create empty externals files
- return None
- return externals
-
-def stupid_fetch_branchrev(svn, hg_editor, branch, branchpath, r, parentctx):
- """Extract all 'branch' content at a given revision.
-
- Return a tuple (files, filectxfn) where 'files' is the list of all files
- in the branch at the given revision, and 'filectxfn' is a memctx compatible
- callable to retrieve individual file information.
- """
- files = []
- if parentctx.node() == revlog.nullid:
- # Initial revision, fetch all files
- for path, kind in svn.list_files(branchpath, r.revnum):
- if kind == 'f':
- files.append(path)
- else:
- branchprefix = branchpath + '/'
- for path, e in r.paths.iteritems():
- if not path.startswith(branchprefix):
- continue
- if not hg_editor._is_path_valid(path):
- continue
- kind = svn.checkpath(path, r.revnum)
- path = path[len(branchprefix):]
- if kind == 'f':
- files.append(path)
- elif kind == 'd':
- if e.action == 'M':
- continue
- dirpath = branchprefix + path
- for child, k in svn.list_files(dirpath, r.revnum):
- if k == 'f':
- files.append(path + '/' + child)
- else:
- if path in parentctx:
- files.append(path)
- continue
- # Assume it's a deleted directory
- path = path + '/'
- deleted = [f for f in parentctx if f.startswith(path)]
- files += deleted
-
- copies = getcopies(svn, hg_editor, branch, branchpath, r, files, parentctx)
-
- def filectxfn(repo, memctx, path):
- data, mode = svn.get_file(branchpath + '/' + path, r.revnum)
- isexec = 'x' in mode
- islink = 'l' in mode
- copied = copies.get(path)
- return context.memfilectx(path=path, data=data, islink=islink,
- isexec=isexec, copied=copied)
-
- return files, filectxfn
-
-def stupid_svn_server_pull_rev(ui, svn, hg_editor, r):
- # this server fails at replay
- branches = hg_editor.branches_in_paths(r.paths, r.revnum, svn.checkpath, svn.list_files)
- deleted_branches = {}
- brpaths = branches.values()
- bad_branch_paths = {}
- for br, bp in branches.iteritems():
- bad_branch_paths[br] = []
-
- # This next block might be needed, but for now I'm omitting it until it can be
- # proven necessary.
- # for bad in brpaths:
- # if bad.startswith(bp) and len(bad) > len(bp):
- # bad_branch_paths[br].append(bad[len(bp)+1:])
-
- # We've go a branch that contains other branches. We have to be careful to
- # get results similar to real replay in this case.
- for existingbr in hg_editor.branches:
- bad = hg_editor._remotename(existingbr)
- if bad.startswith(bp) and len(bad) > len(bp):
- bad_branch_paths[br].append(bad[len(bp)+1:])
- for p in r.paths:
- if hg_editor._is_path_tag(p):
- continue
- branch = hg_editor._localname(p)
- if r.paths[p].action == 'R' and branch in hg_editor.branches:
- branchedits = sorted(filter(lambda x: x[0][1] == branch and x[0][0] < r.revnum,
- hg_editor.revmap.iteritems()), reverse=True)
- is_closed = False
- if len(branchedits) > 0:
- branchtip = branchedits[0][1]
- for child in hg_editor.repo[branchtip].children():
- if child.branch() == 'closed-branches':
- is_closed = True
- break
- if not is_closed:
- deleted_branches[branch] = branchtip
-
- date = hg_editor.fixdate(r.date)
- check_deleted_branches = set()
- for b in branches:
- parentctx = hg_editor.repo[hg_editor.get_parent_revision(r.revnum, b)]
- if parentctx.branch() != (b or 'default'):
- check_deleted_branches.add(b)
- kind = svn.checkpath(branches[b], r.revnum)
- if kind != 'd':
- # Branch does not exist at this revision. Get parent revision and
- # remove everything.
- deleted_branches[b] = parentctx.node()
- continue
- else:
- try:
- files_touched, filectxfn2 = stupid_diff_branchrev(
- ui, svn, hg_editor, b, r, parentctx)
- except BadPatchApply, e:
- # Either this revision or the previous one does not exist.
- ui.status("Fetching entire revision: %s.\n" % e.args[0])
- files_touched, filectxfn2 = stupid_fetch_branchrev(
- svn, hg_editor, b, branches[b], r, parentctx)
-
- externals = stupid_fetch_externals(svn, branches[b], r, parentctx)
- if externals is not None:
- files_touched.append('.hgsvnexternals')
-
- def filectxfn(repo, memctx, path):
- if path == '.hgsvnexternals':
- if not externals:
- raise IOError()
- return context.memfilectx(path=path, data=externals.write(),
- islink=False, isexec=False, copied=None)
- for bad in bad_branch_paths[b]:
- if path.startswith(bad):
- raise IOError()
- return filectxfn2(repo, memctx, path)
-
- extra = util.build_extra(r.revnum, b, svn.uuid, svn.subdir)
- if '' in files_touched:
- files_touched.remove('')
- excluded = [f for f in files_touched
- if not hg_editor._is_file_included(f)]
- for f in excluded:
- files_touched.remove(f)
- if parentctx.node() != node.nullid or files_touched:
- # TODO(augie) remove this debug code? Or maybe it's sane to have it.
- for f in files_touched:
- if f:
- assert f[0] != '/'
- current_ctx = context.memctx(hg_editor.repo,
- [parentctx.node(), revlog.nullid],
- r.message or util.default_commit_msg,
- files_touched,
- filectxfn,
- hg_editor.authorforsvnauthor(r.author),
- date,
- extra)
- ha = hg_editor.repo.commitctx(current_ctx)
- branch = extra.get('branch', None)
- if not branch in hg_editor.branches:
- hg_editor.branches[branch] = None, 0, r.revnum
- hg_editor.add_to_revmap(r.revnum, b, ha)
- hg_editor._save_metadata()
- util.describe_commit(ui, ha, b)
- # These are branches which would have an 'R' status in svn log. This means they were
- # replaced by some other branch, so we need to verify they get marked as closed.
- for branch in check_deleted_branches:
- branchedits = sorted(filter(lambda x: x[0][1] == branch and x[0][0] < r.revnum,
- hg_editor.revmap.iteritems()), reverse=True)
- is_closed = False
- if len(branchedits) > 0:
- branchtip = branchedits[0][1]
- for child in hg_editor.repo[branchtip].children():
- if child.branch() == 'closed-branches':
- is_closed = True
- break
- if not is_closed:
- deleted_branches[branch] = branchtip
- for b, parent in deleted_branches.iteritems():
- if parent == node.nullid:
- continue
- parentctx = hg_editor.repo[parent]
- files_touched = parentctx.manifest().keys()
- def filectxfn(repo, memctx, path):
- raise IOError()
- closed = node.nullid
- if 'closed-branches' in hg_editor.repo.branchtags():
- closed = hg_editor.repo['closed-branches'].node()
- parents = (parent, closed)
- current_ctx = context.memctx(hg_editor.repo,
- parents,
- r.message or util.default_commit_msg,
- files_touched,
- filectxfn,
- hg_editor.authorforsvnauthor(r.author),
- date,
- {'branch': 'closed-branches'})
- ha = hg_editor.repo.commitctx(current_ctx)
- ui.status('Marked branch %s as closed.\n' % (b or 'default'))
- hg_editor._save_metadata()
-
-class BadPatchApply(Exception):
- pass
diff --git a/fetch_command.py b/stupid.py
copy from fetch_command.py
copy to stupid.py
--- a/fetch_command.py
+++ b/stupid.py
@@ -16,133 +16,6 @@
import util
-def print_your_svn_is_old_message(ui): #pragma: no cover
- ui.status("In light of that, I'll fall back and do diffs, but it won't do "
- "as good a job. You should really upgrade your server.\n")
-
-
-def fetch_revisions(ui, svn_url, hg_repo_path, skipto_rev=0, stupid=None,
- tag_locations='tags',
- authors=None,
- filemap=None,
- **opts):
- """pull new revisions from Subversion
- """
- svn_url = util.normalize_url(svn_url)
- old_encoding = util.swap_out_encoding()
- skipto_rev=int(skipto_rev)
- have_replay = not stupid
- if have_replay and not callable(
- delta.svn_txdelta_apply(None, None, None)[0]): #pragma: no cover
- ui.status('You are using old Subversion SWIG bindings. Replay will not'
- ' work until you upgrade to 1.5.0 or newer. Falling back to'
- ' a slower method that may be buggier. Please upgrade, or'
- ' contribute a patch to use the ctypes bindings instead'
- ' of SWIG.\n')
- have_replay = False
- initializing_repo = False
- user = opts.get('username', merc_util.getuser())
- passwd = opts.get('password', '')
- svn = svnwrap.SubversionRepo(svn_url, user, passwd)
- author_host = "@%s" % svn.uuid
- tag_locations = tag_locations.split(',')
- hg_editor = hg_delta_editor.HgChangeReceiver(hg_repo_path,
- ui_=ui,
- subdir=svn.subdir,
- author_host=author_host,
- tag_locations=tag_locations,
- authors=authors,
- filemap=filemap)
- if os.path.exists(hg_editor.uuid_file):
- uuid = open(hg_editor.uuid_file).read()
- assert uuid == svn.uuid
- start = hg_editor.last_known_revision()
- else:
- open(hg_editor.uuid_file, 'w').write(svn.uuid)
- open(hg_editor.svn_url_file, 'w').write(svn_url)
- initializing_repo = True
- start = skipto_rev
-
- if initializing_repo and start > 0:
- raise merc_util.Abort('Revision skipping at repository initialization '
- 'remains unimplemented.')
-
- # start converting revisions
- for r in svn.revisions(start=start):
- valid = True
- hg_editor.update_branch_tag_map_for_rev(r)
- for p in r.paths:
- if hg_editor._is_path_valid(p):
- valid = True
- break
- if valid:
- # got a 502? Try more than once!
- tries = 0
- converted = False
- while not converted:
- try:
- util.describe_revision(ui, r)
- if have_replay:
- try:
- replay_convert_rev(hg_editor, svn, r)
- except svnwrap.SubversionRepoCanNotReplay, e: #pragma: no cover
- ui.status('%s\n' % e.message)
- print_your_svn_is_old_message(ui)
- have_replay = False
- stupid_svn_server_pull_rev(ui, svn, hg_editor, r)
- else:
- stupid_svn_server_pull_rev(ui, svn, hg_editor, r)
- converted = True
- except core.SubversionException, e: #pragma: no cover
- if (e.apr_err == core.SVN_ERR_RA_DAV_REQUEST_FAILED
- and '502' in str(e)
- and tries < 3):
- tries += 1
- ui.status('Got a 502, retrying (%s)\n' % tries)
- else:
- raise merc_util.Abort(*e.args)
- util.swap_out_encoding(old_encoding)
-
-fetch_revisions = util.register_subcommand('pull')(fetch_revisions)
-
-
-def cleanup_file_handles(svn, count):
- if count % 50 == 0:
- svn.init_ra_and_client()
-
-
-def replay_convert_rev(hg_editor, svn, r):
- hg_editor.set_current_rev(r)
- svn.get_replay(r.revnum, hg_editor)
- i = 1
- if hg_editor.missing_plaintexts:
- hg_editor.ui.debug('Fetching %s files that could not use replay.\n' %
- len(hg_editor.missing_plaintexts))
- files_to_grab = set()
- rootpath = svn.subdir and svn.subdir[1:] or ''
- for p in hg_editor.missing_plaintexts:
- hg_editor.ui.note('.')
- hg_editor.ui.flush()
- if p[-1] == '/':
- dirpath = p[len(rootpath):]
- files_to_grab.update([dirpath + f for f,k in
- svn.list_files(dirpath, r.revnum)
- if k == 'f'])
- else:
- files_to_grab.add(p[len(rootpath):])
- hg_editor.ui.note('\nFetching files...\n')
- for p in files_to_grab:
- hg_editor.ui.note('.')
- hg_editor.ui.flush()
- cleanup_file_handles(svn, i)
- i += 1
- data, mode = svn.get_file(p, r.revnum)
- hg_editor.set_file(p, data, 'x' in mode, 'l' in mode)
- hg_editor.missing_plaintexts = set()
- hg_editor.ui.note('\n')
- hg_editor.commit_current_delta()
-
-
binary_file_re = re.compile(r'''Index: ([^\n]*)
=*
Cannot display: file marked as a binary type.''')
@@ -171,6 +44,16 @@
(?:Deleted|Name): svn:special
\-''')
+
+class BadPatchApply(Exception):
+ pass
+
+
+def print_your_svn_is_old_message(ui): #pragma: no cover
+ ui.status("In light of that, I'll fall back and do diffs, but it won't do "
+ "as good a job. You should really upgrade your server.\n")
+
+
def mempatchproxy(parentctx, files):
# Avoid circular references patch.patchfile -> mempatch
patchfile = patch.patchfile
@@ -212,7 +95,8 @@
yield data
return filterhunks
-def stupid_diff_branchrev(ui, svn, hg_editor, branch, r, parentctx):
+
+def diff_branchrev(ui, svn, hg_editor, branch, r, parentctx):
"""Extract all 'branch' content at a given revision.
Return a tuple (files, filectxfn) where 'files' is the list of all files
@@ -446,7 +330,7 @@
hgcopies.update(copies)
return hgcopies
-def stupid_fetch_externals(svn, branchpath, r, parentctx):
+def fetch_externals(svn, branchpath, r, parentctx):
"""Extract svn:externals for the current revision and branch
Return an externalsfile instance or None if there are no externals
@@ -497,7 +381,8 @@
return None
return externals
-def stupid_fetch_branchrev(svn, hg_editor, branch, branchpath, r, parentctx):
+
+def fetch_branchrev(svn, hg_editor, branch, branchpath, r, parentctx):
"""Extract all 'branch' content at a given revision.
Return a tuple (files, filectxfn) where 'files' is the list of all files
@@ -549,7 +434,7 @@
return files, filectxfn
-def stupid_svn_server_pull_rev(ui, svn, hg_editor, r):
+def svn_server_pull_rev(ui, svn, hg_editor, r):
# this server fails at replay
branches = hg_editor.branches_in_paths(r.paths, r.revnum, svn.checkpath, svn.list_files)
deleted_branches = {}
@@ -601,15 +486,15 @@
continue
else:
try:
- files_touched, filectxfn2 = stupid_diff_branchrev(
+ files_touched, filectxfn2 = diff_branchrev(
ui, svn, hg_editor, b, r, parentctx)
except BadPatchApply, e:
# Either this revision or the previous one does not exist.
ui.status("Fetching entire revision: %s.\n" % e.args[0])
- files_touched, filectxfn2 = stupid_fetch_branchrev(
+ files_touched, filectxfn2 = fetch_branchrev(
svn, hg_editor, b, branches[b], r, parentctx)
- externals = stupid_fetch_externals(svn, branches[b], r, parentctx)
+ externals = fetch_externals(svn, branches[b], r, parentctx)
if externals is not None:
files_touched.append('.hgsvnexternals')
@@ -687,6 +572,3 @@
ha = hg_editor.repo.commitctx(current_ctx)
ui.status('Marked branch %s as closed.\n' % (b or 'default'))
hg_editor._save_metadata()
-
-class BadPatchApply(Exception):
- pass
changeset: 240:1aa1d2d406d9
user: Dirkjan Ochtman <dirkjan@ochtman.nl>
date: Wed Apr 08 16:20:14 2009 +0200
summary: Lose old mode constants, not used anymore.
diff --git a/svncommand.py b/svncommand.py
--- a/svncommand.py
+++ b/svncommand.py
@@ -18,10 +18,6 @@
# shut up, pyflakes, we must import those
__x = [print_wc_url, fetch_revisions, commit_from_rev, diff_command, rebuildmeta]
-mode755 = (stat.S_IXUSR | stat.S_IXGRP| stat.S_IXOTH | stat.S_IRUSR |
- stat.S_IRGRP| stat.S_IROTH | stat.S_IWUSR)
-mode644 = (stat.S_IRUSR | stat.S_IRGRP| stat.S_IROTH | stat.S_IWUSR)
-
def svncmd(ui, repo, subcommand, *args, **opts):
if subcommand not in svn_subcommands:
changeset: 241:4950b18cf949
user: Dirkjan Ochtman <dirkjan@ochtman.nl>
date: Wed Apr 08 17:49:30 2009 +0200
summary: Move fetch_command.fetch_revisions() to svncommands.pull().
diff --git a/__init__.py b/__init__.py
--- a/__init__.py
+++ b/__init__.py
@@ -21,7 +21,7 @@
from svn import core
import svncommand
-import fetch_command
+import svncommands
import tag_repo
import util
@@ -58,7 +58,7 @@
should_update = not os.path.exists(hg_repo_path)
svn_url = util.normalize_url(svn_url)
try:
- res = fetch_command.fetch_revisions(ui, svn_url, hg_repo_path, **opts)
+ res = svncommands.pull(ui, svn_url, hg_repo_path, **opts)
except core.SubversionException, e:
if e.apr_err == core.SVN_ERR_RA_SERF_SSL_CERT_UNTRUSTED:
raise mutil.Abort('It appears svn does not trust the ssl cert for this site.\n'
diff --git a/cmdutil.py b/cmdutil.py
new file mode 100644
--- /dev/null
+++ b/cmdutil.py
@@ -0,0 +1,32 @@
+
+def replay_convert_rev(hg_editor, svn, r):
+ hg_editor.set_current_rev(r)
+ svn.get_replay(r.revnum, hg_editor)
+ i = 1
+ if hg_editor.missing_plaintexts:
+ hg_editor.ui.debug('Fetching %s files that could not use replay.\n' %
+ len(hg_editor.missing_plaintexts))
+ files_to_grab = set()
+ rootpath = svn.subdir and svn.subdir[1:] or ''
+ for p in hg_editor.missing_plaintexts:
+ hg_editor.ui.note('.')
+ hg_editor.ui.flush()
+ if p[-1] == '/':
+ dirpath = p[len(rootpath):]
+ files_to_grab.update([dirpath + f for f,k in
+ svn.list_files(dirpath, r.revnum)
+ if k == 'f'])
+ else:
+ files_to_grab.add(p[len(rootpath):])
+ hg_editor.ui.note('\nFetching files...\n')
+ for p in files_to_grab:
+ hg_editor.ui.note('.')
+ hg_editor.ui.flush()
+ if i % 50 == 0:
+ svn.init_ra_and_client()
+ i += 1
+ data, mode = svn.get_file(p, r.revnum)
+ hg_editor.set_file(p, data, 'x' in mode, 'l' in mode)
+ hg_editor.missing_plaintexts = set()
+ hg_editor.ui.note('\n')
+ hg_editor.commit_current_delta()
diff --git a/fetch_command.py b/fetch_command.py
deleted file mode 100644
--- a/fetch_command.py
+++ /dev/null
@@ -1,132 +0,0 @@
-import os
-
-from mercurial import util as merc_util
-from svn import core
-from svn import delta
-
-import hg_delta_editor
-import svnwrap
-import stupid as stupidmod
-import util
-
-
-def fetch_revisions(ui, svn_url, hg_repo_path, skipto_rev=0, stupid=None,
- tag_locations='tags',
- authors=None,
- filemap=None,
- **opts):
- """pull new revisions from Subversion
- """
- svn_url = util.normalize_url(svn_url)
- old_encoding = util.swap_out_encoding()
- skipto_rev=int(skipto_rev)
- have_replay = not stupid
- if have_replay and not callable(
- delta.svn_txdelta_apply(None, None, None)[0]): #pragma: no cover
- ui.status('You are using old Subversion SWIG bindings. Replay will not'
- ' work until you upgrade to 1.5.0 or newer. Falling back to'
- ' a slower method that may be buggier. Please upgrade, or'
- ' contribute a patch to use the ctypes bindings instead'
- ' of SWIG.\n')
- have_replay = False
- initializing_repo = False
- user = opts.get('username', merc_util.getuser())
- passwd = opts.get('password', '')
- svn = svnwrap.SubversionRepo(svn_url, user, passwd)
- author_host = "@%s" % svn.uuid
- tag_locations = tag_locations.split(',')
- hg_editor = hg_delta_editor.HgChangeReceiver(hg_repo_path,
- ui_=ui,
- subdir=svn.subdir,
- author_host=author_host,
- tag_locations=tag_locations,
- authors=authors,
- filemap=filemap)
- if os.path.exists(hg_editor.uuid_file):
- uuid = open(hg_editor.uuid_file).read()
- assert uuid == svn.uuid
- start = hg_editor.last_known_revision()
- else:
- open(hg_editor.uuid_file, 'w').write(svn.uuid)
- open(hg_editor.svn_url_file, 'w').write(svn_url)
- initializing_repo = True
- start = skipto_rev
-
- if initializing_repo and start > 0:
- raise merc_util.Abort('Revision skipping at repository initialization '
- 'remains unimplemented.')
-
- # start converting revisions
- for r in svn.revisions(start=start):
- valid = True
- hg_editor.update_branch_tag_map_for_rev(r)
- for p in r.paths:
- if hg_editor._is_path_valid(p):
- valid = True
- break
- if valid:
- # got a 502? Try more than once!
- tries = 0
- converted = False
- while not converted:
- try:
- util.describe_revision(ui, r)
- if have_replay:
- try:
- replay_convert_rev(hg_editor, svn, r)
- except svnwrap.SubversionRepoCanNotReplay, e: #pragma: no cover
- ui.status('%s\n' % e.message)
- stupidmod.print_your_svn_is_old_message(ui)
- have_replay = False
- stupidmod.svn_server_pull_rev(ui, svn, hg_editor, r)
- else:
- stupidmod.svn_server_pull_rev(ui, svn, hg_editor, r)
- converted = True
- except core.SubversionException, e: #pragma: no cover
- if (e.apr_err == core.SVN_ERR_RA_DAV_REQUEST_FAILED
- and '502' in str(e)
- and tries < 3):
- tries += 1
- ui.status('Got a 502, retrying (%s)\n' % tries)
- else:
- raise merc_util.Abort(*e.args)
- util.swap_out_encoding(old_encoding)
-
-fetch_revisions = util.register_subcommand('pull')(fetch_revisions)
-
-
-def cleanup_file_handles(svn, count):
- if count % 50 == 0:
- svn.init_ra_and_client()
-
-
-def replay_convert_rev(hg_editor, svn, r):
- hg_editor.set_current_rev(r)
- svn.get_replay(r.revnum, hg_editor)
- i = 1
- if hg_editor.missing_plaintexts:
- hg_editor.ui.debug('Fetching %s files that could not use replay.\n' %
- len(hg_editor.missing_plaintexts))
- files_to_grab = set()
- rootpath = svn.subdir and svn.subdir[1:] or ''
- for p in hg_editor.missing_plaintexts:
- hg_editor.ui.note('.')
- hg_editor.ui.flush()
- if p[-1] == '/':
- dirpath = p[len(rootpath):]
- files_to_grab.update([dirpath + f for f,k in
- svn.list_files(dirpath, r.revnum)
- if k == 'f'])
- else:
- files_to_grab.add(p[len(rootpath):])
- hg_editor.ui.note('\nFetching files...\n')
- for p in files_to_grab:
- hg_editor.ui.note('.')
- hg_editor.ui.flush()
- cleanup_file_handles(svn, i)
- i += 1
- data, mode = svn.get_file(p, r.revnum)
- hg_editor.set_file(p, data, 'x' in mode, 'l' in mode)
- hg_editor.missing_plaintexts = set()
- hg_editor.ui.note('\n')
- hg_editor.commit_current_delta()
diff --git a/push_cmd.py b/push_cmd.py
--- a/push_cmd.py
+++ b/push_cmd.py
@@ -7,7 +7,7 @@
import hg_delta_editor
import svnexternals
import svnwrap
-import fetch_command
+import svncommands
import utility_commands
@@ -68,9 +68,8 @@
old_ctx)
return 1
# 3. Fetch revisions from svn
- r = fetch_command.fetch_revisions(ui, svn_url, hg_repo_path,
- stupid=stupid, username=user,
- password=passwd)
+ r = svncommands.pull(ui, svn_url, hg_repo_path, stupid=stupid,
+ username=user, password=passwd)
assert not r or r == 0
# 4. Find the new head of the target branch
repo = hg.repository(ui, hge.path)
diff --git a/svncommand.py b/svncommand.py
--- a/svncommand.py
+++ b/svncommand.py
@@ -10,13 +10,13 @@
import util
from util import register_subcommand, svn_subcommands, generate_help, svn_commands_nourl
# dirty trick to force demandimport to run my decorator anyway.
+from svncommands import pull
from utility_commands import print_wc_url
-from fetch_command import fetch_revisions
from push_cmd import commit_from_rev
from diff_cmd import diff_command
from rebuildmeta import rebuildmeta
# shut up, pyflakes, we must import those
-__x = [print_wc_url, fetch_revisions, commit_from_rev, diff_command, rebuildmeta]
+__x = [print_wc_url, pull, commit_from_rev, diff_command, rebuildmeta]
def svncmd(ui, repo, subcommand, *args, **opts):
diff --git a/svncommands.py b/svncommands.py
new file mode 100644
--- /dev/null
+++ b/svncommands.py
@@ -0,0 +1,93 @@
+import os
+
+from mercurial import util as hgutil
+from svn import core
+from svn import delta
+
+import hg_delta_editor
+import svnwrap
+import stupid as stupidmod
+import cmdutil
+import util
+
+
+def pull(ui, svn_url, hg_repo_path, skipto_rev=0, stupid=None,
+ tag_locations='tags', authors=None, filemap=None, **opts):
+ """pull new revisions from Subversion
+ """
+ svn_url = util.normalize_url(svn_url)
+ old_encoding = util.swap_out_encoding()
+ skipto_rev=int(skipto_rev)
+ have_replay = not stupid
+ if have_replay and not callable(
+ delta.svn_txdelta_apply(None, None, None)[0]): #pragma: no cover
+ ui.status('You are using old Subversion SWIG bindings. Replay will not'
+ ' work until you upgrade to 1.5.0 or newer. Falling back to'
+ ' a slower method that may be buggier. Please upgrade, or'
+ ' contribute a patch to use the ctypes bindings instead'
+ ' of SWIG.\n')
+ have_replay = False
+ initializing_repo = False
+ user = opts.get('username', hgutil.getuser())
+ passwd = opts.get('password', '')
+ svn = svnwrap.SubversionRepo(svn_url, user, passwd)
+ author_host = "@%s" % svn.uuid
+ tag_locations = tag_locations.split(',')
+ hg_editor = hg_delta_editor.HgChangeReceiver(hg_repo_path,
+ ui_=ui,
+ subdir=svn.subdir,
+ author_host=author_host,
+ tag_locations=tag_locations,
+ authors=authors,
+ filemap=filemap)
+ if os.path.exists(hg_editor.uuid_file):
+ uuid = open(hg_editor.uuid_file).read()
+ assert uuid == svn.uuid
+ start = hg_editor.last_known_revision()
+ else:
+ open(hg_editor.uuid_file, 'w').write(svn.uuid)
+ open(hg_editor.svn_url_file, 'w').write(svn_url)
+ initializing_repo = True
+ start = skipto_rev
+
+ if initializing_repo and start > 0:
+ raise hgutil.Abort('Revision skipping at repository initialization '
+ 'remains unimplemented.')
+
+ # start converting revisions
+ for r in svn.revisions(start=start):
+ valid = True
+ hg_editor.update_branch_tag_map_for_rev(r)
+ for p in r.paths:
+ if hg_editor._is_path_valid(p):
+ valid = True
+ break
+ if valid:
+ # got a 502? Try more than once!
+ tries = 0
+ converted = False
+ while not converted:
+ try:
+ util.describe_revision(ui, r)
+ if have_replay:
+ try:
+ cmdutil.replay_convert_rev(hg_editor, svn, r)
+ except svnwrap.SubversionRepoCanNotReplay, e: #pragma: no cover
+ ui.status('%s\n' % e.message)
+ stupidmod.print_your_svn_is_old_message(ui)
+ have_replay = False
+ stupidmod.svn_server_pull_rev(ui, svn, hg_editor, r)
+ else:
+ stupidmod.svn_server_pull_rev(ui, svn, hg_editor, r)
+ converted = True
+ except core.SubversionException, e: #pragma: no cover
+ if (e.apr_err == core.SVN_ERR_RA_DAV_REQUEST_FAILED
+ and '502' in str(e)
+ and tries < 3):
+ tries += 1
+ ui.status('Got a 502, retrying (%s)\n' % tries)
+ else:
+ raise hgutil.Abort(*e.args)
+ util.swap_out_encoding(old_encoding)
+
+pull = util.register_subcommand('pull')(pull)
changeset: 242:06130689a2c8
user: Dirkjan Ochtman <dirkjan@ochtman.nl>
date: Wed Apr 08 17:53:48 2009 +0200
summary: Move push into svncommands.
diff --git a/cmdutil.py b/cmdutil.py
--- a/cmdutil.py
+++ b/cmdutil.py
@@ -1,3 +1,20 @@
+
+from mercurial import util as hgutil
+
+from svn import core
+
+import svnwrap
+import svnexternals
+
+
+class BaseException(Exception):
+ pass
+
+
+class NoFilesException(BaseException):
+ """Exception raised when you try and commit without files.
+ """
+
def replay_convert_rev(hg_editor, svn, r):
hg_editor.set_current_rev(r)
@@ -30,3 +47,196 @@
hg_editor.missing_plaintexts = set()
hg_editor.ui.note('\n')
hg_editor.commit_current_delta()
+
+
+def _isdir(svn, branchpath, svndir):
+ try:
+ svn.list_dir('%s/%s' % (branchpath, svndir))
+ return True
+ except core.SubversionException:
+ return False
+
+
+def _getdirchanges(svn, branchpath, parentctx, ctx, changedfiles, extchanges):
+ """Compute directories to add or delete when moving from parentctx
+ to ctx, assuming only 'changedfiles' files changed, and 'extchanges'
+ external references changed (as returned by svnexternals.diff()).
+
+ Return (added, deleted) where 'added' is the list of all added
+ directories and 'deleted' the list of deleted directories.
+ Intermediate directories are included: if a/b/c is new and requires
+ the addition of a/b and a, those will be listed too. Intermediate
+ deleted directories are also listed, but item order of undefined
+ in either list.
+ """
+ def finddirs(path, includeself=False):
+ if includeself:
+ yield path
+ pos = path.rfind('/')
+ while pos != -1:
+ yield path[:pos]
+ pos = path.rfind('/', 0, pos)
+
+ def getctxdirs(ctx, keptdirs, extdirs):
+ dirs = {}
+ for f in ctx.manifest():
+ for d in finddirs(f):
+ if d in dirs:
+ break
+ if d in keptdirs:
+ dirs[d] = 1
+ for extdir in extdirs:
+ for d in finddirs(extdir, True):
+ dirs[d] = 1
+ return dirs
+
+ deleted, added = [], []
+ changeddirs = {}
+ for f in changedfiles:
+ if f in parentctx and f in ctx:
+ # Updated files cannot cause directories to be created
+ # or removed.
+ continue
+ for d in finddirs(f):
+ changeddirs[d] = 1
+ for e in extchanges:
+ if not e[1] or not e[2]:
+ for d in finddirs(e[0], True):
+ changeddirs[d] = 1
+ if not changeddirs:
+ return added, deleted
+ olddirs = getctxdirs(parentctx, changeddirs,
+ [e[0] for e in extchanges if e[1]])
+ newdirs = getctxdirs(ctx, changeddirs,
+ [e[0] for e in extchanges if e[2]])
+
+ for d in newdirs:
+ if d not in olddirs and not _isdir(svn, branchpath, d):
+ added.append(d)
+
+ for d in olddirs:
+ if d not in newdirs and _isdir(svn, branchpath, d):
+ deleted.append(d)
+
+ return added, deleted
+
+
+def _externals(ctx):
+ ext = svnexternals.externalsfile()
+ if '.hgsvnexternals' in ctx:
+ ext.read(ctx['.hgsvnexternals'].data())
+ return ext
+
+
+def commit_from_rev(ui, repo, rev_ctx, hg_editor, svn_url, base_revision
+ username, password):
+ """Build and send a commit from Mercurial to Subversion.
+ """
+ file_data = {}
+ svn = svnwrap.SubversionRepo(svn_url, username, password)
+ parent = rev_ctx.parents()[0]
+ parent_branch = rev_ctx.parents()[0].branch()
+ branch_path = 'trunk'
+
+ if parent_branch and parent_branch != 'default':
+ branch_path = 'branches/%s' % parent_branch
+
+ extchanges = list(svnexternals.diff(_externals(parent),
+ _externals(rev_ctx)))
+ addeddirs, deleteddirs = _getdirchanges(svn, branch_path, parent, rev_ctx,
+ rev_ctx.files(), extchanges)
+ deleteddirs = set(deleteddirs)
+
+ props = {}
+ copies = {}
+ for file in rev_ctx.files():
+ if file == '.hgsvnexternals':
+ continue
+ new_data = base_data = ''
+ action = ''
+ if file in rev_ctx:
+ fctx = rev_ctx.filectx(file)
+ new_data = fctx.data()
+
+ if 'x' in fctx.flags():
+ props.setdefault(file, {})['svn:executable'] = '*'
+ if 'l' in fctx.flags():
+ props.setdefault(file, {})['svn:special'] = '*'
+
+ if file not in parent:
+ renamed = fctx.renamed()
+ if renamed:
+ # TODO current model (and perhaps svn model) does not support
+ # this kind of renames: a -> b, b -> c
+ copies[file] = renamed[0]
+ base_data = parent[renamed[0]].data()
+
+ action = 'add'
+ dirname = '/'.join(file.split('/')[:-1] + [''])
+ else:
+ base_data = parent.filectx(file).data()
+ if ('x' in parent.filectx(file).flags()
+ and 'x' not in rev_ctx.filectx(file).flags()):
+ props.setdefault(file, {})['svn:executable'] = None
+ if ('l' in parent.filectx(file).flags()
+ and 'l' not in rev_ctx.filectx(file).flags()):
+ props.setdefault(file, {})['svn:special'] = None
+ action = 'modify'
+ else:
+ pos = file.rfind('/')
+ if pos >= 0:
+ if file[:pos] in deleteddirs:
+ # This file will be removed when its directory is removed
+ continue
+ action = 'delete'
+ file_data[file] = base_data, new_data, action
+
+ def svnpath(p):
+ return '%s/%s' % (branch_path, p)
+
+ changeddirs = []
+ for d, v1, v2 in extchanges:
+ props.setdefault(svnpath(d), {})['svn:externals'] = v2
+ if d not in deleteddirs and d not in addeddirs:
+ changeddirs.append(svnpath(d))
+
+ # Now we are done with files, we can prune deleted directories
+ # against themselves: ignore a/b if a/ is already removed
+ deleteddirs2 = list(deleteddirs)
+ deleteddirs2.sort(reverse=True)
+ for d in deleteddirs2:
+ pos = d.rfind('/')
+ if pos >= 0 and d[:pos] in deleteddirs:
+ deleteddirs.remove(d[:pos])
+
+ newcopies = {}
+ for source, dest in copies.iteritems():
+ newcopies[svnpath(source)] = (svnpath(dest), base_revision)
+
+ new_target_files = [svnpath(f) for f in file_data]
+ for tf, ntf in zip(file_data, new_target_files):
+ if tf in file_data:
+ file_data[ntf] = file_data[tf]
+ if tf in props:
+ props[ntf] = props[tf]
+ del props[tf]
+ if hgutil.binary(file_data[ntf][1]):
+ props.setdefault(ntf, {}).update(props.get(ntf, {}))
+ props.setdefault(ntf, {})['svn:mime-type'] = 'application/octet-stream'
+ del file_data[tf]
+
+ addeddirs = [svnpath(d) for d in addeddirs]
+ deleteddirs = [svnpath(d) for d in deleteddirs]
+ new_target_files += addeddirs + deleteddirs + changeddirs
+ if not new_target_files:
+ raise NoFilesException()
+ try:
+ svn.commit(new_target_files, rev_ctx.description(), file_data,
+ base_revision, set(addeddirs), set(deleteddirs),
+ props, newcopies)
+ except core.SubversionException, e:
+ if hasattr(e, 'apr_err') and (e.apr_err == core.SVN_ERR_FS_TXN_OUT_OF_DATE
+ or e.apr_err == core.SVN_ERR_FS_CONFLICT):
+ raise hgutil.Abort('Base text was out of date, maybe rebase?')
+ else:
+ raise
diff --git a/push_cmd.py b/push_cmd.py
deleted file mode 100644
--- a/push_cmd.py
+++ /dev/null
@@ -1,299 +0,0 @@
-from mercurial import util as merc_util
-from mercurial import hg
-from mercurial import node
-from svn import core
-
-import util
-import hg_delta_editor
-import svnexternals
-import svnwrap
-import svncommands
-import utility_commands
-
-
-class BaseException(Exception):
- pass
-
-
-class NoFilesException(BaseException):
- """Exception raised when you try and commit without files.
- """
-
-
-def push_revisions_to_subversion(ui, repo, hg_repo_path, svn_url,
- stupid=False, **opts):
- """push revisions starting at a specified head back to Subversion.
- """
- old_encoding = util.swap_out_encoding()
- hge = hg_delta_editor.HgChangeReceiver(hg_repo_path,
- ui_=ui)
- svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
- hge.revmap.iterkeys()))
- user = opts.get('username', merc_util.getuser())
- passwd = opts.get('password', '')
- # Strategy:
- # 1. Find all outgoing commits from this head
- if len(repo.parents()) != 1:
- ui.status('Cowardly refusing to push branch merge')
- return 1
- workingrev = repo.parents()[0]
- outgoing = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes, workingrev.node())
- if not (outgoing and len(outgoing)):
- ui.status('No revisions to push.')
- return 0
- while outgoing:
- oldest = outgoing.pop(-1)
- old_ctx = repo[oldest]
- if len(old_ctx.parents()) != 1:
- ui.status('Found a branch merge, this needs discussion and '
- 'implementation.')
- return 1
- base_n = old_ctx.parents()[0].node()
- old_children = repo[base_n].children()
- svnbranch = repo[base_n].branch()
- oldtip = base_n
- samebranchchildren = [c for c in repo[oldtip].children() if c.branch() == svnbranch
- and c.node() in svn_commit_hashes]
- while samebranchchildren:
- oldtip = samebranchchildren[0].node()
- samebranchchildren = [c for c in repo[oldtip].children() if c.branch() == svnbranch
- and c.node() in svn_commit_hashes]
- # 2. Commit oldest revision that needs to be pushed
- base_revision = svn_commit_hashes[base_n][0]
- try:
- commit_from_rev(ui, repo, old_ctx, hge, svn_url, base_revision,
- user, passwd)
- except NoFilesException:
- ui.warn("Could not push revision %s because it had no changes in svn.\n" %
- old_ctx)
- return 1
- # 3. Fetch revisions from svn
- r = svncommands.pull(ui, svn_url, hg_repo_path, stupid=stupid,
- username=user, password=passwd)
- assert not r or r == 0
- # 4. Find the new head of the target branch
- repo = hg.repository(ui, hge.path)
- oldtipctx = repo[oldtip]
- replacement = [c for c in oldtipctx.children() if c not in old_children
- and c.branch() == oldtipctx.branch()]
- assert len(replacement) == 1, 'Replacement node came back as: %r' % replacement
- replacement = replacement[0]
- # 5. Rebase all children of the currently-pushing rev to the new branch
- heads = repo.heads(old_ctx.node())
- for needs_transplant in heads:
- def extrafn(ctx, extra):
- if ctx.node() == oldest:
- return
- extra['branch'] = ctx.branch()
- utility_commands.rebase_commits(ui, repo,
- extrafn=extrafn,
- sourcerev=needs_transplant,
- **opts)
- repo = hg.repository(ui, hge.path)
- for child in repo[replacement.node()].children():
- rebasesrc = node.bin(child.extra().get('rebase_source', node.hex(node.nullid)))
- if rebasesrc in outgoing:
- while rebasesrc in outgoing:
- rebsrcindex = outgoing.index(rebasesrc)
- outgoing = (outgoing[0:rebsrcindex] +
- [child.node(), ] + outgoing[rebsrcindex+1:])
- children = [c for c in child.children() if c.branch() == child.branch()]
- if children:
- child = children[0]
- rebasesrc = node.bin(child.extra().get('rebase_source', node.hex(node.nullid)))
- hge = hg_delta_editor.HgChangeReceiver(hg_repo_path, ui_=ui)
- svn_commit_hashes = dict(zip(hge.revmap.itervalues(), hge.revmap.iterkeys()))
- util.swap_out_encoding(old_encoding)
- return 0
-push_revisions_to_subversion = util.register_subcommand('push')(push_revisions_to_subversion)
-# for git expats
-push_revisions_to_subversion = util.register_subcommand('dcommit')(push_revisions_to_subversion)
-
-def _isdir(svn, branchpath, svndir):
- try:
- svn.list_dir('%s/%s' % (branchpath, svndir))
- return True
- except core.SubversionException:
- return False
-
-def _getdirchanges(svn, branchpath, parentctx, ctx, changedfiles, extchanges):
- """Compute directories to add or delete when moving from parentctx
- to ctx, assuming only 'changedfiles' files changed, and 'extchanges'
- external references changed (as returned by svnexternals.diff()).
-
- Return (added, deleted) where 'added' is the list of all added
- directories and 'deleted' the list of deleted directories.
- Intermediate directories are included: if a/b/c is new and requires
- the addition of a/b and a, those will be listed too. Intermediate
- deleted directories are also listed, but item order of undefined
- in either list.
- """
- def finddirs(path, includeself=False):
- if includeself:
- yield path
- pos = path.rfind('/')
- while pos != -1:
- yield path[:pos]
- pos = path.rfind('/', 0, pos)
-
- def getctxdirs(ctx, keptdirs, extdirs):
- dirs = {}
- for f in ctx.manifest():
- for d in finddirs(f):
- if d in dirs:
- break
- if d in keptdirs:
- dirs[d] = 1
- for extdir in extdirs:
- for d in finddirs(extdir, True):
- dirs[d] = 1
- return dirs
-
- deleted, added = [], []
- changeddirs = {}
- for f in changedfiles:
- if f in parentctx and f in ctx:
- # Updated files cannot cause directories to be created
- # or removed.
- continue
- for d in finddirs(f):
- changeddirs[d] = 1
- for e in extchanges:
- if not e[1] or not e[2]:
- for d in finddirs(e[0], True):
- changeddirs[d] = 1
- if not changeddirs:
- return added, deleted
- olddirs = getctxdirs(parentctx, changeddirs,
- [e[0] for e in extchanges if e[1]])
- newdirs = getctxdirs(ctx, changeddirs,
- [e[0] for e in extchanges if e[2]])
-
- for d in newdirs:
- if d not in olddirs and not _isdir(svn, branchpath, d):
- added.append(d)
-
- for d in olddirs:
- if d not in newdirs and _isdir(svn, branchpath, d):
- deleted.append(d)
-
- return added, deleted
-
-def _externals(ctx):
- ext = svnexternals.externalsfile()
- if '.hgsvnexternals' in ctx:
- ext.read(ctx['.hgsvnexternals'].data())
- return ext
-
-def commit_from_rev(ui, repo, rev_ctx, hg_editor, svn_url, base_revision,
- username, password):
- """Build and send a commit from Mercurial to Subversion.
- """
- file_data = {}
- svn = svnwrap.SubversionRepo(svn_url, username, password)
- parent = rev_ctx.parents()[0]
- parent_branch = rev_ctx.parents()[0].branch()
- branch_path = 'trunk'
-
- if parent_branch and parent_branch != 'default':
- branch_path = 'branches/%s' % parent_branch
-
- extchanges = list(svnexternals.diff(_externals(parent),
- _externals(rev_ctx)))
- addeddirs, deleteddirs = _getdirchanges(svn, branch_path, parent, rev_ctx,
- rev_ctx.files(), extchanges)
- deleteddirs = set(deleteddirs)
-
- props = {}
- copies = {}
- for file in rev_ctx.files():
- if file == '.hgsvnexternals':
- continue
- new_data = base_data = ''
- action = ''
- if file in rev_ctx:
- fctx = rev_ctx.filectx(file)
- new_data = fctx.data()
-
- if 'x' in fctx.flags():
- props.setdefault(file, {})['svn:executable'] = '*'
- if 'l' in fctx.flags():
- props.setdefault(file, {})['svn:special'] = '*'
-
- if file not in parent:
- renamed = fctx.renamed()
- if renamed:
- # TODO current model (and perhaps svn model) does not support
- # this kind of renames: a -> b, b -> c
- copies[file] = renamed[0]
- base_data = parent[renamed[0]].data()
-
- action = 'add'
- dirname = '/'.join(file.split('/')[:-1] + [''])
- else:
- base_data = parent.filectx(file).data()
- if ('x' in parent.filectx(file).flags()
- and 'x' not in rev_ctx.filectx(file).flags()):
- props.setdefault(file, {})['svn:executable'] = None
- if ('l' in parent.filectx(file).flags()
- and 'l' not in rev_ctx.filectx(file).flags()):
- props.setdefault(file, {})['svn:special'] = None
- action = 'modify'
- else:
- pos = file.rfind('/')
- if pos >= 0:
- if file[:pos] in deleteddirs:
- # This file will be removed when its directory is removed
- continue
- action = 'delete'
- file_data[file] = base_data, new_data, action
-
- def svnpath(p):
- return '%s/%s' % (branch_path, p)
-
- changeddirs = []
- for d, v1, v2 in extchanges:
- props.setdefault(svnpath(d), {})['svn:externals'] = v2
- if d not in deleteddirs and d not in addeddirs:
- changeddirs.append(svnpath(d))
-
- # Now we are done with files, we can prune deleted directories
- # against themselves: ignore a/b if a/ is already removed
- deleteddirs2 = list(deleteddirs)
- deleteddirs2.sort(reverse=True)
- for d in deleteddirs2:
- pos = d.rfind('/')
- if pos >= 0 and d[:pos] in deleteddirs:
- deleteddirs.remove(d[:pos])
-
- newcopies = {}
- for source, dest in copies.iteritems():
- newcopies[svnpath(source)] = (svnpath(dest), base_revision)
-
- new_target_files = [svnpath(f) for f in file_data]
- for tf, ntf in zip(file_data, new_target_files):
- if tf in file_data:
- file_data[ntf] = file_data[tf]
- if tf in props:
- props[ntf] = props[tf]
- del props[tf]
- if merc_util.binary(file_data[ntf][1]):
- props.setdefault(ntf, {}).update(props.get(ntf, {}))
- props.setdefault(ntf, {})['svn:mime-type'] = 'application/octet-stream'
- del file_data[tf]
-
- addeddirs = [svnpath(d) for d in addeddirs]
- deleteddirs = [svnpath(d) for d in deleteddirs]
- new_target_files += addeddirs + deleteddirs + changeddirs
- if not new_target_files:
- raise NoFilesException()
- try:
- svn.commit(new_target_files, rev_ctx.description(), file_data,
- base_revision, set(addeddirs), set(deleteddirs),
- props, newcopies)
- except core.SubversionException, e:
- if hasattr(e, 'apr_err') and (e.apr_err == core.SVN_ERR_FS_TXN_OUT_OF_DATE
- or e.apr_err == core.SVN_ERR_FS_CONFLICT):
- raise merc_util.Abort('Base text was out of date, maybe rebase?')
- else:
- raise
diff --git a/svncommand.py b/svncommand.py
--- a/svncommand.py
+++ b/svncommand.py
@@ -12,11 +12,10 @@
# dirty trick to force demandimport to run my decorator anyway.
from svncommands import pull
from utility_commands import print_wc_url
-from push_cmd import commit_from_rev
from diff_cmd import diff_command
from rebuildmeta import rebuildmeta
# shut up, pyflakes, we must import those
-__x = [print_wc_url, pull, commit_from_rev, diff_command, rebuildmeta]
+__x = [print_wc_url, pull, diff_command, rebuildmeta]
def svncmd(ui, repo, subcommand, *args, **opts):
diff --git a/svncommands.py b/svncommands.py
--- a/svncommands.py
+++ b/svncommands.py
@@ -1,6 +1,9 @@
import os
+from mercurial import hg
+from mercurial import node
from mercurial import util as hgutil
+
from svn import core
from svn import delta
@@ -9,6 +12,7 @@
import stupid as stupidmod
import cmdutil
import util
+import utility_commands
def pull(ui, svn_url, hg_repo_path, skipto_rev=0, stupid=None,
@@ -91,3 +95,93 @@
util.swap_out_encoding(old_encoding)
pull = util.register_subcommand('pull')(pull)
+
+
+def push(ui, repo, hg_repo_path, svn_url, stupid=False, **opts):
+ """push revisions starting at a specified head back to Subversion.
+ """
+ old_encoding = util.swap_out_encoding()
+ hge = hg_delta_editor.HgChangeReceiver(hg_repo_path,
+ ui_=ui)
+ svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
+ hge.revmap.iterkeys()))
+ user = opts.get('username', hgutil.getuser())
+ passwd = opts.get('password', '')
+
+ # Strategy:
+ # 1. Find all outgoing commits from this head
+ if len(repo.parents()) != 1:
+ ui.status('Cowardly refusing to push branch merge')
+ return 1
+ workingrev = repo.parents()[0]
+ outgoing = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes, workingrev.node())
+ if not (outgoing and len(outgoing)):
+ ui.status('No revisions to push.')
+ return 0
+ while outgoing:
+ oldest = outgoing.pop(-1)
+ old_ctx = repo[oldest]
+ if len(old_ctx.parents()) != 1:
+ ui.status('Found a branch merge, this needs discussion and '
+ 'implementation.')
+ return 1
+ base_n = old_ctx.parents()[0].node()
+ old_children = repo[base_n].children()
+ svnbranch = repo[base_n].branch()
+ oldtip = base_n
+ samebranchchildren = [c for c in repo[oldtip].children() if c.branch() == svnbranch
+ and c.node() in svn_commit_hashes]
+ while samebranchchildren:
+ oldtip = samebranchchildren[0].node()
+ samebranchchildren = [c for c in repo[oldtip].children() if c.branch() == svnbranch
+ and c.node() in svn_commit_hashes]
+ # 2. Commit oldest revision that needs to be pushed
+ base_revision = svn_commit_hashes[base_n][0]
+ try:
+ cmdutil.commit_from_rev(ui, repo, old_ctx, hge, svn_url,
+ base_revision, user, passwd)
+ except cmdutil.NoFilesException:
+ ui.warn("Could not push revision %s because it had no changes in svn.\n" %
+ old_ctx)
+ return 1
+ # 3. Fetch revisions from svn
+ r = pull(ui, svn_url, hg_repo_path, stupid=stupid,
+ username=user, password=passwd)
+ assert not r or r == 0
+ # 4. Find the new head of the target branch
+ repo = hg.repository(ui, hge.path)
+ oldtipctx = repo[oldtip]
+ replacement = [c for c in oldtipctx.children() if c not in old_children
+ and c.branch() == oldtipctx.branch()]
+ assert len(replacement) == 1, 'Replacement node came back as: %r' % replacement
+ replacement = replacement[0]
+ # 5. Rebase all children of the currently-pushing rev to the new branch
+ heads = repo.heads(old_ctx.node())
+ for needs_transplant in heads:
+ def extrafn(ctx, extra):
+ if ctx.node() == oldest:
+ return
+ extra['branch'] = ctx.branch()
+ utility_commands.rebase_commits(ui, repo,
+ extrafn=extrafn,
+ sourcerev=needs_transplant,
+ **opts)
+ repo = hg.repository(ui, hge.path)
+ for child in repo[replacement.node()].children():
+ rebasesrc = node.bin(child.extra().get('rebase_source', node.hex(node.nullid)))
+ if rebasesrc in outgoing:
+ while rebasesrc in outgoing:
+ rebsrcindex = outgoing.index(rebasesrc)
+ outgoing = (outgoing[0:rebsrcindex] +
+ [child.node(), ] + outgoing[rebsrcindex+1:])
+ children = [c for c in child.children() if c.branch() == child.branch()]
+ if children:
+ child = children[0]
+ rebasesrc = node.bin(child.extra().get('rebase_source', node.hex(node.nullid)))
+ hge = hg_delta_editor.HgChangeReceiver(hg_repo_path, ui_=ui)
+ svn_commit_hashes = dict(zip(hge.revmap.itervalues(), hge.revmap.iterkeys()))
+ util.swap_out_encoding(old_encoding)
+ return 0
+push = util.register_subcommand('push')(push)
+# for git expats
+dcommit = util.register_subcommand('dcommit')(push)
changeset: 243:2027f851d60c
user: Dirkjan Ochtman <dirkjan@ochtman.nl>
date: Wed Apr 08 17:54:30 2009 +0200
summary: Small cleanups: remove unneeded imports, useless superclass.
diff --git a/cmdutil.py b/cmdutil.py
--- a/cmdutil.py
+++ b/cmdutil.py
@@ -7,11 +7,7 @@
import svnexternals
-class BaseException(Exception):
- pass
-
-
-class NoFilesException(BaseException):
+class NoFilesException(Exception):
"""Exception raised when you try and commit without files.
"""
@@ -128,7 +124,7 @@
return ext
-def commit_from_rev(ui, repo, rev_ctx, hg_editor, svn_url, base_revision
+def commit_from_rev(ui, repo, rev_ctx, hg_editor, svn_url, base_revision,
username, password):
"""Build and send a commit from Mercurial to Subversion.
"""
diff --git a/svncommand.py b/svncommand.py
--- a/svncommand.py
+++ b/svncommand.py
@@ -1,12 +1,10 @@
import os
-import stat
import sys
import traceback
from mercurial import hg
from mercurial import node
-import svnwrap
import util
from util import register_subcommand, svn_subcommands, generate_help, svn_commands_nourl
# dirty trick to force demandimport to run my decorator anyway.
changeset: 244:28d0ee605308
user: Dirkjan Ochtman <dirkjan@ochtman.nl>
date: Wed Apr 08 18:21:47 2009 +0200
summary: Move diff to svncommands.
diff --git a/cmdutil.py b/cmdutil.py
--- a/cmdutil.py
+++ b/cmdutil.py
@@ -1,3 +1,5 @@
+#!/usr/bin/python
+import re
from mercurial import util as hgutil
@@ -7,11 +9,31 @@
import svnexternals
+b_re = re.compile(r'^\+\+\+ b\/([^\n]*)', re.MULTILINE)
+a_re = re.compile(r'^--- a\/([^\n]*)', re.MULTILINE)
+devnull_re = re.compile(r'^([-+]{3}) /dev/null', re.MULTILINE)
+header_re = re.compile(r'^diff --git .* b\/(.*)', re.MULTILINE)
+newfile_devnull_re = re.compile(r'^--- /dev/null\n\+\+\+ b/([^\n]*)',
+ re.MULTILINE)
+
+
class NoFilesException(Exception):
"""Exception raised when you try and commit without files.
"""
+def filterdiff(diff, base_revision):
+ diff = newfile_devnull_re.sub(r'--- \1\t(revision 0)' '\n'
+ r'+++ \1\t(working copy)',
+ diff)
+ diff = a_re.sub(r'--- \1'+ ('\t(revision %d)' % base_revision), diff)
+ diff = b_re.sub(r'+++ \1' + '\t(working copy)', diff)
+ diff = devnull_re.sub(r'\1 /dev/null' '\t(working copy)', diff)
+
+ diff = header_re.sub(r'Index: \1' + '\n' + ('=' * 67), diff)
+ return diff
+
+
def replay_convert_rev(hg_editor, svn, r):
hg_editor.set_current_rev(r)
svn.get_replay(r.revnum, hg_editor)
diff --git a/diff_cmd.py b/diff_cmd.py
deleted file mode 100644
--- a/diff_cmd.py
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/usr/bin/env python
-import re
-
-from mercurial import patch
-
-import util
-import hg_delta_editor
-
-b_re = re.compile(r'^\+\+\+ b\/([^\n]*)', re.MULTILINE)
-a_re = re.compile(r'^--- a\/([^\n]*)', re.MULTILINE)
-devnull_re = re.compile(r'^([-+]{3}) /dev/null', re.MULTILINE)
-header_re = re.compile(r'^diff --git .* b\/(.*)', re.MULTILINE)
-newfile_devnull_re = re.compile(r'^--- /dev/null\n\+\+\+ b/([^\n]*)',
- re.MULTILINE)
-def filterdiff(diff, base_revision):
- diff = newfile_devnull_re.sub(r'--- \1\t(revision 0)' '\n'
- r'+++ \1\t(working copy)',
- diff)
- diff = a_re.sub(r'--- \1'+ ('\t(revision %d)' % base_revision), diff)
- diff = b_re.sub(r'+++ \1' + '\t(working copy)', diff)
- diff = devnull_re.sub(r'\1 /dev/null' '\t(working copy)', diff)
-
- diff = header_re.sub(r'Index: \1' + '\n' + ('=' * 67), diff)
- return diff
-
-
-def diff_command(ui, repo, hg_repo_path, **opts):
- """show a diff of the most recent revision against its parent from svn
- """
- hge = hg_delta_editor.HgChangeReceiver(hg_repo_path,
- ui_=ui)
- svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
- hge.revmap.iterkeys()))
- parent = repo.parents()[0]
- o_r = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes, parent.node())
- if o_r:
- parent = repo[o_r[-1]].parents()[0]
- base_rev, _junk = svn_commit_hashes[parent.node()]
- it = patch.diff(repo, parent.node(), None,
- opts=patch.diffopts(ui, opts={'git': True,
- 'show_function': False,
- 'ignore_all_space': False,
- 'ignore_space_change': False,
- 'ignore_blank_lines': False,
- 'unified': True,
- 'text': False,
- }))
- ui.write(filterdiff(''.join(it), base_rev))
-diff_command = util.register_subcommand('diff')(diff_command)
diff --git a/svncommand.py b/svncommand.py
--- a/svncommand.py
+++ b/svncommand.py
@@ -8,12 +8,11 @@
import util
from util import register_subcommand, svn_subcommands, generate_help, svn_commands_nourl
# dirty trick to force demandimport to run my decorator anyway.
-from svncommands import pull
+from svncommands import pull, diff
from utility_commands import print_wc_url
-from diff_cmd import diff_command
from rebuildmeta import rebuildmeta
# shut up, pyflakes, we must import those
-__x = [print_wc_url, pull, diff_command, rebuildmeta]
+__x = [print_wc_url, pull, diff, rebuildmeta]
def svncmd(ui, repo, subcommand, *args, **opts):
diff --git a/svncommands.py b/svncommands.py
--- a/svncommands.py
+++ b/svncommands.py
@@ -2,6 +2,7 @@
from mercurial import hg
from mercurial import node
+from mercurial import patch
from mercurial import util as hgutil
from svn import core
@@ -185,3 +186,28 @@
push = util.register_subcommand('push')(push)
# for git expats
dcommit = util.register_subcommand('dcommit')(push)
+
+
+def diff(ui, repo, hg_repo_path, **opts):
+ """show a diff of the most recent revision against its parent from svn
+ """
+ hge = hg_delta_editor.HgChangeReceiver(hg_repo_path,
+ ui_=ui)
+ svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
+ hge.revmap.iterkeys()))
+ parent = repo.parents()[0]
+ o_r = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes, parent.node())
+ if o_r:
+ parent = repo[o_r[-1]].parents()[0]
+ base_rev, _junk = svn_commit_hashes[parent.node()]
+ it = patch.diff(repo, parent.node(), None,
+ opts=patch.diffopts(ui, opts={'git': True,
+ 'show_function': False,
+ 'ignore_all_space': False,
+ 'ignore_space_change': False,
+ 'ignore_blank_lines': False,
+ 'unified': True,
+ 'text': False,
+ }))
+ ui.write(cmdutil.filterdiff(''.join(it), base_rev))
+diff = util.register_subcommand('diff')(diff)
changeset: 246:074f27c68818
user: Dirkjan Ochtman <dirkjan@ochtman.nl>
date: Wed Apr 08 18:49:44 2009 +0200
summary: Move rebuildmeta into svncommands.
diff --git a/rebuildmeta.py b/rebuildmeta.py
deleted file mode 100644
--- a/rebuildmeta.py
+++ /dev/null
@@ -1,126 +0,0 @@
-import os
-import pickle
-
-from mercurial import node
-from mercurial import util as mutil
-
-import svnwrap
-import util
-
-def rebuildmeta(ui, repo, hg_repo_path, args, **opts):
- """rebuild hgsubversion metadata using values stored in revisions
- """
- if len(args) != 1:
- raise mutil.Abort('You must pass the svn URI used to create this repo.')
- uuid = None
- url = args[0].rstrip('/')
- user = opts.get('username', mutil.getuser())
- passwd = opts.get('password', '')
- svn = svnwrap.SubversionRepo(url, user, passwd)
- subdir = svn.subdir
- svnmetadir = os.path.join(repo.path, 'svn')
- if not os.path.exists(svnmetadir):
- os.makedirs(svnmetadir)
-
- revmap = open(os.path.join(svnmetadir, 'rev_map'), 'w')
- revmap.write('1\n')
- last_rev = -1
- branchinfo = {}
- noderevnums = {}
- for rev in repo:
- ctx = repo[rev]
- convinfo = ctx.extra().get('convert_revision', None)
- if convinfo:
- assert convinfo.startswith('svn:')
- revpath, revision = convinfo[40:].split('@')
- if subdir and subdir[0] != '/':
- subdir = '/' + subdir
- if subdir and subdir[-1] == '/':
- subdir = subdir[:-1]
- assert revpath.startswith(subdir), ('That does not look like the '
- 'right location in the repo.')
- if uuid is None:
- uuid = convinfo[4:40]
- assert uuid == svn.uuid, 'UUIDs did not match!'
- urlfile = open(os.path.join(svnmetadir, 'url'), 'w')
- urlfile.write(url)
- urlfile.close()
- uuidfile = open(os.path.join(svnmetadir, 'uuid'), 'w')
- uuidfile.write(uuid)
- uuidfile.close()
- commitpath = revpath[len(subdir)+1:]
- if commitpath.startswith('branches'):
- commitpath = commitpath[len('branches/'):]
- elif commitpath == 'trunk':
- commitpath = ''
- else:
- assert False, 'Unhandled case in rebuildmeta'
- revmap.write('%s %s %s\n' % (revision,
- node.hex(ctx.node()),
- commitpath))
- revision = int(revision)
- noderevnums[ctx.node()] = revision
- if revision > last_rev:
- last_rev = revision
- branch = ctx.branch()
- if branch == 'default':
- branch = None
- if branch not in branchinfo:
- parent = ctx.parents()[0]
- if (parent.node() in noderevnums
- and parent.branch() != ctx.branch()):
- parentbranch = parent.branch()
- if parentbranch == 'default':
- parentbranch = None
- else:
- parentbranch = None
- branchinfo[branch] = (parentbranch,
- noderevnums.get(parent.node(), 0),
- revision)
- for c in ctx.children():
- if c.branch() == 'closed-branches':
- if branch in branchinfo:
- del branchinfo[branch]
- branchinfofile = open(os.path.join(svnmetadir, 'branch_info'), 'w')
- pickle.dump(branchinfo, branchinfofile)
- branchinfofile.close()
-
- # now handle tags
- tagsinfo = {}
- realtags = svn.tags
- tagsleft = realtags.items()
- while tagsleft:
- tag, tagparent = tagsleft.pop(0)
- source, rev = tagparent
- if source.startswith('tags/'):
- src = source[len('tags/'):]
- if src in tagsinfo:
- tagsinfo[tag] = tagsinfo[src]
- elif src in realtags:
- if (realtags[src][1] <= last_rev
- or realtags[src][0].startswith('tags/')):
- tagsleft.append(src)
- else:
- older_tags = svn.tags_at_rev(rev)
- newsrc, newrev = older_tags[src]
- tagsleft.append((tag, (newsrc, newrev)))
- continue
- else:
- source = determinebranch(source)
- if rev <= last_rev and (source or 'default') in repo.branchtags():
- tagsinfo[tag] = source, rev
- tagsinfofile = open(os.path.join(svnmetadir, 'tag_info'), 'w')
- pickle.dump(tagsinfo, tagsinfofile)
- tagsinfofile.close()
-rebuildmeta = util.register_subcommand('rebuildmeta')(rebuildmeta)
-rebuildmeta = util.command_needs_no_url(rebuildmeta)
-
-def determinebranch(branch):
- assert not branch.startswith('tags/'), "Tags can't be tags of other tags."
- if branch.startswith('branches/'):
- branch = branch[len('branches/'):]
- elif branch == 'trunk':
- branch = None
- else:
- branch = '../' + branch
- return branch
diff --git a/svncommand.py b/svncommand.py
--- a/svncommand.py
+++ b/svncommand.py
@@ -8,9 +8,8 @@
import util
from util import register_subcommand, svn_subcommands, generate_help, svn_commands_nourl
# dirty trick to force demandimport to run my decorator anyway.
-from svncommands import pull, diff
+from svncommands import pull, diff, rebuildmeta
from utility_commands import print_wc_url
-from rebuildmeta import rebuildmeta
# shut up, pyflakes, we must import those
__x = [print_wc_url, pull, diff, rebuildmeta]
diff --git a/svncommands.py b/svncommands.py
--- a/svncommands.py
+++ b/svncommands.py
@@ -1,4 +1,5 @@
import os
+import cPickle as pickle
from mercurial import hg
from mercurial import node
@@ -211,3 +212,120 @@
}))
ui.write(cmdutil.filterdiff(''.join(it), base_rev))
diff = util.register_subcommand('diff')(diff)
+
+
+def rebuildmeta(ui, repo, hg_repo_path, args, **opts):
+ """rebuild hgsubversion metadata using values stored in revisions
+ """
+ if len(args) != 1:
+ raise hgutil.Abort('You must pass the svn URI used to create this repo.')
+ uuid = None
+ url = args[0].rstrip('/')
+ user = opts.get('username', hgutil.getuser())
+ passwd = opts.get('password', '')
+ svn = svnwrap.SubversionRepo(url, user, passwd)
+ subdir = svn.subdir
+ svnmetadir = os.path.join(repo.path, 'svn')
+ if not os.path.exists(svnmetadir):
+ os.makedirs(svnmetadir)
+
+ revmap = open(os.path.join(svnmetadir, 'rev_map'), 'w')
+ revmap.write('1\n')
+ last_rev = -1
+ branchinfo = {}
+ noderevnums = {}
+ for rev in repo:
+ ctx = repo[rev]
+ convinfo = ctx.extra().get('convert_revision', None)
+ if convinfo:
+ assert convinfo.startswith('svn:')
+ revpath, revision = convinfo[40:].split('@')
+ if subdir and subdir[0] != '/':
+ subdir = '/' + subdir
+ if subdir and subdir[-1] == '/':
+ subdir = subdir[:-1]
+ assert revpath.startswith(subdir), ('That does not look like the '
+ 'right location in the repo.')
+ if uuid is None:
+ uuid = convinfo[4:40]
+ assert uuid == svn.uuid, 'UUIDs did not match!'
+ urlfile = open(os.path.join(svnmetadir, 'url'), 'w')
+ urlfile.write(url)
+ urlfile.close()
+ uuidfile = open(os.path.join(svnmetadir, 'uuid'), 'w')
+ uuidfile.write(uuid)
+ uuidfile.close()
+ commitpath = revpath[len(subdir)+1:]
+ if commitpath.startswith('branches'):
+ commitpath = commitpath[len('branches/'):]
+ elif commitpath == 'trunk':
+ commitpath = ''
+ else:
+ assert False, 'Unhandled case in rebuildmeta'
+ revmap.write('%s %s %s\n' % (revision,
+ node.hex(ctx.node()),
+ commitpath))
+ revision = int(revision)
+ noderevnums[ctx.node()] = revision
+ if revision > last_rev:
+ last_rev = revision
+ branch = ctx.branch()
+ if branch == 'default':
+ branch = None
+ if branch not in branchinfo:
+ parent = ctx.parents()[0]
+ if (parent.node() in noderevnums
+ and parent.branch() != ctx.branch()):
+ parentbranch = parent.branch()
+ if parentbranch == 'default':
+ parentbranch = None
+ else:
+ parentbranch = None
+ branchinfo[branch] = (parentbranch,
+ noderevnums.get(parent.node(), 0),
+ revision)
+ for c in ctx.children():
+ if c.branch() == 'closed-branches':
+ if branch in branchinfo:
+ del branchinfo[branch]
+ branchinfofile = open(os.path.join(svnmetadir, 'branch_info'), 'w')
+ pickle.dump(branchinfo, branchinfofile)
+ branchinfofile.close()
+
+ # now handle tags
+ tagsinfo = {}
+ realtags = svn.tags
+ tagsleft = realtags.items()
+ while tagsleft:
+ tag, tagparent = tagsleft.pop(0)
+ source, rev = tagparent
+ if source.startswith('tags/'):
+ src = source[len('tags/'):]
+ if src in tagsinfo:
+ tagsinfo[tag] = tagsinfo[src]
+ elif src in realtags:
+ if (realtags[src][1] <= last_rev
+ or realtags[src][0].startswith('tags/')):
+ tagsleft.append(src)
+ else:
+ older_tags = svn.tags_at_rev(rev)
+ newsrc, newrev = older_tags[src]
+ tagsleft.append((tag, (newsrc, newrev)))
+ continue
+ else:
+ # determine the branch
+ assert not source.startswith('tags/'), "Tags can't be tags of other tags."
+ if source.startswith('branches/'):
+ source = source[len('branches/'):]
+ elif source == 'trunk':
+ source = None
+ else:
+ source = '../' + source
+ if rev <= last_rev and (source or 'default') in repo.branchtags():
+ tagsinfo[tag] = source, rev
+
+ tagsinfofile = open(os.path.join(svnmetadir, 'tag_info'), 'w')
+ pickle.dump(tagsinfo, tagsinfofile)
+ tagsinfofile.close()
+rebuildmeta = util.register_subcommand('rebuildmeta')(rebuildmeta)
+rebuildmeta = util.command_needs_no_url(rebuildmeta)
changeset: 247:1272e87546ed
user: Dirkjan Ochtman <dirkjan@ochtman.nl>
date: Wed Apr 08 18:59:50 2009 +0200
summary: Move help, update into svncommands.
diff --git a/__init__.py b/__init__.py
--- a/__init__.py
+++ b/__init__.py
@@ -82,7 +82,7 @@
('', 'username', '', 'username for authentication'),
('', 'password', '', 'password for authentication'),
],
- svncommand.generate_help(),
+ svncommands.generate_help(),
),
"svnclone":
(svn_fetch,
diff --git a/svncommand.py b/svncommand.py
--- a/svncommand.py
+++ b/svncommand.py
@@ -2,11 +2,7 @@
import sys
import traceback
-from mercurial import hg
-from mercurial import node
-
-import util
-from util import register_subcommand, svn_subcommands, generate_help, svn_commands_nourl
+from util import svn_subcommands, svn_commands_nourl
# dirty trick to force demandimport to run my decorator anyway.
from svncommands import pull, diff, rebuildmeta
from utility_commands import print_wc_url
@@ -43,52 +39,3 @@
ui.status('Unknown subcommand %s\n' % subcommand)
else:
raise
-
-
-def help_command(ui, args=None, **opts):
- """show help for a given subcommands or a help overview
- """
- if args:
- subcommand = args[0]
- if subcommand not in svn_subcommands:
- candidates = []
- for c in svn_subcommands:
- if c.startswith(subcommand):
- candidates.append(c)
- if len(candidates) == 1:
- subcommand = candidates[0]
- elif len(candidates) > 1:
- ui.status('Ambiguous command. Could have been:\n%s\n' %
- ' '.join(candidates))
- return
- doc = svn_subcommands[subcommand].__doc__
- if doc is None:
- doc = "No documentation available for %s." % subcommand
- ui.status(doc.strip(), '\n')
- return
- ui.status(generate_help())
-help_command = register_subcommand('help')(help_command)
-
-def update(ui, args, repo, clean=False, **opts):
- """update to a specified Subversion revision number
- """
- assert len(args) == 1
- rev = int(args[0])
- path = os.path.join(repo.path, 'svn', 'rev_map')
- answers = []
- for k,v in util.parse_revmap(path).iteritems():
- if k[0] == rev:
- answers.append((v, k[1]))
- if len(answers) == 1:
- if clean:
- return hg.clean(repo, answers[0][0])
- return hg.update(repo, answers[0][0])
- elif len(answers) == 0:
- ui.status('Revision %s did not produce an hg revision.\n' % rev)
- return 1
- else:
- ui.status('Ambiguous revision!\n')
- ui.status('\n'.join(['%s on %s' % (node.hex(a[0]), a[1]) for a in
- answers]+['']))
- return 1
-update = register_subcommand('up')(update)
diff --git a/svncommands.py b/svncommands.py
--- a/svncommands.py
+++ b/svncommands.py
@@ -16,6 +16,8 @@
import util
import utility_commands
+from util import generate_help, svn_subcommands, register_subcommand
+
def pull(ui, svn_url, hg_repo_path, skipto_rev=0, stupid=None,
tag_locations='tags', authors=None, filemap=None, **opts):
@@ -329,3 +331,53 @@
tagsinfofile.close()
rebuildmeta = util.register_subcommand('rebuildmeta')(rebuildmeta)
rebuildmeta = util.command_needs_no_url(rebuildmeta)
+
+
+def help(ui, args=None, **opts):
+ """show help for a given subcommands or a help overview
+ """
+ if args:
+ subcommand = args[0]
+ if subcommand not in svn_subcommands:
+ candidates = []
+ for c in svn_subcommands:
+ if c.startswith(subcommand):
+ candidates.append(c)
+ if len(candidates) == 1:
+ subcommand = candidates[0]
+ elif len(candidates) > 1:
+ ui.status('Ambiguous command. Could have been:\n%s\n' %
+ ' '.join(candidates))
+ return
+ doc = svn_subcommands[subcommand].__doc__
+ if doc is None:
+ doc = "No documentation available for %s." % subcommand
+ ui.status(doc.strip(), '\n')
+ return
+ ui.status(generate_help())
+help = register_subcommand('help')(help)
+
+
+def update(ui, args, repo, clean=False, **opts):
+ """update to a specified Subversion revision number
+ """
+ assert len(args) == 1
+ rev = int(args[0])
+ path = os.path.join(repo.path, 'svn', 'rev_map')
+ answers = []
+ for k,v in util.parse_revmap(path).iteritems():
+ if k[0] == rev:
+ answers.append((v, k[1]))
+ if len(answers) == 1:
+ if clean:
+ return hg.clean(repo, answers[0][0])
+ return hg.update(repo, answers[0][0])
+ elif len(answers) == 0:
+ ui.status('Revision %s did not produce an hg revision.\n' % rev)
+ return 1
+ else:
+ ui.status('Ambiguous revision!\n')
+ ui.status('\n'.join(['%s on %s' % (node.hex(a[0]), a[1]) for a in
+ answers]+['']))
+ return 1
+update = register_subcommand('up')(update)
changeset: 248:a9134fa28d15
user: Dirkjan Ochtman <dirkjan@ochtman.nl>
date: Wed Apr 08 19:12:23 2009 +0200
summary: Move svncommand code into __init__.py.
diff --git a/__init__.py b/__init__.py
--- a/__init__.py
+++ b/__init__.py
@@ -16,7 +16,7 @@
from mercurial import commands
from mercurial import hg
-from mercurial import util as mutil
+from mercurial import util as hgutil
from svn import core
@@ -25,6 +25,8 @@
import tag_repo
import util
+from util import svn_subcommands, svn_commands_nourl
+
def reposetup(ui, repo):
if not util.is_svn_repo(repo):
return
@@ -34,13 +36,39 @@
def svn(ui, repo, subcommand, *args, **opts):
'''see detailed help for list of subcommands'''
+
+ # guess command if prefix
+ if subcommand not in svn_subcommands:
+ candidates = []
+ for c in svn_subcommands:
+ if c.startswith(subcommand):
+ candidates.append(c)
+ if len(candidates) == 1:
+ subcommand = candidates[0]
+
+ path = os.path.dirname(repo.path)
try:
- return svncommand.svncmd(ui, repo, subcommand, *args, **opts)
+ commandfunc = svn_subcommands[subcommand]
+ if commandfunc not in svn_commands_nourl:
+ opts['svn_url'] = open(os.path.join(repo.path, 'svn', 'url')).read()
+ return commandfunc(ui, args=args, hg_repo_path=path, repo=repo, **opts)
except core.SubversionException, e:
if e.apr_err == core.SVN_ERR_RA_SERF_SSL_CERT_UNTRUSTED:
- raise mutil.Abort('It appears svn does not trust the ssl cert for this site.\n'
+ raise hgutil.Abort('It appears svn does not trust the ssl cert for this site.\n'
'Please try running svn ls on that url first.')
raise
+ except TypeError:
+ tb = traceback.extract_tb(sys.exc_info()[2])
+ if len(tb) == 1:
+ ui.status('Bad arguments for subcommand %s\n' % subcommand)
+ else:
+ raise
+ except KeyError, e:
+ tb = traceback.extract_tb(sys.exc_info()[2])
+ if len(tb) == 1:
+ ui.status('Unknown subcommand %s\n' % subcommand)
+ else:
+ raise
def svn_fetch(ui, svn_url, hg_repo_path=None, **opts):
@@ -61,7 +89,7 @@
res = svncommands.pull(ui, svn_url, hg_repo_path, **opts)
except core.SubversionException, e:
if e.apr_err == core.SVN_ERR_RA_SERF_SSL_CERT_UNTRUSTED:
- raise mutil.Abort('It appears svn does not trust the ssl cert for this site.\n'
+ raise hgutil.Abort('It appears svn does not trust the ssl cert for this site.\n'
'Please try running svn ls on that url first.')
raise
if (res is None or res == 0) and should_update:
diff --git a/svncommand.py b/svncommand.py
deleted file mode 100644
--- a/svncommand.py
+++ /dev/null
@@ -1,41 +0,0 @@
-import os
-import sys
-import traceback
-
-from util import svn_subcommands, svn_commands_nourl
-# dirty trick to force demandimport to run my decorator anyway.
-from svncommands import pull, diff, rebuildmeta
-from utility_commands import print_wc_url
-# shut up, pyflakes, we must import those
-__x = [print_wc_url, pull, diff, rebuildmeta]
-
-
-def svncmd(ui, repo, subcommand, *args, **opts):
- if subcommand not in svn_subcommands:
- candidates = []
- for c in svn_subcommands:
- if c.startswith(subcommand):
- candidates.append(c)
- if len(candidates) == 1:
- subcommand = candidates[0]
- path = os.path.dirname(repo.path)
- try:
- commandfunc = svn_subcommands[subcommand]
- if commandfunc not in svn_commands_nourl:
- opts['svn_url'] = open(os.path.join(repo.path, 'svn', 'url')).read()
- return commandfunc(ui, args=args,
- hg_repo_path=path,
- repo=repo,
- **opts)
- except TypeError:
- tb = traceback.extract_tb(sys.exc_info()[2])
- if len(tb) == 1:
- ui.status('Bad arguments for subcommand %s\n' % subcommand)
- else:
- raise
- except KeyError, e:
- tb = traceback.extract_tb(sys.exc_info()[2])
- if len(tb) == 1:
- ui.status('Unknown subcommand %s\n' % subcommand)
- else:
- raise
changeset: 249:a065bf61caa1
user: Daniel Tang <dytang@cs.purdue.edu>
date: Fri Apr 10 15:43:47 2009 -0500
summary: encoding: fix encoding swap to work with demandimport enabled (#67)
diff --git a/util.py b/util.py
--- a/util.py
+++ b/util.py
@@ -4,10 +4,6 @@
from mercurial import hg
from mercurial import node
from mercurial import util
-try:
- from mercurial import encoding
-except ImportError:
- encoding = None
svn_subcommands = { }
def register_subcommand(name):
@@ -143,10 +139,11 @@
def swap_out_encoding(new_encoding="UTF-8"):
""" Utility for mercurial incompatibility changes, can be removed after 1.3"""
- if encoding is None:
+ try:
+ from mercurial import encoding
+ old = encoding.encoding
+ encoding.encoding = new_encoding
+ except ImportError:
old = util._encoding
util._encoding = new_encoding
- else:
- old = encoding.encoding
- encoding.encoding = new_encoding
return old
changeset: 250:79349fd04836
user: Daniel Tang <dytang@cs.purdue.edu>
date: Fri Apr 10 13:43:44 2009 -0400
summary: utils: standardizing imported name to hgutil, our_util to util
diff --git a/hg_delta_editor.py b/hg_delta_editor.py
--- a/hg_delta_editor.py
+++ b/hg_delta_editor.py
@@ -8,14 +8,14 @@
from mercurial import context
from mercurial import hg
from mercurial import ui
-from mercurial import util
+from mercurial import util as hgutil
from mercurial import revlog
from mercurial import node
from svn import delta
from svn import core
import svnexternals
-import util as our_util
+import util
def pickle_atomic(data, file_path, dir=None):
"""pickle some data to a path atomically.
@@ -31,7 +31,7 @@
except: #pragma: no cover
raise
else:
- util.rename(path, file_path)
+ hgutil.rename(path, file_path)
def stash_exception_on_self(fn):
"""Stash any exception raised in the method on self.
@@ -100,7 +100,7 @@
self.subdir = self.subdir[1:]
self.revmap = {}
if os.path.exists(self.revmap_file):
- self.revmap = our_util.parse_revmap(self.revmap_file)
+ self.revmap = util.parse_revmap(self.revmap_file)
self.branches = {}
if os.path.exists(self.branch_info_file):
f = open(self.branch_info_file)
@@ -160,7 +160,7 @@
self.repo = hg.repository(self.ui, repo_path, create=True)
os.makedirs(os.path.dirname(self.uuid_file))
f = open(self.revmap_file, 'w')
- f.write('%s\n' % our_util.REVMAP_FILE_VERSION)
+ f.write('%s\n' % util.REVMAP_FILE_VERSION)
f.flush()
f.close()
@@ -634,9 +634,9 @@
if parents[0] in closed_revs and branch in self.branches_to_delete:
continue
# TODO this needs to be fixed with the new revmap
- extra = our_util.build_extra(rev.revnum, branch,
- open(self.uuid_file).read(),
- self.subdir)
+ extra = util.build_extra(rev.revnum, branch,
+ open(self.uuid_file).read(),
+ self.subdir)
if branch is not None:
if (branch not in self.branches
and branch not in self.repo.branchtags()):
@@ -676,7 +676,7 @@
date,
extra)
new_hash = self.repo.commitctx(current_ctx)
- our_util.describe_commit(self.ui, new_hash, branch)
+ util.describe_commit(self.ui, new_hash, branch)
if (rev.revnum, branch) not in self.revmap:
self.add_to_revmap(rev.revnum, branch, new_hash)
# now we handle branches that need to be committed without any files
@@ -689,9 +689,9 @@
raise IOError
# True here meant nuke all files, shouldn't happen with branch closing
if self.commit_branches_empty[branch]: #pragma: no cover
- raise util.Abort('Empty commit to an open branch attempted. '
- 'Please report this issue.')
- extra = our_util.build_extra(rev.revnum, branch,
+ raise hgutil.Abort('Empty commit to an open branch attempted. '
+ 'Please report this issue.')
+ extra = util.build_extra(rev.revnum, branch,
open(self.uuid_file).read(),
self.subdir)
current_ctx = context.memctx(self.repo,
@@ -703,7 +703,7 @@
date,
extra)
new_hash = self.repo.commitctx(current_ctx)
- our_util.describe_commit(self.ui, new_hash, branch)
+ util.describe_commit(self.ui, new_hash, branch)
if (rev.revnum, branch) not in self.revmap:
self.add_to_revmap(rev.revnum, branch, new_hash)
self._save_metadata()
@@ -856,7 +856,7 @@
self.externals[path] = None
map(self.delete_file, [pat for pat in self.current_files.iterkeys()
if pat.startswith(path+'/')])
- for f in ctx.walk(our_util.PrefixMatch(br_path2)):
+ for f in ctx.walk(util.PrefixMatch(br_path2)):
f_p = '%s/%s' % (path, f[len(br_path2):])
if f_p not in self.current_files:
self.delete_file(f_p)
@@ -1055,8 +1055,8 @@
handler, baton = delta.svn_txdelta_apply(source, target, None)
if not callable(handler): #pragma: no cover
- raise util.Abort('Error in Subversion bindings: '
- 'cannot call handler!')
+ raise hgutil.Abort('Error in Subversion bindings: '
+ 'cannot call handler!')
def txdelt_window(window):
try:
if not self._is_path_valid(self.current_file):
@@ -1069,7 +1069,7 @@
if e.apr_err == core.SVN_ERR_INCOMPLETE_DATA:
self.missing_plaintexts.add(self.current_file)
else: #pragma: no cover
- raise util.Abort(*e.args)
+ raise hgutil.Abort(*e.args)
except: #pragma: no cover
print len(base), self.current_file
self._exception_info = sys.exc_info()
diff --git a/svnexternals.py b/svnexternals.py
--- a/svnexternals.py
+++ b/svnexternals.py
@@ -1,6 +1,6 @@
import cStringIO
-from mercurial import util as merc_util
+from mercurial import util as hgutil
class externalsfile(dict):
"""Map svn directories to lists of externals entries.
@@ -24,7 +24,7 @@
def write(self):
fp = cStringIO.StringIO()
- for target in merc_util.sort(self):
+ for target in hgutil.sort(self):
lines = self[target]
if not lines:
continue
@@ -47,7 +47,7 @@
if line.startswith('['):
line = line.strip()
if line[-1] != ']':
- raise merc_util.Abort('invalid externals section name: %s' % line)
+ raise hgutil.Abort('invalid externals section name: %s' % line)
target = line[1:-1]
if target == '.':
target = ''
diff --git a/util.py b/util.py
--- a/util.py
+++ b/util.py
@@ -3,7 +3,7 @@
from mercurial import hg
from mercurial import node
-from mercurial import util
+from mercurial import util as hgutil
svn_subcommands = { }
def register_subcommand(name):
@@ -96,7 +96,7 @@
outgoing_rev_hashes.append(sourcerev.node())
sourcerev = sourcerev.parents()
if len(sourcerev) != 1:
- raise util.Abort("Sorry, can't find svn parent of a merge revision.")
+ raise hgutil.Abort("Sorry, can't find svn parent of a merge revision.")
sourcerev = sourcerev[0]
if sourcerev.node() != node.nullid:
return outgoing_rev_hashes
@@ -144,6 +144,6 @@
old = encoding.encoding
encoding.encoding = new_encoding
except ImportError:
- old = util._encoding
- util._encoding = new_encoding
+ old = hgutil._encoding
+ hgutil._encoding = new_encoding
return old
diff --git a/utility_commands.py b/utility_commands.py
--- a/utility_commands.py
+++ b/utility_commands.py
@@ -3,7 +3,7 @@
import mercurial
from mercurial import cmdutil
from mercurial import node
-from mercurial import util as mutil
+from mercurial import util as hgutil
from hgext import rebase
import svnwrap
@@ -34,7 +34,7 @@
"""
ignpath = os.path.join(hg_repo_path, '.hgignore')
if not force and os.path.exists(ignpath):
- raise mutil.Abort('not overwriting existing .hgignore, try --force?')
+ raise hgutil.Abort('not overwriting existing .hgignore, try --force?')
ignorefile = open(ignpath, 'w')
ignorefile.write('.hgignore\nsyntax:glob\n')
hge = hg_delta_editor.HgChangeReceiver(hg_repo_path,
@@ -50,7 +50,7 @@
url = hge.url
if url[-1] == '/':
url = url[:-1]
- user = opts.get('username', mutil.getuser())
+ user = opts.get('username', hgutil.getuser())
passwd = opts.get('passwd', '')
svn = svnwrap.SubversionRepo(url, user, passwd)
dirs = [''] + [d[0] for d in svn.list_files(branchpath, r) if d[1] == 'd']
@@ -108,8 +108,8 @@
'author': author,
'revision': r,
# TODO I'd like to format this to the user's local TZ if possible
- 'date': mutil.datestr(parent.date(),
- '%Y-%m-%d %H:%M:%S %1%2 (%a, %d %b %Y)')
+ 'date': hgutil.datestr(parent.date(),
+ '%Y-%m-%d %H:%M:%S %1%2 (%a, %d %b %Y)')
})
run_svn_info = util.register_subcommand('info')(run_svn_info)
@@ -221,7 +221,7 @@
def version(ui, **opts):
"""Show current version of hg and hgsubversion.
"""
- ui.status('hg: %s\n' % mutil.version())
+ ui.status('hg: %s\n' % hgutil.version())
ui.status('svn bindings: %s\n' % svnwrap.version())
ui.status('hgsubversion: %s\n' % util.version(ui))
version = util.register_subcommand('version')(version)
changeset: 251:23b02f892d9b
user: Dirkjan Ochtman <dirkjan@ochtman.nl>
date: Fri Apr 10 17:12:04 2009 +0200
summary: Fix up imports in __init__.py.
diff --git a/__init__.py b/__init__.py
--- a/__init__.py
+++ b/__init__.py
@@ -13,6 +13,8 @@
'''
import os
+import sys
+import traceback
from mercurial import commands
from mercurial import hg
@@ -20,7 +22,6 @@
from svn import core
-import svncommand
import svncommands
import tag_repo
import util
changeset: 252:4d3bcd2f26ed
user: Dirkjan Ochtman <dirkjan@ochtman.nl>
date: Fri Apr 10 17:13:07 2009 +0200
summary: Remove unused imports from stupid.py.
diff --git a/stupid.py b/stupid.py
--- a/stupid.py
+++ b/stupid.py
@@ -1,16 +1,12 @@
import cStringIO
import re
-import os
from mercurial import patch
from mercurial import node
from mercurial import context
from mercurial import revlog
-from mercurial import util as merc_util
from svn import core
-from svn import delta
-import hg_delta_editor
import svnwrap
import svnexternals
import util
changeset: 253:c3d5c4ae9c7c
user: Dirkjan Ochtman <dirkjan@ochtman.nl>
date: Fri Apr 10 23:09:59 2009 +0200
summary: Work with simple command table instead of decorators.
diff --git a/__init__.py b/__init__.py
--- a/__init__.py
+++ b/__init__.py
@@ -26,8 +26,6 @@
import tag_repo
import util
-from util import svn_subcommands, svn_commands_nourl
-
def reposetup(ui, repo):
if not util.is_svn_repo(repo):
return
@@ -39,9 +37,9 @@
'''see detailed help for list of subcommands'''
# guess command if prefix
- if subcommand not in svn_subcommands:
+ if subcommand not in svncommands.table:
candidates = []
- for c in svn_subcommands:
+ for c in svncommands.table:
if c.startswith(subcommand):
candidates.append(c)
if len(candidates) == 1:
@@ -49,8 +47,8 @@
path = os.path.dirname(repo.path)
try:
- commandfunc = svn_subcommands[subcommand]
- if commandfunc not in svn_commands_nourl:
+ commandfunc = svncommands.table[subcommand]
+ if commandfunc not in svncommands.nourl:
opts['svn_url'] = open(os.path.join(repo.path, 'svn', 'url')).read()
return commandfunc(ui, args=args, hg_repo_path=path, repo=repo, **opts)
except core.SubversionException, e:
@@ -111,7 +109,7 @@
('', 'username', '', 'username for authentication'),
('', 'password', '', 'password for authentication'),
],
- svncommands.generate_help(),
+ svncommands._helpgen(),
),
"svnclone":
(svn_fetch,
diff --git a/svncommands.py b/svncommands.py
--- a/svncommands.py
+++ b/svncommands.py
@@ -16,8 +16,6 @@
import util
import utility_commands
-from util import generate_help, svn_subcommands, register_subcommand
-
def pull(ui, svn_url, hg_repo_path, skipto_rev=0, stupid=None,
tag_locations='tags', authors=None, filemap=None, **opts):
@@ -98,8 +96,6 @@
raise hgutil.Abort(*e.args)
util.swap_out_encoding(old_encoding)
-pull = util.register_subcommand('pull')(pull)
-
def push(ui, repo, hg_repo_path, svn_url, stupid=False, **opts):
"""push revisions starting at a specified head back to Subversion.
@@ -166,10 +162,8 @@
if ctx.node() == oldest:
return
extra['branch'] = ctx.branch()
- utility_commands.rebase_commits(ui, repo,
- extrafn=extrafn,
- sourcerev=needs_transplant,
- **opts)
+ utility_commands.rebase(ui, repo, extrafn=extrafn,
+ sourcerev=needs_transplant, **opts)
repo = hg.repository(ui, hge.path)
for child in repo[replacement.node()].children():
rebasesrc = node.bin(child.extra().get('rebase_source', node.hex(node.nullid)))
@@ -186,9 +180,6 @@
svn_commit_hashes = dict(zip(hge.revmap.itervalues(), hge.revmap.iterkeys()))
util.swap_out_encoding(old_encoding)
return 0
-push = util.register_subcommand('push')(push)
-# for git expats
-dcommit = util.register_subcommand('dcommit')(push)
def diff(ui, repo, hg_repo_path, **opts):
@@ -213,7 +204,6 @@
'text': False,
}))
ui.write(cmdutil.filterdiff(''.join(it), base_rev))
-diff = util.register_subcommand('diff')(diff)
def rebuildmeta(ui, repo, hg_repo_path, args, **opts):
@@ -329,8 +319,6 @@
tagsinfofile = open(os.path.join(svnmetadir, 'tag_info'), 'w')
pickle.dump(tagsinfo, tagsinfofile)
tagsinfofile.close()
-rebuildmeta = util.register_subcommand('rebuildmeta')(rebuildmeta)
-rebuildmeta = util.command_needs_no_url(rebuildmeta)
def help(ui, args=None, **opts):
@@ -338,9 +326,9 @@
"""
if args:
subcommand = args[0]
- if subcommand not in svn_subcommands:
+ if subcommand not in table:
candidates = []
- for c in svn_subcommands:
+ for c in table:
if c.startswith(subcommand):
candidates.append(c)
if len(candidates) == 1:
@@ -349,13 +337,12 @@
ui.status('Ambiguous command. Could have been:\n%s\n' %
' '.join(candidates))
return
- doc = svn_subcommands[subcommand].__doc__
+ doc = table[subcommand].__doc__
if doc is None:
doc = "No documentation available for %s." % subcommand
ui.status(doc.strip(), '\n')
return
- ui.status(generate_help())
-help = register_subcommand('help')(help)
+ ui.status(_helpgen())
def update(ui, args, repo, clean=False, **opts):
@@ -380,4 +367,27 @@
ui.status('\n'.join(['%s on %s' % (node.hex(a[0]), a[1]) for a in
answers]+['']))
return 1
-update = register_subcommand('up')(update)
+
+
+nourl = ['rebuildmeta'] + utility_commands.nourl
+table = {
+ 'pull': pull,
+ 'push': push,
+ 'dcommit': push,
+ 'update': update,
+ 'help': help,
+ 'rebuildmeta': rebuildmeta,
+ 'diff': diff,
+}
+
+table.update(utility_commands.table)
+
+
+def _helpgen():
+ ret = ['hg svn ...', '',
+ 'subcommands for Subversion integration', '',
+ 'list of subcommands:', '']
+ for name, func in sorted(table.items()):
+ short_description = (func.__doc__ or '').splitlines()[0]
+ ret.append(" %-10s %s" % (name, short_description))
+ return '\n'.join(ret) + '\n'
diff --git a/util.py b/util.py
--- a/util.py
+++ b/util.py
@@ -5,18 +5,6 @@
from mercurial import node
from mercurial import util as hgutil
-svn_subcommands = { }
-def register_subcommand(name):
- def inner(fn):
- svn_subcommands[name] = fn
- return fn
- return inner
-
-svn_commands_nourl = set()
-def command_needs_no_url(fn):
- svn_commands_nourl.add(fn)
- return fn
-
def version(ui):
"""Guess the version of hgsubversion.
@@ -27,18 +15,6 @@
return node.hex(ver)[:12]
-def generate_help():
- ret = ['hg svn ...', '',
- 'subcommands for Subversion integration', '',
- 'list of subcommands:', '']
-
- for name, func in sorted(svn_subcommands.items()):
- short_description = (func.__doc__ or '').splitlines()[0]
- ret.append(" %-10s %s" % (name, short_description))
-
- return "\n".join(ret) + '\n'
-
-
def normalize_url(svn_url):
return svn_url.rstrip('/')
diff --git a/utility_commands.py b/utility_commands.py
--- a/utility_commands.py
+++ b/utility_commands.py
@@ -4,19 +4,18 @@
from mercurial import cmdutil
from mercurial import node
from mercurial import util as hgutil
-from hgext import rebase
+from hgext import rebase as hgrebase
import svnwrap
import util
import hg_delta_editor
-def print_wc_url(ui, repo, hg_repo_path, **opts):
+def url(ui, repo, hg_repo_path, **opts):
"""show the location (URL) of the Subversion repository
"""
hge = hg_delta_editor.HgChangeReceiver(hg_repo_path,
ui_=ui)
ui.status(hge.url, '\n')
-print_wc_url = util.register_subcommand('url')(print_wc_url)
def find_wc_parent_rev(ui, repo, hge, svn_commit_hashes):
@@ -29,7 +28,7 @@
return workingctx
-def generate_ignore(ui, repo, hg_repo_path, force=False, **opts):
+def genignore(ui, repo, hg_repo_path, force=False, **opts):
"""generate .hgignore from svn:ignore properties.
"""
ignpath = os.path.join(hg_repo_path, '.hgignore')
@@ -63,10 +62,9 @@
ignorefile.write('%s/%s\n' % (dir, prop))
else:
ignorefile.write('%s\n' % prop)
-generate_ignore = util.register_subcommand('genignore')(generate_ignore)
-def run_svn_info(ui, repo, hg_repo_path, **opts):
+def info(ui, repo, hg_repo_path, **opts):
"""show Subversion details similar to `svn info'
"""
hge = hg_delta_editor.HgChangeReceiver(hg_repo_path,
@@ -111,10 +109,9 @@
'date': hgutil.datestr(parent.date(),
'%Y-%m-%d %H:%M:%S %1%2 (%a, %d %b %Y)')
})
-run_svn_info = util.register_subcommand('info')(run_svn_info)
-def print_parent_revision(ui, repo, hg_repo_path, **opts):
+def parent(ui, repo, hg_repo_path, **opts):
"""show Mercurial & Subversion parents of the working dir or revision
"""
hge = hg_delta_editor.HgChangeReceiver(hg_repo_path,
@@ -129,10 +126,9 @@
else:
ui.status('Working copy seems to have no parent svn revision.\n')
return 0
-print_parent_revision = util.register_subcommand('parent')(print_parent_revision)
-def rebase_commits(ui, repo, extrafn=None, sourcerev=None, **opts):
+def rebase(ui, repo, extrafn=None, sourcerev=None, **opts):
"""rebase current unpushed revisions onto the Subversion head
This moves a line of development from making its own head to the top of
@@ -175,13 +171,12 @@
ui.status('Already up to date!\n')
return 0
# TODO this is really hacky, there must be a more direct way
- return rebase.rebase(ui, repo, dest=node.hex(target_rev.node()),
+ return hgrebase.rebase(ui, repo, dest=node.hex(target_rev.node()),
base=node.hex(sourcerev),
extrafn=extrafn)
-rebase_commits = util.register_subcommand('rebase')(rebase_commits)
-def show_outgoing_to_svn(ui, repo, hg_repo_path, **opts):
+def outgoing(ui, repo, hg_repo_path, **opts):
"""show changesets not found in the Subversion repository
"""
hge = hg_delta_editor.HgChangeReceiver(hg_repo_path,
@@ -195,10 +190,9 @@
displayer = cmdutil.show_changeset(ui, repo, opts, buffered=False)
for node in reversed(o_r):
displayer.show(repo[node])
-show_outgoing_to_svn = util.register_subcommand('outgoing')(show_outgoing_to_svn)
-def list_authors(ui, args, authors=None, **opts):
+def listauthors(ui, args, authors=None, **opts):
"""list all authors in a Subversion repository
"""
if not len(args):
@@ -214,8 +208,6 @@
authorfile.close()
else:
ui.status('%s\n' % '\n'.join(sorted(author_set)))
-list_authors = util.register_subcommand('listauthors')(list_authors)
-list_authors = util.command_needs_no_url(list_authors)
def version(ui, **opts):
@@ -224,5 +216,16 @@
ui.status('hg: %s\n' % hgutil.version())
ui.status('svn bindings: %s\n' % svnwrap.version())
ui.status('hgsubversion: %s\n' % util.version(ui))
-version = util.register_subcommand('version')(version)
-version = util.command_needs_no_url(version)
+
+
+nourl = ['version', 'listauthors']
+table = {
+ 'url': url,
+ 'genignore': genignore,
+ 'info': info,
+ 'parent': parent,
+ 'outgoing': outgoing,
+ 'listauthors': listauthors,
+ 'version': version,
+ 'rebase': rebase,
+}
changeset: 254:9ba31af57e4b
user: Dirkjan Ochtman <dirkjan@ochtman.nl>
date: Fri Apr 10 17:29:45 2009 +0200
summary: Move utility_commands.find_wc_parent_rev() to cmdutil.parentrev().
diff --git a/cmdutil.py b/cmdutil.py
--- a/cmdutil.py
+++ b/cmdutil.py
@@ -5,6 +5,7 @@
from svn import core
+import util
import svnwrap
import svnexternals
@@ -34,6 +35,17 @@
return diff
+def parentrev(ui, repo, hge, svn_commit_hashes):
+ """Find the svn parent revision of the repo's dirstate.
+ """
+ workingctx = repo.parents()[0]
+ outrev = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes,
+ workingctx.node())
+ if outrev:
+ workingctx = repo[outrev[-1]].parents()[0]
+ return workingctx
+
+
def replay_convert_rev(hg_editor, svn, r):
hg_editor.set_current_rev(r)
svn.get_replay(r.revnum, hg_editor)
diff --git a/utility_commands.py b/utility_commands.py
--- a/utility_commands.py
+++ b/utility_commands.py
@@ -1,12 +1,13 @@
import os
import mercurial
-from mercurial import cmdutil
+from mercurial import cmdutil as hgcmdutil
from mercurial import node
from mercurial import util as hgutil
from hgext import rebase as hgrebase
import svnwrap
+import cmdutil
import util
import hg_delta_editor
@@ -18,16 +19,6 @@
ui.status(hge.url, '\n')
-def find_wc_parent_rev(ui, repo, hge, svn_commit_hashes):
- """Find the svn parent revision of the repo's dirstate.
- """
- workingctx = repo.parents()[0]
- o_r = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes, workingctx.node())
- if o_r:
- workingctx = repo[o_r[-1]].parents()[0]
- return workingctx
-
-
def genignore(ui, repo, hg_repo_path, force=False, **opts):
"""generate .hgignore from svn:ignore properties.
"""
@@ -40,7 +31,7 @@
ui_=ui)
svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
hge.revmap.iterkeys()))
- parent = find_wc_parent_rev(ui, repo, hge, svn_commit_hashes)
+ parent = cmdutil.parentrev(ui, repo, hge, svn_commit_hashes)
r, br = svn_commit_hashes[parent.node()]
if br == None:
branchpath = 'trunk'
@@ -71,7 +62,7 @@
ui_=ui)
svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
hge.revmap.iterkeys()))
- parent = find_wc_parent_rev(ui, repo, hge, svn_commit_hashes)
+ parent = cmdutil.parentrev(ui, repo, hge, svn_commit_hashes)
pn = parent.node()
if pn not in svn_commit_hashes:
ui.status('Not a child of an svn revision.\n')
@@ -118,7 +109,7 @@
ui_=ui)
svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
hge.revmap.iterkeys()))
- ha = find_wc_parent_rev(ui, repo, hge, svn_commit_hashes)
+ ha = cmdutil.parentrev(ui, repo, hge, svn_commit_hashes)
if ha.node() != node.nullid:
r, br = svn_commit_hashes[ha.node()]
ui.status('Working copy parent revision is %s: r%s on %s\n' %
@@ -187,7 +178,7 @@
if not (o_r and len(o_r)):
ui.status('No outgoing changes found.\n')
return 0
- displayer = cmdutil.show_changeset(ui, repo, opts, buffered=False)
+ displayer = hgcmdutil.show_changeset(ui, repo, opts, buffered=False)
for node in reversed(o_r):
displayer.show(repo[node])
changeset: 255:246aaefb1cc0
user: Nat Williams <nat.williams@gmail.com>
date: Thu Apr 09 22:08:06 2009 -0500
summary: Add a basic incoming command.
diff --git a/svncommands.py b/svncommands.py
--- a/svncommands.py
+++ b/svncommands.py
@@ -97,6 +97,55 @@
util.swap_out_encoding(old_encoding)
+def incoming(ui, svn_url, hg_repo_path, skipto_rev=0, stupid=None,
+ tag_locations='tags', authors=None, filemap=None, **opts):
+ """show incoming revisions from Subversion
+ """
+ svn_url = util.normalize_url(svn_url)
+
+ initializing_repo = False
+ user = opts.get('username', hgutil.getuser())
+ passwd = opts.get('password', '')
+ svn = svnwrap.SubversionRepo(svn_url, user, passwd)
+ author_host = "@%s" % svn.uuid
+ tag_locations = tag_locations.split(',')
+ hg_editor = hg_delta_editor.HgChangeReceiver(hg_repo_path,
+ ui_=ui,
+ subdir=svn.subdir,
+ author_host=author_host,
+ tag_locations=tag_locations,
+ authors=authors,
+ filemap=filemap)
+ if os.path.exists(hg_editor.uuid_file):
+ uuid = open(hg_editor.uuid_file).read()
+ assert uuid == svn.uuid
+ start = hg_editor.last_known_revision()
+ else:
+ open(hg_editor.uuid_file, 'w').write(svn.uuid)
+ open(hg_editor.svn_url_file, 'w').write(svn_url)
+ initializing_repo = True
+ start = skipto_rev
+
+ if initializing_repo and start > 0:
+ raise hgutil.Abort('Revision skipping at repository initialization '
+ 'remains unimplemented.')
+
+ rev_stuff = (('revision', 'revnum'),
+ ('user', 'author'),
+ ('date', 'date'),
+ ('message', 'message')
+ )
+
+ ui.status('incoming changes from %s\n' % svn_url)
+
+ for r in svn.revisions(start=start):
+ ui.status('\n')
+ for label, attr in rev_stuff:
+ l1 = label+':'
+ ui.status('%s%s\n' % (l1.ljust(13),
+ str(r.__getattribute__(attr)).strip(), ))
+
+
def push(ui, repo, hg_repo_path, svn_url, stupid=False, **opts):
"""push revisions starting at a specified head back to Subversion.
"""
@@ -378,6 +427,7 @@
'help': help,
'rebuildmeta': rebuildmeta,
'diff': diff,
+ 'incoming': incoming,
}
table.update(utility_commands.table)
changeset: 256:7932d098cb5f
user: Augie Fackler <durin42@gmail.com>
date: Fri Apr 10 18:47:18 2009 -0500
summary: Refactor commands to wrap their hg equivalent adding a --svn flag where sane.
diff --git a/__init__.py b/__init__.py
--- a/__init__.py
+++ b/__init__.py
@@ -17,6 +17,7 @@
import traceback
from mercurial import commands
+from mercurial import extensions
from mercurial import hg
from mercurial import util as hgutil
@@ -32,6 +33,45 @@
repo.__class__ = tag_repo.generate_repo_class(ui, repo)
+def uisetup(ui):
+ """Do our UI setup.
+
+ Does the following wrappings:
+ * parent -> utility_commands.parent
+ * outgoing -> utility_commands.outgoing
+ """
+ entry = extensions.wrapcommand(commands.table, 'parents',
+ utility_commands.parent)
+ entry[1].append(('', 'svn', None, "show parent svn revision instead"))
+ entry = extensions.wrapcommand(commands.table, 'outgoing',
+ utility_commands.outgoing)
+ entry[1].append(('', 'svn', None, "show revisions outgoing to subversion"))
+ entry = extensions.wrapcommand(commands.table, 'diff',
+ svncommands.diff)
+ entry[1].append(('', 'svn', None,
+ "show svn-style diffs, default against svn parent"))
+ entry = extensions.wrapcommand(commands.table, 'push',
+ svncommands.push)
+ entry[1].append(('', 'svn', None, "push to subversion"))
+ entry[1].append(('', 'svn-stupid', None, "use stupid replay during push to svn"))
+ entry = extensions.wrapcommand(commands.table, 'pull',
+ svncommands.pull)
+ entry[1].append(('', 'svn', None, "pull from subversion"))
+ entry[1].append(('', 'svn-stupid', None, "use stupid replay during pull from svn"))
+
+ entry = extensions.wrapcommand(commands.table, 'clone',
+ svncommands.clone)
+ entry[1].extend([#('', 'skipto-rev', '0', 'skip commits before this revision.'),
+ ('', 'svn-stupid', False, 'be stupid and use diffy replay.'),
+ ('', 'svn-tag-locations', 'tags', 'Relative path to Subversion tags.'),
+ ('', 'svn-authors', '', 'username mapping filename'),
+ ('', 'svn-filemap', '',
+ 'remap file to exclude paths or include only certain paths'),
+ ])
+# (svn_fetch,
+# ,
+# 'hg svnclone source [dest]'),
+
def svn(ui, repo, subcommand, *args, **opts):
'''see detailed help for list of subcommands'''
@@ -70,33 +110,7 @@
raise
-def svn_fetch(ui, svn_url, hg_repo_path=None, **opts):
- '''clone Subversion repository to a local Mercurial repository.
- If no destination directory name is specified, it defaults to the
- basename of the source plus "-hg".
-
- You can specify multiple paths for the location of tags using comma
- separated values.
- '''
- if not hg_repo_path:
- hg_repo_path = hg.defaultdest(svn_url) + "-hg"
- ui.status("Assuming destination %s\n" % hg_repo_path)
- should_update = not os.path.exists(hg_repo_path)
- svn_url = util.normalize_url(svn_url)
- try:
- res = svncommands.pull(ui, svn_url, hg_repo_path, **opts)
- except core.SubversionException, e:
- if e.apr_err == core.SVN_ERR_RA_SERF_SSL_CERT_UNTRUSTED:
- raise hgutil.Abort('It appears svn does not trust the ssl cert for this site.\n'
- 'Please try running svn ls on that url first.')
- raise
- if (res is None or res == 0) and should_update:
- repo = hg.repository(ui, hg_repo_path)
- commands.update(ui, repo, repo['tip'].node())
- return res
-
-commands.norepo += " svnclone"
cmdtable = {
"svn":
(svn,
@@ -111,16 +125,4 @@
],
svncommands._helpgen(),
),
- "svnclone":
- (svn_fetch,
- [('S', 'skipto-rev', '0', 'skip commits before this revision.'),
- ('', 'stupid', False, 'be stupid and use diffy replay.'),
- ('T', 'tag-locations', 'tags', 'Relative path to Subversion tags.'),
- ('A', 'authors', '', 'username mapping filename'),
- ('', 'filemap', '',
- 'remap file to exclude paths or include only certain paths'),
- ('', 'username', '', 'username for authentication'),
- ('', 'password', '', 'password for authentication'),
- ],
- 'hg svnclone source [dest]'),
}
diff --git a/cmdutil.py b/cmdutil.py
--- a/cmdutil.py
+++ b/cmdutil.py
@@ -1,5 +1,6 @@
#!/usr/bin/python
import re
+import os
from mercurial import util as hgutil
@@ -22,15 +23,21 @@
"""Exception raised when you try and commit without files.
"""
+def formatrev(rev):
+ if rev == -1:
+ return '\t(working copy)'
+ return '\t(revision %d)' % rev
-def filterdiff(diff, base_revision):
+
+def filterdiff(diff, oldrev, newrev):
diff = newfile_devnull_re.sub(r'--- \1\t(revision 0)' '\n'
r'+++ \1\t(working copy)',
diff)
- diff = a_re.sub(r'--- \1'+ ('\t(revision %d)' % base_revision), diff)
- diff = b_re.sub(r'+++ \1' + '\t(working copy)', diff)
- diff = devnull_re.sub(r'\1 /dev/null' '\t(working copy)', diff)
-
+ oldrev = formatrev(oldrev)
+ newrev = formatrev(newrev)
+ diff = a_re.sub(r'--- \1'+ oldrev, diff)
+ diff = b_re.sub(r'+++ \1' + newrev, diff)
+ diff = devnull_re.sub(r'\1 /dev/null\t(working copy)', diff)
diff = header_re.sub(r'Index: \1' + '\n' + ('=' * 67), diff)
return diff
@@ -270,3 +277,17 @@
raise hgutil.Abort('Base text was out of date, maybe rebase?')
else:
raise
+
+def filecheck(path):
+ for x in ('locks', 'hooks', 'format', 'db', ):
+ if not os.path.exists(os.path.join(path, x)):
+ return False
+ return True
+
+
+def issvnurl(url):
+ return url.startswith('svn+') or (
+ url.startswith('file://') and
+ reduce(lambda x,y: x and y,
+ map(lambda p: os.path.exists(os.path.join(url[7:], p)),
+ ('locks', 'hooks', 'format', 'db', ))))
diff --git a/svncommands.py b/svncommands.py
--- a/svncommands.py
+++ b/svncommands.py
@@ -1,6 +1,8 @@
import os
import cPickle as pickle
+from mercurial import cmdutil as hgcmdutil
+from mercurial import commands
from mercurial import hg
from mercurial import node
from mercurial import patch
@@ -17,14 +19,63 @@
import utility_commands
-def pull(ui, svn_url, hg_repo_path, skipto_rev=0, stupid=None,
- tag_locations='tags', authors=None, filemap=None, **opts):
+def clone(orig, ui, source, dest=None, **opts):
+ '''clone Subversion repository to a local Mercurial repository.
+
+ If no destination directory name is specified, it defaults to the
+ basename of the source plus "-hg".
+
+ You can specify multiple paths for the location of tags using comma
+ separated values.
+ '''
+ svnurl = ui.expandpath(source)
+ if not cmdutil.issvnurl(svnurl):
+ orig(ui, repo, source=source, dest=dest, *args, **opts)
+
+ if not dest:
+ dest = hg.defaultdest(source) + '-hg'
+ ui.status("Assuming destination %s\n" % dest)
+
+ if os.path.exists(dest):
+ raise hgutil.Abort("destination '%s' already exists" % dest)
+ url = util.normalize_url(svnurl)
+ res = -1
+ try:
+ try:
+ res = pull(None, ui, None, True, opts.pop('svn_stupid', False),
+ source=url, create_new_dest=dest, **opts)
+ except core.SubversionException, e:
+ if e.apr_err == core.SVN_ERR_RA_SERF_SSL_CERT_UNTRUSTED:
+ raise hgutil.Abort('It appears svn does not trust the ssl cert for this site.\n'
+ 'Please try running svn ls on that url first.')
+ raise
+ finally:
+ if os.path.exists(dest):
+ repo = hg.repository(ui, dest)
+ fp = repo.opener("hgrc", "w", text=True)
+ fp.write("[paths]\n")
+ # percent needs to be escaped for ConfigParser
+ fp.write("default = %(url)s\nsvn = %(url)s\n" % {'url': svnurl.replace('%', '%%')})
+ fp.close()
+ if res is None or res == 0:
+ commands.update(ui, repo, repo['tip'].node())
+
+ return res
+
+
+def pull(orig, ui, repo, svn=None, svn_stupid=False, source="default", create_new_dest=False,
+ *args, **opts):
"""pull new revisions from Subversion
"""
+ url = ui.expandpath(source)
+ if not (cmdutil.issvnurl(url) or svn or create_new_dest):
+ orig(ui, repo, source=source, *args, **opts)
+ svn_url = url
svn_url = util.normalize_url(svn_url)
old_encoding = util.swap_out_encoding()
- skipto_rev=int(skipto_rev)
- have_replay = not stupid
+ # TODO implement skipto support
+ skipto_rev = 0
+ have_replay = not svn_stupid
if have_replay and not callable(
delta.svn_txdelta_apply(None, None, None)[0]): #pragma: no cover
ui.status('You are using old Subversion SWIG bindings. Replay will not'
@@ -38,14 +89,21 @@
passwd = opts.get('password', '')
svn = svnwrap.SubversionRepo(svn_url, user, passwd)
author_host = "@%s" % svn.uuid
- tag_locations = tag_locations.split(',')
- hg_editor = hg_delta_editor.HgChangeReceiver(hg_repo_path,
- ui_=ui,
- subdir=svn.subdir,
- author_host=author_host,
- tag_locations=tag_locations,
- authors=authors,
- filemap=filemap)
+ # TODO these should be configurable again, but I'm torn on how.
+ # Maybe this should be configured in .hg/hgrc for each repo? Seems vaguely reasonable.
+ tag_locations = ['tags', ]
+ authors = None
+ filemap = None
+ if repo:
+ hg_editor = hg_delta_editor.HgChangeReceiver(repo=repo)
+ else:
+ hg_editor = hg_delta_editor.HgChangeReceiver(ui_=ui,
+ path=create_new_dest,
+ subdir=svn.subdir,
+ author_host=author_host,
+ tag_locations=tag_locations,
+ authors=authors,
+ filemap=filemap)
if os.path.exists(hg_editor.uuid_file):
uuid = open(hg_editor.uuid_file).read()
assert uuid == svn.uuid
@@ -146,12 +204,15 @@
str(r.__getattribute__(attr)).strip(), ))
-def push(ui, repo, hg_repo_path, svn_url, stupid=False, **opts):
+def push(orig, ui, repo, dest=None, **opts):
"""push revisions starting at a specified head back to Subversion.
"""
+ svnurl = ui.expandpath(dest or 'default-push', dest or 'default')
+ if not cmdutil.issvnurl(svnurl):
+ orig(ui, repo, dest=dest, *args, **opts)
old_encoding = util.swap_out_encoding()
- hge = hg_delta_editor.HgChangeReceiver(hg_repo_path,
- ui_=ui)
+ hge = hg_delta_editor.HgChangeReceiver(repo=repo)
+ assert svnurl == hge.url
svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
hge.revmap.iterkeys()))
user = opts.get('username', hgutil.getuser())
@@ -187,14 +248,14 @@
# 2. Commit oldest revision that needs to be pushed
base_revision = svn_commit_hashes[base_n][0]
try:
- cmdutil.commit_from_rev(ui, repo, old_ctx, hge, svn_url,
+ cmdutil.commit_from_rev(ui, repo, old_ctx, hge, svnurl,
base_revision, user, passwd)
except cmdutil.NoFilesException:
ui.warn("Could not push revision %s because it had no changes in svn.\n" %
old_ctx)
return 1
# 3. Fetch revisions from svn
- r = pull(ui, svn_url, hg_repo_path, stupid=stupid,
+ r = pull(None, ui, repo, True, stupid=opts.get('svn_stupid', False),
username=user, password=passwd)
assert not r or r == 0
# 4. Find the new head of the target branch
@@ -225,25 +286,32 @@
if children:
child = children[0]
rebasesrc = node.bin(child.extra().get('rebase_source', node.hex(node.nullid)))
- hge = hg_delta_editor.HgChangeReceiver(hg_repo_path, ui_=ui)
+ hge = hg_delta_editor.HgChangeReceiver(hge.path, ui_=ui)
svn_commit_hashes = dict(zip(hge.revmap.itervalues(), hge.revmap.iterkeys()))
util.swap_out_encoding(old_encoding)
return 0
-def diff(ui, repo, hg_repo_path, **opts):
+def diff(orig, ui, repo, *args, **opts):
"""show a diff of the most recent revision against its parent from svn
"""
- hge = hg_delta_editor.HgChangeReceiver(hg_repo_path,
- ui_=ui)
+ if not opts.get('svn', False) or opts.get('change', None):
+ return orig(ui, repo, *args, **opts)
+ svn_commit_hashes = {}
+ hge = hg_delta_editor.HgChangeReceiver(repo=repo)
svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
hge.revmap.iterkeys()))
- parent = repo.parents()[0]
- o_r = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes, parent.node())
- if o_r:
- parent = repo[o_r[-1]].parents()[0]
- base_rev, _junk = svn_commit_hashes[parent.node()]
- it = patch.diff(repo, parent.node(), None,
+ if not opts.get('rev', None):
+ parent = repo.parents()[0]
+ o_r = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes,
+ parent.node())
+ if o_r:
+ parent = repo[o_r[-1]].parents()[0]
+ opts['rev'] = ['%s:.' % node.hex(parent.node()), ]
+ node1, node2 = hgcmdutil.revpair(repo, opts['rev'])
+ baserev, _junk = svn_commit_hashes.get(node1, (-1, 'junk', ))
+ newrev, _junk = svn_commit_hashes.get(node2, (-1, 'junk', ))
+ it = patch.diff(repo, node1, node2,
opts=patch.diffopts(ui, opts={'git': True,
'show_function': False,
'ignore_all_space': False,
@@ -252,7 +320,7 @@
'unified': True,
'text': False,
}))
- ui.write(cmdutil.filterdiff(''.join(it), base_rev))
+ ui.write(cmdutil.filterdiff(''.join(it), baserev, newrev))
def rebuildmeta(ui, repo, hg_repo_path, args, **opts):
@@ -420,13 +488,9 @@
nourl = ['rebuildmeta'] + utility_commands.nourl
table = {
- 'pull': pull,
- 'push': push,
- 'dcommit': push,
'update': update,
'help': help,
'rebuildmeta': rebuildmeta,
- 'diff': diff,
'incoming': incoming,
}
diff --git a/util.py b/util.py
--- a/util.py
+++ b/util.py
@@ -15,8 +15,10 @@
return node.hex(ver)[:12]
-def normalize_url(svn_url):
- return svn_url.rstrip('/')
+def normalize_url(svnurl):
+ if svnurl.startswith('svn+http'):
+ svnurl = svnurl[4:]
+ return svnurl.rstrip('/')
def wipe_all_files(hg_wc_path):
diff --git a/utility_commands.py b/utility_commands.py
--- a/utility_commands.py
+++ b/utility_commands.py
@@ -102,20 +102,19 @@
})
-def parent(ui, repo, hg_repo_path, **opts):
+def parent(orig, ui, repo, *args, **opts):
"""show Mercurial & Subversion parents of the working dir or revision
"""
- hge = hg_delta_editor.HgChangeReceiver(hg_repo_path,
- ui_=ui)
+ if not opts.get('svn', False):
+ return orig(ui, repo, *args, **opts)
+ hge = hg_delta_editor.HgChangeReceiver(repo=repo)
svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
hge.revmap.iterkeys()))
ha = cmdutil.parentrev(ui, repo, hge, svn_commit_hashes)
- if ha.node() != node.nullid:
- r, br = svn_commit_hashes[ha.node()]
- ui.status('Working copy parent revision is %s: r%s on %s\n' %
- (ha, r, br or 'trunk'))
- else:
- ui.status('Working copy seems to have no parent svn revision.\n')
+ if ha.node() == node.nullid:
+ raise mutil.Abort('No parent svn revision!')
+ displayer = hgcmdutil.show_changeset(ui, repo, opts, buffered=False)
+ displayer.show(ha)
return 0
@@ -167,16 +166,20 @@
extrafn=extrafn)
-def outgoing(ui, repo, hg_repo_path, **opts):
+def outgoing(orig, ui, repo, dest=None, *args, **opts):
"""show changesets not found in the Subversion repository
"""
- hge = hg_delta_editor.HgChangeReceiver(hg_repo_path,
- ui_=ui)
+ svnurl = ui.expandpath(dest or 'default-push', dest or 'default')
+ if not (cmdutil.issvnurl(svnurl) or opts.get('svn', False)):
+ return orig(ui, repo, dest, *args, **opts)
+
+ hge = hg_delta_editor.HgChangeReceiver(repo=repo)
svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
hge.revmap.iterkeys()))
- o_r = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes, repo.parents()[0].node())
+ o_r = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes,
+ repo.parents()[0].node())
if not (o_r and len(o_r)):
- ui.status('No outgoing changes found.\n')
+ ui.status('no changes found\n')
return 0
displayer = hgcmdutil.show_changeset(ui, repo, opts, buffered=False)
for node in reversed(o_r):
@@ -214,8 +217,6 @@
'url': url,
'genignore': genignore,
'info': info,
- 'parent': parent,
- 'outgoing': outgoing,
'listauthors': listauthors,
'version': version,
'rebase': rebase,
changeset: 257:ffccf0080e54
user: Augie Fackler <durin42@gmail.com>
date: Fri Apr 10 22:38:29 2009 -0500
summary: Move wrappers for hg commands to their own module.
diff --git a/__init__.py b/__init__.py
--- a/__init__.py
+++ b/__init__.py
@@ -18,7 +18,6 @@
from mercurial import commands
from mercurial import extensions
-from mercurial import hg
from mercurial import util as hgutil
from svn import core
@@ -26,6 +25,7 @@
import svncommands
import tag_repo
import util
+import wrappers
def reposetup(ui, repo):
if not util.is_svn_repo(repo):
@@ -41,26 +41,26 @@
* outgoing -> utility_commands.outgoing
"""
entry = extensions.wrapcommand(commands.table, 'parents',
- utility_commands.parent)
+ wrappers.parent)
entry[1].append(('', 'svn', None, "show parent svn revision instead"))
entry = extensions.wrapcommand(commands.table, 'outgoing',
- utility_commands.outgoing)
+ wrappers.outgoing)
entry[1].append(('', 'svn', None, "show revisions outgoing to subversion"))
entry = extensions.wrapcommand(commands.table, 'diff',
- svncommands.diff)
+ wrappers.diff)
entry[1].append(('', 'svn', None,
"show svn-style diffs, default against svn parent"))
entry = extensions.wrapcommand(commands.table, 'push',
- svncommands.push)
+ wrappers.push)
entry[1].append(('', 'svn', None, "push to subversion"))
entry[1].append(('', 'svn-stupid', None, "use stupid replay during push to svn"))
entry = extensions.wrapcommand(commands.table, 'pull',
- svncommands.pull)
+ wrappers.pull)
entry[1].append(('', 'svn', None, "pull from subversion"))
entry[1].append(('', 'svn-stupid', None, "use stupid replay during pull from svn"))
entry = extensions.wrapcommand(commands.table, 'clone',
- svncommands.clone)
+ wrappers.clone)
entry[1].extend([#('', 'skipto-rev', '0', 'skip commits before this revision.'),
('', 'svn-stupid', False, 'be stupid and use diffy replay.'),
('', 'svn-tag-locations', 'tags', 'Relative path to Subversion tags.'),
@@ -68,9 +68,6 @@
('', 'svn-filemap', '',
'remap file to exclude paths or include only certain paths'),
])
-# (svn_fetch,
-# ,
-# 'hg svnclone source [dest]'),
def svn(ui, repo, subcommand, *args, **opts):
diff --git a/cmdutil.py b/cmdutil.py
--- a/cmdutil.py
+++ b/cmdutil.py
@@ -1,6 +1,7 @@
#!/usr/bin/python
import re
import os
+import urllib
from mercurial import util as hgutil
@@ -278,16 +279,19 @@
else:
raise
-def filecheck(path):
- for x in ('locks', 'hooks', 'format', 'db', ):
- if not os.path.exists(os.path.join(path, x)):
- return False
return True
+def islocalrepo(url):
+ if not url.startswith('file:///'):
+ return False
+ path = urllib.unquote(url[len('file://'):])
+ while '/' in path:
+ if reduce(lambda x,y: x and y,
+ map(lambda p: os.path.exists(os.path.join(path, p)),
+ ('hooks', 'format', 'db', ))):
+ return True
+ path = path.rsplit('/', 1)[0]
+ return False
def issvnurl(url):
- return url.startswith('svn+') or (
- url.startswith('file://') and
- reduce(lambda x,y: x and y,
- map(lambda p: os.path.exists(os.path.join(url[7:], p)),
- ('locks', 'hooks', 'format', 'db', ))))
+ return url.startswith('svn') or islocalrepo(url)
diff --git a/hg_delta_editor.py b/hg_delta_editor.py
--- a/hg_delta_editor.py
+++ b/hg_delta_editor.py
@@ -710,9 +710,9 @@
self.clear_current_info()
def authorforsvnauthor(self, author):
- if(author in self.authors):
+ if author in self.authors:
return self.authors[author]
- return '%s%s' %(author, self.author_host)
+ return '%s%s' % (author, self.author_host)
def svnauthorforauthor(self, author):
for svnauthor, hgauthor in self.authors.iteritems():
diff --git a/svncommands.py b/svncommands.py
--- a/svncommands.py
+++ b/svncommands.py
@@ -1,160 +1,17 @@
import os
import cPickle as pickle
-from mercurial import cmdutil as hgcmdutil
-from mercurial import commands
from mercurial import hg
from mercurial import node
-from mercurial import patch
from mercurial import util as hgutil
-from svn import core
-from svn import delta
import hg_delta_editor
import svnwrap
-import stupid as stupidmod
-import cmdutil
import util
import utility_commands
-def clone(orig, ui, source, dest=None, **opts):
- '''clone Subversion repository to a local Mercurial repository.
-
- If no destination directory name is specified, it defaults to the
- basename of the source plus "-hg".
-
- You can specify multiple paths for the location of tags using comma
- separated values.
- '''
- svnurl = ui.expandpath(source)
- if not cmdutil.issvnurl(svnurl):
- orig(ui, repo, source=source, dest=dest, *args, **opts)
-
- if not dest:
- dest = hg.defaultdest(source) + '-hg'
- ui.status("Assuming destination %s\n" % dest)
-
- if os.path.exists(dest):
- raise hgutil.Abort("destination '%s' already exists" % dest)
- url = util.normalize_url(svnurl)
- res = -1
- try:
- try:
- res = pull(None, ui, None, True, opts.pop('svn_stupid', False),
- source=url, create_new_dest=dest, **opts)
- except core.SubversionException, e:
- if e.apr_err == core.SVN_ERR_RA_SERF_SSL_CERT_UNTRUSTED:
- raise hgutil.Abort('It appears svn does not trust the ssl cert for this site.\n'
- 'Please try running svn ls on that url first.')
- raise
- finally:
- if os.path.exists(dest):
- repo = hg.repository(ui, dest)
- fp = repo.opener("hgrc", "w", text=True)
- fp.write("[paths]\n")
- # percent needs to be escaped for ConfigParser
- fp.write("default = %(url)s\nsvn = %(url)s\n" % {'url': svnurl.replace('%', '%%')})
- fp.close()
- if res is None or res == 0:
- commands.update(ui, repo, repo['tip'].node())
-
- return res
-
-
-def pull(orig, ui, repo, svn=None, svn_stupid=False, source="default", create_new_dest=False,
- *args, **opts):
- """pull new revisions from Subversion
- """
- url = ui.expandpath(source)
- if not (cmdutil.issvnurl(url) or svn or create_new_dest):
- orig(ui, repo, source=source, *args, **opts)
- svn_url = url
- svn_url = util.normalize_url(svn_url)
- old_encoding = util.swap_out_encoding()
- # TODO implement skipto support
- skipto_rev = 0
- have_replay = not svn_stupid
- if have_replay and not callable(
- delta.svn_txdelta_apply(None, None, None)[0]): #pragma: no cover
- ui.status('You are using old Subversion SWIG bindings. Replay will not'
- ' work until you upgrade to 1.5.0 or newer. Falling back to'
- ' a slower method that may be buggier. Please upgrade, or'
- ' contribute a patch to use the ctypes bindings instead'
- ' of SWIG.\n')
- have_replay = False
- initializing_repo = False
- user = opts.get('username', hgutil.getuser())
- passwd = opts.get('password', '')
- svn = svnwrap.SubversionRepo(svn_url, user, passwd)
- author_host = "@%s" % svn.uuid
- # TODO these should be configurable again, but I'm torn on how.
- # Maybe this should be configured in .hg/hgrc for each repo? Seems vaguely reasonable.
- tag_locations = ['tags', ]
- authors = None
- filemap = None
- if repo:
- hg_editor = hg_delta_editor.HgChangeReceiver(repo=repo)
- else:
- hg_editor = hg_delta_editor.HgChangeReceiver(ui_=ui,
- path=create_new_dest,
- subdir=svn.subdir,
- author_host=author_host,
- tag_locations=tag_locations,
- authors=authors,
- filemap=filemap)
- if os.path.exists(hg_editor.uuid_file):
- uuid = open(hg_editor.uuid_file).read()
- assert uuid == svn.uuid
- start = hg_editor.last_known_revision()
- else:
- open(hg_editor.uuid_file, 'w').write(svn.uuid)
- open(hg_editor.svn_url_file, 'w').write(svn_url)
- initializing_repo = True
- start = skipto_rev
-
- if initializing_repo and start > 0:
- raise hgutil.Abort('Revision skipping at repository initialization '
- 'remains unimplemented.')
-
- # start converting revisions
- for r in svn.revisions(start=start):
- valid = True
- hg_editor.update_branch_tag_map_for_rev(r)
- for p in r.paths:
- if hg_editor._is_path_valid(p):
- valid = True
- break
- if valid:
- # got a 502? Try more than once!
- tries = 0
- converted = False
- while not converted:
- try:
- util.describe_revision(ui, r)
- if have_replay:
- try:
- cmdutil.replay_convert_rev(hg_editor, svn, r)
- except svnwrap.SubversionRepoCanNotReplay, e: #pragma: no cover
- ui.status('%s\n' % e.message)
- stupidmod.print_your_svn_is_old_message(ui)
- have_replay = False
- stupidmod.svn_server_pull_rev(ui, svn, hg_editor, r)
- else:
- stupidmod.svn_server_pull_rev(ui, svn, hg_editor, r)
- converted = True
- except core.SubversionException, e: #pragma: no cover
- if (e.apr_err == core.SVN_ERR_RA_DAV_REQUEST_FAILED
- and '502' in str(e)
- and tries < 3):
- tries += 1
- ui.status('Got a 502, retrying (%s)\n' % tries)
- else:
- raise hgutil.Abort(*e.args)
- util.swap_out_encoding(old_encoding)
-
-
def incoming(ui, svn_url, hg_repo_path, skipto_rev=0, stupid=None,
tag_locations='tags', authors=None, filemap=None, **opts):
"""show incoming revisions from Subversion
@@ -204,125 +61,6 @@
str(r.__getattribute__(attr)).strip(), ))
-def push(orig, ui, repo, dest=None, **opts):
- """push revisions starting at a specified head back to Subversion.
- """
- svnurl = ui.expandpath(dest or 'default-push', dest or 'default')
- if not cmdutil.issvnurl(svnurl):
- orig(ui, repo, dest=dest, *args, **opts)
- old_encoding = util.swap_out_encoding()
- hge = hg_delta_editor.HgChangeReceiver(repo=repo)
- assert svnurl == hge.url
- svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
- hge.revmap.iterkeys()))
- user = opts.get('username', hgutil.getuser())
- passwd = opts.get('password', '')
-
- # Strategy:
- # 1. Find all outgoing commits from this head
- if len(repo.parents()) != 1:
- ui.status('Cowardly refusing to push branch merge')
- return 1
- workingrev = repo.parents()[0]
- outgoing = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes, workingrev.node())
- if not (outgoing and len(outgoing)):
- ui.status('No revisions to push.')
- return 0
- while outgoing:
- oldest = outgoing.pop(-1)
- old_ctx = repo[oldest]
- if len(old_ctx.parents()) != 1:
- ui.status('Found a branch merge, this needs discussion and '
- 'implementation.')
- return 1
- base_n = old_ctx.parents()[0].node()
- old_children = repo[base_n].children()
- svnbranch = repo[base_n].branch()
- oldtip = base_n
- samebranchchildren = [c for c in repo[oldtip].children() if c.branch() == svnbranch
- and c.node() in svn_commit_hashes]
- while samebranchchildren:
- oldtip = samebranchchildren[0].node()
- samebranchchildren = [c for c in repo[oldtip].children() if c.branch() == svnbranch
- and c.node() in svn_commit_hashes]
- # 2. Commit oldest revision that needs to be pushed
- base_revision = svn_commit_hashes[base_n][0]
- try:
- cmdutil.commit_from_rev(ui, repo, old_ctx, hge, svnurl,
- base_revision, user, passwd)
- except cmdutil.NoFilesException:
- ui.warn("Could not push revision %s because it had no changes in svn.\n" %
- old_ctx)
- return 1
- # 3. Fetch revisions from svn
- r = pull(None, ui, repo, True, stupid=opts.get('svn_stupid', False),
- username=user, password=passwd)
- assert not r or r == 0
- # 4. Find the new head of the target branch
- repo = hg.repository(ui, hge.path)
- oldtipctx = repo[oldtip]
- replacement = [c for c in oldtipctx.children() if c not in old_children
- and c.branch() == oldtipctx.branch()]
- assert len(replacement) == 1, 'Replacement node came back as: %r' % replacement
- replacement = replacement[0]
- # 5. Rebase all children of the currently-pushing rev to the new branch
- heads = repo.heads(old_ctx.node())
- for needs_transplant in heads:
- def extrafn(ctx, extra):
- if ctx.node() == oldest:
- return
- extra['branch'] = ctx.branch()
- utility_commands.rebase(ui, repo, extrafn=extrafn,
- sourcerev=needs_transplant, **opts)
- repo = hg.repository(ui, hge.path)
- for child in repo[replacement.node()].children():
- rebasesrc = node.bin(child.extra().get('rebase_source', node.hex(node.nullid)))
- if rebasesrc in outgoing:
- while rebasesrc in outgoing:
- rebsrcindex = outgoing.index(rebasesrc)
- outgoing = (outgoing[0:rebsrcindex] +
- [child.node(), ] + outgoing[rebsrcindex+1:])
- children = [c for c in child.children() if c.branch() == child.branch()]
- if children:
- child = children[0]
- rebasesrc = node.bin(child.extra().get('rebase_source', node.hex(node.nullid)))
- hge = hg_delta_editor.HgChangeReceiver(hge.path, ui_=ui)
- svn_commit_hashes = dict(zip(hge.revmap.itervalues(), hge.revmap.iterkeys()))
- util.swap_out_encoding(old_encoding)
- return 0
-
-
-def diff(orig, ui, repo, *args, **opts):
- """show a diff of the most recent revision against its parent from svn
- """
- if not opts.get('svn', False) or opts.get('change', None):
- return orig(ui, repo, *args, **opts)
- svn_commit_hashes = {}
- hge = hg_delta_editor.HgChangeReceiver(repo=repo)
- svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
- hge.revmap.iterkeys()))
- if not opts.get('rev', None):
- parent = repo.parents()[0]
- o_r = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes,
- parent.node())
- if o_r:
- parent = repo[o_r[-1]].parents()[0]
- opts['rev'] = ['%s:.' % node.hex(parent.node()), ]
- node1, node2 = hgcmdutil.revpair(repo, opts['rev'])
- baserev, _junk = svn_commit_hashes.get(node1, (-1, 'junk', ))
- newrev, _junk = svn_commit_hashes.get(node2, (-1, 'junk', ))
- it = patch.diff(repo, node1, node2,
- opts=patch.diffopts(ui, opts={'git': True,
- 'show_function': False,
- 'ignore_all_space': False,
- 'ignore_space_change': False,
- 'ignore_blank_lines': False,
- 'unified': True,
- 'text': False,
- }))
- ui.write(cmdutil.filterdiff(''.join(it), baserev, newrev))
-
-
def rebuildmeta(ui, repo, hg_repo_path, args, **opts):
"""rebuild hgsubversion metadata using values stored in revisions
"""
diff --git a/utility_commands.py b/utility_commands.py
--- a/utility_commands.py
+++ b/utility_commands.py
@@ -1,7 +1,5 @@
import os
-import mercurial
-from mercurial import cmdutil as hgcmdutil
from mercurial import node
from mercurial import util as hgutil
from hgext import rebase as hgrebase
@@ -102,22 +100,6 @@
})
-def parent(orig, ui, repo, *args, **opts):
- """show Mercurial & Subversion parents of the working dir or revision
- """
- if not opts.get('svn', False):
- return orig(ui, repo, *args, **opts)
- hge = hg_delta_editor.HgChangeReceiver(repo=repo)
- svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
- hge.revmap.iterkeys()))
- ha = cmdutil.parentrev(ui, repo, hge, svn_commit_hashes)
- if ha.node() == node.nullid:
- raise mutil.Abort('No parent svn revision!')
- displayer = hgcmdutil.show_changeset(ui, repo, opts, buffered=False)
- displayer.show(ha)
- return 0
-
-
def rebase(ui, repo, extrafn=None, sourcerev=None, **opts):
"""rebase current unpushed revisions onto the Subversion head
@@ -166,26 +148,6 @@
extrafn=extrafn)
-def outgoing(orig, ui, repo, dest=None, *args, **opts):
- """show changesets not found in the Subversion repository
- """
- svnurl = ui.expandpath(dest or 'default-push', dest or 'default')
- if not (cmdutil.issvnurl(svnurl) or opts.get('svn', False)):
- return orig(ui, repo, dest, *args, **opts)
-
- hge = hg_delta_editor.HgChangeReceiver(repo=repo)
- svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
- hge.revmap.iterkeys()))
- o_r = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes,
- repo.parents()[0].node())
- if not (o_r and len(o_r)):
- ui.status('no changes found\n')
- return 0
- displayer = hgcmdutil.show_changeset(ui, repo, opts, buffered=False)
- for node in reversed(o_r):
- displayer.show(repo[node])
-
-
def listauthors(ui, args, authors=None, **opts):
"""list all authors in a Subversion repository
"""
diff --git a/wrappers.py b/wrappers.py
new file mode 100644
--- /dev/null
+++ b/wrappers.py
@@ -0,0 +1,320 @@
+import os
+
+from mercurial import cmdutil as hgcmdutil
+from mercurial import commands
+from mercurial import patch
+from mercurial import hg
+from mercurial import util as hgutil
+from mercurial import node
+
+from svn import core
+from svn import delta
+
+import cmdutil
+import hg_delta_editor
+import stupid as stupidmod
+import svnwrap
+import util
+import utility_commands
+
+def parent(orig, ui, repo, *args, **opts):
+ """show Mercurial & Subversion parents of the working dir or revision
+ """
+ if not opts.get('svn', False):
+ return orig(ui, repo, *args, **opts)
+ hge = hg_delta_editor.HgChangeReceiver(repo=repo)
+ svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
+ hge.revmap.iterkeys()))
+ ha = cmdutil.parentrev(ui, repo, hge, svn_commit_hashes)
+ if ha.node() == node.nullid:
+ raise hgutil.Abort('No parent svn revision!')
+ displayer = hgcmdutil.show_changeset(ui, repo, opts, buffered=False)
+ displayer.show(ha)
+ return 0
+
+
+def outgoing(orig, ui, repo, dest=None, *args, **opts):
+ """show changesets not found in the Subversion repository
+ """
+ svnurl = repo.ui.expandpath(dest or 'default-push', dest or 'default')
+ if not (cmdutil.issvnurl(svnurl) or opts.get('svn', False)):
+ return orig(ui, repo, dest, *args, **opts)
+
+ hge = hg_delta_editor.HgChangeReceiver(repo=repo)
+ svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
+ hge.revmap.iterkeys()))
+ o_r = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes,
+ repo.parents()[0].node())
+ if not (o_r and len(o_r)):
+ ui.status('no changes found\n')
+ return 0
+ displayer = hgcmdutil.show_changeset(ui, repo, opts, buffered=False)
+ for node in reversed(o_r):
+ displayer.show(repo[node])
+
+
+
+
+def diff(orig, ui, repo, *args, **opts):
+ """show a diff of the most recent revision against its parent from svn
+ """
+ if not opts.get('svn', False) or opts.get('change', None):
+ return orig(ui, repo, *args, **opts)
+ svn_commit_hashes = {}
+ hge = hg_delta_editor.HgChangeReceiver(repo=repo)
+ svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
+ hge.revmap.iterkeys()))
+ if not opts.get('rev', None):
+ parent = repo.parents()[0]
+ o_r = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes,
+ parent.node())
+ if o_r:
+ parent = repo[o_r[-1]].parents()[0]
+ opts['rev'] = ['%s:.' % node.hex(parent.node()), ]
+ node1, node2 = hgcmdutil.revpair(repo, opts['rev'])
+ baserev, _junk = svn_commit_hashes.get(node1, (-1, 'junk', ))
+ newrev, _junk = svn_commit_hashes.get(node2, (-1, 'junk', ))
+ it = patch.diff(repo, node1, node2,
+ opts=patch.diffopts(ui, opts={'git': True,
+ 'show_function': False,
+ 'ignore_all_space': False,
+ 'ignore_space_change': False,
+ 'ignore_blank_lines': False,
+ 'unified': True,
+ 'text': False,
+ }))
+ ui.write(cmdutil.filterdiff(''.join(it), baserev, newrev))
+
+
+
+
+def push(orig, ui, repo, dest=None, *args, **opts):
+ """push revisions starting at a specified head back to Subversion.
+ """
+ svnurl = repo.ui.expandpath(dest or 'default-push', dest or 'default')
+ if not cmdutil.issvnurl(svnurl):
+ orig(ui, repo, dest=dest, *args, **opts)
+ old_encoding = util.swap_out_encoding()
+ hge = hg_delta_editor.HgChangeReceiver(repo=repo)
+ svnurl = util.normalize_url(svnurl)
+ if svnurl != hge.url:
+ raise hgutil.Abort('wrong subversion url!')
+ svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
+ hge.revmap.iterkeys()))
+ user = opts.get('username', hgutil.getuser())
+ passwd = opts.get('password', '')
+
+ # Strategy:
+ # 1. Find all outgoing commits from this head
+ if len(repo.parents()) != 1:
+ ui.status('Cowardly refusing to push branch merge')
+ return 1
+ workingrev = repo.parents()[0]
+ outgoing = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes, workingrev.node())
+ if not (outgoing and len(outgoing)):
+ ui.status('No revisions to push.')
+ return 0
+ while outgoing:
+ oldest = outgoing.pop(-1)
+ old_ctx = repo[oldest]
+ if len(old_ctx.parents()) != 1:
+ ui.status('Found a branch merge, this needs discussion and '
+ 'implementation.')
+ return 1
+ base_n = old_ctx.parents()[0].node()
+ old_children = repo[base_n].children()
+ svnbranch = repo[base_n].branch()
+ oldtip = base_n
+ samebranchchildren = [c for c in repo[oldtip].children() if c.branch() == svnbranch
+ and c.node() in svn_commit_hashes]
+ while samebranchchildren:
+ oldtip = samebranchchildren[0].node()
+ samebranchchildren = [c for c in repo[oldtip].children() if c.branch() == svnbranch
+ and c.node() in svn_commit_hashes]
+ # 2. Commit oldest revision that needs to be pushed
+ base_revision = svn_commit_hashes[base_n][0]
+ try:
+ cmdutil.commit_from_rev(ui, repo, old_ctx, hge, svnurl,
+ base_revision, user, passwd)
+ except cmdutil.NoFilesException:
+ ui.warn("Could not push revision %s because it had no changes in svn.\n" %
+ old_ctx)
+ return 1
+ # 3. Fetch revisions from svn
+ r = pull(None, ui, repo, True, stupid=opts.get('svn_stupid', False),
+ username=user, password=passwd)
+ assert not r or r == 0
+ # 4. Find the new head of the target branch
+ repo = hg.repository(ui, hge.path)
+ oldtipctx = repo[oldtip]
+ replacement = [c for c in oldtipctx.children() if c not in old_children
+ and c.branch() == oldtipctx.branch()]
+ assert len(replacement) == 1, 'Replacement node came back as: %r' % replacement
+ replacement = replacement[0]
+ # 5. Rebase all children of the currently-pushing rev to the new branch
+ heads = repo.heads(old_ctx.node())
+ for needs_transplant in heads:
+ def extrafn(ctx, extra):
+ if ctx.node() == oldest:
+ return
+ extra['branch'] = ctx.branch()
+ utility_commands.rebase(ui, repo, extrafn=extrafn,
+ sourcerev=needs_transplant, **opts)
+ repo = hg.repository(ui, hge.path)
+ for child in repo[replacement.node()].children():
+ rebasesrc = node.bin(child.extra().get('rebase_source', node.hex(node.nullid)))
+ if rebasesrc in outgoing:
+ while rebasesrc in outgoing:
+ rebsrcindex = outgoing.index(rebasesrc)
+ outgoing = (outgoing[0:rebsrcindex] +
+ [child.node(), ] + outgoing[rebsrcindex+1:])
+ children = [c for c in child.children() if c.branch() == child.branch()]
+ if children:
+ child = children[0]
+ rebasesrc = node.bin(child.extra().get('rebase_source', node.hex(node.nullid)))
+ hge = hg_delta_editor.HgChangeReceiver(hge.path, ui_=ui)
+ svn_commit_hashes = dict(zip(hge.revmap.itervalues(), hge.revmap.iterkeys()))
+ util.swap_out_encoding(old_encoding)
+ return 0
+
+
+
+def clone(orig, ui, source, dest=None, *args, **opts):
+ '''clone Subversion repository to a local Mercurial repository.
+
+ If no destination directory name is specified, it defaults to the
+ basename of the source plus "-hg".
+
+ You can specify multiple paths for the location of tags using comma
+ separated values.
+ '''
+ svnurl = ui.expandpath(source)
+ if not cmdutil.issvnurl(svnurl):
+ orig(ui, source=source, dest=dest, *args, **opts)
+
+ if not dest:
+ dest = hg.defaultdest(source) + '-hg'
+ ui.status("Assuming destination %s\n" % dest)
+
+ if os.path.exists(dest):
+ raise hgutil.Abort("destination '%s' already exists" % dest)
+ url = util.normalize_url(svnurl)
+ res = -1
+ try:
+ try:
+ res = pull(None, ui, None, True, opts.pop('svn_stupid', False),
+ source=url, create_new_dest=dest, **opts)
+ except core.SubversionException, e:
+ if e.apr_err == core.SVN_ERR_RA_SERF_SSL_CERT_UNTRUSTED:
+ raise hgutil.Abort('It appears svn does not trust the ssl cert for this site.\n'
+ 'Please try running svn ls on that url first.')
+ raise
+ finally:
+ if os.path.exists(dest):
+ repo = hg.repository(ui, dest)
+ fp = repo.opener("hgrc", "w", text=True)
+ fp.write("[paths]\n")
+ # percent needs to be escaped for ConfigParser
+ fp.write("default = %(url)s\nsvn = %(url)s\n" % {'url': svnurl.replace('%', '%%')})
+ fp.close()
+ if (res is None or res == 0) and not opts.get('noupdate', False):
+ commands.update(ui, repo, repo['tip'].node())
+
+ return res
+
+
+def pull(orig, ui, repo, svn=None, svn_stupid=False, source="default", create_new_dest=False,
+ *args, **opts):
+ """pull new revisions from Subversion
+ """
+ url = ((repo and repo.ui) or ui).expandpath(source)
+ if not (cmdutil.issvnurl(url) or svn or create_new_dest):
+ orig(ui, repo, source=source, *args, **opts)
+ svn_url = url
+ svn_url = util.normalize_url(svn_url)
+ old_encoding = util.swap_out_encoding()
+ # TODO implement skipto support
+ skipto_rev = 0
+ have_replay = not svn_stupid
+ if have_replay and not callable(
+ delta.svn_txdelta_apply(None, None, None)[0]): #pragma: no cover
+ ui.status('You are using old Subversion SWIG bindings. Replay will not'
+ ' work until you upgrade to 1.5.0 or newer. Falling back to'
+ ' a slower method that may be buggier. Please upgrade, or'
+ ' contribute a patch to use the ctypes bindings instead'
+ ' of SWIG.\n')
+ have_replay = False
+ initializing_repo = False
+ user = opts.get('username', hgutil.getuser())
+ passwd = opts.get('password', '')
+ svn = svnwrap.SubversionRepo(svn_url, user, passwd)
+ author_host = "@%s" % svn.uuid
+ # TODO these should be configurable again, but I'm torn on how.
+ # Maybe this should be configured in .hg/hgrc for each repo? Seems vaguely reasonable.
+ tag_locations = ['tags', ]
+ authors = opts.pop('svn_authors', None)
+ filemap = opts.pop('svn_filemap', None)
+ if repo:
+ hg_editor = hg_delta_editor.HgChangeReceiver(repo=repo,
+ subdir=svn.subdir,
+ author_host=author_host,
+ tag_locations=tag_locations,
+ authors=authors,
+ filemap=filemap)
+ else:
+ hg_editor = hg_delta_editor.HgChangeReceiver(ui_=ui,
+ path=create_new_dest,
+ subdir=svn.subdir,
+ author_host=author_host,
+ tag_locations=tag_locations,
+ authors=authors,
+ filemap=filemap)
+ if os.path.exists(hg_editor.uuid_file):
+ uuid = open(hg_editor.uuid_file).read()
+ assert uuid == svn.uuid
+ start = hg_editor.last_known_revision()
+ else:
+ open(hg_editor.uuid_file, 'w').write(svn.uuid)
+ open(hg_editor.svn_url_file, 'w').write(svn_url)
+ initializing_repo = True
+ start = skipto_rev
+
+ if initializing_repo and start > 0:
+ raise hgutil.Abort('Revision skipping at repository initialization '
+ 'remains unimplemented.')
+
+ # start converting revisions
+ for r in svn.revisions(start=start):
+ valid = True
+ hg_editor.update_branch_tag_map_for_rev(r)
+ for p in r.paths:
+ if hg_editor._is_path_valid(p):
+ valid = True
+ break
+ if valid:
+ # got a 502? Try more than once!
+ tries = 0
+ converted = False
+ while not converted:
+ try:
+ util.describe_revision(ui, r)
+ if have_replay:
+ try:
+ cmdutil.replay_convert_rev(hg_editor, svn, r)
+ except svnwrap.SubversionRepoCanNotReplay, e: #pragma: no cover
+ ui.status('%s\n' % e.message)
+ stupidmod.print_your_svn_is_old_message(ui)
+ have_replay = False
+ stupidmod.svn_server_pull_rev(ui, svn, hg_editor, r)
+ else:
+ stupidmod.svn_server_pull_rev(ui, svn, hg_editor, r)
+ converted = True
+ except core.SubversionException, e: #pragma: no cover
+ if (e.apr_err == core.SVN_ERR_RA_DAV_REQUEST_FAILED
+ and '502' in str(e)
+ and tries < 3):
+ tries += 1
+ ui.status('Got a 502, retrying (%s)\n' % tries)
+ else:
+ raise hgutil.Abort(*e.args)
+ util.swap_out_encoding(old_encoding)
changeset: 258:4ab27ddbae51
user: Augie Fackler <durin42@gmail.com>
date: Fri Apr 10 22:41:33 2009 -0500
summary: Updated readme a little bit for the new UI.
diff --git a/README b/README
--- a/README
+++ b/README
@@ -42,18 +42,20 @@
-----------
Get a new clone of an svn server::
- $ hg svnclone <svn URI> [destination]
+ $ hg clone <svn URI> [destination]
Real example::
- $ hg svnclone http://python-nose.googlecode.com/svn nose-hg
+ $ hg clone svn+http://python-nose.googlecode.com/svn nose-hg
Note, you should pull from the root subversion directory, not specific
-folders (such as trunk).
+folders (such as trunk). Also, you only need to modify http:// urls as shown.
+This is a side effect of Mercurial and Subversion both claiming the http
+protocol, so svn+http is used to work around that.
Pull new revisions into an already-converted repo::
- $ hg svn pull
+ $ hg pull
For more information, see ``hg help svn`` while in a converted repo.
changeset: 259:52cc0982dfcd
user: Augie Fackler <durin42@gmail.com>
date: Fri Apr 10 22:42:50 2009 -0500
summary: Fix the verify script which broke because of the change in parent.
diff --git a/tools/common.sh b/tools/common.sh
--- a/tools/common.sh
+++ b/tools/common.sh
@@ -1,7 +1,7 @@
function verify_current_revision()
{
/bin/rm -rf *
- exportcmd="svn export `hg svn info 2> /dev/null | grep '^URL: ' | sed 's/URL: //'` -`hg svn parent | sed 's/.*: //;s/ .*//'` . --force"
+ exportcmd="svn export `hg svn info 2> /dev/null | grep '^URL: ' | sed 's/URL: //'` -r`hg svn info | grep ^Revision | sed 's/.*: //;s/ .*//'` . --force"
`echo $exportcmd` > /dev/null
x=$?
if [[ "$x" != "0" ]] ; then
changeset: 260:87dc4d0dd048
user: Augie Fackler <durin42@gmail.com>
date: Fri Apr 10 22:45:50 2009 -0500
summary: Forgot some return statements.
diff --git a/wrappers.py b/wrappers.py
--- a/wrappers.py
+++ b/wrappers.py
@@ -93,7 +93,7 @@
"""
svnurl = repo.ui.expandpath(dest or 'default-push', dest or 'default')
if not cmdutil.issvnurl(svnurl):
- orig(ui, repo, dest=dest, *args, **opts)
+ return orig(ui, repo, dest=dest, *args, **opts)
old_encoding = util.swap_out_encoding()
hge = hg_delta_editor.HgChangeReceiver(repo=repo)
svnurl = util.normalize_url(svnurl)
@@ -190,7 +190,7 @@
'''
svnurl = ui.expandpath(source)
if not cmdutil.issvnurl(svnurl):
- orig(ui, source=source, dest=dest, *args, **opts)
+ return orig(ui, source=source, dest=dest, *args, **opts)
if not dest:
dest = hg.defaultdest(source) + '-hg'
@@ -229,7 +229,7 @@
"""
url = ((repo and repo.ui) or ui).expandpath(source)
if not (cmdutil.issvnurl(url) or svn or create_new_dest):
- orig(ui, repo, source=source, *args, **opts)
+ return orig(ui, repo, source=source, *args, **opts)
svn_url = url
svn_url = util.normalize_url(svn_url)
old_encoding = util.swap_out_encoding()
changeset: 261:141513b5173b
user: Augie Fackler <durin42@gmail.com>
date: Sun Apr 12 12:18:04 2009 -0500
summary: Stop breaking hg pull <path>.
diff --git a/wrappers.py b/wrappers.py
--- a/wrappers.py
+++ b/wrappers.py
@@ -202,8 +202,9 @@
res = -1
try:
try:
- res = pull(None, ui, None, True, opts.pop('svn_stupid', False),
- source=url, create_new_dest=dest, **opts)
+ res = pull(None, ui, None, source=url, svn=None,
+ svn_stupid=opts.pop('svn_stupid', False),
+ create_new_dest=dest, **opts)
except core.SubversionException, e:
if e.apr_err == core.SVN_ERR_RA_SERF_SSL_CERT_UNTRUSTED:
raise hgutil.Abort('It appears svn does not trust the ssl cert for this site.\n'
@@ -223,10 +224,14 @@
return res
-def pull(orig, ui, repo, svn=None, svn_stupid=False, source="default", create_new_dest=False,
- *args, **opts):
+def pull(orig, ui, repo, source="default", *args, **opts):
"""pull new revisions from Subversion
+
+ Also takes svn, svn_stupid, and create_new_dest kwargs.
"""
+ svn = opts.pop('svn', None)
+ svn_stupid = opts.pop('svn_stupid', False)
+ create_new_dest = opts.pop('create_new_dest', False)
url = ((repo and repo.ui) or ui).expandpath(source)
if not (cmdutil.issvnurl(url) or svn or create_new_dest):
return orig(ui, repo, source=source, *args, **opts)
changeset: 262:3b3627611468
user: Augie Fackler <durin42@gmail.com>
date: Sun Apr 12 12:25:20 2009 -0500
summary: Fix pushing after 141513b.
diff --git a/wrappers.py b/wrappers.py
--- a/wrappers.py
+++ b/wrappers.py
@@ -141,7 +141,8 @@
old_ctx)
return 1
# 3. Fetch revisions from svn
- r = pull(None, ui, repo, True, stupid=opts.get('svn_stupid', False),
+ # TODO this probably should pass in the source explicitly
+ r = pull(None, ui, repo, svn=True, stupid=opts.get('svn_stupid', False),
username=user, password=passwd)
assert not r or r == 0
# 4. Find the new head of the target branch
changeset: 263:d978192f0d63
user: Augie Fackler <durin42@gmail.com>
date: Mon Apr 13 21:19:13 2009 -0500
summary: Clean up TODOs that are no longer relevant.
diff --git a/hg_delta_editor.py b/hg_delta_editor.py
--- a/hg_delta_editor.py
+++ b/hg_delta_editor.py
@@ -216,8 +216,6 @@
while paths_need_discovery:
p = paths_need_discovery.pop(0)
path_could_be_file = True
- # TODO(augie) Figure out if you can use break here in a for loop, quick
- # testing of that failed earlier.
ind = 0
while ind < len(paths_need_discovery) and not paths_need_discovery:
if op.startswith(p):
@@ -633,7 +631,6 @@
revlog.nullid)
if parents[0] in closed_revs and branch in self.branches_to_delete:
continue
- # TODO this needs to be fixed with the new revmap
extra = util.build_extra(rev.revnum, branch,
open(self.uuid_file).read(),
self.subdir)
diff --git a/stupid.py b/stupid.py
--- a/stupid.py
+++ b/stupid.py
@@ -513,9 +513,10 @@
for f in excluded:
files_touched.remove(f)
if parentctx.node() != node.nullid or files_touched:
- # TODO(augie) remove this debug code? Or maybe it's sane to have it.
for f in files_touched:
if f:
+ # this is a case that really shouldn't ever happen, it means something
+ # is very wrong
assert f[0] != '/'
current_ctx = context.memctx(hg_editor.repo,
[parentctx.node(), revlog.nullid],
diff --git a/svnwrap/svn_swig_wrapper.py b/svnwrap/svn_swig_wrapper.py
--- a/svnwrap/svn_swig_wrapper.py
+++ b/svnwrap/svn_swig_wrapper.py
@@ -112,7 +112,6 @@
"""
def __init__(self, revnum, author, message, date, paths, strip_path=''):
self.revnum, self.author, self.message = revnum, author, message
- # TODO parse this into a datetime
self.date = date
self.paths = {}
if paths:
diff --git a/util.py b/util.py
--- a/util.py
+++ b/util.py
@@ -80,7 +80,6 @@
return outgoing_rev_hashes
def build_extra(revnum, branch, uuid, subdir):
- # TODO this needs to be fixed with the new revmap
extra = {}
branchpath = 'trunk'
if branch:
diff --git a/utility_commands.py b/utility_commands.py
--- a/utility_commands.py
+++ b/utility_commands.py
@@ -142,7 +142,6 @@
if parent_rev == target_rev:
ui.status('Already up to date!\n')
return 0
- # TODO this is really hacky, there must be a more direct way
return hgrebase.rebase(ui, repo, dest=node.hex(target_rev.node()),
base=node.hex(sourcerev),
extrafn=extrafn)
diff --git a/wrappers.py b/wrappers.py
--- a/wrappers.py
+++ b/wrappers.py
@@ -255,8 +255,6 @@
passwd = opts.get('password', '')
svn = svnwrap.SubversionRepo(svn_url, user, passwd)
author_host = "@%s" % svn.uuid
- # TODO these should be configurable again, but I'm torn on how.
- # Maybe this should be configured in .hg/hgrc for each repo? Seems vaguely reasonable.
tag_locations = ['tags', ]
authors = opts.pop('svn_authors', None)
filemap = opts.pop('svn_filemap', None)
changeset: 264:112d57bb736e
user: Augie Fackler <durin42@gmail.com>
date: Mon Apr 13 21:51:12 2009 -0500
summary: rebase: moved to wrappers, now a wrapper around rebase triggered with --svn.
diff --git a/__init__.py b/__init__.py
--- a/__init__.py
+++ b/__init__.py
@@ -69,6 +69,14 @@
'remap file to exclude paths or include only certain paths'),
])
+ try:
+ rebase = extensions.find('rebase')
+ if rebase:
+ entry = extensions.wrapcommand(rebase.cmdtable, 'rebase', wrappers.rebase)
+ entry[1].append(('', 'svn', None, 'automatic svn rebase', ))
+ except:
+ pass
+
def svn(ui, repo, subcommand, *args, **opts):
'''see detailed help for list of subcommands'''
diff --git a/utility_commands.py b/utility_commands.py
--- a/utility_commands.py
+++ b/utility_commands.py
@@ -1,8 +1,6 @@
import os
-from mercurial import node
from mercurial import util as hgutil
-from hgext import rebase as hgrebase
import svnwrap
import cmdutil
@@ -100,53 +98,6 @@
})
-def rebase(ui, repo, extrafn=None, sourcerev=None, **opts):
- """rebase current unpushed revisions onto the Subversion head
-
- This moves a line of development from making its own head to the top of
- Subversion development, linearizing the changes. In order to make sure you
- rebase on top of the current top of Subversion work, you should probably run
- 'hg svn pull' before running this.
- """
- if extrafn is None:
- def extrafn2(ctx, extra):
- """defined here so we can add things easily.
- """
- extra['branch'] = ctx.branch()
- extrafn = extrafn2
- if sourcerev is None:
- sourcerev = repo.parents()[0].node()
- hge = hg_delta_editor.HgChangeReceiver(repo=repo)
- svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
- hge.revmap.iterkeys()))
- o_r = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes, sourcerev=sourcerev)
- if not o_r:
- ui.status('Nothing to rebase!\n')
- return 0
- if len(repo[sourcerev].children()):
- ui.status('Refusing to rebase non-head commit like a coward\n')
- return 0
- parent_rev = repo[o_r[-1]].parents()[0]
- target_rev = parent_rev
- p_n = parent_rev.node()
- exhausted_choices = False
- while target_rev.children() and not exhausted_choices:
- for c in target_rev.children():
- exhausted_choices = True
- n = c.node()
- if (n in svn_commit_hashes and
- svn_commit_hashes[n][1] == svn_commit_hashes[p_n][1]):
- target_rev = c
- exhausted_choices = False
- break
- if parent_rev == target_rev:
- ui.status('Already up to date!\n')
- return 0
- return hgrebase.rebase(ui, repo, dest=node.hex(target_rev.node()),
- base=node.hex(sourcerev),
- extrafn=extrafn)
-
-
def listauthors(ui, args, authors=None, **opts):
"""list all authors in a Subversion repository
"""
@@ -180,5 +131,4 @@
'info': info,
'listauthors': listauthors,
'version': version,
- 'rebase': rebase,
}
diff --git a/wrappers.py b/wrappers.py
--- a/wrappers.py
+++ b/wrappers.py
@@ -1,5 +1,7 @@
import os
+from hgext import rebase as hgrebase
+
from mercurial import cmdutil as hgcmdutil
from mercurial import commands
from mercurial import patch
@@ -15,7 +17,6 @@
import stupid as stupidmod
import svnwrap
import util
-import utility_commands
def parent(orig, ui, repo, *args, **opts):
"""show Mercurial & Subversion parents of the working dir or revision
@@ -53,8 +54,6 @@
displayer.show(repo[node])
-
-
def diff(orig, ui, repo, *args, **opts):
"""show a diff of the most recent revision against its parent from svn
"""
@@ -86,8 +85,6 @@
ui.write(cmdutil.filterdiff(''.join(it), baserev, newrev))
-
-
def push(orig, ui, repo, dest=None, *args, **opts):
"""push revisions starting at a specified head back to Subversion.
"""
@@ -115,6 +112,7 @@
ui.status('No revisions to push.')
return 0
while outgoing:
+ print [node.hex(x) for x in outgoing]
oldest = outgoing.pop(-1)
old_ctx = repo[oldest]
if len(old_ctx.parents()) != 1:
@@ -159,12 +157,14 @@
if ctx.node() == oldest:
return
extra['branch'] = ctx.branch()
- utility_commands.rebase(ui, repo, extrafn=extrafn,
- sourcerev=needs_transplant, **opts)
+ rebase(hgrebase.rebase, ui, repo, svn=True, svnextrafn=extrafn,
+ svnsourcerev=needs_transplant, **opts)
repo = hg.repository(ui, hge.path)
for child in repo[replacement.node()].children():
rebasesrc = node.bin(child.extra().get('rebase_source', node.hex(node.nullid)))
+ print node.hex(rebasesrc)
if rebasesrc in outgoing:
+ print 'swap outgoin'
while rebasesrc in outgoing:
rebsrcindex = outgoing.index(rebasesrc)
outgoing = (outgoing[0:rebsrcindex] +
@@ -179,7 +179,6 @@
return 0
-
def clone(orig, ui, source, dest=None, *args, **opts):
'''clone Subversion repository to a local Mercurial repository.
@@ -322,3 +321,52 @@
else:
raise hgutil.Abort(*e.args)
util.swap_out_encoding(old_encoding)
+
+
+def rebase(orig, ui, repo, **opts):
+ """rebase current unpushed revisions onto the Subversion head
+
+ This moves a line of development from making its own head to the top of
+ Subversion development, linearizing the changes. In order to make sure you
+ rebase on top of the current top of Subversion work, you should probably run
+ 'hg svn pull' before running this.
+
+ Also looks for svnextrafn and svnsourcerev in **opts.
+ """
+ if not opts.get('svn', False):
+ return orig(ui, repo, **opts)
+ def extrafn2(ctx, extra):
+ """defined here so we can add things easily.
+ """
+ extra['branch'] = ctx.branch()
+ extrafn = opts.get('svnextrafn', extrafn2)
+ sourcerev = opts.get('svnsourcerev', repo.parents()[0].node())
+ hge = hg_delta_editor.HgChangeReceiver(repo=repo)
+ svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
+ hge.revmap.iterkeys()))
+ o_r = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes, sourcerev=sourcerev)
+ if not o_r:
+ ui.status('Nothing to rebase!\n')
+ return 0
+ if len(repo[sourcerev].children()):
+ ui.status('Refusing to rebase non-head commit like a coward\n')
+ return 0
+ parent_rev = repo[o_r[-1]].parents()[0]
+ target_rev = parent_rev
+ p_n = parent_rev.node()
+ exhausted_choices = False
+ while target_rev.children() and not exhausted_choices:
+ for c in target_rev.children():
+ exhausted_choices = True
+ n = c.node()
+ if (n in svn_commit_hashes and
+ svn_commit_hashes[n][1] == svn_commit_hashes[p_n][1]):
+ target_rev = c
+ exhausted_choices = False
+ break
+ if parent_rev == target_rev:
+ ui.status('Already up to date!\n')
+ return 0
+ return orig(ui, repo, dest=node.hex(target_rev.node()),
+ base=node.hex(sourcerev),
+ extrafn=extrafn)
changeset: 265:9f0738587f94
user: Augie Fackler <durin42@gmail.com>
date: Mon Apr 13 21:12:33 2009 -0500
summary: Re-re-refix username support, add a comment so maybe I remember this time.
diff --git a/svncommands.py b/svncommands.py
--- a/svncommands.py
+++ b/svncommands.py
@@ -19,8 +19,7 @@
svn_url = util.normalize_url(svn_url)
initializing_repo = False
- user = opts.get('username', hgutil.getuser())
- passwd = opts.get('password', '')
+ user, passwd = util.getuserpass(opts)
svn = svnwrap.SubversionRepo(svn_url, user, passwd)
author_host = "@%s" % svn.uuid
tag_locations = tag_locations.split(',')
@@ -68,8 +67,7 @@
raise hgutil.Abort('You must pass the svn URI used to create this repo.')
uuid = None
url = args[0].rstrip('/')
- user = opts.get('username', hgutil.getuser())
- passwd = opts.get('password', '')
+ user, passwd = util.getuserpass(opts)
svn = svnwrap.SubversionRepo(url, user, passwd)
subdir = svn.subdir
svnmetadir = os.path.join(repo.path, 'svn')
diff --git a/util.py b/util.py
--- a/util.py
+++ b/util.py
@@ -6,6 +6,14 @@
from mercurial import util as hgutil
+def getuserpass(opts):
+ # DO NOT default the user to hg's getuser(). If you provide
+ # *any* default username to Subversion, it won't use any remembered
+ # username for the desired realm, breaking OS X Keychain support,
+ # GNOME keyring support, and all similar tools.
+ return opts.get('username', None), opts.get('password', '')
+
+
def version(ui):
"""Guess the version of hgsubversion.
"""
diff --git a/utility_commands.py b/utility_commands.py
--- a/utility_commands.py
+++ b/utility_commands.py
@@ -36,8 +36,7 @@
url = hge.url
if url[-1] == '/':
url = url[:-1]
- user = opts.get('username', hgutil.getuser())
- passwd = opts.get('passwd', '')
+ user, passwd = util.getuserpass(opts)
svn = svnwrap.SubversionRepo(url, user, passwd)
dirs = [''] + [d[0] for d in svn.list_files(branchpath, r) if d[1] == 'd']
for dir in dirs:
diff --git a/wrappers.py b/wrappers.py
--- a/wrappers.py
+++ b/wrappers.py
@@ -98,9 +98,7 @@
raise hgutil.Abort('wrong subversion url!')
svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
hge.revmap.iterkeys()))
- user = opts.get('username', hgutil.getuser())
- passwd = opts.get('password', '')
-
+ user, passwd = util.getuserpass(opts)
# Strategy:
# 1. Find all outgoing commits from this head
if len(repo.parents()) != 1:
@@ -250,8 +248,7 @@
' of SWIG.\n')
have_replay = False
initializing_repo = False
- user = opts.get('username', hgutil.getuser())
- passwd = opts.get('password', '')
+ user, passwd = util.getuserpass(opts)
svn = svnwrap.SubversionRepo(svn_url, user, passwd)
author_host = "@%s" % svn.uuid
tag_locations = ['tags', ]
changeset: 266:a5f20358f737
user: Luke Opperman <luke@loppear.com>
date: Tue Apr 14 09:32:09 2009 -0500
summary: Remove stray prints
diff --git a/svnwrap/svn_swig_wrapper.py b/svnwrap/svn_swig_wrapper.py
--- a/svnwrap/svn_swig_wrapper.py
+++ b/svnwrap/svn_swig_wrapper.py
@@ -351,14 +351,10 @@
if action == 'modify':
baton = editor.open_file(path, parent, base_revision, pool)
elif action == 'add':
- try:
- frompath, fromrev = copies.get(path, (None, -1))
- if frompath:
- frompath = self.svn_url + '/' + frompath
- baton = editor.add_file(path, parent, frompath, fromrev, pool)
- except (core.SubversionException, TypeError), e: #pragma: no cover
- print e.message
- raise
+ frompath, fromrev = copies.get(path, (None, -1))
+ if frompath:
+ frompath = self.svn_url + '/' + frompath
+ baton = editor.add_file(path, parent, frompath, fromrev, pool)
elif action == 'delete':
baton = editor.delete_entry(path, base_revision, parent, pool)
compute_delta = False
diff --git a/wrappers.py b/wrappers.py
--- a/wrappers.py
+++ b/wrappers.py
@@ -110,7 +110,6 @@
ui.status('No revisions to push.')
return 0
while outgoing:
- print [node.hex(x) for x in outgoing]
oldest = outgoing.pop(-1)
old_ctx = repo[oldest]
if len(old_ctx.parents()) != 1:
changeset: 267:f000b2392fc2
user: Luke Opperman <luke@loppear.com>
date: Tue Apr 14 09:32:40 2009 -0500
summary: Push status messages, remove svn flag from opts before passing on
diff --git a/wrappers.py b/wrappers.py
--- a/wrappers.py
+++ b/wrappers.py
@@ -88,6 +88,7 @@
def push(orig, ui, repo, dest=None, *args, **opts):
"""push revisions starting at a specified head back to Subversion.
"""
+ opts.pop('svn', None) # unused in this case
svnurl = repo.ui.expandpath(dest or 'default-push', dest or 'default')
if not cmdutil.issvnurl(svnurl):
return orig(ui, repo, dest=dest, *args, **opts)
@@ -105,9 +106,10 @@
ui.status('Cowardly refusing to push branch merge')
return 1
workingrev = repo.parents()[0]
+ ui.status('searching for changes\n')
outgoing = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes, workingrev.node())
if not (outgoing and len(outgoing)):
- ui.status('No revisions to push.')
+ ui.status('no changes found\n')
return 0
while outgoing:
oldest = outgoing.pop(-1)
changeset: 269:14914dbd8a4a
user: Augie Fackler <durin42@gmail.com>
date: Tue Apr 21 15:19:50 2009 -0500
summary: More stray prints.
diff --git a/wrappers.py b/wrappers.py
--- a/wrappers.py
+++ b/wrappers.py
@@ -161,9 +161,7 @@
repo = hg.repository(ui, hge.path)
for child in repo[replacement.node()].children():
rebasesrc = node.bin(child.extra().get('rebase_source', node.hex(node.nullid)))
- print node.hex(rebasesrc)
if rebasesrc in outgoing:
- print 'swap outgoin'
while rebasesrc in outgoing:
rebsrcindex = outgoing.index(rebasesrc)
outgoing = (outgoing[0:rebsrcindex] +
changeset: 271:5278817fe8a1
user: Daniel Tang <dytang@cs.purdue.edu>
date: Tue Apr 21 18:31:17 2009 -0400
summary: Add newlines to ui.status calls where needed
diff --git a/wrappers.py b/wrappers.py
--- a/wrappers.py
+++ b/wrappers.py
@@ -103,7 +103,7 @@
# Strategy:
# 1. Find all outgoing commits from this head
if len(repo.parents()) != 1:
- ui.status('Cowardly refusing to push branch merge')
+ ui.status('Cowardly refusing to push branch merge\n')
return 1
workingrev = repo.parents()[0]
ui.status('searching for changes\n')
@@ -116,7 +116,7 @@
old_ctx = repo[oldest]
if len(old_ctx.parents()) != 1:
ui.status('Found a branch merge, this needs discussion and '
- 'implementation.')
+ 'implementation.\n')
return 1
base_n = old_ctx.parents()[0].node()
old_children = repo[base_n].children()
changeset: 272:25d843281127
user: Patrick Mezard <pmezard@gmail.com>
date: Wed Apr 22 23:24:58 2009 +0200
summary: Use svn 1.6 platform specific auth providers if available
diff --git a/svnwrap/svn_swig_wrapper.py b/svnwrap/svn_swig_wrapper.py
--- a/svnwrap/svn_swig_wrapper.py
+++ b/svnwrap/svn_swig_wrapper.py
@@ -62,7 +62,7 @@
# Give the client context baton a suite of authentication
# providers.h
platform_specific = ['svn_auth_get_gnome_keyring_simple_provider',
- 'svn_auth_get_gnome_keyring_ssl_client_cert_pw_provider',
+ 'svn_auth_get_gnome_keyring_ssl_client_cert_pw_provider',
'svn_auth_get_keychain_simple_provider',
'svn_auth_get_keychain_ssl_client_cert_pw_provider',
'svn_auth_get_kwallet_simple_provider',
@@ -73,13 +73,23 @@
]
providers = []
-
- for p in platform_specific:
- if hasattr(core, p):
- try:
- providers.append(getattr(core, p)())
- except RuntimeError:
- pass
+ # Platform-dependant authentication methods
+ getprovider = getattr(core, 'svn_auth_get_platform_specific_provider',
+ None)
+ if getprovider:
+ # Available in svn >= 1.6
+ for name in ('gnome_keyring', 'keychain', 'kwallet', 'windows'):
+ for type in ('simple', 'ssl_client_cert_pw', 'ssl_server_trust'):
+ p = getprovider(name, type, pool)
+ if p:
+ providers.append(p)
+ else:
+ for p in platform_specific:
+ if hasattr(core, p):
+ try:
+ providers.append(getattr(core, p)())
+ except RuntimeError:
+ pass
providers += [
client.get_simple_provider(),
changeset: 273:e0f9fa0acc03
user: Daniel Tang <dytang@cs.purdue.edu>
date: Thu Apr 23 01:33:38 2009 -0400
summary: Fix the nourl check to use the command name, instead of the function
diff --git a/__init__.py b/__init__.py
--- a/__init__.py
+++ b/__init__.py
@@ -93,7 +93,7 @@
path = os.path.dirname(repo.path)
try:
commandfunc = svncommands.table[subcommand]
- if commandfunc not in svncommands.nourl:
+ if subcommand not in svncommands.nourl:
opts['svn_url'] = open(os.path.join(repo.path, 'svn', 'url')).read()
return commandfunc(ui, args=args, hg_repo_path=path, repo=repo, **opts)
except core.SubversionException, e:
changeset: 274:a119ab6135f3
user: Daniel Tang <dytang@cs.purdue.edu>
date: Thu Apr 23 14:51:13 2009 -0400
summary: Add help to the list of nourl commands
diff --git a/svncommands.py b/svncommands.py
--- a/svncommands.py
+++ b/svncommands.py
@@ -222,7 +222,7 @@
return 1
-nourl = ['rebuildmeta'] + utility_commands.nourl
+nourl = ['rebuildmeta', 'help'] + utility_commands.nourl
table = {
'update': update,
'help': help,
changeset: 275:68aea59116b6
user: Augie Fackler <durin42@gmail.com>
date: Fri Apr 24 20:25:14 2009 -0500
summary: util: kill unused function
diff --git a/util.py b/util.py
--- a/util.py
+++ b/util.py
@@ -29,15 +29,6 @@
return svnurl.rstrip('/')
-def wipe_all_files(hg_wc_path):
- files = [f for f in os.listdir(hg_wc_path) if f != '.hg']
- for f in files:
- f = os.path.join(hg_wc_path, f)
- if os.path.isdir(f):
- shutil.rmtree(f)
- else:
- os.remove(f)
-
REVMAP_FILE_VERSION = 1
def parse_revmap(revmap_filename):
revmap = {}
changeset: 276:b45bae16be32
user: Augie Fackler <durin42@gmail.com>
date: Fri Apr 24 20:26:00 2009 -0500
summary: clone: Fix url-escaping for new config parser
diff --git a/wrappers.py b/wrappers.py
--- a/wrappers.py
+++ b/wrappers.py
@@ -213,7 +213,7 @@
fp = repo.opener("hgrc", "w", text=True)
fp.write("[paths]\n")
# percent needs to be escaped for ConfigParser
- fp.write("default = %(url)s\nsvn = %(url)s\n" % {'url': svnurl.replace('%', '%%')})
+ fp.write("default = %(url)s\nsvn = %(url)s\n" % {'url': svnurl})
fp.close()
if (res is None or res == 0) and not opts.get('noupdate', False):
commands.update(ui, repo, repo['tip'].node())
changeset: 278:60acc38eac96
user: Martijn Pieters <mj@zopatista.com>
date: Fri Apr 24 20:36:38 2009 -0500
summary: clone: prefer tip of default to overall tip when updating
diff --git a/wrappers.py b/wrappers.py
--- a/wrappers.py
+++ b/wrappers.py
@@ -216,7 +216,13 @@
fp.write("default = %(url)s\nsvn = %(url)s\n" % {'url': svnurl})
fp.close()
if (res is None or res == 0) and not opts.get('noupdate', False):
- commands.update(ui, repo, repo['tip'].node())
+ for test in ('default', 'tip'):
+ try:
+ uprev = repo.lookup(test)
+ break
+ except:
+ continue
+ commands.update(ui, repo, uprev)
return res
changeset: 279:376ba9399ce6
user: Max Bowsher <maxb@f2s.com>
date: Fri Apr 24 21:04:25 2009 -0500
summary: hg_delta_editor: Fix a bug in marking branches as deleted.
diff --git a/hg_delta_editor.py b/hg_delta_editor.py
--- a/hg_delta_editor.py
+++ b/hg_delta_editor.py
@@ -521,7 +521,7 @@
# check for case 5
for known in self.branches:
if self._svnpath(known).startswith(p):
- self.branches_to_delete.add(br) # case 5
+ self.branches_to_delete.add(known) # case 5
added_branches.update(self.__determine_parent_branch(p, paths[p].copyfrom_path,
paths[p].copyfrom_rev, revision.revnum))
for t in tags_to_delete:
changeset: 280:4a327bfc69c6
user: Augie Fackler <durin42@gmail.com>
date: Fri Apr 24 21:06:54 2009 -0500
summary: README: Now require 1.3.
diff --git a/README b/README
--- a/README
+++ b/README
@@ -14,7 +14,8 @@
Installation
------------
You need to have Subversion installed with the SWIG Python bindings
-from Subversion 1.5 or later. You need Mercurial 1.1.1 or later.
+from Subversion 1.5 or later. You need Mercurial 1.3 (currently in development)
+or later.
.. _mercurial: http://selenic.com/repo/hg
.. _mercurial-stable: http://selenic.com/repo/hg-stable
changeset: 281:8ff0b3261b7f
user: Augie Fackler <durin42@gmail.com>
date: Fri Apr 24 21:07:59 2009 -0500
summary: util: drop 1.2 support in the encoding swap.
diff --git a/util.py b/util.py
--- a/util.py
+++ b/util.py
@@ -114,12 +114,9 @@
def swap_out_encoding(new_encoding="UTF-8"):
- """ Utility for mercurial incompatibility changes, can be removed after 1.3"""
- try:
- from mercurial import encoding
- old = encoding.encoding
- encoding.encoding = new_encoding
- except ImportError:
- old = hgutil._encoding
- hgutil._encoding = new_encoding
+ """ Utility for mercurial incompatibility changes, can be removed after 1.3
+ """
+ from mercurial import encoding
+ old = encoding.encoding
+ encoding.encoding = new_encoding
return old
changeset: 282:77892f67b1cd
tag: tip
user: Augie Fackler <durin42@gmail.com>
date: Fri Apr 24 22:33:54 2009 -0500
summary: tools: bash script to convert tags as a stopgap until the tags refactor
diff --git a/tools/converttags.sh b/tools/converttags.sh
new file mode 100644
--- /dev/null
+++ b/tools/converttags.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+# This shell script exists to convert hgsubversion tags to real hg tags.
+# This will go away once hgsubversion's tags handling uses .hgtags directly.
+hg tags | sed -E 's/([a-zA-Z0-9./_-]*) [^:]*:([a-f0-9]*)/\2 \1/' | grep -v ' tip$' > .hgtags
+cat .hgtags | sed "$(
+for x in `cat .hgtags| cut -f 1 -d ' '` ;do
+ echo -n "s/$x/" ; hg log --template '{node}' -r $x ; echo -n '/g; '
+done)" > .hgtags.new
+mv .hgtags.new .hgtags