tests/test-copytrace-heuristics.t
author Arun Kulshreshtha <akulshreshtha@janestreet.com>
Tue, 30 Aug 2022 15:29:55 -0400
changeset 49491 c6a1beba27e9
parent 45826 21733e8c924f
permissions -rw-r--r--
bisect: avoid copying ancestor list for non-merge commits During a bisection, hg needs to compute a list of all ancestors for every candidate commit. This is accomplished via a bottom-up traversal of the set of candidates, during which each revision's ancestor list is populated using the ancestor list of its parent(s). Previously, this involved copying the entire list, which could be very long in if the bisection range was large. To help improve this, we can observe that each candidate commit is visited exactly once, at which point its ancestor list is copied into its children's lists and then dropped. In the case of non-merge commits, a commit's ancestor list consists exactly of its parent's list plus itself. This means that we can trivially reuse the parent's existing list for one of its non-merge children, which avoids copying entirely if that commit is the parent's only child. This makes bisections over linear ranges of commits much faster. During some informal testing in the large publicly-available `mozilla-central` repository, this noticeably sped up bisections over large ranges of history: Setup: $ cd mozilla-central $ hg bisect --reset $ hg bisect --good 0 $ hg log -r tip -T '{rev}\n' 628417 Test: $ time hg bisect --bad tip --noupdate Before: real 3m35.927s user 3m35.553s sys 0m0.319s After: real 1m41.142s user 1m40.810s sys 0m0.285s

Test for the heuristic copytracing algorithm
============================================

  $ cat >> $TESTTMP/copytrace.sh << '__EOF__'
  > initclient() {
  > cat >> $1/.hg/hgrc <<EOF
  > [experimental]
  > copytrace = heuristics
  > copytrace.sourcecommitlimit = -1
  > EOF
  > }
  > __EOF__
  $ . "$TESTTMP/copytrace.sh"

  $ cat >> $HGRCPATH << EOF
  > [extensions]
  > rebase=
  > [alias]
  > l = log -G -T 'rev: {rev}\ndesc: {desc}\n'
  > pl = log -G -T 'rev: {rev}, phase: {phase}\ndesc: {desc}\n'
  > EOF

NOTE: calling initclient() set copytrace.sourcecommitlimit=-1 as we want to
prevent the full copytrace algorithm to run and test the heuristic algorithm
without complexing the test cases with public and draft commits.

Check filename heuristics (same dirname and same basename)
----------------------------------------------------------

  $ hg init repo
  $ initclient repo
  $ cd repo
  $ echo a > a
  $ mkdir dir
  $ echo a > dir/file.txt
  $ hg addremove
  adding a
  adding dir/file.txt
  $ hg ci -m initial
  $ hg mv a b
  $ hg mv -q dir dir2
  $ hg ci -m 'mv a b, mv dir/ dir2/'
  $ hg up -q 0
  $ echo b > a
  $ echo b > dir/file.txt
  $ hg ci -qm 'mod a, mod dir/file.txt'

  $ hg l
  @  rev: 2
  |  desc: mod a, mod dir/file.txt
  | o  rev: 1
  |/   desc: mv a b, mv dir/ dir2/
  o  rev: 0
     desc: initial

  $ hg rebase -s . -d 1
  rebasing 2:557f403c0afd tip "mod a, mod dir/file.txt"
  merging b and a to b
  merging dir2/file.txt and dir/file.txt to dir2/file.txt
  saved backup bundle to $TESTTMP/repo/.hg/strip-backup/557f403c0afd-9926eeff-rebase.hg
  $ cd ..
  $ rm -rf repo

Make sure filename heuristics do not when they are not related
--------------------------------------------------------------

  $ hg init repo
  $ initclient repo
  $ cd repo
  $ echo 'somecontent' > a
  $ hg add a
  $ hg ci -m initial
  $ hg rm a
  $ echo 'completelydifferentcontext' > b
  $ hg add b
  $ hg ci -m 'rm a, add b'
  $ hg up -q 0
  $ printf 'somecontent\nmoarcontent' > a
  $ hg ci -qm 'mode a'

  $ hg l
  @  rev: 2
  |  desc: mode a
  | o  rev: 1
  |/   desc: rm a, add b
  o  rev: 0
     desc: initial

  $ hg rebase -s . -d 1
  rebasing 2:d526312210b9 tip "mode a"
  file 'a' was deleted in local [dest] but was modified in other [source].
  You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
  What do you want to do? u
  unresolved conflicts (see 'hg resolve', then 'hg rebase --continue')
  [240]

  $ cd ..
  $ rm -rf repo

Test when lca didn't modified the file that was moved
-----------------------------------------------------

  $ hg init repo
  $ initclient repo
  $ cd repo
  $ echo 'somecontent' > a
  $ hg add a
  $ hg ci -m initial
  $ echo c > c
  $ hg add c
  $ hg ci -m randomcommit
  $ hg mv a b
  $ hg ci -m 'mv a b'
  $ hg up -q 1
  $ echo b > a
  $ hg ci -qm 'mod a'

  $ hg pl
  @  rev: 3, phase: draft
  |  desc: mod a
  | o  rev: 2, phase: draft
  |/   desc: mv a b
  o  rev: 1, phase: draft
  |  desc: randomcommit
  o  rev: 0, phase: draft
     desc: initial

  $ hg rebase -s . -d 2
  rebasing 3:9d5cf99c3d9f tip "mod a"
  merging b and a to b
  saved backup bundle to $TESTTMP/repo/.hg/strip-backup/9d5cf99c3d9f-f02358cc-rebase.hg
  $ cd ..
  $ rm -rf repo

Rebase "backwards"
------------------

  $ hg init repo
  $ initclient repo
  $ cd repo
  $ echo 'somecontent' > a
  $ hg add a
  $ hg ci -m initial
  $ echo c > c
  $ hg add c
  $ hg ci -m randomcommit
  $ hg mv a b
  $ hg ci -m 'mv a b'
  $ hg up -q 2
  $ echo b > b
  $ hg ci -qm 'mod b'

  $ hg l
  @  rev: 3
  |  desc: mod b
  o  rev: 2
  |  desc: mv a b
  o  rev: 1
  |  desc: randomcommit
  o  rev: 0
     desc: initial

  $ hg rebase -s . -d 0
  rebasing 3:fbe97126b396 tip "mod b"
  merging a and b to a
  saved backup bundle to $TESTTMP/repo/.hg/strip-backup/fbe97126b396-cf5452a1-rebase.hg
  $ cd ..
  $ rm -rf repo

Check a few potential move candidates
-------------------------------------

  $ hg init repo
  $ initclient repo
  $ cd repo
  $ mkdir dir
  $ echo a > dir/a
  $ hg add dir/a
  $ hg ci -qm initial
  $ hg mv dir/a dir/b
  $ hg ci -qm 'mv dir/a dir/b'
  $ mkdir dir2
  $ echo b > dir2/a
  $ hg add dir2/a
  $ hg ci -qm 'create dir2/a'
  $ hg up -q 0
  $ echo b > dir/a
  $ hg ci -qm 'mod dir/a'

  $ hg l
  @  rev: 3
  |  desc: mod dir/a
  | o  rev: 2
  | |  desc: create dir2/a
  | o  rev: 1
  |/   desc: mv dir/a dir/b
  o  rev: 0
     desc: initial

  $ hg rebase -s . -d 2
  rebasing 3:6b2f4cece40f tip "mod dir/a"
  merging dir/b and dir/a to dir/b
  saved backup bundle to $TESTTMP/repo/.hg/strip-backup/6b2f4cece40f-503efe60-rebase.hg
  $ cd ..
  $ rm -rf repo

Test the copytrace.movecandidateslimit with many move candidates
----------------------------------------------------------------

  $ hg init repo
  $ initclient repo
  $ cd repo
  $ echo a > a
  $ hg add a
  $ hg ci -m initial
  $ hg mv a foo
  $ echo a > b
  $ echo a > c
  $ echo a > d
  $ echo a > e
  $ echo a > f
  $ echo a > g
  $ hg add b
  $ hg add c
  $ hg add d
  $ hg add e
  $ hg add f
  $ hg add g
  $ hg ci -m 'mv a foo, add many files'
  $ hg up -q ".^"
  $ echo b > a
  $ hg ci -m 'mod a'
  created new head

  $ hg l
  @  rev: 2
  |  desc: mod a
  | o  rev: 1
  |/   desc: mv a foo, add many files
  o  rev: 0
     desc: initial

With small limit

  $ hg rebase -s 2 -d 1 --config experimental.copytrace.movecandidateslimit=0
  rebasing 2:ef716627c70b tip "mod a"
  skipping copytracing for 'a', more candidates than the limit: 7
  file 'a' was deleted in local [dest] but was modified in other [source].
  You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
  What do you want to do? u
  unresolved conflicts (see 'hg resolve', then 'hg rebase --continue')
  [240]

  $ hg rebase --abort
  rebase aborted

With default limit which is 100

  $ hg rebase -s 2 -d 1
  rebasing 2:ef716627c70b tip "mod a"
  merging foo and a to foo
  saved backup bundle to $TESTTMP/repo/.hg/strip-backup/ef716627c70b-24681561-rebase.hg

  $ cd ..
  $ rm -rf repo

Move file in one branch and delete it in another
-----------------------------------------------

  $ hg init repo
  $ initclient repo
  $ cd repo
  $ echo a > a
  $ hg add a
  $ hg ci -m initial
  $ hg mv a b
  $ hg ci -m 'mv a b'
  $ hg up -q ".^"
  $ hg rm a
  $ hg ci -m 'del a'
  created new head

  $ hg pl
  @  rev: 2, phase: draft
  |  desc: del a
  | o  rev: 1, phase: draft
  |/   desc: mv a b
  o  rev: 0, phase: draft
     desc: initial

  $ hg rebase -s 1 -d 2
  rebasing 1:472e38d57782 "mv a b"
  saved backup bundle to $TESTTMP/repo/.hg/strip-backup/472e38d57782-17d50e29-rebase.hg
  $ hg up -q c492ed3c7e35dcd1dc938053b8adf56e2cfbd062
  $ ls -A
  .hg
  b
  $ cd ..
  $ rm -rf repo

Move a directory in draft branch
--------------------------------

  $ hg init repo
  $ initclient repo
  $ cd repo
  $ mkdir dir
  $ echo a > dir/a
  $ hg add dir/a
  $ hg ci -qm initial
  $ echo b > dir/a
  $ hg ci -qm 'mod dir/a'
  $ hg up -q ".^"
  $ hg mv -q dir/ dir2
  $ hg ci -qm 'mv dir/ dir2/'

  $ hg l
  @  rev: 2
  |  desc: mv dir/ dir2/
  | o  rev: 1
  |/   desc: mod dir/a
  o  rev: 0
     desc: initial

  $ hg rebase -s . -d 1
  rebasing 2:a33d80b6e352 tip "mv dir/ dir2/"
  merging dir/a and dir2/a to dir2/a
  saved backup bundle to $TESTTMP/repo/.hg/strip-backup/a33d80b6e352-fecb9ada-rebase.hg
  $ cd ..
  $ rm -rf server
  $ rm -rf repo

Move file twice and rebase mod on top of moves
----------------------------------------------

  $ hg init repo
  $ initclient repo
  $ cd repo
  $ echo a > a
  $ hg add a
  $ hg ci -m initial
  $ hg mv a b
  $ hg ci -m 'mv a b'
  $ hg mv b c
  $ hg ci -m 'mv b c'
  $ hg up -q 0
  $ echo c > a
  $ hg ci -m 'mod a'
  created new head

  $ hg l
  @  rev: 3
  |  desc: mod a
  | o  rev: 2
  | |  desc: mv b c
  | o  rev: 1
  |/   desc: mv a b
  o  rev: 0
     desc: initial
  $ hg rebase -s . -d 2
  rebasing 3:d41316942216 tip "mod a"
  merging c and a to c
  saved backup bundle to $TESTTMP/repo/.hg/strip-backup/d41316942216-2b5949bc-rebase.hg

  $ cd ..
  $ rm -rf repo

Move file twice and rebase moves on top of mods
-----------------------------------------------

  $ hg init repo
  $ initclient repo
  $ cd repo
  $ echo a > a
  $ hg add a
  $ hg ci -m initial
  $ hg mv a b
  $ hg ci -m 'mv a b'
  $ hg mv b c
  $ hg ci -m 'mv b c'
  $ hg up -q 0
  $ echo c > a
  $ hg ci -m 'mod a'
  created new head
  $ hg l
  @  rev: 3
  |  desc: mod a
  | o  rev: 2
  | |  desc: mv b c
  | o  rev: 1
  |/   desc: mv a b
  o  rev: 0
     desc: initial
  $ hg rebase -s 1 -d .
  rebasing 1:472e38d57782 "mv a b"
  merging a and b to b
  rebasing 2:d3efd280421d "mv b c"
  merging b and c to c
  saved backup bundle to $TESTTMP/repo/.hg/strip-backup/472e38d57782-ab8d3c58-rebase.hg

  $ cd ..
  $ rm -rf repo

Move one file and add another file in the same folder in one branch, modify file in another branch
--------------------------------------------------------------------------------------------------

  $ hg init repo
  $ initclient repo
  $ cd repo
  $ echo a > a
  $ hg add a
  $ hg ci -m initial
  $ hg mv a b
  $ hg ci -m 'mv a b'
  $ echo c > c
  $ hg add c
  $ hg ci -m 'add c'
  $ hg up -q 0
  $ echo b > a
  $ hg ci -m 'mod a'
  created new head

  $ hg l
  @  rev: 3
  |  desc: mod a
  | o  rev: 2
  | |  desc: add c
  | o  rev: 1
  |/   desc: mv a b
  o  rev: 0
     desc: initial

  $ hg rebase -s . -d 2
  rebasing 3:ef716627c70b tip "mod a"
  merging b and a to b
  saved backup bundle to $TESTTMP/repo/.hg/strip-backup/ef716627c70b-24681561-rebase.hg
  $ ls -A
  .hg
  b
  c
  $ cat b
  b
  $ rm -rf repo

Merge test
----------

  $ hg init repo
  $ initclient repo
  $ cd repo
  $ echo a > a
  $ hg add a
  $ hg ci -m initial
  $ echo b > a
  $ hg ci -m 'modify a'
  $ hg up -q 0
  $ hg mv a b
  $ hg ci -m 'mv a b'
  created new head
  $ hg up -q 2

  $ hg l
  @  rev: 2
  |  desc: mv a b
  | o  rev: 1
  |/   desc: modify a
  o  rev: 0
     desc: initial

  $ hg merge 1
  merging b and a to b
  0 files updated, 1 files merged, 0 files removed, 0 files unresolved
  (branch merge, don't forget to commit)
  $ hg ci -m merge
  $ ls -A
  .hg
  b
  $ cd ..
  $ rm -rf repo

Copy and move file
------------------

  $ hg init repo
  $ initclient repo
  $ cd repo
  $ echo a > a
  $ hg add a
  $ hg ci -m initial
  $ hg cp a c
  $ hg mv a b
  $ hg ci -m 'cp a c, mv a b'
  $ hg up -q 0
  $ echo b > a
  $ hg ci -m 'mod a'
  created new head

  $ hg l
  @  rev: 2
  |  desc: mod a
  | o  rev: 1
  |/   desc: cp a c, mv a b
  o  rev: 0
     desc: initial

  $ hg rebase -s . -d 1
  rebasing 2:ef716627c70b tip "mod a"
  merging b and a to b
  merging c and a to c
  saved backup bundle to $TESTTMP/repo/repo/.hg/strip-backup/ef716627c70b-24681561-rebase.hg
  $ ls -A
  .hg
  b
  c
  $ cat b
  b
  $ cat c
  b
  $ cd ..
  $ rm -rf repo

Do a merge commit with many consequent moves in one branch
----------------------------------------------------------

  $ hg init repo
  $ initclient repo
  $ cd repo
  $ echo a > a
  $ hg add a
  $ hg ci -m initial
  $ echo b > a
  $ hg ci -qm 'mod a'
  $ hg up -q ".^"
  $ hg mv a b
  $ hg ci -qm 'mv a b'
  $ hg mv b c
  $ hg ci -qm 'mv b c'
  $ hg up -q 1
  $ hg l
  o  rev: 3
  |  desc: mv b c
  o  rev: 2
  |  desc: mv a b
  | @  rev: 1
  |/   desc: mod a
  o  rev: 0
     desc: initial

  $ hg merge 3
  merging a and c to c
  0 files updated, 1 files merged, 0 files removed, 0 files unresolved
  (branch merge, don't forget to commit)
  $ hg ci -qm 'merge'
  $ hg pl
  @    rev: 4, phase: draft
  |\   desc: merge
  | o  rev: 3, phase: draft
  | |  desc: mv b c
  | o  rev: 2, phase: draft
  | |  desc: mv a b
  o |  rev: 1, phase: draft
  |/   desc: mod a
  o  rev: 0, phase: draft
     desc: initial
  $ ls -A
  .hg
  c
  $ cd ..
  $ rm -rf repo

Test shelve/unshelve
-------------------

  $ hg init repo
  $ initclient repo
  $ cd repo
  $ echo a > a
  $ hg add a
  $ hg ci -m initial
  $ echo b > a
  $ hg shelve
  shelved as default
  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
  $ hg mv a b
  $ hg ci -m 'mv a b'

  $ hg l
  @  rev: 1
  |  desc: mv a b
  o  rev: 0
     desc: initial
  $ hg unshelve
  unshelving change 'default'
  rebasing shelved changes
  merging b and a to b
  $ ls -A
  .hg
  b
  $ cat b
  b
  $ cd ..
  $ rm -rf repo

Test full copytrace ability on draft branch
-------------------------------------------

File directory and base name changed in same move
  $ hg init repo
  $ initclient repo
  $ mkdir repo/dir1
  $ cd repo/dir1
  $ echo a > a
  $ hg add a
  $ hg ci -qm initial
  $ cd ..
  $ hg mv -q dir1 dir2
  $ hg mv dir2/a dir2/b
  $ hg ci -qm 'mv a b; mv dir1 dir2'
  $ hg up -q '.^'
  $ cd dir1
  $ echo b >> a
  $ cd ..
  $ hg ci -qm 'mod a'

  $ hg pl
  @  rev: 2, phase: draft
  |  desc: mod a
  | o  rev: 1, phase: draft
  |/   desc: mv a b; mv dir1 dir2
  o  rev: 0, phase: draft
     desc: initial

  $ hg rebase -s . -d 1 --config experimental.copytrace.sourcecommitlimit=100
  rebasing 2:6207d2d318e7 tip "mod a"
  merging dir2/b and dir1/a to dir2/b
  saved backup bundle to $TESTTMP/repo/repo/.hg/strip-backup/6207d2d318e7-1c9779ad-rebase.hg
  $ cat dir2/b
  a
  b
  $ cd ..
  $ rm -rf repo

Move directory in one merge parent, while adding file to original directory
in other merge parent. File moved on rebase.

  $ hg init repo
  $ initclient repo
  $ mkdir repo/dir1
  $ cd repo/dir1
  $ echo dummy > dummy
  $ hg add dummy
  $ cd ..
  $ hg ci -qm initial
  $ cd dir1
  $ echo a > a
  $ hg add a
  $ cd ..
  $ hg ci -qm 'hg add dir1/a'
  $ hg up -q '.^'
  $ hg mv -q dir1 dir2
  $ hg ci -qm 'mv dir1 dir2'

  $ hg pl
  @  rev: 2, phase: draft
  |  desc: mv dir1 dir2
  | o  rev: 1, phase: draft
  |/   desc: hg add dir1/a
  o  rev: 0, phase: draft
     desc: initial

  $ hg rebase -s . -d 1 --config experimental.copytrace.sourcecommitlimit=100
  rebasing 2:e8919e7df8d0 tip "mv dir1 dir2"
  saved backup bundle to $TESTTMP/repo/repo/.hg/strip-backup/e8919e7df8d0-f62fab62-rebase.hg
  $ ls dir2
  a
  dummy
  $ rm -rf repo

Testing the sourcecommitlimit config
-----------------------------------

  $ hg init repo
  $ initclient repo
  $ cd repo
  $ echo a > a
  $ hg ci -Aqm "added a"
  $ echo "more things" >> a
  $ hg ci -qm "added more things to a"
  $ hg up 0
  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
  $ echo b > b
  $ hg ci -Aqm "added b"
  $ mkdir foo
  $ hg mv a foo/bar
  $ hg ci -m "Moved a to foo/bar"
  $ hg pl
  @  rev: 3, phase: draft
  |  desc: Moved a to foo/bar
  o  rev: 2, phase: draft
  |  desc: added b
  | o  rev: 1, phase: draft
  |/   desc: added more things to a
  o  rev: 0, phase: draft
     desc: added a

When the sourcecommitlimit is small and we have more drafts, we use heuristics only

  $ hg rebase -s 1 -d .
  rebasing 1:8b6e13696c38 "added more things to a"
  file 'a' was deleted in local [dest] but was modified in other [source].
  You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
  What do you want to do? u
  unresolved conflicts (see 'hg resolve', then 'hg rebase --continue')
  [240]

But when we have "sourcecommitlimit > (no. of drafts from base to c1)", we do
fullcopytracing

  $ hg rebase --abort
  rebase aborted
  $ hg rebase -s 1 -d . --config experimental.copytrace.sourcecommitlimit=100
  rebasing 1:8b6e13696c38 "added more things to a"
  merging foo/bar and a to foo/bar
  saved backup bundle to $TESTTMP/repo/repo/repo/.hg/strip-backup/8b6e13696c38-fc14ac83-rebase.hg
  $ cd ..
  $ rm -rf repo