mirror of
https://github.com/facebook/sapling.git
synced 2024-10-11 01:07:15 +03:00
db56b7802d
Summary: When a pull causes commits on the tip of a branch to be obsoleted due to landing, make sure that parent commits that were landed in previous pulls also get hidden. The parent commits would already have obsolete markers, but they would have been inhibited because there were non-obsolete children. Once their children are landed their obsolete markers can now be deinhibited. Test Plan: Added a new test case. Reviewers: #sourcecontrol, durham, lcharignon, ikostia, pyd, ttung Subscribers: net-systems-diffs@, yogeshwer, mjpieters Differential Revision: https://phabricator.fb.com/D3028597
124 lines
3.9 KiB
Python
124 lines
3.9 KiB
Python
# pullcreatemarkers.py - create obsolescence markers on pull for better rebases
|
|
#
|
|
# Copyright 2015 Facebook, Inc.
|
|
#
|
|
# This software may be used and distributed according to the terms of the
|
|
# GNU General Public License version 2 or any later version.
|
|
#
|
|
# The goal of this extensions is to create obsolescence markers locally for
|
|
# commits previously landed.
|
|
# It uses the phabricator revision number in the commit message to detect the
|
|
# relationship between a draft commit and its landed counterpart.
|
|
# Thanks to these markers, less information is displayed and rebases can have
|
|
# less irrelevant conflicts.
|
|
|
|
import re
|
|
from mercurial import commands
|
|
from mercurial import obsolete
|
|
from mercurial import phases
|
|
from mercurial import extensions
|
|
|
|
def getdiff(rev):
|
|
m = re.findall("Differential Revision: https://phabricator.fb.com/D(.*)",
|
|
rev.description(),
|
|
re.MULTILINE)
|
|
if m:
|
|
try:
|
|
diffnum = int(m[0])
|
|
return diffnum
|
|
except ValueError:
|
|
return None
|
|
else:
|
|
return None
|
|
|
|
def extsetup(ui):
|
|
extensions.wrapcommand(commands.table, 'pull', _pull)
|
|
|
|
def _pull(orig, ui, repo, *args, **opts):
|
|
if not obsolete.isenabled(repo, obsolete.createmarkersopt):
|
|
return orig(ui, repo, *args, **opts)
|
|
maxrevbeforepull = len(repo.changelog)
|
|
r = orig(ui, repo, *args, **opts)
|
|
maxrevafterpull = len(repo.changelog)
|
|
|
|
# Collect the diff number of the landed diffs
|
|
landeddiffs = {}
|
|
for rev in range(maxrevbeforepull, maxrevafterpull):
|
|
n = repo[rev]
|
|
if n.phase() == phases.public:
|
|
diff = getdiff(n)
|
|
if diff is not None:
|
|
landeddiffs[diff] = n
|
|
|
|
if not landeddiffs:
|
|
return r
|
|
|
|
# Try to find match with the drafts
|
|
tocreate = []
|
|
unfiltered = repo.unfiltered()
|
|
for rev in unfiltered.revs("draft() - hidden()"):
|
|
n = unfiltered[rev]
|
|
diff = getdiff(n)
|
|
if diff in landeddiffs:
|
|
tocreate.append((n, (landeddiffs[diff],)))
|
|
|
|
if not tocreate:
|
|
return r
|
|
|
|
inhibit, deinhibitnodes = _deinhibitancestors(unfiltered, tocreate)
|
|
|
|
with unfiltered.lock() as l:
|
|
with unfiltered.transaction('pullcreatemarkers') as t:
|
|
obsolete.createmarkers(unfiltered, tocreate)
|
|
if deinhibitnodes:
|
|
inhibit._deinhibitmarkers(unfiltered, deinhibitnodes)
|
|
|
|
return r
|
|
|
|
def _deinhibitancestors(repo, markers):
|
|
"""Compute the set of commits that already have obsolescence markers
|
|
which were possibly inhibited, and should be deinhibited because of this
|
|
new pull operation.
|
|
|
|
Returns a tuple of (inhibit module, node set).
|
|
Returns (None, None) if the inhibit extension is not enabled."""
|
|
try:
|
|
inhibit = extensions.find('inhibit')
|
|
except KeyError:
|
|
return None, None
|
|
|
|
if not inhibit._inhibitenabled(repo):
|
|
return None, None
|
|
|
|
# Commits for which we should deinhibit obsolescence markers
|
|
deinhibitset = set()
|
|
# Commits whose parents we should process
|
|
toprocess = set([ctx for ctx, successor in markers])
|
|
# Commits that are already in toprocess or have already been processed
|
|
seen = toprocess.copy()
|
|
# Commits that we deinhibit obsolescence markers for
|
|
while toprocess:
|
|
ctx = toprocess.pop()
|
|
for p in ctx.parents():
|
|
if p in seen:
|
|
continue
|
|
seen.add(p)
|
|
if _isobsolete(p):
|
|
deinhibitset.add(p.node())
|
|
toprocess.add(p)
|
|
|
|
return inhibit, deinhibitset
|
|
|
|
def _isobsolete(ctx):
|
|
# A commit is obsolete if it has at least one successor marker.
|
|
#
|
|
# successormarkers() returns a generator. generators unfortunately
|
|
# evaluate to True even if they are "empty", so pull one element off and
|
|
# see if anything exists or not.
|
|
i = obsolete.successormarkers(ctx)
|
|
try:
|
|
next(i)
|
|
except StopIteration:
|
|
return False
|
|
return True
|