##// END OF EJS Templates
revlog: guarantee that p1 != null if a non-null parent exists...
revlog: guarantee that p1 != null if a non-null parent exists This change does not affect the hashing (which already did this transformation), but can change the log output in the rare case where this behavior was observed in repositories. The change can simplify iteration code where regular changesets and merges are distinct branches. Differential Revision: https://phab.mercurial-scm.org/D10150

File last commit:

r46554:89a2afe3 default
r47537:49fd21f3 default
Show More
similar.py
133 lines | 4.0 KiB | text/x-python | PythonLexer
David Greenaway
Move 'findrenames' code into its own file....
r11059 # similar.py - mechanisms for finding similar files
#
# Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
Gregory Szorc
similar: use absolute_import
r27359 from __future__ import absolute_import
from .i18n import _
Gregory Szorc
py3: finish porting iteritems() to pycompat and remove source transformer...
r43376 from . import (
mdiff,
pycompat,
)
Augie Fackler
formatting: blacken the codebase...
r43346
David Greenaway
Move 'findrenames' code into its own file....
r11059
David Greenaway
findrenames: Optimise "addremove -s100" by matching files by their SHA1 hashes....
r11060 def _findexactmatches(repo, added, removed):
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 """find renamed files that have no changes
David Greenaway
findrenames: Optimise "addremove -s100" by matching files by their SHA1 hashes....
r11060
Takes a list of new filectxs and a list of removed filectxs, and yields
(before, after) tuples of exact matches.
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 """
Yuya Nishihara
similar: use cheaper hash() function to test exact matches...
r31584 # Build table of removed files: {hash(fctx.data()): [fctx, ...]}.
# We use hash() to discard fctx.data() from memory.
David Greenaway
findrenames: Optimise "addremove -s100" by matching files by their SHA1 hashes....
r11060 hashes = {}
Augie Fackler
formatting: blacken the codebase...
r43346 progress = repo.ui.makeprogress(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(b'searching for exact renames'),
Augie Fackler
formatting: blacken the codebase...
r43346 total=(len(added) + len(removed)),
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 unit=_(b'files'),
Augie Fackler
formatting: blacken the codebase...
r43346 )
Martin von Zweigbergk
similar: use progress helper...
r38367 for fctx in removed:
progress.increment()
Yuya Nishihara
similar: use cheaper hash() function to test exact matches...
r31584 h = hash(fctx.data())
if h not in hashes:
hashes[h] = [fctx]
else:
hashes[h].append(fctx)
David Greenaway
findrenames: Optimise "addremove -s100" by matching files by their SHA1 hashes....
r11060
# For each added file, see if it corresponds to a removed file.
Martin von Zweigbergk
similar: use progress helper...
r38367 for fctx in added:
progress.increment()
FUJIWARA Katsunori
similar: compare between actual file contents for exact identity...
r31210 adata = fctx.data()
Yuya Nishihara
similar: use cheaper hash() function to test exact matches...
r31584 h = hash(adata)
for rfctx in hashes.get(h, []):
FUJIWARA Katsunori
similar: compare between actual file contents for exact identity...
r31210 # compare between actual file contents for exact identity
if adata == rfctx.data():
yield (rfctx, fctx)
Yuya Nishihara
similar: use cheaper hash() function to test exact matches...
r31584 break
David Greenaway
findrenames: Optimise "addremove -s100" by matching files by their SHA1 hashes....
r11060
# Done
Martin von Zweigbergk
progress: hide update(None) in a new complete() method...
r38392 progress.complete()
David Greenaway
findrenames: Optimise "addremove -s100" by matching files by their SHA1 hashes....
r11060
Augie Fackler
formatting: blacken the codebase...
r43346
Sean Farley
similar: move score function to module level...
r30805 def _ctxdata(fctx):
# lazily load text
orig = fctx.data()
return orig, mdiff.splitnewlines(orig)
Augie Fackler
formatting: blacken the codebase...
r43346
Pierre-Yves David
similar: remove caching from the module level...
r30809 def _score(fctx, otherdata):
orig, lines = otherdata
text = fctx.data()
Yuya Nishihara
bdiff: proxy through mdiff module...
r32201 # mdiff.blocks() returns blocks of matching lines
Sean Farley
similar: move score function to module level...
r30805 # count the number of bytes in each
equal = 0
Yuya Nishihara
bdiff: proxy through mdiff module...
r32201 matches = mdiff.blocks(text, orig)
Sean Farley
similar: move score function to module level...
r30805 for x1, x2, y1, y2 in matches:
for line in lines[y1:y2]:
equal += len(line)
lengths = len(text) + len(orig)
return equal * 2.0 / lengths
Augie Fackler
formatting: blacken the codebase...
r43346
Pierre-Yves David
similar: remove caching from the module level...
r30809 def score(fctx1, fctx2):
return _score(fctx1, _ctxdata(fctx2))
Augie Fackler
formatting: blacken the codebase...
r43346
David Greenaway
findrenames: Optimise "addremove -s100" by matching files by their SHA1 hashes....
r11060 def _findsimilarmatches(repo, added, removed, threshold):
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 """find potentially renamed files based on similar file content
David Greenaway
findrenames: Optimise "addremove -s100" by matching files by their SHA1 hashes....
r11060
Takes a list of new filectxs and a list of removed filectxs, and yields
(before, after, score) tuples of partial matches.
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 """
David Greenaway
Move 'findrenames' code into its own file....
r11059 copies = {}
Augie Fackler
formatting: blacken the codebase...
r43346 progress = repo.ui.makeprogress(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(b'searching for similar files'), unit=_(b'files'), total=len(removed)
Augie Fackler
formatting: blacken the codebase...
r43346 )
Martin von Zweigbergk
similar: use progress helper...
r38414 for r in removed:
progress.increment()
Pierre-Yves David
similar: remove caching from the module level...
r30809 data = None
David Greenaway
Move 'findrenames' code into its own file....
r11059 for a in added:
bestscore = copies.get(a, (None, threshold))[1]
Pierre-Yves David
similar: remove caching from the module level...
r30809 if data is None:
data = _ctxdata(r)
myscore = _score(a, data)
Yuya Nishihara
similar: take the first match instead of the last...
r31583 if myscore > bestscore:
David Greenaway
Move 'findrenames' code into its own file....
r11059 copies[a] = (r, myscore)
Martin von Zweigbergk
similar: use progress helper...
r38414 progress.complete()
David Greenaway
Move 'findrenames' code into its own file....
r11059
Gregory Szorc
py3: finish porting iteritems() to pycompat and remove source transformer...
r43376 for dest, v in pycompat.iteritems(copies):
Sean Farley
similar: rename local variable to not collide with previous...
r30791 source, bscore = v
yield source, dest, bscore
David Greenaway
Move 'findrenames' code into its own file....
r11059
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
similar: do not look up and create filectx more than once...
r31582 def _dropempty(fctxs):
return [x for x in fctxs if x.size() > 0]
Augie Fackler
formatting: blacken the codebase...
r43346
David Greenaway
findrenames: Optimise "addremove -s100" by matching files by their SHA1 hashes....
r11060 def findrenames(repo, added, removed, threshold):
'''find renamed files -- yields (before, after, score) tuples'''
Yuya Nishihara
similar: use common names for changectx variables...
r31581 wctx = repo[None]
pctx = wctx.p1()
David Greenaway
Move 'findrenames' code into its own file....
r11059
David Greenaway
findrenames: Optimise "addremove -s100" by matching files by their SHA1 hashes....
r11060 # Zero length files will be frequently unrelated to each other, and
# tracking the deletion/addition of such a file will probably cause more
# harm than good. We strip them out here to avoid matching them later on.
Yuya Nishihara
similar: do not look up and create filectx more than once...
r31582 addedfiles = _dropempty(wctx[fp] for fp in sorted(added))
removedfiles = _dropempty(pctx[fp] for fp in sorted(removed) if fp in pctx)
David Greenaway
findrenames: Optimise "addremove -s100" by matching files by their SHA1 hashes....
r11060
# Find exact matches.
Yuya Nishihara
similar: get rid of quadratic addedfiles.remove()...
r31580 matchedfiles = set()
for (a, b) in _findexactmatches(repo, addedfiles, removedfiles):
matchedfiles.add(b)
David Greenaway
findrenames: Optimise "addremove -s100" by matching files by their SHA1 hashes....
r11060 yield (a.path(), b.path(), 1.0)
# If the user requested similar files to be matched, search for them also.
if threshold < 1.0:
Yuya Nishihara
similar: get rid of quadratic addedfiles.remove()...
r31580 addedfiles = [x for x in addedfiles if x not in matchedfiles]
Augie Fackler
formatting: blacken the codebase...
r43346 for (a, b, score) in _findsimilarmatches(
repo, addedfiles, removedfiles, threshold
):
David Greenaway
findrenames: Optimise "addremove -s100" by matching files by their SHA1 hashes....
r11060 yield (a.path(), b.path(), score)