tests/test-symlinks.t
author FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
Fri, 29 Mar 2013 22:57:16 +0900
changeset 18993 0fd0612dc855
parent 18625 2cbd27f4f3c4
child 19157 113681bbef9e
permissions -rw-r--r--
annotate: increase refcount of each revisions correctly (issue3841) Before this patch, refcount (managed in "needed") of parents of each revisions in "visit" is increased, only when parent is not annotated yet (examined by "p not in hist"). But this causes less refcount of the revision like "A" in the tree below ("A" is assumed as the second parent of "C"): A --- B --- C \ / \-----/ Steps of annotation for "C" in this case are shown below: 1. for "C" 1.1 increase refcount of "B" 1.2 increase refcount of "A" (=> 1) 1.3 defer annotation for "C" 2. for "A" 2.1 annotate for "A" (=> put result into "hist[A]") 2.2 clear "pcache[A]" ("pcache[A] = []") 3. for "B" 3.1 not increase refcount of "A", because "A not in hist" is False 3.2 annotate for "B" 3.3 decrease refcount of "A" (=> 0) 3.4 delete "hist[A]", even though "A" is still needed by "C" 3.5 clear "pcache[B]" 4. for "C", again 4.1 not increase refcount of "B", because "B not in hist" is False 4.2 increase refcount of "A" (=> 1) 4.3 defer annotation for "C" 5. for "A", again 5.1 annotate for "A" (=> put result into "hist[A]", again) 5.2 clear "pcache[A]" 6. for "C", once again 6.1 not increase refcount of "B", because "B not in hist" is False 6.2 not increase refcount of "A", because "A not in hist" is False 6.3 annotate for "C" 6.4 decrease refcount of "A", and delete "hist[A]" 6.5 decrease refcount of "B", and delete "hist[B]" 6.6 clear "pcache[C]" At step (5.1), annotation for "A" mis-recognizes that all lines are created at "A", because "pcache[A]" already cleared at step (2.2) prevents from scanning ancestors of "A". So, annotation for "C" or its descendants loses information about "A" or its ancestors. The root cause of this problem is that refcount of "A" is decreased at step (3.3), even though it isn't increased at step (3.1). To increase refcount correctly, this patch increases refcount of each parents of each revisions: - regardless of "p not in hist" or not, and - only once for each revisions in "visit" (by "not pcached") In fact, this problem should occur only on legacy repositories in which a filelog includes the merging between the revision and its ancestor (as the second parent), because: - tree is scanned in depth-first without such merging, revisions in "visit" refer different revisions as parent each other - recent Mercurial doesn't allow such merging changelog and manifest can include such merging someway, but filelogs can't, because "localrepository._filecommit()" converts such merging request to linear history. This patch tests merging cases below: these cases are from filelog of "mercurial/commands.py" in the repository of Mercurial itself. - both parents are same 10 --- 11 --- 12 \_/ filelogrev: changesetid: 10 00ea3613f82c 11 fc4a6e5b5812 12 4f802588cdfb - the second parent is also ancestor of the first one 37 --- 38 --- 39 --- 40 \________/ filelogrev: changesetid: 37 f8d56da6ac8f 38 38919e1c254d 39 d3400605d246 40 f06a4a3b86a7

  $ "$TESTDIR/hghave" symlink || exit 80

== tests added in 0.7 ==

  $ hg init test-symlinks-0.7; cd test-symlinks-0.7;
  $ touch foo; ln -s foo bar;

import with addremove -- symlink walking should _not_ screwup.

  $ hg addremove
  adding bar
  adding foo

commit -- the symlink should _not_ appear added to dir state

  $ hg commit -m 'initial'

  $ touch bomb

again, symlink should _not_ show up on dir state

  $ hg addremove
  adding bomb

Assert screamed here before, should go by without consequence

  $ hg commit -m 'is there a bug?'
  $ cd ..


== fifo & ignore ==

  $ hg init test; cd test;

  $ mkdir dir
  $ touch a.c dir/a.o dir/b.o

test what happens if we want to trick hg

  $ hg commit -A -m 0
  adding a.c
  adding dir/a.o
  adding dir/b.o
  $ echo "relglob:*.o" > .hgignore
  $ rm a.c
  $ rm dir/a.o
  $ rm dir/b.o
  $ mkdir dir/a.o
  $ ln -s nonexistent dir/b.o
  $ mkfifo a.c

it should show a.c, dir/a.o and dir/b.o deleted

  $ hg status
  M dir/b.o
  ! a.c
  ! dir/a.o
  ? .hgignore
  $ hg status a.c
  a.c: unsupported file type (type is fifo)
  ! a.c
  $ cd ..


== symlinks from outside the tree ==

test absolute path through symlink outside repo

  $ p=`pwd`
  $ hg init x
  $ ln -s x y
  $ cd x
  $ touch f
  $ hg add f
  $ hg status "$p"/y/f
  A f

try symlink outside repo to file inside

  $ ln -s x/f ../z

this should fail

  $ hg status ../z && { echo hg mistakenly exited with status 0; exit 1; } || :
  abort: ../z not under root '$TESTTMP/x'
  $ cd ..


== cloning symlinks ==
  $ hg init clone; cd clone;

try cloning symlink in a subdir
1. commit a symlink

  $ mkdir -p a/b/c
  $ cd a/b/c
  $ ln -s /path/to/symlink/source demo
  $ cd ../../..
  $ hg stat
  ? a/b/c/demo
  $ hg commit -A -m 'add symlink in a/b/c subdir'
  adding a/b/c/demo

2. clone it

  $ cd ..
  $ hg clone clone clonedest
  updating to branch default
  1 files updated, 0 files merged, 0 files removed, 0 files unresolved


== symlink and git diffs ==

git symlink diff

  $ cd clonedest
  $ hg diff --git -r null:tip
  diff --git a/a/b/c/demo b/a/b/c/demo
  new file mode 120000
  --- /dev/null
  +++ b/a/b/c/demo
  @@ -0,0 +1,1 @@
  +/path/to/symlink/source
  \ No newline at end of file
  $ hg export --git tip > ../sl.diff

import git symlink diff

  $ hg rm a/b/c/demo
  $ hg commit -m'remove link'
  $ hg import ../sl.diff
  applying ../sl.diff
  $ hg diff --git -r 1:tip
  diff --git a/a/b/c/demo b/a/b/c/demo
  new file mode 120000
  --- /dev/null
  +++ b/a/b/c/demo
  @@ -0,0 +1,1 @@
  +/path/to/symlink/source
  \ No newline at end of file

== symlinks and addremove ==

directory moved and symlinked

  $ mkdir foo
  $ touch foo/a
  $ hg ci -Ama
  adding foo/a
  $ mv foo bar
  $ ln -s bar foo
  $ hg status
  ! foo/a
  ? bar/a
  ? foo

now addremove should remove old files

  $ hg addremove
  adding bar/a
  adding foo
  removing foo/a
  $ cd ..

== root of repository is symlinked ==

  $ hg init root
  $ ln -s root link
  $ cd root
  $ echo foo > foo
  $ hg status
  ? foo
  $ hg status ../link
  ? foo
  $ hg add foo
  $ hg cp foo "$TESTTMP/link/bar"
  foo has not been committed yet, so no copy data will be stored for bar.
  $ cd ..


  $ hg init b
  $ cd b
  $ ln -s nothing dangling
  $ hg commit -m 'commit symlink without adding' dangling
  abort: dangling: file not tracked!
  [255]
  $ hg add dangling
  $ hg commit -m 'add symlink'

  $ hg tip -v
  changeset:   0:cabd88b706fc
  tag:         tip
  user:        test
  date:        Thu Jan 01 00:00:00 1970 +0000
  files:       dangling
  description:
  add symlink
  
  
  $ hg manifest --debug
  2564acbe54bbbedfbf608479340b359f04597f80 644 @ dangling
  $ "$TESTDIR/readlink.py" dangling
  dangling -> nothing

  $ rm dangling
  $ ln -s void dangling
  $ hg commit -m 'change symlink'
  $ "$TESTDIR/readlink.py" dangling
  dangling -> void


modifying link

  $ rm dangling
  $ ln -s empty dangling
  $ "$TESTDIR/readlink.py" dangling
  dangling -> empty


reverting to rev 0:

  $ hg revert -r 0 -a
  reverting dangling
  $ "$TESTDIR/readlink.py" dangling
  dangling -> nothing


backups:

  $ "$TESTDIR/readlink.py" *.orig
  dangling.orig -> empty
  $ rm *.orig
  $ hg up -C
  1 files updated, 0 files merged, 0 files removed, 0 files unresolved

copies

  $ hg cp -v dangling dangling2
  copying dangling to dangling2
  $ hg st -Cmard
  A dangling2
    dangling
  $ "$TESTDIR/readlink.py" dangling dangling2
  dangling -> void
  dangling2 -> void


Issue995: hg copy -A incorrectly handles symbolic links

  $ hg up -C
  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
  $ mkdir dir
  $ ln -s dir dirlink
  $ hg ci -qAm 'add dirlink'
  $ mkdir newdir
  $ mv dir newdir/dir
  $ mv dirlink newdir/dirlink
  $ hg mv -A dirlink newdir/dirlink

  $ cd ..