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 |
General Comments 0
You need to be logged in to leave comments.
Login now