##// END OF EJS Templates
dirstate: introduce a "tracked-key" feature...
marmoute -
r49533:568f63b5 default
parent child Browse files
Show More
@@ -0,0 +1,163 b''
1 ==============================
2 Test the "tracked key" feature
3 ==============================
4
5 The tracked key feature provide a file that get updated when the set of tracked
6 files get updated.
7
8 basic setup
9
10 $ cat << EOF >> $HGRCPATH
11 > [format]
12 > exp-dirstate-tracked-key-version=1
13 > EOF
14
15 $ hg init tracked-key-test
16 $ cd tracked-key-test
17 $ hg debugbuilddag '.+10' -n
18 $ hg log -G -T '{rev} {desc} {files}\n'
19 o 10 r10 nf10
20 |
21 o 9 r9 nf9
22 |
23 o 8 r8 nf8
24 |
25 o 7 r7 nf7
26 |
27 o 6 r6 nf6
28 |
29 o 5 r5 nf5
30 |
31 o 4 r4 nf4
32 |
33 o 3 r3 nf3
34 |
35 o 2 r2 nf2
36 |
37 o 1 r1 nf1
38 |
39 o 0 r0 nf0
40
41 $ hg up tip
42 11 files updated, 0 files merged, 0 files removed, 0 files unresolved
43 $ hg files
44 nf0
45 nf1
46 nf10
47 nf2
48 nf3
49 nf4
50 nf5
51 nf6
52 nf7
53 nf8
54 nf9
55
56 key-file exists
57 -----------
58
59 The tracked key file should exist
60
61 $ ls -1 .hg/dirstate*
62 .hg/dirstate
63 .hg/dirstate-tracked-key
64
65 key-file stay the same if the tracked set is unchanged
66 ------------------------------------------------------
67
68 (copy its content for later comparison)
69
70 $ cp .hg/dirstate-tracked-key ../key-bck
71 $ echo foo >> nf0
72 $ sleep 1
73 $ hg status
74 M nf0
75 $ diff --brief .hg/dirstate-tracked-key ../key-bck
76 $ hg revert -C nf0
77 $ sleep 1
78 $ hg status
79 $ diff --brief .hg/dirstate-tracked-key ../key-bck
80
81 key-file change if the tracked set is changed manually
82 ------------------------------------------------------
83
84 adding a file to tracking
85
86 $ cp .hg/dirstate-tracked-key ../key-bck
87 $ echo x > x
88 $ hg add x
89 $ diff --brief .hg/dirstate-tracked-key ../key-bck
90 Files .hg/dirstate-tracked-key and ../key-bck differ
91 [1]
92
93 remove a file from tracking
94 (forget)
95
96 $ cp .hg/dirstate-tracked-key ../key-bck
97 $ hg forget x
98 $ diff --brief .hg/dirstate-tracked-key ../key-bck
99 Files .hg/dirstate-tracked-key and ../key-bck differ
100 [1]
101
102 (remove)
103
104 $ cp .hg/dirstate-tracked-key ../key-bck
105 $ hg remove nf1
106 $ diff --brief .hg/dirstate-tracked-key ../key-bck
107 Files .hg/dirstate-tracked-key and ../key-bck differ
108 [1]
109
110 key-file changes on revert (when applicable)
111 --------------------------------------------
112
113 $ cp .hg/dirstate-tracked-key ../key-bck
114 $ hg status
115 R nf1
116 ? x
117 $ hg revert --all
118 undeleting nf1
119 $ hg status
120 ? x
121 $ diff --brief .hg/dirstate-tracked-key ../key-bck
122 Files .hg/dirstate-tracked-key and ../key-bck differ
123 [1]
124
125
126 `hg update` does affect the key-file (when needed)
127 --------------------------------------------------
128
129 update changing the tracked set
130
131 (removing)
132
133 $ cp .hg/dirstate-tracked-key ../key-bck
134 $ hg status --rev . --rev '.#generations[-1]'
135 R nf10
136 $ hg up '.#generations[-1]'
137 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
138 $ diff --brief .hg/dirstate-tracked-key ../key-bck
139 Files .hg/dirstate-tracked-key and ../key-bck differ
140 [1]
141
142 (adding)
143
144 $ cp .hg/dirstate-tracked-key ../key-bck
145 $ hg status --rev . --rev '.#generations[1]'
146 A nf10
147 $ hg up '.#generations[1]'
148 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
149 $ diff --brief .hg/dirstate-tracked-key ../key-bck
150 Files .hg/dirstate-tracked-key and ../key-bck differ
151 [1]
152
153 update not affecting the tracked set
154
155 $ echo foo >> nf0
156 $ hg commit -m foo
157
158 $ cp .hg/dirstate-tracked-key ../key-bck
159 $ hg status --rev . --rev '.#generations[-1]'
160 M nf0
161 $ hg up '.#generations[-1]'
162 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
163 $ diff --brief .hg/dirstate-tracked-key ../key-bck
@@ -1284,6 +1284,12 b' coreconfigitem('
1284 1284 )
1285 1285 coreconfigitem(
1286 1286 b'format',
1287 b'exp-dirstate-tracked-key-version',
1288 default=0,
1289 experimental=True,
1290 )
1291 coreconfigitem(
1292 b'format',
1287 1293 b'dotencode',
1288 1294 default=True,
1289 1295 )
@@ -12,6 +12,7 b' import contextlib'
12 12 import errno
13 13 import os
14 14 import stat
15 import uuid
15 16
16 17 from .i18n import _
17 18 from .pycompat import delattr
@@ -23,6 +24,7 b' from . import ('
23 24 encoding,
24 25 error,
25 26 match as matchmod,
27 node,
26 28 pathutil,
27 29 policy,
28 30 pycompat,
@@ -99,6 +101,7 b' class dirstate(object):'
99 101 sparsematchfn,
100 102 nodeconstants,
101 103 use_dirstate_v2,
104 use_tracked_key=False,
102 105 ):
103 106 """Create a new dirstate object.
104 107
@@ -107,6 +110,7 b' class dirstate(object):'
107 110 the dirstate.
108 111 """
109 112 self._use_dirstate_v2 = use_dirstate_v2
113 self._use_tracked_key = use_tracked_key
110 114 self._nodeconstants = nodeconstants
111 115 self._opener = opener
112 116 self._validate = validate
@@ -115,11 +119,15 b' class dirstate(object):'
115 119 # ntpath.join(root, '') of Python 2.7.9 does not add sep if root is
116 120 # UNC path pointing to root share (issue4557)
117 121 self._rootdir = pathutil.normasprefix(root)
122 # True is any internal state may be different
118 123 self._dirty = False
124 # True if the set of tracked file may be different
125 self._dirty_tracked_set = False
119 126 self._ui = ui
120 127 self._filecache = {}
121 128 self._parentwriters = 0
122 129 self._filename = b'dirstate'
130 self._filename_tk = b'dirstate-tracked-key'
123 131 self._pendingfilename = b'%s.pending' % self._filename
124 132 self._plchangecallbacks = {}
125 133 self._origpl = None
@@ -409,6 +417,7 b' class dirstate(object):'
409 417 if a in self.__dict__:
410 418 delattr(self, a)
411 419 self._dirty = False
420 self._dirty_tracked_set = False
412 421 self._parentwriters = 0
413 422 self._origpl = None
414 423
@@ -446,6 +455,8 b' class dirstate(object):'
446 455 pre_tracked = self._map.set_tracked(filename)
447 456 if reset_copy:
448 457 self._map.copymap.pop(filename, None)
458 if pre_tracked:
459 self._dirty_tracked_set = True
449 460 return pre_tracked
450 461
451 462 @requires_no_parents_change
@@ -460,6 +471,7 b' class dirstate(object):'
460 471 ret = self._map.set_untracked(filename)
461 472 if ret:
462 473 self._dirty = True
474 self._dirty_tracked_set = True
463 475 return ret
464 476
465 477 @requires_no_parents_change
@@ -544,6 +556,13 b' class dirstate(object):'
544 556 # this. The test agrees
545 557
546 558 self._dirty = True
559 old_entry = self._map.get(filename)
560 if old_entry is None:
561 prev_tracked = False
562 else:
563 prev_tracked = old_entry.tracked
564 if prev_tracked != wc_tracked:
565 self._dirty_tracked_set = True
547 566
548 567 self._map.reset_state(
549 568 filename,
@@ -702,20 +721,44 b' class dirstate(object):'
702 721 if not self._dirty:
703 722 return
704 723
705 filename = self._filename
724 write_key = self._use_tracked_key and self._dirty_tracked_set
706 725 if tr:
707 726 # delay writing in-memory changes out
727 if write_key:
728 tr.addfilegenerator(
729 b'dirstate-0-key-pre',
730 (self._filename_tk,),
731 lambda f: self._write_tracked_key(tr, f),
732 location=b'plain',
733 )
708 734 tr.addfilegenerator(
709 735 b'dirstate-1-main',
710 736 (self._filename,),
711 737 lambda f: self._writedirstate(tr, f),
712 738 location=b'plain',
713 739 )
740 if write_key:
741 tr.addfilegenerator(
742 b'dirstate-2-key-post',
743 (self._filename_tk,),
744 lambda f: self._write_tracked_key(tr, f),
745 location=b'plain',
746 )
714 747 return
715 748
716 749 file = lambda f: self._opener(f, b"w", atomictemp=True, checkambig=True)
750 if write_key:
751 # we change the key-file before changing the dirstate to make sure
752 # reading invalidate there cache before we start writing
753 with file(self._filename_tk) as f:
754 self._write_tracked_key(tr, f)
717 755 with file(self._filename) as f:
718 756 self._writedirstate(tr, f)
757 if write_key:
758 # we update the key-file after writing to make sure reader have a
759 # key that match the newly written content
760 with file(self._filename_tk) as f:
761 self._write_tracked_key(tr, f)
719 762
720 763 def addparentchangecallback(self, category, callback):
721 764 """add a callback to be called when the wd parents are changed
@@ -736,9 +779,13 b' class dirstate(object):'
736 779 ):
737 780 callback(self, self._origpl, self._pl)
738 781 self._origpl = None
739
740 782 self._map.write(tr, st)
741 783 self._dirty = False
784 self._dirty_tracked_set = False
785
786 def _write_tracked_key(self, tr, f):
787 key = node.hex(uuid.uuid4().bytes)
788 f.write(b"1\n%s\n" % key) # 1 is the format version
742 789
743 790 def _dirignore(self, f):
744 791 if self._ignore(f):
@@ -944,6 +944,42 b' https://www.mercurial-scm.org/wiki/Missi'
944 944
945 945 For a more comprehensive guide, see :hg:`help internals.dirstate-v2`.
946 946
947 ``exp-dirstate-tracked-key-version``
948 Enable or disable the writing of "tracked key" file alongside the dirstate.
949
950 That "tracked-key" can help external automations to detect changes to the
951 set of tracked files.
952
953 Two values are currently supported:
954 - 0: no file is written (the default),
955 - 1: a file in version "1" is written.
956
957 The tracked-key is written in a new `.hg/dirstate-tracked-key`. That file
958 contains two lines:
959 - the first line is the file version (currently: 1),
960 - the second line contains the "tracked-key".
961
962 The tracked-key changes whenever the set of file tracked in the dirstate
963 changes. The general guarantees are:
964 - if the tracked key is identical, the set of tracked file MUST not have changed,
965 - if the tracked key is different, the set of tracked file MIGHT differ.
966
967 They are two "ways" to use this feature:
968
969 1) monitoring changes to the `.hg/dirstate-tracked-key`, if the file changes
970 the tracked set might have changed.
971
972 2) storing the value and comparing it to a later value. Beware that it is
973 impossible to achieve atomic writing or reading of the two files involved
974 files (`.hg/dirstate` and `.hg/dirstate-tracked-key`). So it is needed to
975 read the `tracked-key` files twice: before and after reading the tracked
976 set. The `tracked-key` is only usable as a cache key if it had the same
977 value in both cases and must be discarded otherwise.
978
979 To enforce that the `tracked-key` value can be used race-free (with double
980 reading as explained in (2)), the `.hg/dirstate-tracked-key` is written
981 twice: before and after we change the associated `.hg/dirstate` file.
982
947 983 ``use-persistent-nodemap``
948 984 Enable or disable the "persistent-nodemap" feature which improves
949 985 performance if the Rust extensions are available.
@@ -1278,6 +1278,7 b' class localrepository(object):'
1278 1278 requirementsmod.BOOKMARKS_IN_STORE_REQUIREMENT,
1279 1279 requirementsmod.CHANGELOGV2_REQUIREMENT,
1280 1280 requirementsmod.COPIESSDC_REQUIREMENT,
1281 requirementsmod.DIRSTATE_TRACKED_KEY_V1,
1281 1282 requirementsmod.DIRSTATE_V2_REQUIREMENT,
1282 1283 requirementsmod.DOTENCODE_REQUIREMENT,
1283 1284 requirementsmod.FNCACHE_REQUIREMENT,
@@ -1742,7 +1743,9 b' class localrepository(object):'
1742 1743 """Extension point for wrapping the dirstate per-repo."""
1743 1744 sparsematchfn = lambda: sparse.matcher(self)
1744 1745 v2_req = requirementsmod.DIRSTATE_V2_REQUIREMENT
1746 tk = requirementsmod.DIRSTATE_TRACKED_KEY_V1
1745 1747 use_dirstate_v2 = v2_req in self.requirements
1748 use_tracked_key = tk in self.requirements
1746 1749
1747 1750 return dirstate.dirstate(
1748 1751 self.vfs,
@@ -1752,6 +1755,7 b' class localrepository(object):'
1752 1755 sparsematchfn,
1753 1756 self.nodeconstants,
1754 1757 use_dirstate_v2,
1758 use_tracked_key=use_tracked_key,
1755 1759 )
1756 1760
1757 1761 def _dirstatevalidate(self, node):
@@ -3691,6 +3695,17 b' def newreporequirements(ui, createopts):'
3691 3695 else:
3692 3696 requirements.add(requirementsmod.SHARED_REQUIREMENT)
3693 3697
3698 tracked_key = ui.configint(b'format', b'exp-dirstate-tracked-key-version')
3699 if tracked_key:
3700 if tracked_key != 1:
3701 msg = _("ignoring unknown tracked key version: %d\n")
3702 hint = _(
3703 "see `hg help config.format.exp-dirstate-tracked-key-version"
3704 )
3705 ui.warn(msg % tracked_key, hint=hint)
3706 else:
3707 requirements.add(requirementsmod.DIRSTATE_TRACKED_KEY_V1)
3708
3694 3709 return requirements
3695 3710
3696 3711
@@ -18,6 +18,7 b" DOTENCODE_REQUIREMENT = b'dotencode'"
18 18 STORE_REQUIREMENT = b'store'
19 19 FNCACHE_REQUIREMENT = b'fncache'
20 20
21 DIRSTATE_TRACKED_KEY_V1 = b'exp-dirstate-tracked-key-v1'
21 22 DIRSTATE_V2_REQUIREMENT = b'dirstate-v2'
22 23
23 24 # When narrowing is finalized and no longer subject to format changes,
@@ -96,6 +97,7 b' WORKING_DIR_REQUIREMENTS = {'
96 97 SHARED_REQUIREMENT,
97 98 RELATIVE_SHARED_REQUIREMENT,
98 99 SHARESAFE_REQUIREMENT,
100 DIRSTATE_TRACKED_KEY_V1,
99 101 DIRSTATE_V2_REQUIREMENT,
100 102 }
101 103
@@ -30,7 +30,9 b' version = 2'
30 30 # the changelog having been written).
31 31 postfinalizegenerators = {
32 32 b'bookmarks',
33 b'dirstate-0-key-pre',
33 34 b'dirstate-1-main',
35 b'dirstate-2-key-post',
34 36 }
35 37
36 38 GEN_GROUP_ALL = b'all'
@@ -1599,6 +1599,8 b' Separate sections from subsections'
1599 1599
1600 1600 "use-dirstate-v2"
1601 1601
1602 "exp-dirstate-tracked-key-version"
1603
1602 1604 "use-persistent-nodemap"
1603 1605
1604 1606 "use-share-safe"
General Comments 0
You need to be logged in to leave comments. Login now