Mercurial > hg
changeset 28129:7c40b4b7f8f1
automv: new experimental extension
Automatically detect moves and record them at commit time.
This extension was originally developed at
https://bitbucket.org/facebook/hg-experimental
author | Martijn Pieters <mjpieters@fb.com> |
---|---|
date | Mon, 08 Feb 2016 13:52:51 +0000 |
parents | 92f2c69ee5a5 |
children | 47317570ab8c |
files | hgext/automv.py tests/test-automv.t |
diffstat | 2 files changed, 368 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgext/automv.py Mon Feb 08 13:52:51 2016 +0000 @@ -0,0 +1,83 @@ +# automv.py +# +# Copyright 2013-2016 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. +"""Check for unrecorded moves at commit time (EXPERIMENTAL) + +This extension checks at commit/amend time if any of the committed files +comes from an unrecorded mv. + +The threshold at which a file is considered a move can be set with the +``automv.similarity`` config option; the default value is 1.00. + +""" +from __future__ import absolute_import + +from mercurial import ( + commands, + copies, + extensions, + scmutil, + similar +) +from mercurial.i18n import _ + +def extsetup(ui): + entry = extensions.wrapcommand( + commands.table, 'commit', mvcheck) + entry[1].append( + ('', 'no-automv', None, + _('disable automatic file move detection'))) + +def mvcheck(orig, ui, repo, *pats, **opts): + disabled = opts.pop('no_automv', False) + if not disabled: + threshold = float(ui.config('automv', 'similarity', '1.00')) + if threshold > 0: + match = scmutil.match(repo[None], pats, opts) + added, removed = _interestingfiles(repo, match) + renames = _findrenames(repo, match, added, removed, threshold) + _markchanges(repo, renames) + + # developer config: automv.testmode + if not ui.configbool('automv', 'testmode'): + return orig(ui, repo, *pats, **opts) + +def _interestingfiles(repo, matcher): + stat = repo.status(repo['.'], repo[None], matcher) + added = stat[1] + removed = stat[2] + + copy = copies._forwardcopies(repo['.'], repo[None], matcher) + # remove the copy files for which we already have copy info + added = [f for f in added if f not in copy] + + return added, removed + +def _findrenames(repo, matcher, added, removed, similarity): + """Find renames from removed files of the current commit/amend files + to the added ones""" + renames = {} + if similarity > 0: + for src, dst, score in similar.findrenames( + repo, added, removed, similarity): + if repo.ui.verbose: + repo.ui.status( + _('detected move of %s as %s (%d%% similar)\n') % ( + matcher.rel(src), matcher.rel(dst), score * 100)) + renames[dst] = src + if renames: + repo.ui.status(_('detected move of %d files\n') % len(renames)) + return renames + +def _markchanges(repo, renames): + """Marks the files in renames as copied.""" + wctx = repo[None] + wlock = repo.wlock() + try: + for dst, src in renames.iteritems(): + wctx.copy(src, dst) + finally: + wlock.release()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-automv.t Mon Feb 08 13:52:51 2016 +0000 @@ -0,0 +1,285 @@ + $ cat >> $HGRCPATH << EOF + > [extensions] + > automv= + > rebase= + > EOF + +Setup repo + + $ hg init repo + $ cd repo + +Test automv command for commit + + $ echo 'foo' > a.txt + $ hg add a.txt + $ hg commit -m 'init repo with a' + +mv/rm/add + $ mv a.txt b.txt + $ hg rm a.txt + $ hg add b.txt + $ hg status -C + A b.txt + R a.txt + $ hg commit -m 'msg' + detected move of 1 files + $ hg status --change . -C + A b.txt + a.txt + R a.txt + $ hg up -r 0 + 1 files updated, 0 files merged, 1 files removed, 0 files unresolved + +mv/rm/add/modif + $ mv a.txt b.txt + $ hg rm a.txt + $ hg add b.txt + $ printf '\nfoo\n' >> b.txt + $ hg status -C + A b.txt + R a.txt + $ hg commit -m 'msg' + created new head + $ hg status --change . -C + A b.txt + R a.txt + $ hg up -r 0 + 1 files updated, 0 files merged, 1 files removed, 0 files unresolved + +mv/rm/add/modif/changethreshold + $ mv a.txt b.txt + $ hg rm a.txt + $ hg add b.txt + $ printf '\nfoo\n' >> b.txt + $ hg status -C + A b.txt + R a.txt + $ hg commit --config automv.similarity='0.6' -m 'msg' + detected move of 1 files + created new head + $ hg status --change . -C + A b.txt + a.txt + R a.txt + $ hg up -r 0 + 1 files updated, 0 files merged, 1 files removed, 0 files unresolved + +mv + $ mv a.txt b.txt + $ hg status -C + ! a.txt + ? b.txt + $ hg commit -m 'msg' + nothing changed (1 missing files, see 'hg status') + [1] + $ hg status -C + ! a.txt + ? b.txt + $ hg revert -aqC + $ rm b.txt + +mv/rm/add/notincommitfiles + $ mv a.txt b.txt + $ hg rm a.txt + $ hg add b.txt + $ echo 'bar' > c.txt + $ hg add c.txt + $ hg status -C + A b.txt + A c.txt + R a.txt + $ hg commit c.txt -m 'msg' + created new head + $ hg status --change . -C + A c.txt + $ hg status -C + A b.txt + R a.txt + $ hg up -r 0 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ hg rm a.txt + $ echo 'bar' > c.txt + $ hg add c.txt + $ hg commit -m 'msg' + detected move of 1 files + created new head + $ hg status --change . -C + A b.txt + a.txt + A c.txt + R a.txt + $ hg up -r 0 + 1 files updated, 0 files merged, 2 files removed, 0 files unresolved + +mv/rm/add/--no-automv + $ mv a.txt b.txt + $ hg rm a.txt + $ hg add b.txt + $ hg status -C + A b.txt + R a.txt + $ hg commit --no-automv -m 'msg' + created new head + $ hg status --change . -C + A b.txt + R a.txt + $ hg up -r 0 + 1 files updated, 0 files merged, 1 files removed, 0 files unresolved + +Test automv command for commit --amend + +mv/rm/add + $ echo 'c' > c.txt + $ hg add c.txt + $ hg commit -m 'revision to amend to' + created new head + $ mv a.txt b.txt + $ hg rm a.txt + $ hg add b.txt + $ hg status -C + A b.txt + R a.txt + $ hg commit --amend -m 'amended' + detected move of 1 files + saved backup bundle to $TESTTMP/repo/.hg/strip-backup/*-amend-backup.hg (glob) + $ hg status --change . -C + A b.txt + a.txt + A c.txt + R a.txt + $ hg up -r 0 + 1 files updated, 0 files merged, 2 files removed, 0 files unresolved + +mv/rm/add/modif + $ echo 'c' > c.txt + $ hg add c.txt + $ hg commit -m 'revision to amend to' + created new head + $ mv a.txt b.txt + $ hg rm a.txt + $ hg add b.txt + $ printf '\nfoo\n' >> b.txt + $ hg status -C + A b.txt + R a.txt + $ hg commit --amend -m 'amended' + saved backup bundle to $TESTTMP/repo/.hg/strip-backup/*-amend-backup.hg (glob) + $ hg status --change . -C + A b.txt + A c.txt + R a.txt + $ hg up -r 0 + 1 files updated, 0 files merged, 2 files removed, 0 files unresolved + +mv/rm/add/modif/changethreshold + $ echo 'c' > c.txt + $ hg add c.txt + $ hg commit -m 'revision to amend to' + created new head + $ mv a.txt b.txt + $ hg rm a.txt + $ hg add b.txt + $ printf '\nfoo\n' >> b.txt + $ hg status -C + A b.txt + R a.txt + $ hg commit --amend --config automv.similarity='0.6' -m 'amended' + detected move of 1 files + saved backup bundle to $TESTTMP/repo/.hg/strip-backup/*-amend-backup.hg (glob) + $ hg status --change . -C + A b.txt + a.txt + A c.txt + R a.txt + $ hg up -r 0 + 1 files updated, 0 files merged, 2 files removed, 0 files unresolved + +mv + $ echo 'c' > c.txt + $ hg add c.txt + $ hg commit -m 'revision to amend to' + created new head + $ mv a.txt b.txt + $ hg status -C + ! a.txt + ? b.txt + $ hg commit --amend -m 'amended' + saved backup bundle to $TESTTMP/repo/.hg/strip-backup/*-amend-backup.hg (glob) + $ hg status -C + ! a.txt + ? b.txt + $ hg up -Cr 0 + 1 files updated, 0 files merged, 1 files removed, 0 files unresolved + +mv/rm/add/notincommitfiles + $ echo 'c' > c.txt + $ hg add c.txt + $ hg commit -m 'revision to amend to' + created new head + $ mv a.txt b.txt + $ hg rm a.txt + $ hg add b.txt + $ echo 'bar' > d.txt + $ hg add d.txt + $ hg status -C + A b.txt + A d.txt + R a.txt + $ hg commit --amend -m 'amended' d.txt + saved backup bundle to $TESTTMP/repo/.hg/strip-backup/*-amend-backup.hg (glob) + $ hg status --change . -C + A c.txt + A d.txt + $ hg status -C + A b.txt + R a.txt + $ hg commit --amend -m 'amended' + detected move of 1 files + saved backup bundle to $TESTTMP/repo/.hg/strip-backup/*-amend-backup.hg (glob) + $ hg status --change . -C + A b.txt + a.txt + A c.txt + A d.txt + R a.txt + $ hg up -r 0 + 1 files updated, 0 files merged, 3 files removed, 0 files unresolved + +mv/rm/add/--no-automv + $ echo 'c' > c.txt + $ hg add c.txt + $ hg commit -m 'revision to amend to' + created new head + $ mv a.txt b.txt + $ hg rm a.txt + $ hg add b.txt + $ hg status -C + A b.txt + R a.txt + $ hg commit --amend -m 'amended' --no-automv + saved backup bundle to $TESTTMP/repo/.hg/strip-backup/*-amend-backup.hg (glob) + $ hg status --change . -C + A b.txt + A c.txt + R a.txt + $ hg up -r 0 + 1 files updated, 0 files merged, 2 files removed, 0 files unresolved + +mv/rm/commit/add/amend + $ echo 'c' > c.txt + $ hg add c.txt + $ hg commit -m 'revision to amend to' + created new head + $ mv a.txt b.txt + $ hg rm a.txt + $ hg status -C + R a.txt + ? b.txt + $ hg commit -m "removed a" + $ hg add b.txt + $ hg commit --amend -m 'amended' + saved backup bundle to $TESTTMP/repo/.hg/strip-backup/*-amend-backup.hg (glob) + $ hg status --change . -C + A b.txt + R a.txt