comparison hgext/convert/subversion.py @ 16511:ecd2fbe68b25 stable

convert/svn: make svn sink work with svn 1.7 "svn add file" now fails if "file" is already tracked. To filter them we have to mirror the svn manifest in the sink. Tested with svn 1.6.12 and 1.7.4.
author Patrick Mezard <patrick@mezard.eu>
date Tue, 24 Apr 2012 12:50:41 +0200
parents c53a49c345e1
children 363e808de349
comparison
equal deleted inserted replaced
16510:c7c9473fcc46 16511:ecd2fbe68b25
1 # Subversion 1.4/1.5 Python API backend 1 # Subversion 1.4/1.5 Python API backend
2 # 2 #
3 # Copyright(C) 2007 Daniel Holth et al 3 # Copyright(C) 2007 Daniel Holth et al
4 4
5 import os 5 import os, re, sys, tempfile, urllib, urllib2, xml.dom.minidom
6 import re
7 import sys
8 import cPickle as pickle 6 import cPickle as pickle
9 import tempfile
10 import urllib
11 import urllib2
12 7
13 from mercurial import strutil, scmutil, util, encoding 8 from mercurial import strutil, scmutil, util, encoding
14 from mercurial.i18n import _ 9 from mercurial.i18n import _
10
11 propertycache = util.propertycache
15 12
16 # Subversion stuff. Works best with very recent Python SVN bindings 13 # Subversion stuff. Works best with very recent Python SVN bindings
17 # e.g. SVN 1.5 or backports. Thanks to the bzr folks for enhancing 14 # e.g. SVN 1.5 or backports. Thanks to the bzr folks for enhancing
18 # these bindings. 15 # these bindings.
19 16
1055 self.uuid = self.uuid_re.search(output).group(1).strip() 1052 self.uuid = self.uuid_re.search(output).group(1).strip()
1056 1053
1057 def wjoin(self, *names): 1054 def wjoin(self, *names):
1058 return os.path.join(self.wc, *names) 1055 return os.path.join(self.wc, *names)
1059 1056
1057 @propertycache
1058 def manifest(self):
1059 # As of svn 1.7, the "add" command fails when receiving
1060 # already tracked entries, so we have to track and filter them
1061 # ourselves.
1062 m = set()
1063 output = self.run0('ls', recursive=True, xml=True)
1064 doc = xml.dom.minidom.parseString(output)
1065 for e in doc.getElementsByTagName('entry'):
1066 for n in e.childNodes:
1067 if n.nodeType != n.ELEMENT_NODE or n.tagName != 'name':
1068 continue
1069 name = ''.join(c.data for c in n.childNodes
1070 if c.nodeType == c.TEXT_NODE)
1071 # Entries are compared with names coming from
1072 # mercurial, so bytes with undefined encoding. Our
1073 # best bet is to assume they are in local
1074 # encoding. They will be passed to command line calls
1075 # later anyway, so they better be.
1076 m.add(encoding.tolocal(name.encode('utf-8')))
1077 break
1078 return m
1079
1060 def putfile(self, filename, flags, data): 1080 def putfile(self, filename, flags, data):
1061 if 'l' in flags: 1081 if 'l' in flags:
1062 self.wopener.symlink(data, filename) 1082 self.wopener.symlink(data, filename)
1063 else: 1083 else:
1064 try: 1084 try:
1097 os.unlink(tempname) 1117 os.unlink(tempname)
1098 os.rename(wdest, tempname) 1118 os.rename(wdest, tempname)
1099 try: 1119 try:
1100 self.run0('copy', source, dest) 1120 self.run0('copy', source, dest)
1101 finally: 1121 finally:
1122 self.manifest.add(dest)
1102 if exists: 1123 if exists:
1103 try: 1124 try:
1104 os.unlink(wdest) 1125 os.unlink(wdest)
1105 except OSError: 1126 except OSError:
1106 pass 1127 pass
1115 dirs.add(f[:i]) 1136 dirs.add(f[:i])
1116 return dirs 1137 return dirs
1117 1138
1118 def add_dirs(self, files): 1139 def add_dirs(self, files):
1119 add_dirs = [d for d in sorted(self.dirs_of(files)) 1140 add_dirs = [d for d in sorted(self.dirs_of(files))
1120 if not os.path.exists(self.wjoin(d, '.svn', 'entries'))] 1141 if d not in self.manifest]
1121 if add_dirs: 1142 if add_dirs:
1143 self.manifest.update(add_dirs)
1122 self.xargs(add_dirs, 'add', non_recursive=True, quiet=True) 1144 self.xargs(add_dirs, 'add', non_recursive=True, quiet=True)
1123 return add_dirs 1145 return add_dirs
1124 1146
1125 def add_files(self, files): 1147 def add_files(self, files):
1148 files = [f for f in files if f not in self.manifest]
1126 if files: 1149 if files:
1150 self.manifest.update(files)
1127 self.xargs(files, 'add', quiet=True) 1151 self.xargs(files, 'add', quiet=True)
1128 return files 1152 return files
1129 1153
1130 def tidy_dirs(self, names): 1154 def tidy_dirs(self, names):
1131 deleted = [] 1155 deleted = []
1132 for d in sorted(self.dirs_of(names), reverse=True): 1156 for d in sorted(self.dirs_of(names), reverse=True):
1133 wd = self.wjoin(d) 1157 wd = self.wjoin(d)
1134 if os.listdir(wd) == '.svn': 1158 if os.listdir(wd) == '.svn':
1135 self.run0('delete', d) 1159 self.run0('delete', d)
1160 self.manifest.remove(d)
1136 deleted.append(d) 1161 deleted.append(d)
1137 return deleted 1162 return deleted
1138 1163
1139 def addchild(self, parent, child): 1164 def addchild(self, parent, child):
1140 self.childmap[parent] = child 1165 self.childmap[parent] = child
1168 for s, d in self.copies: 1193 for s, d in self.copies:
1169 self._copyfile(s, d) 1194 self._copyfile(s, d)
1170 self.copies = [] 1195 self.copies = []
1171 if self.delete: 1196 if self.delete:
1172 self.xargs(self.delete, 'delete') 1197 self.xargs(self.delete, 'delete')
1198 for f in self.delete:
1199 self.manifest.remove(f)
1173 self.delete = [] 1200 self.delete = []
1174 entries.update(self.add_files(files.difference(entries))) 1201 entries.update(self.add_files(files.difference(entries)))
1175 entries.update(self.tidy_dirs(entries)) 1202 entries.update(self.tidy_dirs(entries))
1176 if self.delexec: 1203 if self.delexec:
1177 self.xargs(self.delexec, 'propdel', 'svn:executable') 1204 self.xargs(self.delexec, 'propdel', 'svn:executable')