def _revive(repo, rev): """Brings the given rev back into the repository. Finding it in backup bundles if necessary. """ unfi = repo try: ctx = unfi[rev] except error.RepoLookupError: # It could either be a revset or a stripped commit. pass else: if obsolete.isenabled(repo, obsolete.createmarkersopt): if ctx.obsolete(): torevive = unfi.set("::%d & obsolete()", ctx.rev()) obsolete.revive(torevive, operation="reset") visibility.add(repo, [ctx.node()]) try: revs = scmutil.revrange(repo, [rev]) if len(revs) > 1: raise error.Abort(_("exactly one revision must be specified")) if len(revs) == 1: return repo[revs.first()] except error.RepoLookupError: revs = [] return _pullbundle(repo, rev)
def _applycloudchanges(repo, remotepath, lastsyncstate, cloudrefs, maxage, state, tr): # Pull all the new heads and any bookmark hashes we don't have. We need to # filter cloudrefs before pull as pull doesn't check if a rev is present # locally. unfi = repo newheads = [head for head in cloudrefs.heads if head not in unfi] if maxage is not None and maxage >= 0: mindate = time.time() - maxage * 86400 omittedheads = [ head for head in newheads if head in cloudrefs.headdates and cloudrefs.headdates[head] < mindate ] if omittedheads: repo.ui.status(_("omitting heads that are older than %d days:\n") % maxage) for head in omittedheads: headdatestr = util.datestr(util.makedate(cloudrefs.headdates[head])) repo.ui.status(_(" %s from %s\n") % (head[:12], headdatestr)) newheads = [head for head in newheads if head not in omittedheads] else: omittedheads = [] omittedbookmarks = [] omittedremotebookmarks = [] newvisibleheads = None if visibility.tracking(repo): localheads = _getheads(repo) localheadsset = set(localheads) cloudheads = [head for head in cloudrefs.heads if head not in omittedheads] cloudheadsset = set(cloudheads) if localheadsset != cloudheadsset: oldvisibleheads = [ head for head in lastsyncstate.heads if head not in lastsyncstate.omittedheads ] newvisibleheads = util.removeduplicates( oldvisibleheads + cloudheads + localheads ) toremove = { head for head in oldvisibleheads if head not in localheadsset or head not in cloudheadsset } newvisibleheads = [head for head in newvisibleheads if head not in toremove] remotebookmarknewnodes = set() remotebookmarkupdates = {} if _isremotebookmarkssyncenabled(repo.ui): (remotebookmarkupdates, remotebookmarknewnodes) = _processremotebookmarks( repo, cloudrefs.remotebookmarks, lastsyncstate ) try: snapshot = extensions.find("snapshot") except KeyError: snapshot = None addedsnapshots = [] removedsnapshots = [] newsnapshots = lastsyncstate.snapshots else: addedsnapshots = [ s for s in cloudrefs.snapshots if s not in lastsyncstate.snapshots ] removedsnapshots = [ s for s in lastsyncstate.snapshots if s not in cloudrefs.snapshots ] newsnapshots = cloudrefs.snapshots newheads += addedsnapshots if remotebookmarknewnodes or newheads: # Partition the heads into groups we can pull together. headgroups = _partitionheads( list(remotebookmarknewnodes) + newheads, cloudrefs.headdates ) _pullheadgroups(repo, remotepath, headgroups) omittedbookmarks.extend( _mergebookmarks(repo, tr, cloudrefs.bookmarks, lastsyncstate) ) newremotebookmarks = {} if _isremotebookmarkssyncenabled(repo.ui): newremotebookmarks, omittedremotebookmarks = _updateremotebookmarks( repo, tr, remotebookmarkupdates ) if snapshot: with repo.lock(), repo.transaction("sync-snapshots") as tr: repo.snapshotlist.update( tr, addnodes=addedsnapshots, removenodes=removedsnapshots ) _mergeobsmarkers(repo, tr, cloudrefs.obsmarkers) if newvisibleheads is not None: visibility.setvisibleheads(repo, [nodemod.bin(n) for n in newvisibleheads]) # Obsmarker sharing is unreliable. Some of the commits that should now # be visible might be hidden still, and some commits that should be # hidden might still be visible. Create local obsmarkers to resolve # this. if obsolete.isenabled(repo, obsolete.createmarkersopt) and not repo.ui.configbool( "mutation", "proxy-obsstore" ): unfi = repo # Commits that are only visible in the cloud are commits that are # ancestors of the cloud heads but are hidden locally. cloudvisibleonly = list( unfi.set( "not public() & ::%ls & hidden()", [head for head in cloudrefs.heads if head not in omittedheads], ) ) # Commits that are only hidden in the cloud are commits that are # ancestors of the previous cloud heads that are not ancestors of the # current cloud heads, but have not been hidden or obsoleted locally. cloudhiddenonly = list( unfi.set( "(not public() & ::%ls) - (not public() & ::%ls) - hidden() - obsolete()", [ head for head in lastsyncstate.heads if head not in lastsyncstate.omittedheads ], [head for head in cloudrefs.heads if head not in omittedheads], ) ) if cloudvisibleonly or cloudhiddenonly: msg = _( "detected obsmarker inconsistency (fixing by obsoleting [%s] and reviving [%s])\n" ) % ( ", ".join([nodemod.short(ctx.node()) for ctx in cloudhiddenonly]), ", ".join([nodemod.short(ctx.node()) for ctx in cloudvisibleonly]), ) repo.ui.log("commitcloud_sync", msg) repo.ui.warn(msg) repo._commitcloudskippendingobsmarkers = True with repo.lock(): obsolete.createmarkers(repo, [(ctx, ()) for ctx in cloudhiddenonly]) obsolete.revive(cloudvisibleonly) repo._commitcloudskippendingobsmarkers = False # We have now synced the repo to the cloud version. Store this. logsyncop( repo, "from_cloud", cloudrefs.version, lastsyncstate.heads, cloudrefs.heads, lastsyncstate.bookmarks, cloudrefs.bookmarks, lastsyncstate.remotebookmarks, newremotebookmarks, lastsyncstate.snapshots, newsnapshots, ) lastsyncstate.update( tr, newversion=cloudrefs.version, newheads=cloudrefs.heads, newbookmarks=cloudrefs.bookmarks, newremotebookmarks=newremotebookmarks, newmaxage=maxage, newomittedheads=omittedheads, newomittedbookmarks=omittedbookmarks, newomittedremotebookmarks=omittedremotebookmarks, newsnapshots=newsnapshots, ) # Also update backup state. These new heads are already backed up, # otherwise the server wouldn't have told us about them. state.update([nodemod.bin(head) for head in newheads], tr)
def _dounhide(repo, revs): unfi = repo if obsolete.isenabled(repo, obsolete.createmarkersopt): ctxs = unfi.set("not public() & ::(%ld) & obsolete()", revs) obsolete.revive(ctxs, operation="unhide") visibility.add(repo, [unfi[r].node() for r in revs])
def _dopull(orig, ui, repo, source="default", **opts): # Copy paste from `pull` command source, branches = hg.parseurl(ui.expandpath(source), opts.get("branch")) scratchbookmarks = {} unfi = repo.unfiltered() unknownnodes = [] pullbookmarks = opts.get("bookmark") or [] for rev in opts.get("rev", []): if repo._scratchbranchmatcher.match(rev): # rev is a scratch bookmark, treat it as a bookmark pullbookmarks.append(rev) elif rev not in unfi: unknownnodes.append(rev) if pullbookmarks: realbookmarks = [] revs = opts.get("rev") or [] for bookmark in pullbookmarks: if repo._scratchbranchmatcher.match(bookmark): # rev is not known yet # it will be fetched with listkeyspatterns next scratchbookmarks[bookmark] = "REVTOFETCH" else: realbookmarks.append(bookmark) if scratchbookmarks: other = hg.peer(repo, opts, source) fetchedbookmarks = other.listkeyspatterns( "bookmarks", patterns=scratchbookmarks) for bookmark in scratchbookmarks: if bookmark not in fetchedbookmarks: raise error.Abort("remote bookmark %s not found!" % bookmark) scratchbookmarks[bookmark] = fetchedbookmarks[bookmark] revs.append(fetchedbookmarks[bookmark]) opts["bookmark"] = realbookmarks opts["rev"] = [rev for rev in revs if rev not in scratchbookmarks] # Pulling revisions that were filtered results in a error. # Let's revive them. unfi = repo.unfiltered() torevive = [] for rev in opts.get("rev", []): try: repo[rev] except error.FilteredRepoLookupError: torevive.append(rev) except error.RepoLookupError: pass if obsolete.isenabled(repo, obsolete.createmarkersopt): obsolete.revive([unfi[r] for r in torevive]) visibility.add(repo, [unfi[r].node() for r in torevive]) if scratchbookmarks or unknownnodes: # Set anyincoming to True extensions.wrapfunction(discovery, "findcommonincoming", _findcommonincoming) try: # Remote scratch bookmarks will be deleted because remotenames doesn't # know about them. Let's save it before pull and restore after remotescratchbookmarks = bookmarks.readremotebookmarks( ui, repo, source) result = orig(ui, repo, source, **opts) # TODO(stash): race condition is possible # if scratch bookmarks was updated right after orig. # But that's unlikely and shouldn't be harmful. with repo.wlock(), repo.lock(), repo.transaction("pull"): if bookmarks.remotebookmarksenabled(ui): remotescratchbookmarks.update(scratchbookmarks) bookmarks.saveremotebookmarks(repo, remotescratchbookmarks, source) else: bookmarks.savelocalbookmarks(repo, scratchbookmarks) return result finally: if scratchbookmarks: extensions.unwrapfunction(discovery, "findcommonincoming")
def revealcommits(repo, rev): ctxs = list(repo.set(rev)) if obsolete.isenabled(repo, obsolete.createmarkersopt): obsolete.revive(ctxs) visibility.add(repo, [ctx.node() for ctx in ctxs])
def _applycloudchanges(repo, remotepath, lastsyncstate, cloudrefs, maxage, state, tr): pullcmd, pullopts = ccutil.getcommandandoptions("pull|pul") try: remotenames = extensions.find("remotenames") except KeyError: remotenames = None # Pull all the new heads and any bookmark hashes we don't have. We need to # filter cloudrefs before pull as pull doesn't check if a rev is present # locally. unfi = repo.unfiltered() newheads = [head for head in cloudrefs.heads if head not in unfi] if maxage is not None and maxage >= 0: mindate = time.time() - maxage * 86400 omittedheads = [ head for head in newheads if head in cloudrefs.headdates and cloudrefs.headdates[head] < mindate ] if omittedheads: repo.ui.status( _("omitting heads that are older than %d days:\n") % maxage) for head in omittedheads: headdatestr = util.datestr( util.makedate(cloudrefs.headdates[head])) repo.ui.status(_(" %s from %s\n") % (head[:12], headdatestr)) newheads = [head for head in newheads if head not in omittedheads] else: omittedheads = [] omittedbookmarks = [] newvisibleheads = None if visibility.tracking(repo): localheads = _getheads(repo) localheadsset = set(localheads) cloudheads = [ head for head in cloudrefs.heads if head not in omittedheads ] cloudheadsset = set(cloudheads) if localheadsset != cloudheadsset: oldvisibleheads = [ head for head in lastsyncstate.heads if head not in lastsyncstate.omittedheads ] newvisibleheads = util.removeduplicates(oldvisibleheads + cloudheads + localheads) toremove = { head for head in oldvisibleheads if head not in localheadsset or head not in cloudheadsset } newvisibleheads = [ head for head in newvisibleheads if head not in toremove ] remotebookmarknodes = [] newremotebookmarks = {} if _isremotebookmarkssyncenabled(repo.ui): newremotebookmarks = _processremotebookmarks(repo, cloudrefs.remotebookmarks, lastsyncstate) # Pull public commits, which remote bookmarks point to, if they are not # present locally. for node in newremotebookmarks.values(): if node not in unfi: remotebookmarknodes.append(node) try: snapshot = extensions.find("snapshot") except KeyError: snapshot = None addedsnapshots = [] removedsnapshots = [] newsnapshots = lastsyncstate.snapshots else: addedsnapshots = [ s for s in cloudrefs.snapshots if s not in lastsyncstate.snapshots ] removedsnapshots = [ s for s in lastsyncstate.snapshots if s not in cloudrefs.snapshots ] newsnapshots = cloudrefs.snapshots # TODO(alexeyqu): pull snapshots separately newheads += addedsnapshots backuplock.progresspulling(repo, [nodemod.bin(node) for node in newheads]) if remotebookmarknodes or newheads: # Partition the heads into groups we can pull together. headgroups = ([remotebookmarknodes] if remotebookmarknodes else []) + _partitionheads(newheads, cloudrefs.headdates) def disabled(*args, **kwargs): pass # Disable pulling of obsmarkers wrapobs = extensions.wrappedfunction(exchange, "_pullobsolete", disabled) # Disable pulling of bookmarks wrapbook = extensions.wrappedfunction(exchange, "_pullbookmarks", disabled) # Disable pulling of remote bookmarks if remotenames: wrapremotenames = extensions.wrappedfunction( remotenames, "pullremotenames", disabled) else: wrapremotenames = util.nullcontextmanager() # Disable automigration and prefetching of trees configoverride = repo.ui.configoverride( { ("pull", "automigrate"): False, ("treemanifest", "pullprefetchrevs"): "" }, "cloudsyncpull", ) prog = progress.bar(repo.ui, _("pulling from commit cloud"), total=len(headgroups)) with wrapobs, wrapbook, wrapremotenames, configoverride, prog: for index, headgroup in enumerate(headgroups): headgroupstr = " ".join([head[:12] for head in headgroup]) repo.ui.status(_("pulling %s\n") % headgroupstr) prog.value = (index, headgroupstr) pullopts["rev"] = headgroup pullcmd(repo.ui, repo, remotepath, **pullopts) repo.connectionpool.close() omittedbookmarks.extend( _mergebookmarks(repo, tr, cloudrefs.bookmarks, lastsyncstate)) if _isremotebookmarkssyncenabled(repo.ui): _updateremotebookmarks(repo, tr, newremotebookmarks) if snapshot: with repo.lock(), repo.transaction("sync-snapshots") as tr: repo.snapshotlist.update(tr, addnodes=addedsnapshots, removenodes=removedsnapshots) _mergeobsmarkers(repo, tr, cloudrefs.obsmarkers) if newvisibleheads is not None: visibility.setvisibleheads(repo, [nodemod.bin(n) for n in newvisibleheads]) # Obsmarker sharing is unreliable. Some of the commits that should now # be visible might be hidden still, and some commits that should be # hidden might still be visible. Create local obsmarkers to resolve # this. if obsolete.isenabled(repo, obsolete.createmarkersopt): unfi = repo.unfiltered() # Commits that are only visible in the cloud are commits that are # ancestors of the cloud heads but are hidden locally. cloudvisibleonly = list( unfi.set( "not public() & ::%ls & hidden()", [head for head in cloudrefs.heads if head not in omittedheads], )) # Commits that are only hidden in the cloud are commits that are # ancestors of the previous cloud heads that are not ancestors of the # current cloud heads, but have not been hidden or obsoleted locally. cloudhiddenonly = list( unfi.set( "(not public() & ::%ls) - (not public() & ::%ls) - hidden() - obsolete()", [ head for head in lastsyncstate.heads if head not in lastsyncstate.omittedheads ], [head for head in cloudrefs.heads if head not in omittedheads], )) if cloudvisibleonly or cloudhiddenonly: msg = _( "detected obsmarker inconsistency (fixing by obsoleting [%s] and reviving [%s])\n" ) % ( ", ".join( [nodemod.short(ctx.node()) for ctx in cloudhiddenonly]), ", ".join( [nodemod.short(ctx.node()) for ctx in cloudvisibleonly]), ) repo.ui.log("commitcloud_sync", msg) repo.ui.warn(msg) repo._commitcloudskippendingobsmarkers = True with repo.lock(): obsolete.createmarkers(repo, [(ctx, ()) for ctx in cloudhiddenonly]) obsolete.revive(cloudvisibleonly) repo._commitcloudskippendingobsmarkers = False # We have now synced the repo to the cloud version. Store this. logsyncop( repo, "from_cloud", cloudrefs.version, lastsyncstate.heads, cloudrefs.heads, lastsyncstate.bookmarks, cloudrefs.bookmarks, lastsyncstate.remotebookmarks, newremotebookmarks, lastsyncstate.snapshots, newsnapshots, ) lastsyncstate.update( tr, cloudrefs.version, cloudrefs.heads, cloudrefs.bookmarks, omittedheads, omittedbookmarks, maxage, newremotebookmarks, newsnapshots, ) # Also update backup state. These new heads are already backed up, # otherwise the server wouldn't have told us about them. state.update([nodemod.bin(head) for head in newheads], tr)