def migrate_path(self, path): sys.stdout.write("Searching for merge tracking information...\n") # Get svnmerge-integrated property for PATH, as Subversion mergeinfo. integrated_mergeinfo = svnmerge_prop_to_mergeinfo( self.cc.get_path_property(path, 'svnmerge-integrated')) if integrated_mergeinfo and self.verbose: sys.stdout.write("Found svnmerge-integrated:\n") pretty_print_mergeinfo(integrated_mergeinfo, 3) # Get svnmerge-blocked property for PATH, as Subversion mergeinfo. blocked_mergeinfo = svnmerge_prop_to_mergeinfo( self.cc.get_path_property(path, 'svnmerge-blocked')) if blocked_mergeinfo and self.verbose: sys.stdout.write("Found svnmerge-blocked:\n") pretty_print_mergeinfo(blocked_mergeinfo, 3) # No svnmerge tracking data? Nothing to do. if not (integrated_mergeinfo or blocked_mergeinfo): errput("No svnmerge.py tracking data found for '%s'." % (path), True) return # Fetch Subversion mergeinfo for PATH. Hopefully there is # none, but if there is, we'll assume folks want to keep it. orig_mergeinfo = self.cc.get_path_mergeinfo(path) if orig_mergeinfo and self.verbose: sys.stdout.write("Found Subversion mergeinfo:\n") pretty_print_mergeinfo(orig_mergeinfo, 3) # Merge all our mergeinfos together. new_mergeinfo = mergeinfo_merge(orig_mergeinfo, integrated_mergeinfo) new_mergeinfo = mergeinfo_merge(new_mergeinfo, blocked_mergeinfo) # Unless we're doing a naive migration (or we've no, or only # empty, mergeinfo anyway), start trying to cleanup after # svnmerge.py's history-ignorant initialization. if not self.naive and new_mergeinfo: sys.stdout.write("Sanitizing mergeinfo (this can take a " "while)...\n") # What we need: # - the relative path in the repository for PATH # - repository root URL and an RA session rooted thereat # - the base revision of PATH path_url, root_url = self.cc.get_path_urls(path) if root_url: ras = self.cc.open_ra_session(root_url) else: ras = self.cc.open_ra_session(path_url) root_url = ra.get_repos_root(ras) ra.reparent(ras, root_url) assert path_url.startswith(root_url) rel_path = relative_path_from_urls(root_url, path_url) path_rev = self.cc.get_path_revision(path) # We begin by subtracting the natural history of the merge # target from its own mergeinfo. implicit_mergeinfo = \ self.cc.get_history_as_mergeinfo(ras, rel_path, path_rev) if self.verbose: sys.stdout.write(" subtracting natural history:\n") pretty_print_mergeinfo(implicit_mergeinfo, 6) new_mergeinfo = core.svn_mergeinfo_remove(implicit_mergeinfo, new_mergeinfo) if self.verbose: sys.stdout.write(" remaining mergeinfo to be filtered:\n") pretty_print_mergeinfo(new_mergeinfo, 6) # Unfortunately, svnmerge.py tends to initialize using # oft-bogus revision ranges like 1-SOMETHING when the # merge source didn't even exist in r1. So if the natural # history of a branch begins in some revision other than # r1, there's still going to be cruft revisions left in # NEW_MERGEINFO after subtracting the natural history. # So, we also examine the natural history of the merge # sources, and use that as a filter for the explicit # mergeinfo we've calculated so far. mergeinfo_so_far = new_mergeinfo new_mergeinfo = {} for source_path, ranges in mergeinfo_so_far.items(): # If by some chance it is the case that /path:RANGE1 # and /path:RANGE2 a) represent different lines of # history, and b) were combined into # /path:RANGE1+RANGE2 (due to the ranges being # contiguous), we'll foul this up. But the chances # are preeeeeeeetty slim. for range in ranges: try: history = self.cc.get_history_as_mergeinfo( ras, source_path[1:], range.end, range.start + 1) if self.verbose: sys.stdout.write(" new sanitized chunk:\n") pretty_print_mergeinfo(history, 6) new_mergeinfo = mergeinfo_merge(new_mergeinfo, history) except core.SubversionException, e: if not (e.apr_err == core.SVN_ERR_FS_NOT_FOUND or e.apr_err == core.SVN_ERR_FS_NO_SUCH_REVISION): raise