Mercurial > hgsubversion
comparison hg_delta_editor.py @ 133:2242dd1163c6
hg_delta_editor: fix bad parent revision calculation in the case of a branch
recycling a name.
Also implemented marking branches as closed in both replay and stupid paths.
| author | Augie Fackler <durin42@gmail.com> |
|---|---|
| date | Wed, 10 Dec 2008 14:29:05 -0600 |
| parents | 4d42dbbb5127 |
| children | cf6fe8457570 |
comparison
equal
deleted
inserted
replaced
| 132:3a9d6cd18332 | 133:2242dd1163c6 |
|---|---|
| 105 | 105 |
| 106 self.clear_current_info() | 106 self.clear_current_info() |
| 107 self.author_host = author_host | 107 self.author_host = author_host |
| 108 | 108 |
| 109 def __setup_repo(self, repo_path): | 109 def __setup_repo(self, repo_path): |
| 110 '''Verify the repo is going to work out for us. | 110 """Verify the repo is going to work out for us. |
| 111 | 111 |
| 112 This method will fail an assertion if the repo exists but doesn't have | 112 This method will fail an assertion if the repo exists but doesn't have |
| 113 the Subversion metadata. | 113 the Subversion metadata. |
| 114 ''' | 114 """ |
| 115 if os.path.isdir(repo_path) and len(os.listdir(repo_path)): | 115 if os.path.isdir(repo_path) and len(os.listdir(repo_path)): |
| 116 self.repo = hg.repository(self.ui, repo_path) | 116 self.repo = hg.repository(self.ui, repo_path) |
| 117 assert os.path.isfile(self.revmap_file) | 117 assert os.path.isfile(self.revmap_file) |
| 118 assert os.path.isfile(self.svn_url_file) | 118 assert os.path.isfile(self.svn_url_file) |
| 119 assert os.path.isfile(self.uuid_file) | 119 assert os.path.isfile(self.uuid_file) |
| 139 # Map fully qualified destination file paths to module source path | 139 # Map fully qualified destination file paths to module source path |
| 140 self.copies = {} | 140 self.copies = {} |
| 141 self.missing_plaintexts = set() | 141 self.missing_plaintexts = set() |
| 142 self.commit_branches_empty = {} | 142 self.commit_branches_empty = {} |
| 143 self.base_revision = None | 143 self.base_revision = None |
| 144 self.branches_to_delete = set() | |
| 144 | 145 |
| 145 def _save_metadata(self): | 146 def _save_metadata(self): |
| 146 '''Save the Subversion metadata. This should really be called after | 147 '''Save the Subversion metadata. This should really be called after |
| 147 every revision is created. | 148 every revision is created. |
| 148 ''' | 149 ''' |
| 162 | 163 |
| 163 def _path_and_branch_for_path(self, path): | 164 def _path_and_branch_for_path(self, path): |
| 164 return self._split_branch_path(path)[:2] | 165 return self._split_branch_path(path)[:2] |
| 165 | 166 |
| 166 def _split_branch_path(self, path): | 167 def _split_branch_path(self, path): |
| 167 '''Figure out which branch inside our repo this path represents, and | 168 """Figure out which branch inside our repo this path represents, and |
| 168 also figure out which path inside that branch it is. | 169 also figure out which path inside that branch it is. |
| 169 | 170 |
| 170 Raises an exception if it can't perform its job. | 171 Raises an exception if it can't perform its job. |
| 171 ''' | 172 """ |
| 172 path = self._normalize_path(path) | 173 path = self._normalize_path(path) |
| 173 if path.startswith('trunk'): | 174 if path.startswith('trunk'): |
| 174 p = path[len('trunk'):] | 175 p = path[len('trunk'):] |
| 175 if p and p[0] == '/': | 176 if p and p[0] == '/': |
| 176 p = p[1:] | 177 p = p[1:] |
| 184 p = p[1:] | 185 p = p[1:] |
| 185 return p, br, 'branches/' + br | 186 return p, br, 'branches/' + br |
| 186 return None, None, None | 187 return None, None, None |
| 187 | 188 |
| 188 def set_current_rev(self, rev): | 189 def set_current_rev(self, rev): |
| 189 '''Set the revision we're currently converting. | 190 """Set the revision we're currently converting. |
| 190 ''' | 191 """ |
| 191 self.current_rev = rev | 192 self.current_rev = rev |
| 192 | 193 |
| 193 def set_file(self, path, data, isexec=False, islink=False): | 194 def set_file(self, path, data, isexec=False, islink=False): |
| 194 if islink: | 195 if islink: |
| 195 data = 'link ' + data | 196 data = 'link ' + data |
| 234 for num, br in self.revmap.iterkeys(): | 235 for num, br in self.revmap.iterkeys(): |
| 235 if br != branch: | 236 if br != branch: |
| 236 continue | 237 continue |
| 237 if num <= number and num > real_num: | 238 if num <= number and num > real_num: |
| 238 real_num = num | 239 real_num = num |
| 239 if real_num == 0: | 240 if branch in self.branches: |
| 240 if branch in self.branches: | 241 parent_branch = self.branches[branch][0] |
| 241 parent_branch = self.branches[branch][0] | 242 parent_branch_rev = self.branches[branch][1] |
| 242 parent_branch_rev = self.branches[branch][1] | 243 # check to see if this branch already existed and is the same |
| 243 if parent_branch_rev <= 0: | 244 if parent_branch_rev < real_num: |
| 244 return None, None | 245 return real_num, branch |
| 245 branch_created_rev = self.branches[branch][2] | 246 # if that wasn't true, then this is the a new branch with the |
| 246 if parent_branch == 'trunk': | 247 # same name as some old deleted branch |
| 247 parent_branch = None | 248 if parent_branch_rev <= 0 and real_num == 0: |
| 248 if branch_created_rev <= number+1 and branch != parent_branch: | 249 return None, None |
| 249 return self.get_parent_svn_branch_and_rev( | 250 branch_created_rev = self.branches[branch][2] |
| 250 parent_branch_rev+1, | 251 if parent_branch == 'trunk': |
| 251 parent_branch) | 252 parent_branch = None |
| 253 if branch_created_rev <= number+1 and branch != parent_branch: | |
| 254 return self.get_parent_svn_branch_and_rev( | |
| 255 parent_branch_rev+1, | |
| 256 parent_branch) | |
| 252 if real_num != 0: | 257 if real_num != 0: |
| 253 return real_num, branch | 258 return real_num, branch |
| 254 return None, None | 259 return None, None |
| 255 | 260 |
| 256 def get_parent_revision(self, number, branch): | 261 def get_parent_revision(self, number, branch): |
| 263 | 268 |
| 264 def update_branch_tag_map_for_rev(self, revision): | 269 def update_branch_tag_map_for_rev(self, revision): |
| 265 paths = revision.paths | 270 paths = revision.paths |
| 266 added_branches = {} | 271 added_branches = {} |
| 267 added_tags = {} | 272 added_tags = {} |
| 273 self.branches_to_delete = set() | |
| 268 tags_to_delete = set() | 274 tags_to_delete = set() |
| 269 branches_to_delete = set() | |
| 270 for p in sorted(paths): | 275 for p in sorted(paths): |
| 271 fi, br = self._path_and_branch_for_path(p) | 276 fi, br = self._path_and_branch_for_path(p) |
| 272 if fi is not None: | 277 if fi is not None: |
| 273 if fi == '' and br not in self.branches: | 278 if fi == '' and paths[p].action != 'D': |
| 274 src_p = paths[p].copyfrom_path | 279 src_p = paths[p].copyfrom_path |
| 275 src_rev = paths[p].copyfrom_rev | 280 src_rev = paths[p].copyfrom_rev |
| 276 src_tag = self._is_path_tag(src_p) | 281 src_tag = self._is_path_tag(src_p) |
| 277 | 282 |
| 278 if not ((src_p and self._is_path_valid(src_p)) or | 283 if not ((src_p and self._is_path_valid(src_p)) or |
| 292 continue | 297 continue |
| 293 added_branches[br] = src_branch, src_rev, revision.revnum | 298 added_branches[br] = src_branch, src_rev, revision.revnum |
| 294 elif fi == '' and br in self.branches: | 299 elif fi == '' and br in self.branches: |
| 295 br2 = br or 'default' | 300 br2 = br or 'default' |
| 296 if br2 not in self.repo.branchtags() and paths[p].action == 'D': | 301 if br2 not in self.repo.branchtags() and paths[p].action == 'D': |
| 297 branches_to_delete.add(br) | 302 self.branches_to_delete.add(br) |
| 298 else: | 303 else: |
| 299 t_name = self._is_path_tag(p) | 304 t_name = self._is_path_tag(p) |
| 300 if t_name == False: | 305 if t_name == False: |
| 301 continue | 306 continue |
| 302 src_p, src_rev = paths[p].copyfrom_path, paths[p].copyfrom_rev | 307 src_p, src_rev = paths[p].copyfrom_path, paths[p].copyfrom_rev |
| 318 elif (paths[p].action == 'D' and p.endswith(t_name) | 323 elif (paths[p].action == 'D' and p.endswith(t_name) |
| 319 and t_name in self.tags): | 324 and t_name in self.tags): |
| 320 tags_to_delete.add(t_name) | 325 tags_to_delete.add(t_name) |
| 321 for t in tags_to_delete: | 326 for t in tags_to_delete: |
| 322 del self.tags[t] | 327 del self.tags[t] |
| 323 for br in branches_to_delete: | 328 for br in self.branches_to_delete: |
| 324 del self.branches[br] | 329 del self.branches[br] |
| 325 self.tags.update(added_tags) | 330 self.tags.update(added_tags) |
| 326 self.branches.update(added_branches) | 331 self.branches.update(added_branches) |
| 327 self._save_metadata() | 332 self._save_metadata() |
| 328 | 333 |
| 357 files = dict(files) | 362 files = dict(files) |
| 358 | 363 |
| 359 parents = (self.get_parent_revision(rev.revnum, branch), | 364 parents = (self.get_parent_revision(rev.revnum, branch), |
| 360 revlog.nullid) | 365 revlog.nullid) |
| 361 if branch is not None: | 366 if branch is not None: |
| 362 if branch not in self.branches and branch not in self.repo.branchtags(): | 367 if (branch not in self.branches |
| 368 and branch not in self.repo.branchtags()): | |
| 363 continue | 369 continue |
| 364 extra['branch'] = branch | 370 extra['branch'] = branch |
| 371 if (branch in self.branches_to_delete): | |
| 372 continue | |
| 365 parent_ctx = self.repo.changectx(parents[0]) | 373 parent_ctx = self.repo.changectx(parents[0]) |
| 366 def filectxfn(repo, memctx, path): | 374 def filectxfn(repo, memctx, path): |
| 367 current_file = files[path] | 375 current_file = files[path] |
| 368 if current_file in self.deleted_files: | 376 if current_file in self.deleted_files: |
| 369 raise IOError() | 377 raise IOError() |
| 394 self.ui.status('committed as %s on branch %s\n' % | 402 self.ui.status('committed as %s on branch %s\n' % |
| 395 (node.hex(new_hash), (branch or 'default'))) | 403 (node.hex(new_hash), (branch or 'default'))) |
| 396 if (rev.revnum, branch) not in self.revmap: | 404 if (rev.revnum, branch) not in self.revmap: |
| 397 self.add_to_revmap(rev.revnum, branch, new_hash) | 405 self.add_to_revmap(rev.revnum, branch, new_hash) |
| 398 # now we handle branches that need to be committed without any files | 406 # now we handle branches that need to be committed without any files |
| 407 for branch in self.branches_to_delete: | |
| 408 closed = revlog.nullid | |
| 409 if 'closed-branches' in self.repo.branchtags(): | |
| 410 closed = self.repo['closed-branches'].node() | |
| 411 ha = self.get_parent_revision(rev.revnum, branch) | |
| 412 parentctx = self.repo.changectx(ha) | |
| 413 if parentctx.children(): | |
| 414 continue | |
| 415 parents = (ha, closed) | |
| 416 def del_all_files(*args): | |
| 417 raise IOError | |
| 418 files = parentctx.manifest().keys() | |
| 419 current_ctx = context.memctx(self.repo, | |
| 420 parents, | |
| 421 rev.message or ' ', | |
| 422 files, | |
| 423 del_all_files, | |
| 424 '%s%s' % (rev.author, | |
| 425 self.author_host), | |
| 426 date, | |
| 427 {'branch': 'closed-branches'}) | |
| 428 new_hash = self.repo.commitctx(current_ctx) | |
| 429 self.ui.status('Marked branch %s as closed.' % (branch or 'default')) | |
| 399 for branch in self.commit_branches_empty: | 430 for branch in self.commit_branches_empty: |
| 400 ha = self.get_parent_revision(rev.revnum, branch) | 431 ha = self.get_parent_revision(rev.revnum, branch) |
| 401 if ha == node.nullid: | 432 if ha == node.nullid: |
| 402 continue | 433 continue |
| 403 parent_ctx = self.repo.changectx(ha) | 434 parent_ctx = self.repo.changectx(ha) |
| 404 def del_all_files(*args): | 435 def del_all_files(*args): |
| 405 raise IOError | 436 raise IOError |
| 406 extra = {} | 437 extra = {} |
| 407 if branch: | 438 if parent_ctx.children(): |
| 408 extra['branch'] = branch | 439 # Target isn't an active head, no need to do things to it. |
| 440 continue | |
| 441 if branch in self.branches_to_delete: | |
| 442 extra['branch'] = 'closed-branch' | |
| 409 # True here means nuke all files | 443 # True here means nuke all files |
| 410 files = [] | 444 files = [] |
| 411 if self.commit_branches_empty[branch]: | 445 if self.commit_branches_empty[branch]: |
| 412 files = parent_ctx.manifest().keys() | 446 files = parent_ctx.manifest().keys() |
| 413 current_ctx = context.memctx(self.repo, | 447 current_ctx = context.memctx(self.repo, |
| 466 return open(self.svn_url_file).read() | 500 return open(self.svn_url_file).read() |
| 467 | 501 |
| 468 @stash_exception_on_self | 502 @stash_exception_on_self |
| 469 def delete_entry(self, path, revision_bogus, parent_baton, pool=None): | 503 def delete_entry(self, path, revision_bogus, parent_baton, pool=None): |
| 470 br_path, branch = self._path_and_branch_for_path(path) | 504 br_path, branch = self._path_and_branch_for_path(path) |
| 505 if br_path == '': | |
| 506 self.branches_to_delete.add(branch) | |
| 471 if br_path is not None: | 507 if br_path is not None: |
| 472 ha = self.get_parent_revision(self.current_rev.revnum, branch) | 508 ha = self.get_parent_revision(self.current_rev.revnum, branch) |
| 473 if ha == revlog.nullid: | 509 if ha == revlog.nullid: |
| 474 return | 510 return |
| 475 ctx = self.repo.changectx(ha) | 511 ctx = self.repo.changectx(ha) |
