Mercurial > hgsubversion
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 |