comparison cmdutil.py @ 242:06130689a2c8

Move push into svncommands.
author Dirkjan Ochtman <dirkjan@ochtman.nl>
date Wed, 08 Apr 2009 17:53:48 +0200
parents 4950b18cf949
children 2027f851d60c
comparison
equal deleted inserted replaced
241:4950b18cf949 242:06130689a2c8
1
2 from mercurial import util as hgutil
3
4 from svn import core
5
6 import svnwrap
7 import svnexternals
8
9
10 class BaseException(Exception):
11 pass
12
13
14 class NoFilesException(BaseException):
15 """Exception raised when you try and commit without files.
16 """
17
1 18
2 def replay_convert_rev(hg_editor, svn, r): 19 def replay_convert_rev(hg_editor, svn, r):
3 hg_editor.set_current_rev(r) 20 hg_editor.set_current_rev(r)
4 svn.get_replay(r.revnum, hg_editor) 21 svn.get_replay(r.revnum, hg_editor)
5 i = 1 22 i = 1
28 data, mode = svn.get_file(p, r.revnum) 45 data, mode = svn.get_file(p, r.revnum)
29 hg_editor.set_file(p, data, 'x' in mode, 'l' in mode) 46 hg_editor.set_file(p, data, 'x' in mode, 'l' in mode)
30 hg_editor.missing_plaintexts = set() 47 hg_editor.missing_plaintexts = set()
31 hg_editor.ui.note('\n') 48 hg_editor.ui.note('\n')
32 hg_editor.commit_current_delta() 49 hg_editor.commit_current_delta()
50
51
52 def _isdir(svn, branchpath, svndir):
53 try:
54 svn.list_dir('%s/%s' % (branchpath, svndir))
55 return True
56 except core.SubversionException:
57 return False
58
59
60 def _getdirchanges(svn, branchpath, parentctx, ctx, changedfiles, extchanges):
61 """Compute directories to add or delete when moving from parentctx
62 to ctx, assuming only 'changedfiles' files changed, and 'extchanges'
63 external references changed (as returned by svnexternals.diff()).
64
65 Return (added, deleted) where 'added' is the list of all added
66 directories and 'deleted' the list of deleted directories.
67 Intermediate directories are included: if a/b/c is new and requires
68 the addition of a/b and a, those will be listed too. Intermediate
69 deleted directories are also listed, but item order of undefined
70 in either list.
71 """
72 def finddirs(path, includeself=False):
73 if includeself:
74 yield path
75 pos = path.rfind('/')
76 while pos != -1:
77 yield path[:pos]
78 pos = path.rfind('/', 0, pos)
79
80 def getctxdirs(ctx, keptdirs, extdirs):
81 dirs = {}
82 for f in ctx.manifest():
83 for d in finddirs(f):
84 if d in dirs:
85 break
86 if d in keptdirs:
87 dirs[d] = 1
88 for extdir in extdirs:
89 for d in finddirs(extdir, True):
90 dirs[d] = 1
91 return dirs
92
93 deleted, added = [], []
94 changeddirs = {}
95 for f in changedfiles:
96 if f in parentctx and f in ctx:
97 # Updated files cannot cause directories to be created
98 # or removed.
99 continue
100 for d in finddirs(f):
101 changeddirs[d] = 1
102 for e in extchanges:
103 if not e[1] or not e[2]:
104 for d in finddirs(e[0], True):
105 changeddirs[d] = 1
106 if not changeddirs:
107 return added, deleted
108 olddirs = getctxdirs(parentctx, changeddirs,
109 [e[0] for e in extchanges if e[1]])
110 newdirs = getctxdirs(ctx, changeddirs,
111 [e[0] for e in extchanges if e[2]])
112
113 for d in newdirs:
114 if d not in olddirs and not _isdir(svn, branchpath, d):
115 added.append(d)
116
117 for d in olddirs:
118 if d not in newdirs and _isdir(svn, branchpath, d):
119 deleted.append(d)
120
121 return added, deleted
122
123
124 def _externals(ctx):
125 ext = svnexternals.externalsfile()
126 if '.hgsvnexternals' in ctx:
127 ext.read(ctx['.hgsvnexternals'].data())
128 return ext
129
130
131 def commit_from_rev(ui, repo, rev_ctx, hg_editor, svn_url, base_revision
132 username, password):
133 """Build and send a commit from Mercurial to Subversion.
134 """
135 file_data = {}
136 svn = svnwrap.SubversionRepo(svn_url, username, password)
137 parent = rev_ctx.parents()[0]
138 parent_branch = rev_ctx.parents()[0].branch()
139 branch_path = 'trunk'
140
141 if parent_branch and parent_branch != 'default':
142 branch_path = 'branches/%s' % parent_branch
143
144 extchanges = list(svnexternals.diff(_externals(parent),
145 _externals(rev_ctx)))
146 addeddirs, deleteddirs = _getdirchanges(svn, branch_path, parent, rev_ctx,
147 rev_ctx.files(), extchanges)
148 deleteddirs = set(deleteddirs)
149
150 props = {}
151 copies = {}
152 for file in rev_ctx.files():
153 if file == '.hgsvnexternals':
154 continue
155 new_data = base_data = ''
156 action = ''
157 if file in rev_ctx:
158 fctx = rev_ctx.filectx(file)
159 new_data = fctx.data()
160
161 if 'x' in fctx.flags():
162 props.setdefault(file, {})['svn:executable'] = '*'
163 if 'l' in fctx.flags():
164 props.setdefault(file, {})['svn:special'] = '*'
165
166 if file not in parent:
167 renamed = fctx.renamed()
168 if renamed:
169 # TODO current model (and perhaps svn model) does not support
170 # this kind of renames: a -> b, b -> c
171 copies[file] = renamed[0]
172 base_data = parent[renamed[0]].data()
173
174 action = 'add'
175 dirname = '/'.join(file.split('/')[:-1] + [''])
176 else:
177 base_data = parent.filectx(file).data()
178 if ('x' in parent.filectx(file).flags()
179 and 'x' not in rev_ctx.filectx(file).flags()):
180 props.setdefault(file, {})['svn:executable'] = None
181 if ('l' in parent.filectx(file).flags()
182 and 'l' not in rev_ctx.filectx(file).flags()):
183 props.setdefault(file, {})['svn:special'] = None
184 action = 'modify'
185 else:
186 pos = file.rfind('/')
187 if pos >= 0:
188 if file[:pos] in deleteddirs:
189 # This file will be removed when its directory is removed
190 continue
191 action = 'delete'
192 file_data[file] = base_data, new_data, action
193
194 def svnpath(p):
195 return '%s/%s' % (branch_path, p)
196
197 changeddirs = []
198 for d, v1, v2 in extchanges:
199 props.setdefault(svnpath(d), {})['svn:externals'] = v2
200 if d not in deleteddirs and d not in addeddirs:
201 changeddirs.append(svnpath(d))
202
203 # Now we are done with files, we can prune deleted directories
204 # against themselves: ignore a/b if a/ is already removed
205 deleteddirs2 = list(deleteddirs)
206 deleteddirs2.sort(reverse=True)
207 for d in deleteddirs2:
208 pos = d.rfind('/')
209 if pos >= 0 and d[:pos] in deleteddirs:
210 deleteddirs.remove(d[:pos])
211
212 newcopies = {}
213 for source, dest in copies.iteritems():
214 newcopies[svnpath(source)] = (svnpath(dest), base_revision)
215
216 new_target_files = [svnpath(f) for f in file_data]
217 for tf, ntf in zip(file_data, new_target_files):
218 if tf in file_data:
219 file_data[ntf] = file_data[tf]
220 if tf in props:
221 props[ntf] = props[tf]
222 del props[tf]
223 if hgutil.binary(file_data[ntf][1]):
224 props.setdefault(ntf, {}).update(props.get(ntf, {}))
225 props.setdefault(ntf, {})['svn:mime-type'] = 'application/octet-stream'
226 del file_data[tf]
227
228 addeddirs = [svnpath(d) for d in addeddirs]
229 deleteddirs = [svnpath(d) for d in deleteddirs]
230 new_target_files += addeddirs + deleteddirs + changeddirs
231 if not new_target_files:
232 raise NoFilesException()
233 try:
234 svn.commit(new_target_files, rev_ctx.description(), file_data,
235 base_revision, set(addeddirs), set(deleteddirs),
236 props, newcopies)
237 except core.SubversionException, e:
238 if hasattr(e, 'apr_err') and (e.apr_err == core.SVN_ERR_FS_TXN_OUT_OF_DATE
239 or e.apr_err == core.SVN_ERR_FS_CONFLICT):
240 raise hgutil.Abort('Base text was out of date, maybe rebase?')
241 else:
242 raise