Show More
@@ -1,513 +1,540 | |||||
1 | # transaction.py - simple journaling scheme for mercurial |
|
1 | # transaction.py - simple journaling scheme for mercurial | |
2 | # |
|
2 | # | |
3 | # This transaction scheme is intended to gracefully handle program |
|
3 | # This transaction scheme is intended to gracefully handle program | |
4 | # errors and interruptions. More serious failures like system crashes |
|
4 | # errors and interruptions. More serious failures like system crashes | |
5 | # can be recovered with an fsck-like tool. As the whole repository is |
|
5 | # can be recovered with an fsck-like tool. As the whole repository is | |
6 | # effectively log-structured, this should amount to simply truncating |
|
6 | # effectively log-structured, this should amount to simply truncating | |
7 | # anything that isn't referenced in the changelog. |
|
7 | # anything that isn't referenced in the changelog. | |
8 | # |
|
8 | # | |
9 | # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com> |
|
9 | # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com> | |
10 | # |
|
10 | # | |
11 | # This software may be used and distributed according to the terms of the |
|
11 | # This software may be used and distributed according to the terms of the | |
12 | # GNU General Public License version 2 or any later version. |
|
12 | # GNU General Public License version 2 or any later version. | |
13 |
|
13 | |||
14 | from i18n import _ |
|
14 | from i18n import _ | |
15 | import errno |
|
15 | import errno | |
16 | import error, util |
|
16 | import error, util | |
17 |
|
17 | |||
18 | version = 2 |
|
18 | version = 2 | |
19 |
|
19 | |||
20 | def active(func): |
|
20 | def active(func): | |
21 | def _active(self, *args, **kwds): |
|
21 | def _active(self, *args, **kwds): | |
22 | if self.count == 0: |
|
22 | if self.count == 0: | |
23 | raise error.Abort(_( |
|
23 | raise error.Abort(_( | |
24 | 'cannot use transaction when it is already committed/aborted')) |
|
24 | 'cannot use transaction when it is already committed/aborted')) | |
25 | return func(self, *args, **kwds) |
|
25 | return func(self, *args, **kwds) | |
26 | return _active |
|
26 | return _active | |
27 |
|
27 | |||
28 | def _playback(journal, report, opener, vfsmap, entries, backupentries, |
|
28 | def _playback(journal, report, opener, vfsmap, entries, backupentries, | |
29 | unlink=True): |
|
29 | unlink=True): | |
30 | for f, o, _ignore in entries: |
|
30 | for f, o, _ignore in entries: | |
31 | if o or not unlink: |
|
31 | if o or not unlink: | |
32 | try: |
|
32 | try: | |
33 | fp = opener(f, 'a') |
|
33 | fp = opener(f, 'a') | |
34 | fp.truncate(o) |
|
34 | fp.truncate(o) | |
35 | fp.close() |
|
35 | fp.close() | |
36 | except IOError: |
|
36 | except IOError: | |
37 | report(_("failed to truncate %s\n") % f) |
|
37 | report(_("failed to truncate %s\n") % f) | |
38 | raise |
|
38 | raise | |
39 | else: |
|
39 | else: | |
40 | try: |
|
40 | try: | |
41 | opener.unlink(f) |
|
41 | opener.unlink(f) | |
42 | except (IOError, OSError), inst: |
|
42 | except (IOError, OSError), inst: | |
43 | if inst.errno != errno.ENOENT: |
|
43 | if inst.errno != errno.ENOENT: | |
44 | raise |
|
44 | raise | |
45 |
|
45 | |||
46 | backupfiles = [] |
|
46 | backupfiles = [] | |
47 | for l, f, b, c in backupentries: |
|
47 | for l, f, b, c in backupentries: | |
48 | if l not in vfsmap and c: |
|
48 | if l not in vfsmap and c: | |
49 | report("couldn't handle %s: unknown cache location %s\n" |
|
49 | report("couldn't handle %s: unknown cache location %s\n" | |
50 | % (b, l)) |
|
50 | % (b, l)) | |
51 | vfs = vfsmap[l] |
|
51 | vfs = vfsmap[l] | |
52 | try: |
|
52 | try: | |
53 | if f and b: |
|
53 | if f and b: | |
54 | filepath = vfs.join(f) |
|
54 | filepath = vfs.join(f) | |
55 | backuppath = vfs.join(b) |
|
55 | backuppath = vfs.join(b) | |
56 | try: |
|
56 | try: | |
57 | util.copyfile(backuppath, filepath) |
|
57 | util.copyfile(backuppath, filepath) | |
58 | backupfiles.append(b) |
|
58 | backupfiles.append(b) | |
59 | except IOError: |
|
59 | except IOError: | |
60 | report(_("failed to recover %s\n") % f) |
|
60 | report(_("failed to recover %s\n") % f) | |
61 | else: |
|
61 | else: | |
62 | target = f or b |
|
62 | target = f or b | |
63 | try: |
|
63 | try: | |
64 | vfs.unlink(target) |
|
64 | vfs.unlink(target) | |
65 | except (IOError, OSError), inst: |
|
65 | except (IOError, OSError), inst: | |
66 | if inst.errno != errno.ENOENT: |
|
66 | if inst.errno != errno.ENOENT: | |
67 | raise |
|
67 | raise | |
68 | except (IOError, OSError, util.Abort), inst: |
|
68 | except (IOError, OSError, util.Abort), inst: | |
69 | if not c: |
|
69 | if not c: | |
70 | raise |
|
70 | raise | |
71 |
|
71 | |||
72 | opener.unlink(journal) |
|
72 | opener.unlink(journal) | |
73 | backuppath = "%s.backupfiles" % journal |
|
73 | backuppath = "%s.backupfiles" % journal | |
74 | if opener.exists(backuppath): |
|
74 | if opener.exists(backuppath): | |
75 | opener.unlink(backuppath) |
|
75 | opener.unlink(backuppath) | |
76 | try: |
|
76 | try: | |
77 | for f in backupfiles: |
|
77 | for f in backupfiles: | |
78 | if opener.exists(f): |
|
78 | if opener.exists(f): | |
79 | opener.unlink(f) |
|
79 | opener.unlink(f) | |
80 | except (IOError, OSError, util.Abort), inst: |
|
80 | except (IOError, OSError, util.Abort), inst: | |
81 | # only pure backup file remains, it is sage to ignore any error |
|
81 | # only pure backup file remains, it is sage to ignore any error | |
82 | pass |
|
82 | pass | |
83 |
|
83 | |||
84 | class transaction(object): |
|
84 | class transaction(object): | |
85 | def __init__(self, report, opener, vfsmap, journalname, undoname=None, |
|
85 | def __init__(self, report, opener, vfsmap, journalname, undoname=None, | |
86 | after=None, createmode=None): |
|
86 | after=None, createmode=None): | |
87 | """Begin a new transaction |
|
87 | """Begin a new transaction | |
88 |
|
88 | |||
89 | Begins a new transaction that allows rolling back writes in the event of |
|
89 | Begins a new transaction that allows rolling back writes in the event of | |
90 | an exception. |
|
90 | an exception. | |
91 |
|
91 | |||
92 | * `after`: called after the transaction has been committed |
|
92 | * `after`: called after the transaction has been committed | |
93 | * `createmode`: the mode of the journal file that will be created |
|
93 | * `createmode`: the mode of the journal file that will be created | |
94 | """ |
|
94 | """ | |
95 | self.count = 1 |
|
95 | self.count = 1 | |
96 | self.usages = 1 |
|
96 | self.usages = 1 | |
97 | self.report = report |
|
97 | self.report = report | |
98 | # a vfs to the store content |
|
98 | # a vfs to the store content | |
99 | self.opener = opener |
|
99 | self.opener = opener | |
100 | # a map to access file in various {location -> vfs} |
|
100 | # a map to access file in various {location -> vfs} | |
101 | vfsmap = vfsmap.copy() |
|
101 | vfsmap = vfsmap.copy() | |
102 | vfsmap[''] = opener # set default value |
|
102 | vfsmap[''] = opener # set default value | |
103 | self._vfsmap = vfsmap |
|
103 | self._vfsmap = vfsmap | |
104 | self.after = after |
|
104 | self.after = after | |
105 | self.entries = [] |
|
105 | self.entries = [] | |
106 | self.map = {} |
|
106 | self.map = {} | |
107 | self.journal = journalname |
|
107 | self.journal = journalname | |
108 | self.undoname = undoname |
|
108 | self.undoname = undoname | |
109 | self._queue = [] |
|
109 | self._queue = [] | |
110 | # a dict of arguments to be passed to hooks |
|
110 | # a dict of arguments to be passed to hooks | |
111 | self.hookargs = {} |
|
111 | self.hookargs = {} | |
112 | self.file = opener.open(self.journal, "w") |
|
112 | self.file = opener.open(self.journal, "w") | |
113 |
|
113 | |||
114 | # a list of ('location', 'path', 'backuppath', cache) entries. |
|
114 | # a list of ('location', 'path', 'backuppath', cache) entries. | |
115 | # - if 'backuppath' is empty, no file existed at backup time |
|
115 | # - if 'backuppath' is empty, no file existed at backup time | |
116 | # - if 'path' is empty, this is a temporary transaction file |
|
116 | # - if 'path' is empty, this is a temporary transaction file | |
117 | # - if 'location' is not empty, the path is outside main opener reach. |
|
117 | # - if 'location' is not empty, the path is outside main opener reach. | |
118 | # use 'location' value as a key in a vfsmap to find the right 'vfs' |
|
118 | # use 'location' value as a key in a vfsmap to find the right 'vfs' | |
119 | # (cache is currently unused) |
|
119 | # (cache is currently unused) | |
120 | self._backupentries = [] |
|
120 | self._backupentries = [] | |
121 | self._backupmap = {} |
|
121 | self._backupmap = {} | |
122 | self._backupjournal = "%s.backupfiles" % self.journal |
|
122 | self._backupjournal = "%s.backupfiles" % self.journal | |
123 | self._backupsfile = opener.open(self._backupjournal, 'w') |
|
123 | self._backupsfile = opener.open(self._backupjournal, 'w') | |
124 | self._backupsfile.write('%d\n' % version) |
|
124 | self._backupsfile.write('%d\n' % version) | |
125 |
|
125 | |||
126 | if createmode is not None: |
|
126 | if createmode is not None: | |
127 | opener.chmod(self.journal, createmode & 0666) |
|
127 | opener.chmod(self.journal, createmode & 0666) | |
128 | opener.chmod(self._backupjournal, createmode & 0666) |
|
128 | opener.chmod(self._backupjournal, createmode & 0666) | |
129 |
|
129 | |||
130 | # hold file generations to be performed on commit |
|
130 | # hold file generations to be performed on commit | |
131 | self._filegenerators = {} |
|
131 | self._filegenerators = {} | |
132 | # hold callback to write pending data for hooks |
|
132 | # hold callback to write pending data for hooks | |
133 | self._pendingcallback = {} |
|
133 | self._pendingcallback = {} | |
134 | # True is any pending data have been written ever |
|
134 | # True is any pending data have been written ever | |
135 | self._anypending = False |
|
135 | self._anypending = False | |
136 | # holds callback to call when writing the transaction |
|
136 | # holds callback to call when writing the transaction | |
137 | self._finalizecallback = {} |
|
137 | self._finalizecallback = {} | |
138 | # hold callback for post transaction close |
|
138 | # hold callback for post transaction close | |
139 | self._postclosecallback = {} |
|
139 | self._postclosecallback = {} | |
140 | # holds callbacks to call during abort |
|
140 | # holds callbacks to call during abort | |
141 | self._abortcallback = {} |
|
141 | self._abortcallback = {} | |
142 |
|
142 | |||
143 | def __del__(self): |
|
143 | def __del__(self): | |
144 | if self.journal: |
|
144 | if self.journal: | |
145 | self._abort() |
|
145 | self._abort() | |
146 |
|
146 | |||
147 | @active |
|
147 | @active | |
148 | def startgroup(self): |
|
148 | def startgroup(self): | |
149 | """delay registration of file entry |
|
149 | """delay registration of file entry | |
150 |
|
150 | |||
151 | This is used by strip to delay vision of strip offset. The transaction |
|
151 | This is used by strip to delay vision of strip offset. The transaction | |
152 | sees either none or all of the strip actions to be done.""" |
|
152 | sees either none or all of the strip actions to be done.""" | |
153 | self._queue.append([]) |
|
153 | self._queue.append([]) | |
154 |
|
154 | |||
155 | @active |
|
155 | @active | |
156 | def endgroup(self): |
|
156 | def endgroup(self): | |
157 | """apply delayed registration of file entry. |
|
157 | """apply delayed registration of file entry. | |
158 |
|
158 | |||
159 | This is used by strip to delay vision of strip offset. The transaction |
|
159 | This is used by strip to delay vision of strip offset. The transaction | |
160 | sees either none or all of the strip actions to be done.""" |
|
160 | sees either none or all of the strip actions to be done.""" | |
161 | q = self._queue.pop() |
|
161 | q = self._queue.pop() | |
162 | for f, o, data in q: |
|
162 | for f, o, data in q: | |
163 | self._addentry(f, o, data) |
|
163 | self._addentry(f, o, data) | |
164 |
|
164 | |||
165 | @active |
|
165 | @active | |
166 | def add(self, file, offset, data=None): |
|
166 | def add(self, file, offset, data=None): | |
167 | """record the state of an append-only file before update""" |
|
167 | """record the state of an append-only file before update""" | |
168 | if file in self.map or file in self._backupmap: |
|
168 | if file in self.map or file in self._backupmap: | |
169 | return |
|
169 | return | |
170 | if self._queue: |
|
170 | if self._queue: | |
171 | self._queue[-1].append((file, offset, data)) |
|
171 | self._queue[-1].append((file, offset, data)) | |
172 | return |
|
172 | return | |
173 |
|
173 | |||
174 | self._addentry(file, offset, data) |
|
174 | self._addentry(file, offset, data) | |
175 |
|
175 | |||
176 | def _addentry(self, file, offset, data): |
|
176 | def _addentry(self, file, offset, data): | |
177 | """add a append-only entry to memory and on-disk state""" |
|
177 | """add a append-only entry to memory and on-disk state""" | |
178 | if file in self.map or file in self._backupmap: |
|
178 | if file in self.map or file in self._backupmap: | |
179 | return |
|
179 | return | |
180 | self.entries.append((file, offset, data)) |
|
180 | self.entries.append((file, offset, data)) | |
181 | self.map[file] = len(self.entries) - 1 |
|
181 | self.map[file] = len(self.entries) - 1 | |
182 | # add enough data to the journal to do the truncate |
|
182 | # add enough data to the journal to do the truncate | |
183 | self.file.write("%s\0%d\n" % (file, offset)) |
|
183 | self.file.write("%s\0%d\n" % (file, offset)) | |
184 | self.file.flush() |
|
184 | self.file.flush() | |
185 |
|
185 | |||
186 | @active |
|
186 | @active | |
187 | def addbackup(self, file, hardlink=True, location=''): |
|
187 | def addbackup(self, file, hardlink=True, location=''): | |
188 | """Adds a backup of the file to the transaction |
|
188 | """Adds a backup of the file to the transaction | |
189 |
|
189 | |||
190 | Calling addbackup() creates a hardlink backup of the specified file |
|
190 | Calling addbackup() creates a hardlink backup of the specified file | |
191 | that is used to recover the file in the event of the transaction |
|
191 | that is used to recover the file in the event of the transaction | |
192 | aborting. |
|
192 | aborting. | |
193 |
|
193 | |||
194 | * `file`: the file path, relative to .hg/store |
|
194 | * `file`: the file path, relative to .hg/store | |
195 | * `hardlink`: use a hardlink to quickly create the backup |
|
195 | * `hardlink`: use a hardlink to quickly create the backup | |
196 | """ |
|
196 | """ | |
197 | if self._queue: |
|
197 | if self._queue: | |
198 | msg = 'cannot use transaction.addbackup inside "group"' |
|
198 | msg = 'cannot use transaction.addbackup inside "group"' | |
199 | raise RuntimeError(msg) |
|
199 | raise RuntimeError(msg) | |
200 |
|
200 | |||
201 | if file in self.map or file in self._backupmap: |
|
201 | if file in self.map or file in self._backupmap: | |
202 | return |
|
202 | return | |
203 | vfs = self._vfsmap[location] |
|
203 | vfs = self._vfsmap[location] | |
204 | dirname, filename = vfs.split(file) |
|
204 | dirname, filename = vfs.split(file) | |
205 | backupfilename = "%s.backup.%s" % (self.journal, filename) |
|
205 | backupfilename = "%s.backup.%s" % (self.journal, filename) | |
206 | backupfile = vfs.reljoin(dirname, backupfilename) |
|
206 | backupfile = vfs.reljoin(dirname, backupfilename) | |
207 | if vfs.exists(file): |
|
207 | if vfs.exists(file): | |
208 | filepath = vfs.join(file) |
|
208 | filepath = vfs.join(file) | |
209 | backuppath = vfs.join(backupfile) |
|
209 | backuppath = vfs.join(backupfile) | |
210 | util.copyfile(filepath, backuppath, hardlink=hardlink) |
|
210 | util.copyfile(filepath, backuppath, hardlink=hardlink) | |
211 | else: |
|
211 | else: | |
212 | backupfile = '' |
|
212 | backupfile = '' | |
213 |
|
213 | |||
214 | self._addbackupentry((location, file, backupfile, False)) |
|
214 | self._addbackupentry((location, file, backupfile, False)) | |
215 |
|
215 | |||
216 | def _addbackupentry(self, entry): |
|
216 | def _addbackupentry(self, entry): | |
217 | """register a new backup entry and write it to disk""" |
|
217 | """register a new backup entry and write it to disk""" | |
218 | self._backupentries.append(entry) |
|
218 | self._backupentries.append(entry) | |
219 | self._backupmap[file] = len(self._backupentries) - 1 |
|
219 | self._backupmap[file] = len(self._backupentries) - 1 | |
220 | self._backupsfile.write("%s\0%s\0%s\0%d\n" % entry) |
|
220 | self._backupsfile.write("%s\0%s\0%s\0%d\n" % entry) | |
221 | self._backupsfile.flush() |
|
221 | self._backupsfile.flush() | |
222 |
|
222 | |||
223 | @active |
|
223 | @active | |
224 | def registertmp(self, tmpfile, location=''): |
|
224 | def registertmp(self, tmpfile, location=''): | |
225 | """register a temporary transaction file |
|
225 | """register a temporary transaction file | |
226 |
|
226 | |||
227 | Such files will be deleted when the transaction exits (on both |
|
227 | Such files will be deleted when the transaction exits (on both | |
228 | failure and success). |
|
228 | failure and success). | |
229 | """ |
|
229 | """ | |
230 | self._addbackupentry((location, '', tmpfile, False)) |
|
230 | self._addbackupentry((location, '', tmpfile, False)) | |
231 |
|
231 | |||
232 | @active |
|
232 | @active | |
233 | def addfilegenerator(self, genid, filenames, genfunc, order=0, |
|
233 | def addfilegenerator(self, genid, filenames, genfunc, order=0, | |
234 | location=''): |
|
234 | location=''): | |
235 | """add a function to generates some files at transaction commit |
|
235 | """add a function to generates some files at transaction commit | |
236 |
|
236 | |||
237 | The `genfunc` argument is a function capable of generating proper |
|
237 | The `genfunc` argument is a function capable of generating proper | |
238 | content of each entry in the `filename` tuple. |
|
238 | content of each entry in the `filename` tuple. | |
239 |
|
239 | |||
240 | At transaction close time, `genfunc` will be called with one file |
|
240 | At transaction close time, `genfunc` will be called with one file | |
241 | object argument per entries in `filenames`. |
|
241 | object argument per entries in `filenames`. | |
242 |
|
242 | |||
243 | The transaction itself is responsible for the backup, creation and |
|
243 | The transaction itself is responsible for the backup, creation and | |
244 | final write of such file. |
|
244 | final write of such file. | |
245 |
|
245 | |||
246 | The `genid` argument is used to ensure the same set of file is only |
|
246 | The `genid` argument is used to ensure the same set of file is only | |
247 | generated once. Call to `addfilegenerator` for a `genid` already |
|
247 | generated once. Call to `addfilegenerator` for a `genid` already | |
248 | present will overwrite the old entry. |
|
248 | present will overwrite the old entry. | |
249 |
|
249 | |||
250 | The `order` argument may be used to control the order in which multiple |
|
250 | The `order` argument may be used to control the order in which multiple | |
251 | generator will be executed. |
|
251 | generator will be executed. | |
252 |
|
252 | |||
253 | The `location` arguments may be used to indicate the files are located |
|
253 | The `location` arguments may be used to indicate the files are located | |
254 | outside of the the standard directory for transaction. It should match |
|
254 | outside of the the standard directory for transaction. It should match | |
255 | one of the key of the `transaction.vfsmap` dictionary. |
|
255 | one of the key of the `transaction.vfsmap` dictionary. | |
256 | """ |
|
256 | """ | |
257 | # For now, we are unable to do proper backup and restore of custom vfs |
|
257 | # For now, we are unable to do proper backup and restore of custom vfs | |
258 | # but for bookmarks that are handled outside this mechanism. |
|
258 | # but for bookmarks that are handled outside this mechanism. | |
259 | self._filegenerators[genid] = (order, filenames, genfunc, location) |
|
259 | self._filegenerators[genid] = (order, filenames, genfunc, location) | |
260 |
|
260 | |||
261 | def _generatefiles(self, suffix=''): |
|
261 | def _generatefiles(self, suffix=''): | |
262 | # write files registered for generation |
|
262 | # write files registered for generation | |
263 | any = False |
|
263 | any = False | |
264 | for entry in sorted(self._filegenerators.values()): |
|
264 | for entry in sorted(self._filegenerators.values()): | |
265 | any = True |
|
265 | any = True | |
266 | order, filenames, genfunc, location = entry |
|
266 | order, filenames, genfunc, location = entry | |
267 | vfs = self._vfsmap[location] |
|
267 | vfs = self._vfsmap[location] | |
268 | files = [] |
|
268 | files = [] | |
269 | try: |
|
269 | try: | |
270 | for name in filenames: |
|
270 | for name in filenames: | |
271 | name += suffix |
|
271 | name += suffix | |
272 | if suffix: |
|
272 | if suffix: | |
273 | self.registertmp(name, location=location) |
|
273 | self.registertmp(name, location=location) | |
274 | else: |
|
274 | else: | |
275 | self.addbackup(name, location=location) |
|
275 | self.addbackup(name, location=location) | |
276 | files.append(vfs(name, 'w', atomictemp=True)) |
|
276 | files.append(vfs(name, 'w', atomictemp=True)) | |
277 | genfunc(*files) |
|
277 | genfunc(*files) | |
278 | finally: |
|
278 | finally: | |
279 | for f in files: |
|
279 | for f in files: | |
280 | f.close() |
|
280 | f.close() | |
281 | return any |
|
281 | return any | |
282 |
|
282 | |||
283 | @active |
|
283 | @active | |
284 | def find(self, file): |
|
284 | def find(self, file): | |
285 | if file in self.map: |
|
285 | if file in self.map: | |
286 | return self.entries[self.map[file]] |
|
286 | return self.entries[self.map[file]] | |
287 | if file in self._backupmap: |
|
287 | if file in self._backupmap: | |
288 | return self._backupentries[self._backupmap[file]] |
|
288 | return self._backupentries[self._backupmap[file]] | |
289 | return None |
|
289 | return None | |
290 |
|
290 | |||
291 | @active |
|
291 | @active | |
292 | def replace(self, file, offset, data=None): |
|
292 | def replace(self, file, offset, data=None): | |
293 | ''' |
|
293 | ''' | |
294 | replace can only replace already committed entries |
|
294 | replace can only replace already committed entries | |
295 | that are not pending in the queue |
|
295 | that are not pending in the queue | |
296 | ''' |
|
296 | ''' | |
297 |
|
297 | |||
298 | if file not in self.map: |
|
298 | if file not in self.map: | |
299 | raise KeyError(file) |
|
299 | raise KeyError(file) | |
300 | index = self.map[file] |
|
300 | index = self.map[file] | |
301 | self.entries[index] = (file, offset, data) |
|
301 | self.entries[index] = (file, offset, data) | |
302 | self.file.write("%s\0%d\n" % (file, offset)) |
|
302 | self.file.write("%s\0%d\n" % (file, offset)) | |
303 | self.file.flush() |
|
303 | self.file.flush() | |
304 |
|
304 | |||
305 | @active |
|
305 | @active | |
306 | def nest(self): |
|
306 | def nest(self): | |
307 | self.count += 1 |
|
307 | self.count += 1 | |
308 | self.usages += 1 |
|
308 | self.usages += 1 | |
309 | return self |
|
309 | return self | |
310 |
|
310 | |||
311 | def release(self): |
|
311 | def release(self): | |
312 | if self.count > 0: |
|
312 | if self.count > 0: | |
313 | self.usages -= 1 |
|
313 | self.usages -= 1 | |
314 | # if the transaction scopes are left without being closed, fail |
|
314 | # if the transaction scopes are left without being closed, fail | |
315 | if self.count > 0 and self.usages == 0: |
|
315 | if self.count > 0 and self.usages == 0: | |
316 | self._abort() |
|
316 | self._abort() | |
317 |
|
317 | |||
318 | def running(self): |
|
318 | def running(self): | |
319 | return self.count > 0 |
|
319 | return self.count > 0 | |
320 |
|
320 | |||
321 | def addpending(self, category, callback): |
|
321 | def addpending(self, category, callback): | |
322 | """add a callback to be called when the transaction is pending |
|
322 | """add a callback to be called when the transaction is pending | |
323 |
|
323 | |||
324 | The transaction will be given as callback's first argument. |
|
324 | The transaction will be given as callback's first argument. | |
325 |
|
325 | |||
326 | Category is a unique identifier to allow overwriting an old callback |
|
326 | Category is a unique identifier to allow overwriting an old callback | |
327 | with a newer callback. |
|
327 | with a newer callback. | |
328 | """ |
|
328 | """ | |
329 | self._pendingcallback[category] = callback |
|
329 | self._pendingcallback[category] = callback | |
330 |
|
330 | |||
331 | @active |
|
331 | @active | |
332 | def writepending(self): |
|
332 | def writepending(self): | |
333 | '''write pending file to temporary version |
|
333 | '''write pending file to temporary version | |
334 |
|
334 | |||
335 | This is used to allow hooks to view a transaction before commit''' |
|
335 | This is used to allow hooks to view a transaction before commit''' | |
336 | categories = sorted(self._pendingcallback) |
|
336 | categories = sorted(self._pendingcallback) | |
337 | for cat in categories: |
|
337 | for cat in categories: | |
338 | # remove callback since the data will have been flushed |
|
338 | # remove callback since the data will have been flushed | |
339 | any = self._pendingcallback.pop(cat)(self) |
|
339 | any = self._pendingcallback.pop(cat)(self) | |
340 | self._anypending = self._anypending or any |
|
340 | self._anypending = self._anypending or any | |
341 | self._anypending |= self._generatefiles(suffix='.pending') |
|
341 | self._anypending |= self._generatefiles(suffix='.pending') | |
342 | return self._anypending |
|
342 | return self._anypending | |
343 |
|
343 | |||
344 | @active |
|
344 | @active | |
345 | def addfinalize(self, category, callback): |
|
345 | def addfinalize(self, category, callback): | |
346 | """add a callback to be called when the transaction is closed |
|
346 | """add a callback to be called when the transaction is closed | |
347 |
|
347 | |||
348 | The transaction will be given as callback's first argument. |
|
348 | The transaction will be given as callback's first argument. | |
349 |
|
349 | |||
350 | Category is a unique identifier to allow overwriting old callbacks with |
|
350 | Category is a unique identifier to allow overwriting old callbacks with | |
351 | newer callbacks. |
|
351 | newer callbacks. | |
352 | """ |
|
352 | """ | |
353 | self._finalizecallback[category] = callback |
|
353 | self._finalizecallback[category] = callback | |
354 |
|
354 | |||
355 | @active |
|
355 | @active | |
356 | def addpostclose(self, category, callback): |
|
356 | def addpostclose(self, category, callback): | |
357 | """add a callback to be called after the transaction is closed |
|
357 | """add a callback to be called after the transaction is closed | |
358 |
|
358 | |||
359 | The transaction will be given as callback's first argument. |
|
359 | The transaction will be given as callback's first argument. | |
360 |
|
360 | |||
361 | Category is a unique identifier to allow overwriting an old callback |
|
361 | Category is a unique identifier to allow overwriting an old callback | |
362 | with a newer callback. |
|
362 | with a newer callback. | |
363 | """ |
|
363 | """ | |
364 | self._postclosecallback[category] = callback |
|
364 | self._postclosecallback[category] = callback | |
365 |
|
365 | |||
366 | @active |
|
366 | @active | |
367 | def addabort(self, category, callback): |
|
367 | def addabort(self, category, callback): | |
368 | """add a callback to be called when the transaction is aborted. |
|
368 | """add a callback to be called when the transaction is aborted. | |
369 |
|
369 | |||
370 | The transaction will be given as the first argument to the callback. |
|
370 | The transaction will be given as the first argument to the callback. | |
371 |
|
371 | |||
372 | Category is a unique identifier to allow overwriting an old callback |
|
372 | Category is a unique identifier to allow overwriting an old callback | |
373 | with a newer callback. |
|
373 | with a newer callback. | |
374 | """ |
|
374 | """ | |
375 | self._abortcallback[category] = callback |
|
375 | self._abortcallback[category] = callback | |
376 |
|
376 | |||
377 | @active |
|
377 | @active | |
378 | def close(self): |
|
378 | def close(self): | |
379 | '''commit the transaction''' |
|
379 | '''commit the transaction''' | |
380 | if self.count == 1: |
|
380 | if self.count == 1: | |
381 | self._generatefiles() |
|
381 | self._generatefiles() | |
382 | categories = sorted(self._finalizecallback) |
|
382 | categories = sorted(self._finalizecallback) | |
383 | for cat in categories: |
|
383 | for cat in categories: | |
384 | self._finalizecallback[cat](self) |
|
384 | self._finalizecallback[cat](self) | |
385 |
|
385 | |||
386 | self.count -= 1 |
|
386 | self.count -= 1 | |
387 | if self.count != 0: |
|
387 | if self.count != 0: | |
388 | return |
|
388 | return | |
389 | self.file.close() |
|
389 | self.file.close() | |
390 | self._backupsfile.close() |
|
390 | self._backupsfile.close() | |
391 | # cleanup temporary files |
|
391 | # cleanup temporary files | |
392 | for l, f, b, c in self._backupentries: |
|
392 | for l, f, b, c in self._backupentries: | |
393 | if l not in self._vfsmap and c: |
|
393 | if l not in self._vfsmap and c: | |
394 | self.report("couldn't remote %s: unknown cache location %s\n" |
|
394 | self.report("couldn't remote %s: unknown cache location %s\n" | |
395 | % (b, l)) |
|
395 | % (b, l)) | |
396 | continue |
|
396 | continue | |
397 | vfs = self._vfsmap[l] |
|
397 | vfs = self._vfsmap[l] | |
398 | if not f and b and vfs.exists(b): |
|
398 | if not f and b and vfs.exists(b): | |
399 | try: |
|
399 | try: | |
400 | vfs.unlink(b) |
|
400 | vfs.unlink(b) | |
401 | except (IOError, OSError, util.Abort), inst: |
|
401 | except (IOError, OSError, util.Abort), inst: | |
402 | if not c: |
|
402 | if not c: | |
403 | raise |
|
403 | raise | |
404 | # Abort may be raise by read only opener |
|
404 | # Abort may be raise by read only opener | |
405 | self.report("couldn't remote %s: %s\n" |
|
405 | self.report("couldn't remote %s: %s\n" | |
406 | % (vfs.join(b), inst)) |
|
406 | % (vfs.join(b), inst)) | |
407 | self.entries = [] |
|
407 | self.entries = [] | |
|
408 | self._writeundo() | |||
408 | if self.after: |
|
409 | if self.after: | |
409 | self.after() |
|
410 | self.after() | |
410 | if self.opener.isfile(self.journal): |
|
411 | if self.opener.isfile(self.journal): | |
411 | self.opener.unlink(self.journal) |
|
412 | self.opener.unlink(self.journal) | |
412 | if self.opener.isfile(self._backupjournal): |
|
413 | if self.opener.isfile(self._backupjournal): | |
413 | self.opener.unlink(self._backupjournal) |
|
414 | self.opener.unlink(self._backupjournal) | |
414 | for l, _f, b, c in self._backupentries: |
|
415 | for l, _f, b, c in self._backupentries: | |
415 | if l not in self._vfsmap and c: |
|
416 | if l not in self._vfsmap and c: | |
416 | self.report("couldn't remote %s: unknown cache location" |
|
417 | self.report("couldn't remote %s: unknown cache location" | |
417 | "%s\n" % (b, l)) |
|
418 | "%s\n" % (b, l)) | |
418 | continue |
|
419 | continue | |
419 | vfs = self._vfsmap[l] |
|
420 | vfs = self._vfsmap[l] | |
420 | if b and vfs.exists(b): |
|
421 | if b and vfs.exists(b): | |
421 | try: |
|
422 | try: | |
422 | vfs.unlink(b) |
|
423 | vfs.unlink(b) | |
423 | except (IOError, OSError, util.Abort), inst: |
|
424 | except (IOError, OSError, util.Abort), inst: | |
424 | if not c: |
|
425 | if not c: | |
425 | raise |
|
426 | raise | |
426 | # Abort may be raise by read only opener |
|
427 | # Abort may be raise by read only opener | |
427 | self.report("couldn't remote %s: %s\n" |
|
428 | self.report("couldn't remote %s: %s\n" | |
428 | % (vfs.join(b), inst)) |
|
429 | % (vfs.join(b), inst)) | |
429 | self._backupentries = [] |
|
430 | self._backupentries = [] | |
430 | self.journal = None |
|
431 | self.journal = None | |
431 | # run post close action |
|
432 | # run post close action | |
432 | categories = sorted(self._postclosecallback) |
|
433 | categories = sorted(self._postclosecallback) | |
433 | for cat in categories: |
|
434 | for cat in categories: | |
434 | self._postclosecallback[cat](self) |
|
435 | self._postclosecallback[cat](self) | |
435 |
|
436 | |||
436 | @active |
|
437 | @active | |
437 | def abort(self): |
|
438 | def abort(self): | |
438 | '''abort the transaction (generally called on error, or when the |
|
439 | '''abort the transaction (generally called on error, or when the | |
439 | transaction is not explicitly committed before going out of |
|
440 | transaction is not explicitly committed before going out of | |
440 | scope)''' |
|
441 | scope)''' | |
441 | self._abort() |
|
442 | self._abort() | |
442 |
|
443 | |||
|
444 | def _writeundo(self): | |||
|
445 | """write transaction data for possible future undo call""" | |||
|
446 | if self.undoname is None: | |||
|
447 | return | |||
|
448 | undobackupfile = self.opener.open("%s.backupfiles" % self.undoname, 'w') | |||
|
449 | undobackupfile.write('%d\n' % version) | |||
|
450 | for l, f, b, c in self._backupentries: | |||
|
451 | if not f: # temporary file | |||
|
452 | continue | |||
|
453 | if not b: | |||
|
454 | u = '' | |||
|
455 | else: | |||
|
456 | if l not in self._vfsmap and c: | |||
|
457 | self.report("couldn't remote %s: unknown cache location" | |||
|
458 | "%s\n" % (b, l)) | |||
|
459 | continue | |||
|
460 | vfs = self._vfsmap[l] | |||
|
461 | base, name = vfs.split(b) | |||
|
462 | assert name.startswith(self.journal), name | |||
|
463 | uname = name.replace(self.journal, self.undoname, 1) | |||
|
464 | u = vfs.reljoin(base, uname) | |||
|
465 | util.copyfile(vfs.join(b), vfs.join(u), hardlink=True) | |||
|
466 | undobackupfile.write("%s\0%s\0%s\0%d\n" % (l, f, u, c)) | |||
|
467 | undobackupfile.close() | |||
|
468 | ||||
|
469 | ||||
443 | def _abort(self): |
|
470 | def _abort(self): | |
444 | self.count = 0 |
|
471 | self.count = 0 | |
445 | self.usages = 0 |
|
472 | self.usages = 0 | |
446 | self.file.close() |
|
473 | self.file.close() | |
447 | self._backupsfile.close() |
|
474 | self._backupsfile.close() | |
448 |
|
475 | |||
449 | try: |
|
476 | try: | |
450 | if not self.entries and not self._backupentries: |
|
477 | if not self.entries and not self._backupentries: | |
451 | if self.journal: |
|
478 | if self.journal: | |
452 | self.opener.unlink(self.journal) |
|
479 | self.opener.unlink(self.journal) | |
453 | if self._backupjournal: |
|
480 | if self._backupjournal: | |
454 | self.opener.unlink(self._backupjournal) |
|
481 | self.opener.unlink(self._backupjournal) | |
455 | return |
|
482 | return | |
456 |
|
483 | |||
457 | self.report(_("transaction abort!\n")) |
|
484 | self.report(_("transaction abort!\n")) | |
458 |
|
485 | |||
459 | try: |
|
486 | try: | |
460 | for cat in sorted(self._abortcallback): |
|
487 | for cat in sorted(self._abortcallback): | |
461 | self._abortcallback[cat](self) |
|
488 | self._abortcallback[cat](self) | |
462 | _playback(self.journal, self.report, self.opener, self._vfsmap, |
|
489 | _playback(self.journal, self.report, self.opener, self._vfsmap, | |
463 | self.entries, self._backupentries, False) |
|
490 | self.entries, self._backupentries, False) | |
464 | self.report(_("rollback completed\n")) |
|
491 | self.report(_("rollback completed\n")) | |
465 | except Exception: |
|
492 | except Exception: | |
466 | self.report(_("rollback failed - please run hg recover\n")) |
|
493 | self.report(_("rollback failed - please run hg recover\n")) | |
467 | finally: |
|
494 | finally: | |
468 | self.journal = None |
|
495 | self.journal = None | |
469 |
|
496 | |||
470 |
|
497 | |||
471 | def rollback(opener, vfsmap, file, report): |
|
498 | def rollback(opener, vfsmap, file, report): | |
472 | """Rolls back the transaction contained in the given file |
|
499 | """Rolls back the transaction contained in the given file | |
473 |
|
500 | |||
474 | Reads the entries in the specified file, and the corresponding |
|
501 | Reads the entries in the specified file, and the corresponding | |
475 | '*.backupfiles' file, to recover from an incomplete transaction. |
|
502 | '*.backupfiles' file, to recover from an incomplete transaction. | |
476 |
|
503 | |||
477 | * `file`: a file containing a list of entries, specifying where |
|
504 | * `file`: a file containing a list of entries, specifying where | |
478 | to truncate each file. The file should contain a list of |
|
505 | to truncate each file. The file should contain a list of | |
479 | file\0offset pairs, delimited by newlines. The corresponding |
|
506 | file\0offset pairs, delimited by newlines. The corresponding | |
480 | '*.backupfiles' file should contain a list of file\0backupfile |
|
507 | '*.backupfiles' file should contain a list of file\0backupfile | |
481 | pairs, delimited by \0. |
|
508 | pairs, delimited by \0. | |
482 | """ |
|
509 | """ | |
483 | entries = [] |
|
510 | entries = [] | |
484 | backupentries = [] |
|
511 | backupentries = [] | |
485 |
|
512 | |||
486 | fp = opener.open(file) |
|
513 | fp = opener.open(file) | |
487 | lines = fp.readlines() |
|
514 | lines = fp.readlines() | |
488 | fp.close() |
|
515 | fp.close() | |
489 | for l in lines: |
|
516 | for l in lines: | |
490 | try: |
|
517 | try: | |
491 | f, o = l.split('\0') |
|
518 | f, o = l.split('\0') | |
492 | entries.append((f, int(o), None)) |
|
519 | entries.append((f, int(o), None)) | |
493 | except ValueError: |
|
520 | except ValueError: | |
494 | report(_("couldn't read journal entry %r!\n") % l) |
|
521 | report(_("couldn't read journal entry %r!\n") % l) | |
495 |
|
522 | |||
496 | backupjournal = "%s.backupfiles" % file |
|
523 | backupjournal = "%s.backupfiles" % file | |
497 | if opener.exists(backupjournal): |
|
524 | if opener.exists(backupjournal): | |
498 | fp = opener.open(backupjournal) |
|
525 | fp = opener.open(backupjournal) | |
499 | lines = fp.readlines() |
|
526 | lines = fp.readlines() | |
500 | if lines: |
|
527 | if lines: | |
501 | ver = lines[0][:-1] |
|
528 | ver = lines[0][:-1] | |
502 | if ver == str(version): |
|
529 | if ver == str(version): | |
503 | for line in lines[1:]: |
|
530 | for line in lines[1:]: | |
504 | if line: |
|
531 | if line: | |
505 | # Shave off the trailing newline |
|
532 | # Shave off the trailing newline | |
506 | line = line[:-1] |
|
533 | line = line[:-1] | |
507 | l, f, b, c = line.split('\0') |
|
534 | l, f, b, c = line.split('\0') | |
508 | backupentries.append((l, f, b, bool(c))) |
|
535 | backupentries.append((l, f, b, bool(c))) | |
509 | else: |
|
536 | else: | |
510 | report(_("journal was created by a different version of " |
|
537 | report(_("journal was created by a different version of " | |
511 | "Mercurial")) |
|
538 | "Mercurial")) | |
512 |
|
539 | |||
513 | _playback(file, report, opener, vfsmap, entries, backupentries) |
|
540 | _playback(file, report, opener, vfsmap, entries, backupentries) |
@@ -1,285 +1,287 | |||||
1 | Init repo1: |
|
1 | Init repo1: | |
2 |
|
2 | |||
3 | $ hg init repo1 |
|
3 | $ hg init repo1 | |
4 | $ cd repo1 |
|
4 | $ cd repo1 | |
5 | $ echo "some text" > a |
|
5 | $ echo "some text" > a | |
6 | $ hg add |
|
6 | $ hg add | |
7 | adding a |
|
7 | adding a | |
8 | $ hg ci -m first |
|
8 | $ hg ci -m first | |
9 | $ cat .hg/store/fncache | sort |
|
9 | $ cat .hg/store/fncache | sort | |
10 | data/a.i |
|
10 | data/a.i | |
11 |
|
11 | |||
12 | Testing a.i/b: |
|
12 | Testing a.i/b: | |
13 |
|
13 | |||
14 | $ mkdir a.i |
|
14 | $ mkdir a.i | |
15 | $ echo "some other text" > a.i/b |
|
15 | $ echo "some other text" > a.i/b | |
16 | $ hg add |
|
16 | $ hg add | |
17 | adding a.i/b (glob) |
|
17 | adding a.i/b (glob) | |
18 | $ hg ci -m second |
|
18 | $ hg ci -m second | |
19 | $ cat .hg/store/fncache | sort |
|
19 | $ cat .hg/store/fncache | sort | |
20 | data/a.i |
|
20 | data/a.i | |
21 | data/a.i.hg/b.i |
|
21 | data/a.i.hg/b.i | |
22 |
|
22 | |||
23 | Testing a.i.hg/c: |
|
23 | Testing a.i.hg/c: | |
24 |
|
24 | |||
25 | $ mkdir a.i.hg |
|
25 | $ mkdir a.i.hg | |
26 | $ echo "yet another text" > a.i.hg/c |
|
26 | $ echo "yet another text" > a.i.hg/c | |
27 | $ hg add |
|
27 | $ hg add | |
28 | adding a.i.hg/c (glob) |
|
28 | adding a.i.hg/c (glob) | |
29 | $ hg ci -m third |
|
29 | $ hg ci -m third | |
30 | $ cat .hg/store/fncache | sort |
|
30 | $ cat .hg/store/fncache | sort | |
31 | data/a.i |
|
31 | data/a.i | |
32 | data/a.i.hg.hg/c.i |
|
32 | data/a.i.hg.hg/c.i | |
33 | data/a.i.hg/b.i |
|
33 | data/a.i.hg/b.i | |
34 |
|
34 | |||
35 | Testing verify: |
|
35 | Testing verify: | |
36 |
|
36 | |||
37 | $ hg verify |
|
37 | $ hg verify | |
38 | checking changesets |
|
38 | checking changesets | |
39 | checking manifests |
|
39 | checking manifests | |
40 | crosschecking files in changesets and manifests |
|
40 | crosschecking files in changesets and manifests | |
41 | checking files |
|
41 | checking files | |
42 | 3 files, 3 changesets, 3 total revisions |
|
42 | 3 files, 3 changesets, 3 total revisions | |
43 |
|
43 | |||
44 | $ rm .hg/store/fncache |
|
44 | $ rm .hg/store/fncache | |
45 |
|
45 | |||
46 | $ hg verify |
|
46 | $ hg verify | |
47 | checking changesets |
|
47 | checking changesets | |
48 | checking manifests |
|
48 | checking manifests | |
49 | crosschecking files in changesets and manifests |
|
49 | crosschecking files in changesets and manifests | |
50 | checking files |
|
50 | checking files | |
51 | data/a.i@0: missing revlog! |
|
51 | data/a.i@0: missing revlog! | |
52 | data/a.i.hg/c.i@2: missing revlog! |
|
52 | data/a.i.hg/c.i@2: missing revlog! | |
53 | data/a.i/b.i@1: missing revlog! |
|
53 | data/a.i/b.i@1: missing revlog! | |
54 | 3 files, 3 changesets, 3 total revisions |
|
54 | 3 files, 3 changesets, 3 total revisions | |
55 | 3 integrity errors encountered! |
|
55 | 3 integrity errors encountered! | |
56 | (first damaged changeset appears to be 0) |
|
56 | (first damaged changeset appears to be 0) | |
57 | [1] |
|
57 | [1] | |
58 | $ cd .. |
|
58 | $ cd .. | |
59 |
|
59 | |||
60 | Non store repo: |
|
60 | Non store repo: | |
61 |
|
61 | |||
62 | $ hg --config format.usestore=False init foo |
|
62 | $ hg --config format.usestore=False init foo | |
63 | $ cd foo |
|
63 | $ cd foo | |
64 | $ mkdir tst.d |
|
64 | $ mkdir tst.d | |
65 | $ echo foo > tst.d/foo |
|
65 | $ echo foo > tst.d/foo | |
66 | $ hg ci -Amfoo |
|
66 | $ hg ci -Amfoo | |
67 | adding tst.d/foo |
|
67 | adding tst.d/foo | |
68 | $ find .hg | sort |
|
68 | $ find .hg | sort | |
69 | .hg |
|
69 | .hg | |
70 | .hg/00changelog.i |
|
70 | .hg/00changelog.i | |
71 | .hg/00manifest.i |
|
71 | .hg/00manifest.i | |
72 | .hg/cache |
|
72 | .hg/cache | |
73 | .hg/cache/branch2-served |
|
73 | .hg/cache/branch2-served | |
74 | .hg/cache/rbc-names-v1 |
|
74 | .hg/cache/rbc-names-v1 | |
75 | .hg/cache/rbc-revs-v1 |
|
75 | .hg/cache/rbc-revs-v1 | |
76 | .hg/data |
|
76 | .hg/data | |
77 | .hg/data/tst.d.hg |
|
77 | .hg/data/tst.d.hg | |
78 | .hg/data/tst.d.hg/foo.i |
|
78 | .hg/data/tst.d.hg/foo.i | |
79 | .hg/dirstate |
|
79 | .hg/dirstate | |
80 | .hg/last-message.txt |
|
80 | .hg/last-message.txt | |
81 | .hg/phaseroots |
|
81 | .hg/phaseroots | |
82 | .hg/requires |
|
82 | .hg/requires | |
83 | .hg/undo |
|
83 | .hg/undo | |
|
84 | .hg/undo.backupfiles | |||
84 | .hg/undo.bookmarks |
|
85 | .hg/undo.bookmarks | |
85 | .hg/undo.branch |
|
86 | .hg/undo.branch | |
86 | .hg/undo.desc |
|
87 | .hg/undo.desc | |
87 | .hg/undo.dirstate |
|
88 | .hg/undo.dirstate | |
88 | .hg/undo.phaseroots |
|
89 | .hg/undo.phaseroots | |
89 | $ cd .. |
|
90 | $ cd .. | |
90 |
|
91 | |||
91 | Non fncache repo: |
|
92 | Non fncache repo: | |
92 |
|
93 | |||
93 | $ hg --config format.usefncache=False init bar |
|
94 | $ hg --config format.usefncache=False init bar | |
94 | $ cd bar |
|
95 | $ cd bar | |
95 | $ mkdir tst.d |
|
96 | $ mkdir tst.d | |
96 | $ echo foo > tst.d/Foo |
|
97 | $ echo foo > tst.d/Foo | |
97 | $ hg ci -Amfoo |
|
98 | $ hg ci -Amfoo | |
98 | adding tst.d/Foo |
|
99 | adding tst.d/Foo | |
99 | $ find .hg | sort |
|
100 | $ find .hg | sort | |
100 | .hg |
|
101 | .hg | |
101 | .hg/00changelog.i |
|
102 | .hg/00changelog.i | |
102 | .hg/cache |
|
103 | .hg/cache | |
103 | .hg/cache/branch2-served |
|
104 | .hg/cache/branch2-served | |
104 | .hg/cache/rbc-names-v1 |
|
105 | .hg/cache/rbc-names-v1 | |
105 | .hg/cache/rbc-revs-v1 |
|
106 | .hg/cache/rbc-revs-v1 | |
106 | .hg/dirstate |
|
107 | .hg/dirstate | |
107 | .hg/last-message.txt |
|
108 | .hg/last-message.txt | |
108 | .hg/requires |
|
109 | .hg/requires | |
109 | .hg/store |
|
110 | .hg/store | |
110 | .hg/store/00changelog.i |
|
111 | .hg/store/00changelog.i | |
111 | .hg/store/00manifest.i |
|
112 | .hg/store/00manifest.i | |
112 | .hg/store/data |
|
113 | .hg/store/data | |
113 | .hg/store/data/tst.d.hg |
|
114 | .hg/store/data/tst.d.hg | |
114 | .hg/store/data/tst.d.hg/_foo.i |
|
115 | .hg/store/data/tst.d.hg/_foo.i | |
115 | .hg/store/phaseroots |
|
116 | .hg/store/phaseroots | |
116 | .hg/store/undo |
|
117 | .hg/store/undo | |
|
118 | .hg/store/undo.backupfiles | |||
117 | .hg/store/undo.phaseroots |
|
119 | .hg/store/undo.phaseroots | |
118 | .hg/undo.bookmarks |
|
120 | .hg/undo.bookmarks | |
119 | .hg/undo.branch |
|
121 | .hg/undo.branch | |
120 | .hg/undo.desc |
|
122 | .hg/undo.desc | |
121 | .hg/undo.dirstate |
|
123 | .hg/undo.dirstate | |
122 | $ cd .. |
|
124 | $ cd .. | |
123 |
|
125 | |||
124 | Encoding of reserved / long paths in the store |
|
126 | Encoding of reserved / long paths in the store | |
125 |
|
127 | |||
126 | $ hg init r2 |
|
128 | $ hg init r2 | |
127 | $ cd r2 |
|
129 | $ cd r2 | |
128 | $ cat <<EOF > .hg/hgrc |
|
130 | $ cat <<EOF > .hg/hgrc | |
129 | > [ui] |
|
131 | > [ui] | |
130 | > portablefilenames = ignore |
|
132 | > portablefilenames = ignore | |
131 | > EOF |
|
133 | > EOF | |
132 |
|
134 | |||
133 | $ hg import -q --bypass - <<EOF |
|
135 | $ hg import -q --bypass - <<EOF | |
134 | > # HG changeset patch |
|
136 | > # HG changeset patch | |
135 | > # User test |
|
137 | > # User test | |
136 | > # Date 0 0 |
|
138 | > # Date 0 0 | |
137 | > # Node ID 1c7a2f7cb77be1a0def34e4c7cabc562ad98fbd7 |
|
139 | > # Node ID 1c7a2f7cb77be1a0def34e4c7cabc562ad98fbd7 | |
138 | > # Parent 0000000000000000000000000000000000000000 |
|
140 | > # Parent 0000000000000000000000000000000000000000 | |
139 | > 1 |
|
141 | > 1 | |
140 | > |
|
142 | > | |
141 | > diff --git a/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3456789-12345-ABCDEFGHIJKLMNOPRSTUVWXYZ-abcdefghjiklmnopqrstuvwxyz b/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3456789-12345-ABCDEFGHIJKLMNOPRSTUVWXYZ-abcdefghjiklmnopqrstuvwxyz |
|
143 | > diff --git a/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3456789-12345-ABCDEFGHIJKLMNOPRSTUVWXYZ-abcdefghjiklmnopqrstuvwxyz b/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3456789-12345-ABCDEFGHIJKLMNOPRSTUVWXYZ-abcdefghjiklmnopqrstuvwxyz | |
142 | > new file mode 100644 |
|
144 | > new file mode 100644 | |
143 | > --- /dev/null |
|
145 | > --- /dev/null | |
144 | > +++ b/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3456789-12345-ABCDEFGHIJKLMNOPRSTUVWXYZ-abcdefghjiklmnopqrstuvwxyz |
|
146 | > +++ b/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3456789-12345-ABCDEFGHIJKLMNOPRSTUVWXYZ-abcdefghjiklmnopqrstuvwxyz | |
145 | > @@ -0,0 +1,1 @@ |
|
147 | > @@ -0,0 +1,1 @@ | |
146 | > +foo |
|
148 | > +foo | |
147 | > diff --git a/AUX/SECOND/X.PRN/FOURTH/FI:FTH/SIXTH/SEVENTH/EIGHTH/NINETH/TENTH/ELEVENTH/LOREMIPSUM.TXT b/AUX/SECOND/X.PRN/FOURTH/FI:FTH/SIXTH/SEVENTH/EIGHTH/NINETH/TENTH/ELEVENTH/LOREMIPSUM.TXT |
|
149 | > diff --git a/AUX/SECOND/X.PRN/FOURTH/FI:FTH/SIXTH/SEVENTH/EIGHTH/NINETH/TENTH/ELEVENTH/LOREMIPSUM.TXT b/AUX/SECOND/X.PRN/FOURTH/FI:FTH/SIXTH/SEVENTH/EIGHTH/NINETH/TENTH/ELEVENTH/LOREMIPSUM.TXT | |
148 | > new file mode 100644 |
|
150 | > new file mode 100644 | |
149 | > --- /dev/null |
|
151 | > --- /dev/null | |
150 | > +++ b/AUX/SECOND/X.PRN/FOURTH/FI:FTH/SIXTH/SEVENTH/EIGHTH/NINETH/TENTH/ELEVENTH/LOREMIPSUM.TXT |
|
152 | > +++ b/AUX/SECOND/X.PRN/FOURTH/FI:FTH/SIXTH/SEVENTH/EIGHTH/NINETH/TENTH/ELEVENTH/LOREMIPSUM.TXT | |
151 | > @@ -0,0 +1,1 @@ |
|
153 | > @@ -0,0 +1,1 @@ | |
152 | > +foo |
|
154 | > +foo | |
153 | > diff --git a/Project Planning/Resources/AnotherLongDirectoryName/Followedbyanother/AndAnother/AndThenAnExtremelyLongFileName.txt b/Project Planning/Resources/AnotherLongDirectoryName/Followedbyanother/AndAnother/AndThenAnExtremelyLongFileName.txt |
|
155 | > diff --git a/Project Planning/Resources/AnotherLongDirectoryName/Followedbyanother/AndAnother/AndThenAnExtremelyLongFileName.txt b/Project Planning/Resources/AnotherLongDirectoryName/Followedbyanother/AndAnother/AndThenAnExtremelyLongFileName.txt | |
154 | > new file mode 100644 |
|
156 | > new file mode 100644 | |
155 | > --- /dev/null |
|
157 | > --- /dev/null | |
156 | > +++ b/Project Planning/Resources/AnotherLongDirectoryName/Followedbyanother/AndAnother/AndThenAnExtremelyLongFileName.txt |
|
158 | > +++ b/Project Planning/Resources/AnotherLongDirectoryName/Followedbyanother/AndAnother/AndThenAnExtremelyLongFileName.txt | |
157 | > @@ -0,0 +1,1 @@ |
|
159 | > @@ -0,0 +1,1 @@ | |
158 | > +foo |
|
160 | > +foo | |
159 | > diff --git a/bla.aux/prn/PRN/lpt/com3/nul/coma/foo.NUL/normal.c b/bla.aux/prn/PRN/lpt/com3/nul/coma/foo.NUL/normal.c |
|
161 | > diff --git a/bla.aux/prn/PRN/lpt/com3/nul/coma/foo.NUL/normal.c b/bla.aux/prn/PRN/lpt/com3/nul/coma/foo.NUL/normal.c | |
160 | > new file mode 100644 |
|
162 | > new file mode 100644 | |
161 | > --- /dev/null |
|
163 | > --- /dev/null | |
162 | > +++ b/bla.aux/prn/PRN/lpt/com3/nul/coma/foo.NUL/normal.c |
|
164 | > +++ b/bla.aux/prn/PRN/lpt/com3/nul/coma/foo.NUL/normal.c | |
163 | > @@ -0,0 +1,1 @@ |
|
165 | > @@ -0,0 +1,1 @@ | |
164 | > +foo |
|
166 | > +foo | |
165 | > diff --git a/enterprise/openesbaddons/contrib-imola/corba-bc/netbeansplugin/wsdlExtension/src/main/java/META-INF/services/org.netbeans.modules.xml.wsdl.bindingsupport.spi.ExtensibilityElementTemplateProvider b/enterprise/openesbaddons/contrib-imola/corba-bc/netbeansplugin/wsdlExtension/src/main/java/META-INF/services/org.netbeans.modules.xml.wsdl.bindingsupport.spi.ExtensibilityElementTemplateProvider |
|
167 | > diff --git a/enterprise/openesbaddons/contrib-imola/corba-bc/netbeansplugin/wsdlExtension/src/main/java/META-INF/services/org.netbeans.modules.xml.wsdl.bindingsupport.spi.ExtensibilityElementTemplateProvider b/enterprise/openesbaddons/contrib-imola/corba-bc/netbeansplugin/wsdlExtension/src/main/java/META-INF/services/org.netbeans.modules.xml.wsdl.bindingsupport.spi.ExtensibilityElementTemplateProvider | |
166 | > new file mode 100644 |
|
168 | > new file mode 100644 | |
167 | > --- /dev/null |
|
169 | > --- /dev/null | |
168 | > +++ b/enterprise/openesbaddons/contrib-imola/corba-bc/netbeansplugin/wsdlExtension/src/main/java/META-INF/services/org.netbeans.modules.xml.wsdl.bindingsupport.spi.ExtensibilityElementTemplateProvider |
|
170 | > +++ b/enterprise/openesbaddons/contrib-imola/corba-bc/netbeansplugin/wsdlExtension/src/main/java/META-INF/services/org.netbeans.modules.xml.wsdl.bindingsupport.spi.ExtensibilityElementTemplateProvider | |
169 | > @@ -0,0 +1,1 @@ |
|
171 | > @@ -0,0 +1,1 @@ | |
170 | > +foo |
|
172 | > +foo | |
171 | > EOF |
|
173 | > EOF | |
172 |
|
174 | |||
173 | $ find .hg/store -name *.i | sort |
|
175 | $ find .hg/store -name *.i | sort | |
174 | .hg/store/00changelog.i |
|
176 | .hg/store/00changelog.i | |
175 | .hg/store/00manifest.i |
|
177 | .hg/store/00manifest.i | |
176 | .hg/store/data/bla.aux/pr~6e/_p_r_n/lpt/co~6d3/nu~6c/coma/foo._n_u_l/normal.c.i |
|
178 | .hg/store/data/bla.aux/pr~6e/_p_r_n/lpt/co~6d3/nu~6c/coma/foo._n_u_l/normal.c.i | |
177 | .hg/store/dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxx168e07b38e65eff86ab579afaaa8e30bfbe0f35f.i |
|
179 | .hg/store/dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxx168e07b38e65eff86ab579afaaa8e30bfbe0f35f.i | |
178 | .hg/store/dh/au~78/second/x.prn/fourth/fi~3afth/sixth/seventh/eighth/nineth/tenth/loremia20419e358ddff1bf8751e38288aff1d7c32ec05.i |
|
180 | .hg/store/dh/au~78/second/x.prn/fourth/fi~3afth/sixth/seventh/eighth/nineth/tenth/loremia20419e358ddff1bf8751e38288aff1d7c32ec05.i | |
179 | .hg/store/dh/enterpri/openesba/contrib-/corba-bc/netbeans/wsdlexte/src/main/java/org.net7018f27961fdf338a598a40c4683429e7ffb9743.i |
|
181 | .hg/store/dh/enterpri/openesba/contrib-/corba-bc/netbeans/wsdlexte/src/main/java/org.net7018f27961fdf338a598a40c4683429e7ffb9743.i | |
180 | .hg/store/dh/project_/resource/anotherl/followed/andanoth/andthenanextremelylongfilename0d8e1f4187c650e2f1fdca9fd90f786bc0976b6b.i |
|
182 | .hg/store/dh/project_/resource/anotherl/followed/andanoth/andthenanextremelylongfilename0d8e1f4187c650e2f1fdca9fd90f786bc0976b6b.i | |
181 |
|
183 | |||
182 | $ cd .. |
|
184 | $ cd .. | |
183 |
|
185 | |||
184 | Aborting lock does not prevent fncache writes |
|
186 | Aborting lock does not prevent fncache writes | |
185 |
|
187 | |||
186 | $ cat > exceptionext.py <<EOF |
|
188 | $ cat > exceptionext.py <<EOF | |
187 | > import os |
|
189 | > import os | |
188 | > from mercurial import commands, util |
|
190 | > from mercurial import commands, util | |
189 | > from mercurial.extensions import wrapfunction |
|
191 | > from mercurial.extensions import wrapfunction | |
190 | > |
|
192 | > | |
191 | > def lockexception(orig, vfs, lockname, wait, releasefn, acquirefn, desc): |
|
193 | > def lockexception(orig, vfs, lockname, wait, releasefn, acquirefn, desc): | |
192 | > def releasewrap(): |
|
194 | > def releasewrap(): | |
193 | > raise util.Abort("forced lock failure") |
|
195 | > raise util.Abort("forced lock failure") | |
194 | > return orig(vfs, lockname, wait, releasewrap, acquirefn, desc) |
|
196 | > return orig(vfs, lockname, wait, releasewrap, acquirefn, desc) | |
195 | > |
|
197 | > | |
196 | > def reposetup(ui, repo): |
|
198 | > def reposetup(ui, repo): | |
197 | > wrapfunction(repo, '_lock', lockexception) |
|
199 | > wrapfunction(repo, '_lock', lockexception) | |
198 | > |
|
200 | > | |
199 | > cmdtable = {} |
|
201 | > cmdtable = {} | |
200 | > |
|
202 | > | |
201 | > EOF |
|
203 | > EOF | |
202 | $ extpath=`pwd`/exceptionext.py |
|
204 | $ extpath=`pwd`/exceptionext.py | |
203 | $ hg init fncachetxn |
|
205 | $ hg init fncachetxn | |
204 | $ cd fncachetxn |
|
206 | $ cd fncachetxn | |
205 | $ printf "[extensions]\nexceptionext=$extpath\n" >> .hg/hgrc |
|
207 | $ printf "[extensions]\nexceptionext=$extpath\n" >> .hg/hgrc | |
206 | $ touch y |
|
208 | $ touch y | |
207 | $ hg ci -qAm y |
|
209 | $ hg ci -qAm y | |
208 | abort: forced lock failure |
|
210 | abort: forced lock failure | |
209 | [255] |
|
211 | [255] | |
210 | $ cat .hg/store/fncache |
|
212 | $ cat .hg/store/fncache | |
211 | data/y.i |
|
213 | data/y.i | |
212 |
|
214 | |||
213 | Aborting transaction prevents fncache change |
|
215 | Aborting transaction prevents fncache change | |
214 |
|
216 | |||
215 | $ cat > ../exceptionext.py <<EOF |
|
217 | $ cat > ../exceptionext.py <<EOF | |
216 | > import os |
|
218 | > import os | |
217 | > from mercurial import commands, util, localrepo |
|
219 | > from mercurial import commands, util, localrepo | |
218 | > from mercurial.extensions import wrapfunction |
|
220 | > from mercurial.extensions import wrapfunction | |
219 | > |
|
221 | > | |
220 | > def wrapper(orig, self, *args, **kwargs): |
|
222 | > def wrapper(orig, self, *args, **kwargs): | |
221 | > tr = orig(self, *args, **kwargs) |
|
223 | > tr = orig(self, *args, **kwargs) | |
222 | > def fail(tr): |
|
224 | > def fail(tr): | |
223 | > raise util.Abort("forced transaction failure") |
|
225 | > raise util.Abort("forced transaction failure") | |
224 | > # zzz prefix to ensure it sorted after store.write |
|
226 | > # zzz prefix to ensure it sorted after store.write | |
225 | > tr.addfinalize('zzz-forcefails', fail) |
|
227 | > tr.addfinalize('zzz-forcefails', fail) | |
226 | > return tr |
|
228 | > return tr | |
227 | > |
|
229 | > | |
228 | > def uisetup(ui): |
|
230 | > def uisetup(ui): | |
229 | > wrapfunction(localrepo.localrepository, 'transaction', wrapper) |
|
231 | > wrapfunction(localrepo.localrepository, 'transaction', wrapper) | |
230 | > |
|
232 | > | |
231 | > cmdtable = {} |
|
233 | > cmdtable = {} | |
232 | > |
|
234 | > | |
233 | > EOF |
|
235 | > EOF | |
234 | $ rm -f "${extpath}c" |
|
236 | $ rm -f "${extpath}c" | |
235 | $ touch z |
|
237 | $ touch z | |
236 | $ hg ci -qAm z |
|
238 | $ hg ci -qAm z | |
237 | transaction abort! |
|
239 | transaction abort! | |
238 | rollback completed |
|
240 | rollback completed | |
239 | abort: forced transaction failure |
|
241 | abort: forced transaction failure | |
240 | [255] |
|
242 | [255] | |
241 | $ cat .hg/store/fncache |
|
243 | $ cat .hg/store/fncache | |
242 | data/y.i |
|
244 | data/y.i | |
243 |
|
245 | |||
244 | Aborted transactions can be recovered later |
|
246 | Aborted transactions can be recovered later | |
245 |
|
247 | |||
246 | $ cat > ../exceptionext.py <<EOF |
|
248 | $ cat > ../exceptionext.py <<EOF | |
247 | > import os |
|
249 | > import os | |
248 | > from mercurial import commands, util, transaction, localrepo |
|
250 | > from mercurial import commands, util, transaction, localrepo | |
249 | > from mercurial.extensions import wrapfunction |
|
251 | > from mercurial.extensions import wrapfunction | |
250 | > |
|
252 | > | |
251 | > def trwrapper(orig, self, *args, **kwargs): |
|
253 | > def trwrapper(orig, self, *args, **kwargs): | |
252 | > tr = orig(self, *args, **kwargs) |
|
254 | > tr = orig(self, *args, **kwargs) | |
253 | > def fail(tr): |
|
255 | > def fail(tr): | |
254 | > raise util.Abort("forced transaction failure") |
|
256 | > raise util.Abort("forced transaction failure") | |
255 | > # zzz prefix to ensure it sorted after store.write |
|
257 | > # zzz prefix to ensure it sorted after store.write | |
256 | > tr.addfinalize('zzz-forcefails', fail) |
|
258 | > tr.addfinalize('zzz-forcefails', fail) | |
257 | > return tr |
|
259 | > return tr | |
258 | > |
|
260 | > | |
259 | > def abortwrapper(orig, self, *args, **kwargs): |
|
261 | > def abortwrapper(orig, self, *args, **kwargs): | |
260 | > raise util.Abort("forced transaction failure") |
|
262 | > raise util.Abort("forced transaction failure") | |
261 | > |
|
263 | > | |
262 | > def uisetup(ui): |
|
264 | > def uisetup(ui): | |
263 | > wrapfunction(localrepo.localrepository, 'transaction', trwrapper) |
|
265 | > wrapfunction(localrepo.localrepository, 'transaction', trwrapper) | |
264 | > wrapfunction(transaction.transaction, '_abort', abortwrapper) |
|
266 | > wrapfunction(transaction.transaction, '_abort', abortwrapper) | |
265 | > |
|
267 | > | |
266 | > cmdtable = {} |
|
268 | > cmdtable = {} | |
267 | > |
|
269 | > | |
268 | > EOF |
|
270 | > EOF | |
269 | $ rm -f "${extpath}c" |
|
271 | $ rm -f "${extpath}c" | |
270 | $ hg up -q 1 |
|
272 | $ hg up -q 1 | |
271 | $ touch z |
|
273 | $ touch z | |
272 | $ hg ci -qAm z 2>/dev/null |
|
274 | $ hg ci -qAm z 2>/dev/null | |
273 | [255] |
|
275 | [255] | |
274 | $ cat .hg/store/fncache | sort |
|
276 | $ cat .hg/store/fncache | sort | |
275 | data/y.i |
|
277 | data/y.i | |
276 | data/z.i |
|
278 | data/z.i | |
277 | $ hg recover |
|
279 | $ hg recover | |
278 | rolling back interrupted transaction |
|
280 | rolling back interrupted transaction | |
279 | checking changesets |
|
281 | checking changesets | |
280 | checking manifests |
|
282 | checking manifests | |
281 | crosschecking files in changesets and manifests |
|
283 | crosschecking files in changesets and manifests | |
282 | checking files |
|
284 | checking files | |
283 | 1 files, 1 changesets, 1 total revisions |
|
285 | 1 files, 1 changesets, 1 total revisions | |
284 | $ cat .hg/store/fncache |
|
286 | $ cat .hg/store/fncache | |
285 | data/y.i |
|
287 | data/y.i |
@@ -1,353 +1,367 | |||||
1 | #require hardlink |
|
1 | #require hardlink | |
2 |
|
2 | |||
3 | $ cat > nlinks.py <<EOF |
|
3 | $ cat > nlinks.py <<EOF | |
4 | > import sys |
|
4 | > import sys | |
5 | > from mercurial import util |
|
5 | > from mercurial import util | |
6 | > for f in sorted(sys.stdin.readlines()): |
|
6 | > for f in sorted(sys.stdin.readlines()): | |
7 | > f = f[:-1] |
|
7 | > f = f[:-1] | |
8 | > print util.nlinks(f), f |
|
8 | > print util.nlinks(f), f | |
9 | > EOF |
|
9 | > EOF | |
10 |
|
10 | |||
11 | $ nlinksdir() |
|
11 | $ nlinksdir() | |
12 | > { |
|
12 | > { | |
13 | > find $1 -type f | python $TESTTMP/nlinks.py |
|
13 | > find $1 -type f | python $TESTTMP/nlinks.py | |
14 | > } |
|
14 | > } | |
15 |
|
15 | |||
16 | Some implementations of cp can't create hardlinks (replaces 'cp -al' on Linux): |
|
16 | Some implementations of cp can't create hardlinks (replaces 'cp -al' on Linux): | |
17 |
|
17 | |||
18 | $ cat > linkcp.py <<EOF |
|
18 | $ cat > linkcp.py <<EOF | |
19 | > from mercurial import util |
|
19 | > from mercurial import util | |
20 | > import sys |
|
20 | > import sys | |
21 | > util.copyfiles(sys.argv[1], sys.argv[2], hardlink=True) |
|
21 | > util.copyfiles(sys.argv[1], sys.argv[2], hardlink=True) | |
22 | > EOF |
|
22 | > EOF | |
23 |
|
23 | |||
24 | $ linkcp() |
|
24 | $ linkcp() | |
25 | > { |
|
25 | > { | |
26 | > python $TESTTMP/linkcp.py $1 $2 |
|
26 | > python $TESTTMP/linkcp.py $1 $2 | |
27 | > } |
|
27 | > } | |
28 |
|
28 | |||
29 | Prepare repo r1: |
|
29 | Prepare repo r1: | |
30 |
|
30 | |||
31 | $ hg init r1 |
|
31 | $ hg init r1 | |
32 | $ cd r1 |
|
32 | $ cd r1 | |
33 |
|
33 | |||
34 | $ echo c1 > f1 |
|
34 | $ echo c1 > f1 | |
35 | $ hg add f1 |
|
35 | $ hg add f1 | |
36 | $ hg ci -m0 |
|
36 | $ hg ci -m0 | |
37 |
|
37 | |||
38 | $ mkdir d1 |
|
38 | $ mkdir d1 | |
39 | $ cd d1 |
|
39 | $ cd d1 | |
40 | $ echo c2 > f2 |
|
40 | $ echo c2 > f2 | |
41 | $ hg add f2 |
|
41 | $ hg add f2 | |
42 | $ hg ci -m1 |
|
42 | $ hg ci -m1 | |
43 | $ cd ../.. |
|
43 | $ cd ../.. | |
44 |
|
44 | |||
45 | $ nlinksdir r1/.hg/store |
|
45 | $ nlinksdir r1/.hg/store | |
46 | 1 r1/.hg/store/00changelog.i |
|
46 | 1 r1/.hg/store/00changelog.i | |
47 | 1 r1/.hg/store/00manifest.i |
|
47 | 1 r1/.hg/store/00manifest.i | |
48 | 1 r1/.hg/store/data/d1/f2.i |
|
48 | 1 r1/.hg/store/data/d1/f2.i | |
49 | 1 r1/.hg/store/data/f1.i |
|
49 | 1 r1/.hg/store/data/f1.i | |
50 | 1 r1/.hg/store/fncache |
|
50 | 1 r1/.hg/store/fncache | |
51 | 1 r1/.hg/store/phaseroots |
|
51 | 1 r1/.hg/store/phaseroots | |
52 | 1 r1/.hg/store/undo |
|
52 | 1 r1/.hg/store/undo | |
|
53 | 1 r1/.hg/store/undo.backup.fncache | |||
|
54 | 1 r1/.hg/store/undo.backupfiles | |||
53 | 1 r1/.hg/store/undo.phaseroots |
|
55 | 1 r1/.hg/store/undo.phaseroots | |
54 |
|
56 | |||
55 |
|
57 | |||
56 | Create hardlinked clone r2: |
|
58 | Create hardlinked clone r2: | |
57 |
|
59 | |||
58 | $ hg clone -U --debug r1 r2 |
|
60 | $ hg clone -U --debug r1 r2 | |
59 | linked 7 files |
|
61 | linked 7 files | |
60 |
|
62 | |||
61 | Create non-hardlinked clone r3: |
|
63 | Create non-hardlinked clone r3: | |
62 |
|
64 | |||
63 | $ hg clone --pull r1 r3 |
|
65 | $ hg clone --pull r1 r3 | |
64 | requesting all changes |
|
66 | requesting all changes | |
65 | adding changesets |
|
67 | adding changesets | |
66 | adding manifests |
|
68 | adding manifests | |
67 | adding file changes |
|
69 | adding file changes | |
68 | added 2 changesets with 2 changes to 2 files |
|
70 | added 2 changesets with 2 changes to 2 files | |
69 | updating to branch default |
|
71 | updating to branch default | |
70 | 2 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
72 | 2 files updated, 0 files merged, 0 files removed, 0 files unresolved | |
71 |
|
73 | |||
72 |
|
74 | |||
73 | Repos r1 and r2 should now contain hardlinked files: |
|
75 | Repos r1 and r2 should now contain hardlinked files: | |
74 |
|
76 | |||
75 | $ nlinksdir r1/.hg/store |
|
77 | $ nlinksdir r1/.hg/store | |
76 | 2 r1/.hg/store/00changelog.i |
|
78 | 2 r1/.hg/store/00changelog.i | |
77 | 2 r1/.hg/store/00manifest.i |
|
79 | 2 r1/.hg/store/00manifest.i | |
78 | 2 r1/.hg/store/data/d1/f2.i |
|
80 | 2 r1/.hg/store/data/d1/f2.i | |
79 | 2 r1/.hg/store/data/f1.i |
|
81 | 2 r1/.hg/store/data/f1.i | |
80 | 2 r1/.hg/store/fncache |
|
82 | 2 r1/.hg/store/fncache | |
81 | 1 r1/.hg/store/phaseroots |
|
83 | 1 r1/.hg/store/phaseroots | |
82 | 1 r1/.hg/store/undo |
|
84 | 1 r1/.hg/store/undo | |
|
85 | 1 r1/.hg/store/undo.backup.fncache | |||
|
86 | 1 r1/.hg/store/undo.backupfiles | |||
83 | 1 r1/.hg/store/undo.phaseroots |
|
87 | 1 r1/.hg/store/undo.phaseroots | |
84 |
|
88 | |||
85 | $ nlinksdir r2/.hg/store |
|
89 | $ nlinksdir r2/.hg/store | |
86 | 2 r2/.hg/store/00changelog.i |
|
90 | 2 r2/.hg/store/00changelog.i | |
87 | 2 r2/.hg/store/00manifest.i |
|
91 | 2 r2/.hg/store/00manifest.i | |
88 | 2 r2/.hg/store/data/d1/f2.i |
|
92 | 2 r2/.hg/store/data/d1/f2.i | |
89 | 2 r2/.hg/store/data/f1.i |
|
93 | 2 r2/.hg/store/data/f1.i | |
90 | 2 r2/.hg/store/fncache |
|
94 | 2 r2/.hg/store/fncache | |
91 |
|
95 | |||
92 | Repo r3 should not be hardlinked: |
|
96 | Repo r3 should not be hardlinked: | |
93 |
|
97 | |||
94 | $ nlinksdir r3/.hg/store |
|
98 | $ nlinksdir r3/.hg/store | |
95 | 1 r3/.hg/store/00changelog.i |
|
99 | 1 r3/.hg/store/00changelog.i | |
96 | 1 r3/.hg/store/00manifest.i |
|
100 | 1 r3/.hg/store/00manifest.i | |
97 | 1 r3/.hg/store/data/d1/f2.i |
|
101 | 1 r3/.hg/store/data/d1/f2.i | |
98 | 1 r3/.hg/store/data/f1.i |
|
102 | 1 r3/.hg/store/data/f1.i | |
99 | 1 r3/.hg/store/fncache |
|
103 | 1 r3/.hg/store/fncache | |
100 | 1 r3/.hg/store/phaseroots |
|
104 | 1 r3/.hg/store/phaseroots | |
101 | 1 r3/.hg/store/undo |
|
105 | 1 r3/.hg/store/undo | |
|
106 | 1 r3/.hg/store/undo.backupfiles | |||
102 | 1 r3/.hg/store/undo.phaseroots |
|
107 | 1 r3/.hg/store/undo.phaseroots | |
103 |
|
108 | |||
104 |
|
109 | |||
105 | Create a non-inlined filelog in r3: |
|
110 | Create a non-inlined filelog in r3: | |
106 |
|
111 | |||
107 | $ cd r3/d1 |
|
112 | $ cd r3/d1 | |
108 | >>> f = open('data1', 'wb') |
|
113 | >>> f = open('data1', 'wb') | |
109 | >>> for x in range(10000): |
|
114 | >>> for x in range(10000): | |
110 | ... f.write("%s\n" % str(x)) |
|
115 | ... f.write("%s\n" % str(x)) | |
111 | >>> f.close() |
|
116 | >>> f.close() | |
112 | $ for j in 0 1 2 3 4 5 6 7 8 9; do |
|
117 | $ for j in 0 1 2 3 4 5 6 7 8 9; do | |
113 | > cat data1 >> f2 |
|
118 | > cat data1 >> f2 | |
114 | > hg commit -m$j |
|
119 | > hg commit -m$j | |
115 | > done |
|
120 | > done | |
116 | $ cd ../.. |
|
121 | $ cd ../.. | |
117 |
|
122 | |||
118 | $ nlinksdir r3/.hg/store |
|
123 | $ nlinksdir r3/.hg/store | |
119 | 1 r3/.hg/store/00changelog.i |
|
124 | 1 r3/.hg/store/00changelog.i | |
120 | 1 r3/.hg/store/00manifest.i |
|
125 | 1 r3/.hg/store/00manifest.i | |
121 | 1 r3/.hg/store/data/d1/f2.d |
|
126 | 1 r3/.hg/store/data/d1/f2.d | |
122 | 1 r3/.hg/store/data/d1/f2.i |
|
127 | 1 r3/.hg/store/data/d1/f2.i | |
123 | 1 r3/.hg/store/data/f1.i |
|
128 | 1 r3/.hg/store/data/f1.i | |
124 | 1 r3/.hg/store/fncache |
|
129 | 1 r3/.hg/store/fncache | |
125 | 1 r3/.hg/store/phaseroots |
|
130 | 1 r3/.hg/store/phaseroots | |
126 | 1 r3/.hg/store/undo |
|
131 | 1 r3/.hg/store/undo | |
|
132 | 1 r3/.hg/store/undo.backup.fncache | |||
|
133 | 1 r3/.hg/store/undo.backup.phaseroots | |||
|
134 | 1 r3/.hg/store/undo.backupfiles | |||
127 | 1 r3/.hg/store/undo.phaseroots |
|
135 | 1 r3/.hg/store/undo.phaseroots | |
128 |
|
136 | |||
129 | Push to repo r1 should break up most hardlinks in r2: |
|
137 | Push to repo r1 should break up most hardlinks in r2: | |
130 |
|
138 | |||
131 | $ hg -R r2 verify |
|
139 | $ hg -R r2 verify | |
132 | checking changesets |
|
140 | checking changesets | |
133 | checking manifests |
|
141 | checking manifests | |
134 | crosschecking files in changesets and manifests |
|
142 | crosschecking files in changesets and manifests | |
135 | checking files |
|
143 | checking files | |
136 | 2 files, 2 changesets, 2 total revisions |
|
144 | 2 files, 2 changesets, 2 total revisions | |
137 |
|
145 | |||
138 | $ cd r3 |
|
146 | $ cd r3 | |
139 | $ hg push |
|
147 | $ hg push | |
140 | pushing to $TESTTMP/r1 (glob) |
|
148 | pushing to $TESTTMP/r1 (glob) | |
141 | searching for changes |
|
149 | searching for changes | |
142 | adding changesets |
|
150 | adding changesets | |
143 | adding manifests |
|
151 | adding manifests | |
144 | adding file changes |
|
152 | adding file changes | |
145 | added 10 changesets with 10 changes to 1 files |
|
153 | added 10 changesets with 10 changes to 1 files | |
146 |
|
154 | |||
147 | $ cd .. |
|
155 | $ cd .. | |
148 |
|
156 | |||
149 | $ nlinksdir r2/.hg/store |
|
157 | $ nlinksdir r2/.hg/store | |
150 | 1 r2/.hg/store/00changelog.i |
|
158 | 1 r2/.hg/store/00changelog.i | |
151 | 1 r2/.hg/store/00manifest.i |
|
159 | 1 r2/.hg/store/00manifest.i | |
152 | 1 r2/.hg/store/data/d1/f2.i |
|
160 | 1 r2/.hg/store/data/d1/f2.i | |
153 | 2 r2/.hg/store/data/f1.i |
|
161 | 2 r2/.hg/store/data/f1.i | |
154 |
|
|
162 | 2 r2/.hg/store/fncache | |
155 |
|
163 | |||
156 | $ hg -R r2 verify |
|
164 | $ hg -R r2 verify | |
157 | checking changesets |
|
165 | checking changesets | |
158 | checking manifests |
|
166 | checking manifests | |
159 | crosschecking files in changesets and manifests |
|
167 | crosschecking files in changesets and manifests | |
160 | checking files |
|
168 | checking files | |
161 | 2 files, 2 changesets, 2 total revisions |
|
169 | 2 files, 2 changesets, 2 total revisions | |
162 |
|
170 | |||
163 |
|
171 | |||
164 | $ cd r1 |
|
172 | $ cd r1 | |
165 | $ hg up |
|
173 | $ hg up | |
166 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
174 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved | |
167 |
|
175 | |||
168 | Committing a change to f1 in r1 must break up hardlink f1.i in r2: |
|
176 | Committing a change to f1 in r1 must break up hardlink f1.i in r2: | |
169 |
|
177 | |||
170 | $ echo c1c1 >> f1 |
|
178 | $ echo c1c1 >> f1 | |
171 | $ hg ci -m00 |
|
179 | $ hg ci -m00 | |
172 | $ cd .. |
|
180 | $ cd .. | |
173 |
|
181 | |||
174 | $ nlinksdir r2/.hg/store |
|
182 | $ nlinksdir r2/.hg/store | |
175 | 1 r2/.hg/store/00changelog.i |
|
183 | 1 r2/.hg/store/00changelog.i | |
176 | 1 r2/.hg/store/00manifest.i |
|
184 | 1 r2/.hg/store/00manifest.i | |
177 | 1 r2/.hg/store/data/d1/f2.i |
|
185 | 1 r2/.hg/store/data/d1/f2.i | |
178 | 1 r2/.hg/store/data/f1.i |
|
186 | 1 r2/.hg/store/data/f1.i | |
179 |
|
|
187 | 2 r2/.hg/store/fncache | |
180 |
|
188 | |||
181 |
|
189 | |||
182 | $ cd r3 |
|
190 | $ cd r3 | |
183 | $ hg tip --template '{rev}:{node|short}\n' |
|
191 | $ hg tip --template '{rev}:{node|short}\n' | |
184 | 11:a6451b6bc41f |
|
192 | 11:a6451b6bc41f | |
185 | $ echo bla > f1 |
|
193 | $ echo bla > f1 | |
186 | $ hg ci -m1 |
|
194 | $ hg ci -m1 | |
187 | $ cd .. |
|
195 | $ cd .. | |
188 |
|
196 | |||
189 | Create hardlinked copy r4 of r3 (on Linux, we would call 'cp -al'): |
|
197 | Create hardlinked copy r4 of r3 (on Linux, we would call 'cp -al'): | |
190 |
|
198 | |||
191 | $ linkcp r3 r4 |
|
199 | $ linkcp r3 r4 | |
192 |
|
200 | |||
193 | r4 has hardlinks in the working dir (not just inside .hg): |
|
201 | r4 has hardlinks in the working dir (not just inside .hg): | |
194 |
|
202 | |||
195 | $ nlinksdir r4 |
|
203 | $ nlinksdir r4 | |
196 | 2 r4/.hg/00changelog.i |
|
204 | 2 r4/.hg/00changelog.i | |
197 | 2 r4/.hg/branch |
|
205 | 2 r4/.hg/branch | |
198 | 2 r4/.hg/cache/branch2-served |
|
206 | 2 r4/.hg/cache/branch2-served | |
199 | 2 r4/.hg/cache/rbc-names-v1 |
|
207 | 2 r4/.hg/cache/rbc-names-v1 | |
200 | 2 r4/.hg/cache/rbc-revs-v1 |
|
208 | 2 r4/.hg/cache/rbc-revs-v1 | |
201 | 2 r4/.hg/dirstate |
|
209 | 2 r4/.hg/dirstate | |
202 | 2 r4/.hg/hgrc |
|
210 | 2 r4/.hg/hgrc | |
203 | 2 r4/.hg/last-message.txt |
|
211 | 2 r4/.hg/last-message.txt | |
204 | 2 r4/.hg/requires |
|
212 | 2 r4/.hg/requires | |
205 | 2 r4/.hg/store/00changelog.i |
|
213 | 2 r4/.hg/store/00changelog.i | |
206 | 2 r4/.hg/store/00manifest.i |
|
214 | 2 r4/.hg/store/00manifest.i | |
207 | 2 r4/.hg/store/data/d1/f2.d |
|
215 | 2 r4/.hg/store/data/d1/f2.d | |
208 | 2 r4/.hg/store/data/d1/f2.i |
|
216 | 2 r4/.hg/store/data/d1/f2.i | |
209 | 2 r4/.hg/store/data/f1.i |
|
217 | 2 r4/.hg/store/data/f1.i | |
210 | 2 r4/.hg/store/fncache |
|
218 | 2 r4/.hg/store/fncache | |
211 | 2 r4/.hg/store/phaseroots |
|
219 | 2 r4/.hg/store/phaseroots | |
212 | 2 r4/.hg/store/undo |
|
220 | 2 r4/.hg/store/undo | |
|
221 | 2 r4/.hg/store/undo.backup.fncache | |||
|
222 | 2 r4/.hg/store/undo.backup.phaseroots | |||
|
223 | 2 r4/.hg/store/undo.backupfiles | |||
213 | 2 r4/.hg/store/undo.phaseroots |
|
224 | 2 r4/.hg/store/undo.phaseroots | |
214 | 2 r4/.hg/undo.bookmarks |
|
225 | 2 r4/.hg/undo.bookmarks | |
215 | 2 r4/.hg/undo.branch |
|
226 | 2 r4/.hg/undo.branch | |
216 | 2 r4/.hg/undo.desc |
|
227 | 2 r4/.hg/undo.desc | |
217 | 2 r4/.hg/undo.dirstate |
|
228 | 2 r4/.hg/undo.dirstate | |
218 | 2 r4/d1/data1 |
|
229 | 2 r4/d1/data1 | |
219 | 2 r4/d1/f2 |
|
230 | 2 r4/d1/f2 | |
220 | 2 r4/f1 |
|
231 | 2 r4/f1 | |
221 |
|
232 | |||
222 | Update back to revision 11 in r4 should break hardlink of file f1: |
|
233 | Update back to revision 11 in r4 should break hardlink of file f1: | |
223 |
|
234 | |||
224 | $ hg -R r4 up 11 |
|
235 | $ hg -R r4 up 11 | |
225 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
236 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved | |
226 |
|
237 | |||
227 | $ nlinksdir r4 |
|
238 | $ nlinksdir r4 | |
228 | 2 r4/.hg/00changelog.i |
|
239 | 2 r4/.hg/00changelog.i | |
229 | 1 r4/.hg/branch |
|
240 | 1 r4/.hg/branch | |
230 | 2 r4/.hg/cache/branch2-served |
|
241 | 2 r4/.hg/cache/branch2-served | |
231 | 2 r4/.hg/cache/rbc-names-v1 |
|
242 | 2 r4/.hg/cache/rbc-names-v1 | |
232 | 2 r4/.hg/cache/rbc-revs-v1 |
|
243 | 2 r4/.hg/cache/rbc-revs-v1 | |
233 | 1 r4/.hg/dirstate |
|
244 | 1 r4/.hg/dirstate | |
234 | 2 r4/.hg/hgrc |
|
245 | 2 r4/.hg/hgrc | |
235 | 2 r4/.hg/last-message.txt |
|
246 | 2 r4/.hg/last-message.txt | |
236 | 2 r4/.hg/requires |
|
247 | 2 r4/.hg/requires | |
237 | 2 r4/.hg/store/00changelog.i |
|
248 | 2 r4/.hg/store/00changelog.i | |
238 | 2 r4/.hg/store/00manifest.i |
|
249 | 2 r4/.hg/store/00manifest.i | |
239 | 2 r4/.hg/store/data/d1/f2.d |
|
250 | 2 r4/.hg/store/data/d1/f2.d | |
240 | 2 r4/.hg/store/data/d1/f2.i |
|
251 | 2 r4/.hg/store/data/d1/f2.i | |
241 | 2 r4/.hg/store/data/f1.i |
|
252 | 2 r4/.hg/store/data/f1.i | |
242 | 2 r4/.hg/store/fncache |
|
253 | 2 r4/.hg/store/fncache | |
243 | 2 r4/.hg/store/phaseroots |
|
254 | 2 r4/.hg/store/phaseroots | |
244 | 2 r4/.hg/store/undo |
|
255 | 2 r4/.hg/store/undo | |
|
256 | 2 r4/.hg/store/undo.backup.fncache | |||
|
257 | 2 r4/.hg/store/undo.backup.phaseroots | |||
|
258 | 2 r4/.hg/store/undo.backupfiles | |||
245 | 2 r4/.hg/store/undo.phaseroots |
|
259 | 2 r4/.hg/store/undo.phaseroots | |
246 | 2 r4/.hg/undo.bookmarks |
|
260 | 2 r4/.hg/undo.bookmarks | |
247 | 2 r4/.hg/undo.branch |
|
261 | 2 r4/.hg/undo.branch | |
248 | 2 r4/.hg/undo.desc |
|
262 | 2 r4/.hg/undo.desc | |
249 | 2 r4/.hg/undo.dirstate |
|
263 | 2 r4/.hg/undo.dirstate | |
250 | 2 r4/d1/data1 |
|
264 | 2 r4/d1/data1 | |
251 | 2 r4/d1/f2 |
|
265 | 2 r4/d1/f2 | |
252 | 1 r4/f1 |
|
266 | 1 r4/f1 | |
253 |
|
267 | |||
254 |
|
268 | |||
255 | Test hardlinking outside hg: |
|
269 | Test hardlinking outside hg: | |
256 |
|
270 | |||
257 | $ mkdir x |
|
271 | $ mkdir x | |
258 | $ echo foo > x/a |
|
272 | $ echo foo > x/a | |
259 |
|
273 | |||
260 | $ linkcp x y |
|
274 | $ linkcp x y | |
261 | $ echo bar >> y/a |
|
275 | $ echo bar >> y/a | |
262 |
|
276 | |||
263 | No diff if hardlink: |
|
277 | No diff if hardlink: | |
264 |
|
278 | |||
265 | $ diff x/a y/a |
|
279 | $ diff x/a y/a | |
266 |
|
280 | |||
267 | Test mq hardlinking: |
|
281 | Test mq hardlinking: | |
268 |
|
282 | |||
269 | $ echo "[extensions]" >> $HGRCPATH |
|
283 | $ echo "[extensions]" >> $HGRCPATH | |
270 | $ echo "mq=" >> $HGRCPATH |
|
284 | $ echo "mq=" >> $HGRCPATH | |
271 |
|
285 | |||
272 | $ hg init a |
|
286 | $ hg init a | |
273 | $ cd a |
|
287 | $ cd a | |
274 |
|
288 | |||
275 | $ hg qimport -n foo - << EOF |
|
289 | $ hg qimport -n foo - << EOF | |
276 | > # HG changeset patch |
|
290 | > # HG changeset patch | |
277 | > # Date 1 0 |
|
291 | > # Date 1 0 | |
278 | > diff -r 2588a8b53d66 a |
|
292 | > diff -r 2588a8b53d66 a | |
279 | > --- /dev/null Thu Jan 01 00:00:00 1970 +0000 |
|
293 | > --- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
280 | > +++ b/a Wed Jul 23 15:54:29 2008 +0200 |
|
294 | > +++ b/a Wed Jul 23 15:54:29 2008 +0200 | |
281 | > @@ -0,0 +1,1 @@ |
|
295 | > @@ -0,0 +1,1 @@ | |
282 | > +a |
|
296 | > +a | |
283 | > EOF |
|
297 | > EOF | |
284 | adding foo to series file |
|
298 | adding foo to series file | |
285 |
|
299 | |||
286 | $ hg qpush |
|
300 | $ hg qpush | |
287 | applying foo |
|
301 | applying foo | |
288 | now at: foo |
|
302 | now at: foo | |
289 |
|
303 | |||
290 | $ cd .. |
|
304 | $ cd .. | |
291 | $ linkcp a b |
|
305 | $ linkcp a b | |
292 | $ cd b |
|
306 | $ cd b | |
293 |
|
307 | |||
294 | $ hg qimport -n bar - << EOF |
|
308 | $ hg qimport -n bar - << EOF | |
295 | > # HG changeset patch |
|
309 | > # HG changeset patch | |
296 | > # Date 2 0 |
|
310 | > # Date 2 0 | |
297 | > diff -r 2588a8b53d66 a |
|
311 | > diff -r 2588a8b53d66 a | |
298 | > --- /dev/null Thu Jan 01 00:00:00 1970 +0000 |
|
312 | > --- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
299 | > +++ b/b Wed Jul 23 15:54:29 2008 +0200 |
|
313 | > +++ b/b Wed Jul 23 15:54:29 2008 +0200 | |
300 | > @@ -0,0 +1,1 @@ |
|
314 | > @@ -0,0 +1,1 @@ | |
301 | > +b |
|
315 | > +b | |
302 | > EOF |
|
316 | > EOF | |
303 | adding bar to series file |
|
317 | adding bar to series file | |
304 |
|
318 | |||
305 | $ hg qpush |
|
319 | $ hg qpush | |
306 | applying bar |
|
320 | applying bar | |
307 | now at: bar |
|
321 | now at: bar | |
308 |
|
322 | |||
309 | $ cat .hg/patches/status |
|
323 | $ cat .hg/patches/status | |
310 | 430ed4828a74fa4047bc816a25500f7472ab4bfe:foo |
|
324 | 430ed4828a74fa4047bc816a25500f7472ab4bfe:foo | |
311 | 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c:bar |
|
325 | 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c:bar | |
312 |
|
326 | |||
313 | $ cat .hg/patches/series |
|
327 | $ cat .hg/patches/series | |
314 | foo |
|
328 | foo | |
315 | bar |
|
329 | bar | |
316 |
|
330 | |||
317 | $ cat ../a/.hg/patches/status |
|
331 | $ cat ../a/.hg/patches/status | |
318 | 430ed4828a74fa4047bc816a25500f7472ab4bfe:foo |
|
332 | 430ed4828a74fa4047bc816a25500f7472ab4bfe:foo | |
319 |
|
333 | |||
320 | $ cat ../a/.hg/patches/series |
|
334 | $ cat ../a/.hg/patches/series | |
321 | foo |
|
335 | foo | |
322 |
|
336 | |||
323 | Test tags hardlinking: |
|
337 | Test tags hardlinking: | |
324 |
|
338 | |||
325 | $ hg qdel -r qbase:qtip |
|
339 | $ hg qdel -r qbase:qtip | |
326 | patch foo finalized without changeset message |
|
340 | patch foo finalized without changeset message | |
327 | patch bar finalized without changeset message |
|
341 | patch bar finalized without changeset message | |
328 |
|
342 | |||
329 | $ hg tag -l lfoo |
|
343 | $ hg tag -l lfoo | |
330 | $ hg tag foo |
|
344 | $ hg tag foo | |
331 |
|
345 | |||
332 | $ cd .. |
|
346 | $ cd .. | |
333 | $ linkcp b c |
|
347 | $ linkcp b c | |
334 | $ cd c |
|
348 | $ cd c | |
335 |
|
349 | |||
336 | $ hg tag -l -r 0 lbar |
|
350 | $ hg tag -l -r 0 lbar | |
337 | $ hg tag -r 0 bar |
|
351 | $ hg tag -r 0 bar | |
338 |
|
352 | |||
339 | $ cat .hgtags |
|
353 | $ cat .hgtags | |
340 | 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c foo |
|
354 | 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c foo | |
341 | 430ed4828a74fa4047bc816a25500f7472ab4bfe bar |
|
355 | 430ed4828a74fa4047bc816a25500f7472ab4bfe bar | |
342 |
|
356 | |||
343 | $ cat .hg/localtags |
|
357 | $ cat .hg/localtags | |
344 | 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c lfoo |
|
358 | 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c lfoo | |
345 | 430ed4828a74fa4047bc816a25500f7472ab4bfe lbar |
|
359 | 430ed4828a74fa4047bc816a25500f7472ab4bfe lbar | |
346 |
|
360 | |||
347 | $ cat ../b/.hgtags |
|
361 | $ cat ../b/.hgtags | |
348 | 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c foo |
|
362 | 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c foo | |
349 |
|
363 | |||
350 | $ cat ../b/.hg/localtags |
|
364 | $ cat ../b/.hg/localtags | |
351 | 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c lfoo |
|
365 | 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c lfoo | |
352 |
|
366 | |||
353 | $ cd .. |
|
367 | $ cd .. |
@@ -1,660 +1,662 | |||||
1 | commit hooks can see env vars |
|
1 | commit hooks can see env vars | |
2 |
|
2 | |||
3 | $ hg init a |
|
3 | $ hg init a | |
4 | $ cd a |
|
4 | $ cd a | |
5 | $ cat > .hg/hgrc <<EOF |
|
5 | $ cat > .hg/hgrc <<EOF | |
6 | > [hooks] |
|
6 | > [hooks] | |
7 | > commit = sh -c "HG_LOCAL= HG_TAG= python \"$TESTDIR/printenv.py\" commit" |
|
7 | > commit = sh -c "HG_LOCAL= HG_TAG= python \"$TESTDIR/printenv.py\" commit" | |
8 | > commit.b = sh -c "HG_LOCAL= HG_TAG= python \"$TESTDIR/printenv.py\" commit.b" |
|
8 | > commit.b = sh -c "HG_LOCAL= HG_TAG= python \"$TESTDIR/printenv.py\" commit.b" | |
9 | > precommit = sh -c "HG_LOCAL= HG_NODE= HG_TAG= python \"$TESTDIR/printenv.py\" precommit" |
|
9 | > precommit = sh -c "HG_LOCAL= HG_NODE= HG_TAG= python \"$TESTDIR/printenv.py\" precommit" | |
10 | > pretxncommit = sh -c "HG_LOCAL= HG_TAG= python \"$TESTDIR/printenv.py\" pretxncommit" |
|
10 | > pretxncommit = sh -c "HG_LOCAL= HG_TAG= python \"$TESTDIR/printenv.py\" pretxncommit" | |
11 | > pretxncommit.tip = hg -q tip |
|
11 | > pretxncommit.tip = hg -q tip | |
12 | > pre-identify = python "$TESTDIR/printenv.py" pre-identify 1 |
|
12 | > pre-identify = python "$TESTDIR/printenv.py" pre-identify 1 | |
13 | > pre-cat = python "$TESTDIR/printenv.py" pre-cat |
|
13 | > pre-cat = python "$TESTDIR/printenv.py" pre-cat | |
14 | > post-cat = python "$TESTDIR/printenv.py" post-cat |
|
14 | > post-cat = python "$TESTDIR/printenv.py" post-cat | |
15 | > EOF |
|
15 | > EOF | |
16 | $ echo a > a |
|
16 | $ echo a > a | |
17 | $ hg add a |
|
17 | $ hg add a | |
18 | $ hg commit -m a |
|
18 | $ hg commit -m a | |
19 | precommit hook: HG_PARENT1=0000000000000000000000000000000000000000 |
|
19 | precommit hook: HG_PARENT1=0000000000000000000000000000000000000000 | |
20 | pretxncommit hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000 HG_PENDING=$TESTTMP/a |
|
20 | pretxncommit hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000 HG_PENDING=$TESTTMP/a | |
21 | 0:cb9a9f314b8b |
|
21 | 0:cb9a9f314b8b | |
22 | commit hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000 |
|
22 | commit hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000 | |
23 | commit.b hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000 |
|
23 | commit.b hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000 | |
24 |
|
24 | |||
25 | $ hg clone . ../b |
|
25 | $ hg clone . ../b | |
26 | updating to branch default |
|
26 | updating to branch default | |
27 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
27 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved | |
28 | $ cd ../b |
|
28 | $ cd ../b | |
29 |
|
29 | |||
30 | changegroup hooks can see env vars |
|
30 | changegroup hooks can see env vars | |
31 |
|
31 | |||
32 | $ cat > .hg/hgrc <<EOF |
|
32 | $ cat > .hg/hgrc <<EOF | |
33 | > [hooks] |
|
33 | > [hooks] | |
34 | > prechangegroup = python "$TESTDIR/printenv.py" prechangegroup |
|
34 | > prechangegroup = python "$TESTDIR/printenv.py" prechangegroup | |
35 | > changegroup = python "$TESTDIR/printenv.py" changegroup |
|
35 | > changegroup = python "$TESTDIR/printenv.py" changegroup | |
36 | > incoming = python "$TESTDIR/printenv.py" incoming |
|
36 | > incoming = python "$TESTDIR/printenv.py" incoming | |
37 | > EOF |
|
37 | > EOF | |
38 |
|
38 | |||
39 | pretxncommit and commit hooks can see both parents of merge |
|
39 | pretxncommit and commit hooks can see both parents of merge | |
40 |
|
40 | |||
41 | $ cd ../a |
|
41 | $ cd ../a | |
42 | $ echo b >> a |
|
42 | $ echo b >> a | |
43 | $ hg commit -m a1 -d "1 0" |
|
43 | $ hg commit -m a1 -d "1 0" | |
44 | precommit hook: HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b |
|
44 | precommit hook: HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b | |
45 | pretxncommit hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PENDING=$TESTTMP/a |
|
45 | pretxncommit hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PENDING=$TESTTMP/a | |
46 | 1:ab228980c14d |
|
46 | 1:ab228980c14d | |
47 | commit hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b |
|
47 | commit hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b | |
48 | commit.b hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b |
|
48 | commit.b hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b | |
49 | $ hg update -C 0 |
|
49 | $ hg update -C 0 | |
50 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
50 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved | |
51 | $ echo b > b |
|
51 | $ echo b > b | |
52 | $ hg add b |
|
52 | $ hg add b | |
53 | $ hg commit -m b -d '1 0' |
|
53 | $ hg commit -m b -d '1 0' | |
54 | precommit hook: HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b |
|
54 | precommit hook: HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b | |
55 | pretxncommit hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PENDING=$TESTTMP/a |
|
55 | pretxncommit hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PENDING=$TESTTMP/a | |
56 | 2:ee9deb46ab31 |
|
56 | 2:ee9deb46ab31 | |
57 | commit hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b |
|
57 | commit hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b | |
58 | commit.b hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b |
|
58 | commit.b hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b | |
59 | created new head |
|
59 | created new head | |
60 | $ hg merge 1 |
|
60 | $ hg merge 1 | |
61 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
61 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved | |
62 | (branch merge, don't forget to commit) |
|
62 | (branch merge, don't forget to commit) | |
63 | $ hg commit -m merge -d '2 0' |
|
63 | $ hg commit -m merge -d '2 0' | |
64 | precommit hook: HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd |
|
64 | precommit hook: HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd | |
65 | pretxncommit hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd HG_PENDING=$TESTTMP/a |
|
65 | pretxncommit hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd HG_PENDING=$TESTTMP/a | |
66 | 3:07f3376c1e65 |
|
66 | 3:07f3376c1e65 | |
67 | commit hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd |
|
67 | commit hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd | |
68 | commit.b hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd |
|
68 | commit.b hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd | |
69 |
|
69 | |||
70 | test generic hooks |
|
70 | test generic hooks | |
71 |
|
71 | |||
72 | $ hg id |
|
72 | $ hg id | |
73 | pre-identify hook: HG_ARGS=id HG_OPTS={'bookmarks': None, 'branch': None, 'id': None, 'insecure': None, 'num': None, 'remotecmd': '', 'rev': '', 'ssh': '', 'tags': None} HG_PATS=[] |
|
73 | pre-identify hook: HG_ARGS=id HG_OPTS={'bookmarks': None, 'branch': None, 'id': None, 'insecure': None, 'num': None, 'remotecmd': '', 'rev': '', 'ssh': '', 'tags': None} HG_PATS=[] | |
74 | abort: pre-identify hook exited with status 1 |
|
74 | abort: pre-identify hook exited with status 1 | |
75 | [255] |
|
75 | [255] | |
76 | $ hg cat b |
|
76 | $ hg cat b | |
77 | pre-cat hook: HG_ARGS=cat b HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': ''} HG_PATS=['b'] |
|
77 | pre-cat hook: HG_ARGS=cat b HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': ''} HG_PATS=['b'] | |
78 | b |
|
78 | b | |
79 | post-cat hook: HG_ARGS=cat b HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': ''} HG_PATS=['b'] HG_RESULT=0 |
|
79 | post-cat hook: HG_ARGS=cat b HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': ''} HG_PATS=['b'] HG_RESULT=0 | |
80 |
|
80 | |||
81 | $ cd ../b |
|
81 | $ cd ../b | |
82 | $ hg pull ../a |
|
82 | $ hg pull ../a | |
83 | pulling from ../a |
|
83 | pulling from ../a | |
84 | searching for changes |
|
84 | searching for changes | |
85 | prechangegroup hook: HG_SOURCE=pull HG_URL=file:$TESTTMP/a |
|
85 | prechangegroup hook: HG_SOURCE=pull HG_URL=file:$TESTTMP/a | |
86 | adding changesets |
|
86 | adding changesets | |
87 | adding manifests |
|
87 | adding manifests | |
88 | adding file changes |
|
88 | adding file changes | |
89 | added 3 changesets with 2 changes to 2 files |
|
89 | added 3 changesets with 2 changes to 2 files | |
90 | changegroup hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_SOURCE=pull HG_URL=file:$TESTTMP/a |
|
90 | changegroup hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_SOURCE=pull HG_URL=file:$TESTTMP/a | |
91 | incoming hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_SOURCE=pull HG_URL=file:$TESTTMP/a |
|
91 | incoming hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_SOURCE=pull HG_URL=file:$TESTTMP/a | |
92 | incoming hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_SOURCE=pull HG_URL=file:$TESTTMP/a |
|
92 | incoming hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_SOURCE=pull HG_URL=file:$TESTTMP/a | |
93 | incoming hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_SOURCE=pull HG_URL=file:$TESTTMP/a |
|
93 | incoming hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_SOURCE=pull HG_URL=file:$TESTTMP/a | |
94 | (run 'hg update' to get a working copy) |
|
94 | (run 'hg update' to get a working copy) | |
95 |
|
95 | |||
96 | tag hooks can see env vars |
|
96 | tag hooks can see env vars | |
97 |
|
97 | |||
98 | $ cd ../a |
|
98 | $ cd ../a | |
99 | $ cat >> .hg/hgrc <<EOF |
|
99 | $ cat >> .hg/hgrc <<EOF | |
100 | > pretag = python "$TESTDIR/printenv.py" pretag |
|
100 | > pretag = python "$TESTDIR/printenv.py" pretag | |
101 | > tag = sh -c "HG_PARENT1= HG_PARENT2= python \"$TESTDIR/printenv.py\" tag" |
|
101 | > tag = sh -c "HG_PARENT1= HG_PARENT2= python \"$TESTDIR/printenv.py\" tag" | |
102 | > EOF |
|
102 | > EOF | |
103 | $ hg tag -d '3 0' a |
|
103 | $ hg tag -d '3 0' a | |
104 | pretag hook: HG_LOCAL=0 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_TAG=a |
|
104 | pretag hook: HG_LOCAL=0 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_TAG=a | |
105 | precommit hook: HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2 |
|
105 | precommit hook: HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2 | |
106 | pretxncommit hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PENDING=$TESTTMP/a |
|
106 | pretxncommit hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PENDING=$TESTTMP/a | |
107 | 4:539e4b31b6dc |
|
107 | 4:539e4b31b6dc | |
108 | tag hook: HG_LOCAL=0 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_TAG=a |
|
108 | tag hook: HG_LOCAL=0 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_TAG=a | |
109 | commit hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2 |
|
109 | commit hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2 | |
110 | commit.b hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2 |
|
110 | commit.b hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2 | |
111 | $ hg tag -l la |
|
111 | $ hg tag -l la | |
112 | pretag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=la |
|
112 | pretag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=la | |
113 | tag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=la |
|
113 | tag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=la | |
114 |
|
114 | |||
115 | pretag hook can forbid tagging |
|
115 | pretag hook can forbid tagging | |
116 |
|
116 | |||
117 | $ echo "pretag.forbid = python \"$TESTDIR/printenv.py\" pretag.forbid 1" >> .hg/hgrc |
|
117 | $ echo "pretag.forbid = python \"$TESTDIR/printenv.py\" pretag.forbid 1" >> .hg/hgrc | |
118 | $ hg tag -d '4 0' fa |
|
118 | $ hg tag -d '4 0' fa | |
119 | pretag hook: HG_LOCAL=0 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fa |
|
119 | pretag hook: HG_LOCAL=0 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fa | |
120 | pretag.forbid hook: HG_LOCAL=0 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fa |
|
120 | pretag.forbid hook: HG_LOCAL=0 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fa | |
121 | abort: pretag.forbid hook exited with status 1 |
|
121 | abort: pretag.forbid hook exited with status 1 | |
122 | [255] |
|
122 | [255] | |
123 | $ hg tag -l fla |
|
123 | $ hg tag -l fla | |
124 | pretag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fla |
|
124 | pretag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fla | |
125 | pretag.forbid hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fla |
|
125 | pretag.forbid hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fla | |
126 | abort: pretag.forbid hook exited with status 1 |
|
126 | abort: pretag.forbid hook exited with status 1 | |
127 | [255] |
|
127 | [255] | |
128 |
|
128 | |||
129 | pretxncommit hook can see changeset, can roll back txn, changeset no |
|
129 | pretxncommit hook can see changeset, can roll back txn, changeset no | |
130 | more there after |
|
130 | more there after | |
131 |
|
131 | |||
132 | $ echo "pretxncommit.forbid0 = hg tip -q" >> .hg/hgrc |
|
132 | $ echo "pretxncommit.forbid0 = hg tip -q" >> .hg/hgrc | |
133 | $ echo "pretxncommit.forbid1 = python \"$TESTDIR/printenv.py\" pretxncommit.forbid 1" >> .hg/hgrc |
|
133 | $ echo "pretxncommit.forbid1 = python \"$TESTDIR/printenv.py\" pretxncommit.forbid 1" >> .hg/hgrc | |
134 | $ echo z > z |
|
134 | $ echo z > z | |
135 | $ hg add z |
|
135 | $ hg add z | |
136 | $ hg -q tip |
|
136 | $ hg -q tip | |
137 | 4:539e4b31b6dc |
|
137 | 4:539e4b31b6dc | |
138 | $ hg commit -m 'fail' -d '4 0' |
|
138 | $ hg commit -m 'fail' -d '4 0' | |
139 | precommit hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 |
|
139 | precommit hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 | |
140 | pretxncommit hook: HG_NODE=6f611f8018c10e827fee6bd2bc807f937e761567 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/a |
|
140 | pretxncommit hook: HG_NODE=6f611f8018c10e827fee6bd2bc807f937e761567 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/a | |
141 | 5:6f611f8018c1 |
|
141 | 5:6f611f8018c1 | |
142 | 5:6f611f8018c1 |
|
142 | 5:6f611f8018c1 | |
143 | pretxncommit.forbid hook: HG_NODE=6f611f8018c10e827fee6bd2bc807f937e761567 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/a |
|
143 | pretxncommit.forbid hook: HG_NODE=6f611f8018c10e827fee6bd2bc807f937e761567 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/a | |
144 | transaction abort! |
|
144 | transaction abort! | |
145 | rollback completed |
|
145 | rollback completed | |
146 | abort: pretxncommit.forbid1 hook exited with status 1 |
|
146 | abort: pretxncommit.forbid1 hook exited with status 1 | |
147 | [255] |
|
147 | [255] | |
148 | $ hg -q tip |
|
148 | $ hg -q tip | |
149 | 4:539e4b31b6dc |
|
149 | 4:539e4b31b6dc | |
150 |
|
150 | |||
151 | (Check that no 'changelog.i.a' file were left behind) |
|
151 | (Check that no 'changelog.i.a' file were left behind) | |
152 |
|
152 | |||
153 | $ ls -1 .hg/store/ |
|
153 | $ ls -1 .hg/store/ | |
154 | 00changelog.i |
|
154 | 00changelog.i | |
155 | 00manifest.i |
|
155 | 00manifest.i | |
156 | data |
|
156 | data | |
157 | fncache |
|
157 | fncache | |
158 | journal.phaseroots |
|
158 | journal.phaseroots | |
159 | phaseroots |
|
159 | phaseroots | |
160 | undo |
|
160 | undo | |
|
161 | undo.backup.fncache | |||
|
162 | undo.backupfiles | |||
161 | undo.phaseroots |
|
163 | undo.phaseroots | |
162 |
|
164 | |||
163 |
|
165 | |||
164 | precommit hook can prevent commit |
|
166 | precommit hook can prevent commit | |
165 |
|
167 | |||
166 | $ echo "precommit.forbid = python \"$TESTDIR/printenv.py\" precommit.forbid 1" >> .hg/hgrc |
|
168 | $ echo "precommit.forbid = python \"$TESTDIR/printenv.py\" precommit.forbid 1" >> .hg/hgrc | |
167 | $ hg commit -m 'fail' -d '4 0' |
|
169 | $ hg commit -m 'fail' -d '4 0' | |
168 | precommit hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 |
|
170 | precommit hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 | |
169 | precommit.forbid hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 |
|
171 | precommit.forbid hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 | |
170 | abort: precommit.forbid hook exited with status 1 |
|
172 | abort: precommit.forbid hook exited with status 1 | |
171 | [255] |
|
173 | [255] | |
172 | $ hg -q tip |
|
174 | $ hg -q tip | |
173 | 4:539e4b31b6dc |
|
175 | 4:539e4b31b6dc | |
174 |
|
176 | |||
175 | preupdate hook can prevent update |
|
177 | preupdate hook can prevent update | |
176 |
|
178 | |||
177 | $ echo "preupdate = python \"$TESTDIR/printenv.py\" preupdate" >> .hg/hgrc |
|
179 | $ echo "preupdate = python \"$TESTDIR/printenv.py\" preupdate" >> .hg/hgrc | |
178 | $ hg update 1 |
|
180 | $ hg update 1 | |
179 | preupdate hook: HG_PARENT1=ab228980c14d |
|
181 | preupdate hook: HG_PARENT1=ab228980c14d | |
180 | 0 files updated, 0 files merged, 2 files removed, 0 files unresolved |
|
182 | 0 files updated, 0 files merged, 2 files removed, 0 files unresolved | |
181 |
|
183 | |||
182 | update hook |
|
184 | update hook | |
183 |
|
185 | |||
184 | $ echo "update = python \"$TESTDIR/printenv.py\" update" >> .hg/hgrc |
|
186 | $ echo "update = python \"$TESTDIR/printenv.py\" update" >> .hg/hgrc | |
185 | $ hg update |
|
187 | $ hg update | |
186 | preupdate hook: HG_PARENT1=539e4b31b6dc |
|
188 | preupdate hook: HG_PARENT1=539e4b31b6dc | |
187 | update hook: HG_ERROR=0 HG_PARENT1=539e4b31b6dc |
|
189 | update hook: HG_ERROR=0 HG_PARENT1=539e4b31b6dc | |
188 | 2 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
190 | 2 files updated, 0 files merged, 0 files removed, 0 files unresolved | |
189 |
|
191 | |||
190 | pushkey hook |
|
192 | pushkey hook | |
191 |
|
193 | |||
192 | $ echo "pushkey = python \"$TESTDIR/printenv.py\" pushkey" >> .hg/hgrc |
|
194 | $ echo "pushkey = python \"$TESTDIR/printenv.py\" pushkey" >> .hg/hgrc | |
193 | $ cd ../b |
|
195 | $ cd ../b | |
194 | $ hg bookmark -r null foo |
|
196 | $ hg bookmark -r null foo | |
195 | $ hg push -B foo ../a |
|
197 | $ hg push -B foo ../a | |
196 | pushing to ../a |
|
198 | pushing to ../a | |
197 | searching for changes |
|
199 | searching for changes | |
198 | no changes found |
|
200 | no changes found | |
199 | pushkey hook: HG_KEY=foo HG_NAMESPACE=bookmarks HG_NEW=0000000000000000000000000000000000000000 HG_RET=1 |
|
201 | pushkey hook: HG_KEY=foo HG_NAMESPACE=bookmarks HG_NEW=0000000000000000000000000000000000000000 HG_RET=1 | |
200 | exporting bookmark foo |
|
202 | exporting bookmark foo | |
201 | [1] |
|
203 | [1] | |
202 | $ cd ../a |
|
204 | $ cd ../a | |
203 |
|
205 | |||
204 | listkeys hook |
|
206 | listkeys hook | |
205 |
|
207 | |||
206 | $ echo "listkeys = python \"$TESTDIR/printenv.py\" listkeys" >> .hg/hgrc |
|
208 | $ echo "listkeys = python \"$TESTDIR/printenv.py\" listkeys" >> .hg/hgrc | |
207 | $ hg bookmark -r null bar |
|
209 | $ hg bookmark -r null bar | |
208 | $ cd ../b |
|
210 | $ cd ../b | |
209 | $ hg pull -B bar ../a |
|
211 | $ hg pull -B bar ../a | |
210 | pulling from ../a |
|
212 | pulling from ../a | |
211 | listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'} |
|
213 | listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'} | |
212 | listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'} |
|
214 | listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'} | |
213 | no changes found |
|
215 | no changes found | |
214 | listkeys hook: HG_NAMESPACE=phases HG_VALUES={'cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b': '1', 'publishing': 'True'} |
|
216 | listkeys hook: HG_NAMESPACE=phases HG_VALUES={'cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b': '1', 'publishing': 'True'} | |
215 | adding remote bookmark bar |
|
217 | adding remote bookmark bar | |
216 | $ cd ../a |
|
218 | $ cd ../a | |
217 |
|
219 | |||
218 | test that prepushkey can prevent incoming keys |
|
220 | test that prepushkey can prevent incoming keys | |
219 |
|
221 | |||
220 | $ echo "prepushkey = python \"$TESTDIR/printenv.py\" prepushkey.forbid 1" >> .hg/hgrc |
|
222 | $ echo "prepushkey = python \"$TESTDIR/printenv.py\" prepushkey.forbid 1" >> .hg/hgrc | |
221 | $ cd ../b |
|
223 | $ cd ../b | |
222 | $ hg bookmark -r null baz |
|
224 | $ hg bookmark -r null baz | |
223 | $ hg push -B baz ../a |
|
225 | $ hg push -B baz ../a | |
224 | pushing to ../a |
|
226 | pushing to ../a | |
225 | searching for changes |
|
227 | searching for changes | |
226 | listkeys hook: HG_NAMESPACE=phases HG_VALUES={'cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b': '1', 'publishing': 'True'} |
|
228 | listkeys hook: HG_NAMESPACE=phases HG_VALUES={'cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b': '1', 'publishing': 'True'} | |
227 | listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'} |
|
229 | listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'} | |
228 | no changes found |
|
230 | no changes found | |
229 | listkeys hook: HG_NAMESPACE=phases HG_VALUES={'cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b': '1', 'publishing': 'True'} |
|
231 | listkeys hook: HG_NAMESPACE=phases HG_VALUES={'cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b': '1', 'publishing': 'True'} | |
230 | prepushkey.forbid hook: HG_KEY=baz HG_NAMESPACE=bookmarks HG_NEW=0000000000000000000000000000000000000000 |
|
232 | prepushkey.forbid hook: HG_KEY=baz HG_NAMESPACE=bookmarks HG_NEW=0000000000000000000000000000000000000000 | |
231 | pushkey-abort: prepushkey hook exited with status 1 |
|
233 | pushkey-abort: prepushkey hook exited with status 1 | |
232 | exporting bookmark baz failed! |
|
234 | exporting bookmark baz failed! | |
233 | [1] |
|
235 | [1] | |
234 | $ cd ../a |
|
236 | $ cd ../a | |
235 |
|
237 | |||
236 | test that prelistkeys can prevent listing keys |
|
238 | test that prelistkeys can prevent listing keys | |
237 |
|
239 | |||
238 | $ echo "prelistkeys = python \"$TESTDIR/printenv.py\" prelistkeys.forbid 1" >> .hg/hgrc |
|
240 | $ echo "prelistkeys = python \"$TESTDIR/printenv.py\" prelistkeys.forbid 1" >> .hg/hgrc | |
239 | $ hg bookmark -r null quux |
|
241 | $ hg bookmark -r null quux | |
240 | $ cd ../b |
|
242 | $ cd ../b | |
241 | $ hg pull -B quux ../a |
|
243 | $ hg pull -B quux ../a | |
242 | pulling from ../a |
|
244 | pulling from ../a | |
243 | prelistkeys.forbid hook: HG_NAMESPACE=bookmarks |
|
245 | prelistkeys.forbid hook: HG_NAMESPACE=bookmarks | |
244 | abort: prelistkeys hook exited with status 1 |
|
246 | abort: prelistkeys hook exited with status 1 | |
245 | [255] |
|
247 | [255] | |
246 | $ cd ../a |
|
248 | $ cd ../a | |
247 | $ rm .hg/hgrc |
|
249 | $ rm .hg/hgrc | |
248 |
|
250 | |||
249 | prechangegroup hook can prevent incoming changes |
|
251 | prechangegroup hook can prevent incoming changes | |
250 |
|
252 | |||
251 | $ cd ../b |
|
253 | $ cd ../b | |
252 | $ hg -q tip |
|
254 | $ hg -q tip | |
253 | 3:07f3376c1e65 |
|
255 | 3:07f3376c1e65 | |
254 | $ cat > .hg/hgrc <<EOF |
|
256 | $ cat > .hg/hgrc <<EOF | |
255 | > [hooks] |
|
257 | > [hooks] | |
256 | > prechangegroup.forbid = python "$TESTDIR/printenv.py" prechangegroup.forbid 1 |
|
258 | > prechangegroup.forbid = python "$TESTDIR/printenv.py" prechangegroup.forbid 1 | |
257 | > EOF |
|
259 | > EOF | |
258 | $ hg pull ../a |
|
260 | $ hg pull ../a | |
259 | pulling from ../a |
|
261 | pulling from ../a | |
260 | searching for changes |
|
262 | searching for changes | |
261 | prechangegroup.forbid hook: HG_SOURCE=pull HG_URL=file:$TESTTMP/a |
|
263 | prechangegroup.forbid hook: HG_SOURCE=pull HG_URL=file:$TESTTMP/a | |
262 | abort: prechangegroup.forbid hook exited with status 1 |
|
264 | abort: prechangegroup.forbid hook exited with status 1 | |
263 | [255] |
|
265 | [255] | |
264 |
|
266 | |||
265 | pretxnchangegroup hook can see incoming changes, can roll back txn, |
|
267 | pretxnchangegroup hook can see incoming changes, can roll back txn, | |
266 | incoming changes no longer there after |
|
268 | incoming changes no longer there after | |
267 |
|
269 | |||
268 | $ cat > .hg/hgrc <<EOF |
|
270 | $ cat > .hg/hgrc <<EOF | |
269 | > [hooks] |
|
271 | > [hooks] | |
270 | > pretxnchangegroup.forbid0 = hg tip -q |
|
272 | > pretxnchangegroup.forbid0 = hg tip -q | |
271 | > pretxnchangegroup.forbid1 = python "$TESTDIR/printenv.py" pretxnchangegroup.forbid 1 |
|
273 | > pretxnchangegroup.forbid1 = python "$TESTDIR/printenv.py" pretxnchangegroup.forbid 1 | |
272 | > EOF |
|
274 | > EOF | |
273 | $ hg pull ../a |
|
275 | $ hg pull ../a | |
274 | pulling from ../a |
|
276 | pulling from ../a | |
275 | searching for changes |
|
277 | searching for changes | |
276 | adding changesets |
|
278 | adding changesets | |
277 | adding manifests |
|
279 | adding manifests | |
278 | adding file changes |
|
280 | adding file changes | |
279 | added 1 changesets with 1 changes to 1 files |
|
281 | added 1 changesets with 1 changes to 1 files | |
280 | 4:539e4b31b6dc |
|
282 | 4:539e4b31b6dc | |
281 | pretxnchangegroup.forbid hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/b HG_SOURCE=pull HG_URL=file:$TESTTMP/a |
|
283 | pretxnchangegroup.forbid hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/b HG_SOURCE=pull HG_URL=file:$TESTTMP/a | |
282 | transaction abort! |
|
284 | transaction abort! | |
283 | rollback completed |
|
285 | rollback completed | |
284 | abort: pretxnchangegroup.forbid1 hook exited with status 1 |
|
286 | abort: pretxnchangegroup.forbid1 hook exited with status 1 | |
285 | [255] |
|
287 | [255] | |
286 | $ hg -q tip |
|
288 | $ hg -q tip | |
287 | 3:07f3376c1e65 |
|
289 | 3:07f3376c1e65 | |
288 |
|
290 | |||
289 | outgoing hooks can see env vars |
|
291 | outgoing hooks can see env vars | |
290 |
|
292 | |||
291 | $ rm .hg/hgrc |
|
293 | $ rm .hg/hgrc | |
292 | $ cat > ../a/.hg/hgrc <<EOF |
|
294 | $ cat > ../a/.hg/hgrc <<EOF | |
293 | > [hooks] |
|
295 | > [hooks] | |
294 | > preoutgoing = python "$TESTDIR/printenv.py" preoutgoing |
|
296 | > preoutgoing = python "$TESTDIR/printenv.py" preoutgoing | |
295 | > outgoing = python "$TESTDIR/printenv.py" outgoing |
|
297 | > outgoing = python "$TESTDIR/printenv.py" outgoing | |
296 | > EOF |
|
298 | > EOF | |
297 | $ hg pull ../a |
|
299 | $ hg pull ../a | |
298 | pulling from ../a |
|
300 | pulling from ../a | |
299 | searching for changes |
|
301 | searching for changes | |
300 | preoutgoing hook: HG_SOURCE=pull |
|
302 | preoutgoing hook: HG_SOURCE=pull | |
301 | adding changesets |
|
303 | adding changesets | |
302 | outgoing hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_SOURCE=pull |
|
304 | outgoing hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_SOURCE=pull | |
303 | adding manifests |
|
305 | adding manifests | |
304 | adding file changes |
|
306 | adding file changes | |
305 | added 1 changesets with 1 changes to 1 files |
|
307 | added 1 changesets with 1 changes to 1 files | |
306 | adding remote bookmark quux |
|
308 | adding remote bookmark quux | |
307 | (run 'hg update' to get a working copy) |
|
309 | (run 'hg update' to get a working copy) | |
308 | $ hg rollback |
|
310 | $ hg rollback | |
309 | repository tip rolled back to revision 3 (undo pull) |
|
311 | repository tip rolled back to revision 3 (undo pull) | |
310 |
|
312 | |||
311 | preoutgoing hook can prevent outgoing changes |
|
313 | preoutgoing hook can prevent outgoing changes | |
312 |
|
314 | |||
313 | $ echo "preoutgoing.forbid = python \"$TESTDIR/printenv.py\" preoutgoing.forbid 1" >> ../a/.hg/hgrc |
|
315 | $ echo "preoutgoing.forbid = python \"$TESTDIR/printenv.py\" preoutgoing.forbid 1" >> ../a/.hg/hgrc | |
314 | $ hg pull ../a |
|
316 | $ hg pull ../a | |
315 | pulling from ../a |
|
317 | pulling from ../a | |
316 | searching for changes |
|
318 | searching for changes | |
317 | preoutgoing hook: HG_SOURCE=pull |
|
319 | preoutgoing hook: HG_SOURCE=pull | |
318 | preoutgoing.forbid hook: HG_SOURCE=pull |
|
320 | preoutgoing.forbid hook: HG_SOURCE=pull | |
319 | abort: preoutgoing.forbid hook exited with status 1 |
|
321 | abort: preoutgoing.forbid hook exited with status 1 | |
320 | [255] |
|
322 | [255] | |
321 |
|
323 | |||
322 | outgoing hooks work for local clones |
|
324 | outgoing hooks work for local clones | |
323 |
|
325 | |||
324 | $ cd .. |
|
326 | $ cd .. | |
325 | $ cat > a/.hg/hgrc <<EOF |
|
327 | $ cat > a/.hg/hgrc <<EOF | |
326 | > [hooks] |
|
328 | > [hooks] | |
327 | > preoutgoing = python "$TESTDIR/printenv.py" preoutgoing |
|
329 | > preoutgoing = python "$TESTDIR/printenv.py" preoutgoing | |
328 | > outgoing = python "$TESTDIR/printenv.py" outgoing |
|
330 | > outgoing = python "$TESTDIR/printenv.py" outgoing | |
329 | > EOF |
|
331 | > EOF | |
330 | $ hg clone a c |
|
332 | $ hg clone a c | |
331 | preoutgoing hook: HG_SOURCE=clone |
|
333 | preoutgoing hook: HG_SOURCE=clone | |
332 | outgoing hook: HG_NODE=0000000000000000000000000000000000000000 HG_SOURCE=clone |
|
334 | outgoing hook: HG_NODE=0000000000000000000000000000000000000000 HG_SOURCE=clone | |
333 | updating to branch default |
|
335 | updating to branch default | |
334 | 3 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
336 | 3 files updated, 0 files merged, 0 files removed, 0 files unresolved | |
335 | $ rm -rf c |
|
337 | $ rm -rf c | |
336 |
|
338 | |||
337 | preoutgoing hook can prevent outgoing changes for local clones |
|
339 | preoutgoing hook can prevent outgoing changes for local clones | |
338 |
|
340 | |||
339 | $ echo "preoutgoing.forbid = python \"$TESTDIR/printenv.py\" preoutgoing.forbid 1" >> a/.hg/hgrc |
|
341 | $ echo "preoutgoing.forbid = python \"$TESTDIR/printenv.py\" preoutgoing.forbid 1" >> a/.hg/hgrc | |
340 | $ hg clone a zzz |
|
342 | $ hg clone a zzz | |
341 | preoutgoing hook: HG_SOURCE=clone |
|
343 | preoutgoing hook: HG_SOURCE=clone | |
342 | preoutgoing.forbid hook: HG_SOURCE=clone |
|
344 | preoutgoing.forbid hook: HG_SOURCE=clone | |
343 | abort: preoutgoing.forbid hook exited with status 1 |
|
345 | abort: preoutgoing.forbid hook exited with status 1 | |
344 | [255] |
|
346 | [255] | |
345 |
|
347 | |||
346 | $ cd "$TESTTMP/b" |
|
348 | $ cd "$TESTTMP/b" | |
347 |
|
349 | |||
348 | $ cat > hooktests.py <<EOF |
|
350 | $ cat > hooktests.py <<EOF | |
349 | > from mercurial import util |
|
351 | > from mercurial import util | |
350 | > |
|
352 | > | |
351 | > uncallable = 0 |
|
353 | > uncallable = 0 | |
352 | > |
|
354 | > | |
353 | > def printargs(args): |
|
355 | > def printargs(args): | |
354 | > args.pop('ui', None) |
|
356 | > args.pop('ui', None) | |
355 | > args.pop('repo', None) |
|
357 | > args.pop('repo', None) | |
356 | > a = list(args.items()) |
|
358 | > a = list(args.items()) | |
357 | > a.sort() |
|
359 | > a.sort() | |
358 | > print 'hook args:' |
|
360 | > print 'hook args:' | |
359 | > for k, v in a: |
|
361 | > for k, v in a: | |
360 | > print ' ', k, v |
|
362 | > print ' ', k, v | |
361 | > |
|
363 | > | |
362 | > def passhook(**args): |
|
364 | > def passhook(**args): | |
363 | > printargs(args) |
|
365 | > printargs(args) | |
364 | > |
|
366 | > | |
365 | > def failhook(**args): |
|
367 | > def failhook(**args): | |
366 | > printargs(args) |
|
368 | > printargs(args) | |
367 | > return True |
|
369 | > return True | |
368 | > |
|
370 | > | |
369 | > class LocalException(Exception): |
|
371 | > class LocalException(Exception): | |
370 | > pass |
|
372 | > pass | |
371 | > |
|
373 | > | |
372 | > def raisehook(**args): |
|
374 | > def raisehook(**args): | |
373 | > raise LocalException('exception from hook') |
|
375 | > raise LocalException('exception from hook') | |
374 | > |
|
376 | > | |
375 | > def aborthook(**args): |
|
377 | > def aborthook(**args): | |
376 | > raise util.Abort('raise abort from hook') |
|
378 | > raise util.Abort('raise abort from hook') | |
377 | > |
|
379 | > | |
378 | > def brokenhook(**args): |
|
380 | > def brokenhook(**args): | |
379 | > return 1 + {} |
|
381 | > return 1 + {} | |
380 | > |
|
382 | > | |
381 | > def verbosehook(ui, **args): |
|
383 | > def verbosehook(ui, **args): | |
382 | > ui.note('verbose output from hook\n') |
|
384 | > ui.note('verbose output from hook\n') | |
383 | > |
|
385 | > | |
384 | > def printtags(ui, repo, **args): |
|
386 | > def printtags(ui, repo, **args): | |
385 | > print sorted(repo.tags()) |
|
387 | > print sorted(repo.tags()) | |
386 | > |
|
388 | > | |
387 | > class container: |
|
389 | > class container: | |
388 | > unreachable = 1 |
|
390 | > unreachable = 1 | |
389 | > EOF |
|
391 | > EOF | |
390 |
|
392 | |||
391 | test python hooks |
|
393 | test python hooks | |
392 |
|
394 | |||
393 | #if windows |
|
395 | #if windows | |
394 | $ PYTHONPATH="$TESTTMP/b;$PYTHONPATH" |
|
396 | $ PYTHONPATH="$TESTTMP/b;$PYTHONPATH" | |
395 | #else |
|
397 | #else | |
396 | $ PYTHONPATH="$TESTTMP/b:$PYTHONPATH" |
|
398 | $ PYTHONPATH="$TESTTMP/b:$PYTHONPATH" | |
397 | #endif |
|
399 | #endif | |
398 | $ export PYTHONPATH |
|
400 | $ export PYTHONPATH | |
399 |
|
401 | |||
400 | $ echo '[hooks]' > ../a/.hg/hgrc |
|
402 | $ echo '[hooks]' > ../a/.hg/hgrc | |
401 | $ echo 'preoutgoing.broken = python:hooktests.brokenhook' >> ../a/.hg/hgrc |
|
403 | $ echo 'preoutgoing.broken = python:hooktests.brokenhook' >> ../a/.hg/hgrc | |
402 | $ hg pull ../a 2>&1 | grep 'raised an exception' |
|
404 | $ hg pull ../a 2>&1 | grep 'raised an exception' | |
403 | error: preoutgoing.broken hook raised an exception: unsupported operand type(s) for +: 'int' and 'dict' |
|
405 | error: preoutgoing.broken hook raised an exception: unsupported operand type(s) for +: 'int' and 'dict' | |
404 |
|
406 | |||
405 | $ echo '[hooks]' > ../a/.hg/hgrc |
|
407 | $ echo '[hooks]' > ../a/.hg/hgrc | |
406 | $ echo 'preoutgoing.raise = python:hooktests.raisehook' >> ../a/.hg/hgrc |
|
408 | $ echo 'preoutgoing.raise = python:hooktests.raisehook' >> ../a/.hg/hgrc | |
407 | $ hg pull ../a 2>&1 | grep 'raised an exception' |
|
409 | $ hg pull ../a 2>&1 | grep 'raised an exception' | |
408 | error: preoutgoing.raise hook raised an exception: exception from hook |
|
410 | error: preoutgoing.raise hook raised an exception: exception from hook | |
409 |
|
411 | |||
410 | $ echo '[hooks]' > ../a/.hg/hgrc |
|
412 | $ echo '[hooks]' > ../a/.hg/hgrc | |
411 | $ echo 'preoutgoing.abort = python:hooktests.aborthook' >> ../a/.hg/hgrc |
|
413 | $ echo 'preoutgoing.abort = python:hooktests.aborthook' >> ../a/.hg/hgrc | |
412 | $ hg pull ../a |
|
414 | $ hg pull ../a | |
413 | pulling from ../a |
|
415 | pulling from ../a | |
414 | searching for changes |
|
416 | searching for changes | |
415 | error: preoutgoing.abort hook failed: raise abort from hook |
|
417 | error: preoutgoing.abort hook failed: raise abort from hook | |
416 | abort: raise abort from hook |
|
418 | abort: raise abort from hook | |
417 | [255] |
|
419 | [255] | |
418 |
|
420 | |||
419 | $ echo '[hooks]' > ../a/.hg/hgrc |
|
421 | $ echo '[hooks]' > ../a/.hg/hgrc | |
420 | $ echo 'preoutgoing.fail = python:hooktests.failhook' >> ../a/.hg/hgrc |
|
422 | $ echo 'preoutgoing.fail = python:hooktests.failhook' >> ../a/.hg/hgrc | |
421 | $ hg pull ../a |
|
423 | $ hg pull ../a | |
422 | pulling from ../a |
|
424 | pulling from ../a | |
423 | searching for changes |
|
425 | searching for changes | |
424 | hook args: |
|
426 | hook args: | |
425 | hooktype preoutgoing |
|
427 | hooktype preoutgoing | |
426 | source pull |
|
428 | source pull | |
427 | abort: preoutgoing.fail hook failed |
|
429 | abort: preoutgoing.fail hook failed | |
428 | [255] |
|
430 | [255] | |
429 |
|
431 | |||
430 | $ echo '[hooks]' > ../a/.hg/hgrc |
|
432 | $ echo '[hooks]' > ../a/.hg/hgrc | |
431 | $ echo 'preoutgoing.uncallable = python:hooktests.uncallable' >> ../a/.hg/hgrc |
|
433 | $ echo 'preoutgoing.uncallable = python:hooktests.uncallable' >> ../a/.hg/hgrc | |
432 | $ hg pull ../a |
|
434 | $ hg pull ../a | |
433 | pulling from ../a |
|
435 | pulling from ../a | |
434 | searching for changes |
|
436 | searching for changes | |
435 | abort: preoutgoing.uncallable hook is invalid ("hooktests.uncallable" is not callable) |
|
437 | abort: preoutgoing.uncallable hook is invalid ("hooktests.uncallable" is not callable) | |
436 | [255] |
|
438 | [255] | |
437 |
|
439 | |||
438 | $ echo '[hooks]' > ../a/.hg/hgrc |
|
440 | $ echo '[hooks]' > ../a/.hg/hgrc | |
439 | $ echo 'preoutgoing.nohook = python:hooktests.nohook' >> ../a/.hg/hgrc |
|
441 | $ echo 'preoutgoing.nohook = python:hooktests.nohook' >> ../a/.hg/hgrc | |
440 | $ hg pull ../a |
|
442 | $ hg pull ../a | |
441 | pulling from ../a |
|
443 | pulling from ../a | |
442 | searching for changes |
|
444 | searching for changes | |
443 | abort: preoutgoing.nohook hook is invalid ("hooktests.nohook" is not defined) |
|
445 | abort: preoutgoing.nohook hook is invalid ("hooktests.nohook" is not defined) | |
444 | [255] |
|
446 | [255] | |
445 |
|
447 | |||
446 | $ echo '[hooks]' > ../a/.hg/hgrc |
|
448 | $ echo '[hooks]' > ../a/.hg/hgrc | |
447 | $ echo 'preoutgoing.nomodule = python:nomodule' >> ../a/.hg/hgrc |
|
449 | $ echo 'preoutgoing.nomodule = python:nomodule' >> ../a/.hg/hgrc | |
448 | $ hg pull ../a |
|
450 | $ hg pull ../a | |
449 | pulling from ../a |
|
451 | pulling from ../a | |
450 | searching for changes |
|
452 | searching for changes | |
451 | abort: preoutgoing.nomodule hook is invalid ("nomodule" not in a module) |
|
453 | abort: preoutgoing.nomodule hook is invalid ("nomodule" not in a module) | |
452 | [255] |
|
454 | [255] | |
453 |
|
455 | |||
454 | $ echo '[hooks]' > ../a/.hg/hgrc |
|
456 | $ echo '[hooks]' > ../a/.hg/hgrc | |
455 | $ echo 'preoutgoing.badmodule = python:nomodule.nowhere' >> ../a/.hg/hgrc |
|
457 | $ echo 'preoutgoing.badmodule = python:nomodule.nowhere' >> ../a/.hg/hgrc | |
456 | $ hg pull ../a |
|
458 | $ hg pull ../a | |
457 | pulling from ../a |
|
459 | pulling from ../a | |
458 | searching for changes |
|
460 | searching for changes | |
459 | abort: preoutgoing.badmodule hook is invalid (import of "nomodule" failed) |
|
461 | abort: preoutgoing.badmodule hook is invalid (import of "nomodule" failed) | |
460 | [255] |
|
462 | [255] | |
461 |
|
463 | |||
462 | $ echo '[hooks]' > ../a/.hg/hgrc |
|
464 | $ echo '[hooks]' > ../a/.hg/hgrc | |
463 | $ echo 'preoutgoing.unreachable = python:hooktests.container.unreachable' >> ../a/.hg/hgrc |
|
465 | $ echo 'preoutgoing.unreachable = python:hooktests.container.unreachable' >> ../a/.hg/hgrc | |
464 | $ hg pull ../a |
|
466 | $ hg pull ../a | |
465 | pulling from ../a |
|
467 | pulling from ../a | |
466 | searching for changes |
|
468 | searching for changes | |
467 | abort: preoutgoing.unreachable hook is invalid (import of "hooktests.container" failed) |
|
469 | abort: preoutgoing.unreachable hook is invalid (import of "hooktests.container" failed) | |
468 | [255] |
|
470 | [255] | |
469 |
|
471 | |||
470 | $ echo '[hooks]' > ../a/.hg/hgrc |
|
472 | $ echo '[hooks]' > ../a/.hg/hgrc | |
471 | $ echo 'preoutgoing.pass = python:hooktests.passhook' >> ../a/.hg/hgrc |
|
473 | $ echo 'preoutgoing.pass = python:hooktests.passhook' >> ../a/.hg/hgrc | |
472 | $ hg pull ../a |
|
474 | $ hg pull ../a | |
473 | pulling from ../a |
|
475 | pulling from ../a | |
474 | searching for changes |
|
476 | searching for changes | |
475 | hook args: |
|
477 | hook args: | |
476 | hooktype preoutgoing |
|
478 | hooktype preoutgoing | |
477 | source pull |
|
479 | source pull | |
478 | adding changesets |
|
480 | adding changesets | |
479 | adding manifests |
|
481 | adding manifests | |
480 | adding file changes |
|
482 | adding file changes | |
481 | added 1 changesets with 1 changes to 1 files |
|
483 | added 1 changesets with 1 changes to 1 files | |
482 | adding remote bookmark quux |
|
484 | adding remote bookmark quux | |
483 | (run 'hg update' to get a working copy) |
|
485 | (run 'hg update' to get a working copy) | |
484 |
|
486 | |||
485 | make sure --traceback works |
|
487 | make sure --traceback works | |
486 |
|
488 | |||
487 | $ echo '[hooks]' > .hg/hgrc |
|
489 | $ echo '[hooks]' > .hg/hgrc | |
488 | $ echo 'commit.abort = python:hooktests.aborthook' >> .hg/hgrc |
|
490 | $ echo 'commit.abort = python:hooktests.aborthook' >> .hg/hgrc | |
489 |
|
491 | |||
490 | $ echo aa > a |
|
492 | $ echo aa > a | |
491 | $ hg --traceback commit -d '0 0' -ma 2>&1 | grep '^Traceback' |
|
493 | $ hg --traceback commit -d '0 0' -ma 2>&1 | grep '^Traceback' | |
492 | Traceback (most recent call last): |
|
494 | Traceback (most recent call last): | |
493 |
|
495 | |||
494 | $ cd .. |
|
496 | $ cd .. | |
495 | $ hg init c |
|
497 | $ hg init c | |
496 | $ cd c |
|
498 | $ cd c | |
497 |
|
499 | |||
498 | $ cat > hookext.py <<EOF |
|
500 | $ cat > hookext.py <<EOF | |
499 | > def autohook(**args): |
|
501 | > def autohook(**args): | |
500 | > print "Automatically installed hook" |
|
502 | > print "Automatically installed hook" | |
501 | > |
|
503 | > | |
502 | > def reposetup(ui, repo): |
|
504 | > def reposetup(ui, repo): | |
503 | > repo.ui.setconfig("hooks", "commit.auto", autohook) |
|
505 | > repo.ui.setconfig("hooks", "commit.auto", autohook) | |
504 | > EOF |
|
506 | > EOF | |
505 | $ echo '[extensions]' >> .hg/hgrc |
|
507 | $ echo '[extensions]' >> .hg/hgrc | |
506 | $ echo 'hookext = hookext.py' >> .hg/hgrc |
|
508 | $ echo 'hookext = hookext.py' >> .hg/hgrc | |
507 |
|
509 | |||
508 | $ touch foo |
|
510 | $ touch foo | |
509 | $ hg add foo |
|
511 | $ hg add foo | |
510 | $ hg ci -d '0 0' -m 'add foo' |
|
512 | $ hg ci -d '0 0' -m 'add foo' | |
511 | Automatically installed hook |
|
513 | Automatically installed hook | |
512 | $ echo >> foo |
|
514 | $ echo >> foo | |
513 | $ hg ci --debug -d '0 0' -m 'change foo' |
|
515 | $ hg ci --debug -d '0 0' -m 'change foo' | |
514 | committing files: |
|
516 | committing files: | |
515 | foo |
|
517 | foo | |
516 | committing manifest |
|
518 | committing manifest | |
517 | committing changelog |
|
519 | committing changelog | |
518 | calling hook commit.auto: hgext_hookext.autohook |
|
520 | calling hook commit.auto: hgext_hookext.autohook | |
519 | Automatically installed hook |
|
521 | Automatically installed hook | |
520 | committed changeset 1:52998019f6252a2b893452765fcb0a47351a5708 |
|
522 | committed changeset 1:52998019f6252a2b893452765fcb0a47351a5708 | |
521 |
|
523 | |||
522 | $ hg showconfig hooks |
|
524 | $ hg showconfig hooks | |
523 | hooks.commit.auto=<function autohook at *> (glob) |
|
525 | hooks.commit.auto=<function autohook at *> (glob) | |
524 |
|
526 | |||
525 | test python hook configured with python:[file]:[hook] syntax |
|
527 | test python hook configured with python:[file]:[hook] syntax | |
526 |
|
528 | |||
527 | $ cd .. |
|
529 | $ cd .. | |
528 | $ mkdir d |
|
530 | $ mkdir d | |
529 | $ cd d |
|
531 | $ cd d | |
530 | $ hg init repo |
|
532 | $ hg init repo | |
531 | $ mkdir hooks |
|
533 | $ mkdir hooks | |
532 |
|
534 | |||
533 | $ cd hooks |
|
535 | $ cd hooks | |
534 | $ cat > testhooks.py <<EOF |
|
536 | $ cat > testhooks.py <<EOF | |
535 | > def testhook(**args): |
|
537 | > def testhook(**args): | |
536 | > print 'hook works' |
|
538 | > print 'hook works' | |
537 | > EOF |
|
539 | > EOF | |
538 | $ echo '[hooks]' > ../repo/.hg/hgrc |
|
540 | $ echo '[hooks]' > ../repo/.hg/hgrc | |
539 | $ echo "pre-commit.test = python:`pwd`/testhooks.py:testhook" >> ../repo/.hg/hgrc |
|
541 | $ echo "pre-commit.test = python:`pwd`/testhooks.py:testhook" >> ../repo/.hg/hgrc | |
540 |
|
542 | |||
541 | $ cd ../repo |
|
543 | $ cd ../repo | |
542 | $ hg commit -d '0 0' |
|
544 | $ hg commit -d '0 0' | |
543 | hook works |
|
545 | hook works | |
544 | nothing changed |
|
546 | nothing changed | |
545 | [1] |
|
547 | [1] | |
546 |
|
548 | |||
547 | $ echo '[hooks]' > .hg/hgrc |
|
549 | $ echo '[hooks]' > .hg/hgrc | |
548 | $ echo "update.ne = python:`pwd`/nonexistent.py:testhook" >> .hg/hgrc |
|
550 | $ echo "update.ne = python:`pwd`/nonexistent.py:testhook" >> .hg/hgrc | |
549 | $ echo "pre-identify.npmd = python:`pwd`/:no_python_module_dir" >> .hg/hgrc |
|
551 | $ echo "pre-identify.npmd = python:`pwd`/:no_python_module_dir" >> .hg/hgrc | |
550 |
|
552 | |||
551 | $ hg up null |
|
553 | $ hg up null | |
552 | loading update.ne hook failed: |
|
554 | loading update.ne hook failed: | |
553 | abort: No such file or directory: $TESTTMP/d/repo/nonexistent.py |
|
555 | abort: No such file or directory: $TESTTMP/d/repo/nonexistent.py | |
554 | [255] |
|
556 | [255] | |
555 |
|
557 | |||
556 | $ hg id |
|
558 | $ hg id | |
557 | loading pre-identify.npmd hook failed: |
|
559 | loading pre-identify.npmd hook failed: | |
558 | abort: No module named repo! |
|
560 | abort: No module named repo! | |
559 | [255] |
|
561 | [255] | |
560 |
|
562 | |||
561 | $ cd ../../b |
|
563 | $ cd ../../b | |
562 |
|
564 | |||
563 | make sure --traceback works on hook import failure |
|
565 | make sure --traceback works on hook import failure | |
564 |
|
566 | |||
565 | $ cat > importfail.py <<EOF |
|
567 | $ cat > importfail.py <<EOF | |
566 | > import somebogusmodule |
|
568 | > import somebogusmodule | |
567 | > # dereference something in the module to force demandimport to load it |
|
569 | > # dereference something in the module to force demandimport to load it | |
568 | > somebogusmodule.whatever |
|
570 | > somebogusmodule.whatever | |
569 | > EOF |
|
571 | > EOF | |
570 |
|
572 | |||
571 | $ echo '[hooks]' > .hg/hgrc |
|
573 | $ echo '[hooks]' > .hg/hgrc | |
572 | $ echo 'precommit.importfail = python:importfail.whatever' >> .hg/hgrc |
|
574 | $ echo 'precommit.importfail = python:importfail.whatever' >> .hg/hgrc | |
573 |
|
575 | |||
574 | $ echo a >> a |
|
576 | $ echo a >> a | |
575 | $ hg --traceback commit -ma 2>&1 | egrep -v '^( +File| [a-zA-Z(])' |
|
577 | $ hg --traceback commit -ma 2>&1 | egrep -v '^( +File| [a-zA-Z(])' | |
576 | exception from first failed import attempt: |
|
578 | exception from first failed import attempt: | |
577 | Traceback (most recent call last): |
|
579 | Traceback (most recent call last): | |
578 | ImportError: No module named somebogusmodule |
|
580 | ImportError: No module named somebogusmodule | |
579 | exception from second failed import attempt: |
|
581 | exception from second failed import attempt: | |
580 | Traceback (most recent call last): |
|
582 | Traceback (most recent call last): | |
581 | ImportError: No module named hgext_importfail |
|
583 | ImportError: No module named hgext_importfail | |
582 | Traceback (most recent call last): |
|
584 | Traceback (most recent call last): | |
583 | Abort: precommit.importfail hook is invalid (import of "importfail" failed) |
|
585 | Abort: precommit.importfail hook is invalid (import of "importfail" failed) | |
584 | abort: precommit.importfail hook is invalid (import of "importfail" failed) |
|
586 | abort: precommit.importfail hook is invalid (import of "importfail" failed) | |
585 |
|
587 | |||
586 | Issue1827: Hooks Update & Commit not completely post operation |
|
588 | Issue1827: Hooks Update & Commit not completely post operation | |
587 |
|
589 | |||
588 | commit and update hooks should run after command completion |
|
590 | commit and update hooks should run after command completion | |
589 |
|
591 | |||
590 | $ echo '[hooks]' > .hg/hgrc |
|
592 | $ echo '[hooks]' > .hg/hgrc | |
591 | $ echo 'commit = hg id' >> .hg/hgrc |
|
593 | $ echo 'commit = hg id' >> .hg/hgrc | |
592 | $ echo 'update = hg id' >> .hg/hgrc |
|
594 | $ echo 'update = hg id' >> .hg/hgrc | |
593 | $ echo bb > a |
|
595 | $ echo bb > a | |
594 | $ hg ci -ma |
|
596 | $ hg ci -ma | |
595 | 223eafe2750c tip |
|
597 | 223eafe2750c tip | |
596 | $ hg up 0 |
|
598 | $ hg up 0 | |
597 | cb9a9f314b8b |
|
599 | cb9a9f314b8b | |
598 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
600 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved | |
599 |
|
601 | |||
600 | make sure --verbose (and --quiet/--debug etc.) are propagated to the local ui |
|
602 | make sure --verbose (and --quiet/--debug etc.) are propagated to the local ui | |
601 | that is passed to pre/post hooks |
|
603 | that is passed to pre/post hooks | |
602 |
|
604 | |||
603 | $ echo '[hooks]' > .hg/hgrc |
|
605 | $ echo '[hooks]' > .hg/hgrc | |
604 | $ echo 'pre-identify = python:hooktests.verbosehook' >> .hg/hgrc |
|
606 | $ echo 'pre-identify = python:hooktests.verbosehook' >> .hg/hgrc | |
605 | $ hg id |
|
607 | $ hg id | |
606 | cb9a9f314b8b |
|
608 | cb9a9f314b8b | |
607 | $ hg id --verbose |
|
609 | $ hg id --verbose | |
608 | calling hook pre-identify: hooktests.verbosehook |
|
610 | calling hook pre-identify: hooktests.verbosehook | |
609 | verbose output from hook |
|
611 | verbose output from hook | |
610 | cb9a9f314b8b |
|
612 | cb9a9f314b8b | |
611 |
|
613 | |||
612 | Ensure hooks can be prioritized |
|
614 | Ensure hooks can be prioritized | |
613 |
|
615 | |||
614 | $ echo '[hooks]' > .hg/hgrc |
|
616 | $ echo '[hooks]' > .hg/hgrc | |
615 | $ echo 'pre-identify.a = python:hooktests.verbosehook' >> .hg/hgrc |
|
617 | $ echo 'pre-identify.a = python:hooktests.verbosehook' >> .hg/hgrc | |
616 | $ echo 'pre-identify.b = python:hooktests.verbosehook' >> .hg/hgrc |
|
618 | $ echo 'pre-identify.b = python:hooktests.verbosehook' >> .hg/hgrc | |
617 | $ echo 'priority.pre-identify.b = 1' >> .hg/hgrc |
|
619 | $ echo 'priority.pre-identify.b = 1' >> .hg/hgrc | |
618 | $ echo 'pre-identify.c = python:hooktests.verbosehook' >> .hg/hgrc |
|
620 | $ echo 'pre-identify.c = python:hooktests.verbosehook' >> .hg/hgrc | |
619 | $ hg id --verbose |
|
621 | $ hg id --verbose | |
620 | calling hook pre-identify.b: hooktests.verbosehook |
|
622 | calling hook pre-identify.b: hooktests.verbosehook | |
621 | verbose output from hook |
|
623 | verbose output from hook | |
622 | calling hook pre-identify.a: hooktests.verbosehook |
|
624 | calling hook pre-identify.a: hooktests.verbosehook | |
623 | verbose output from hook |
|
625 | verbose output from hook | |
624 | calling hook pre-identify.c: hooktests.verbosehook |
|
626 | calling hook pre-identify.c: hooktests.verbosehook | |
625 | verbose output from hook |
|
627 | verbose output from hook | |
626 | cb9a9f314b8b |
|
628 | cb9a9f314b8b | |
627 |
|
629 | |||
628 | new tags must be visible in pretxncommit (issue3210) |
|
630 | new tags must be visible in pretxncommit (issue3210) | |
629 |
|
631 | |||
630 | $ echo 'pretxncommit.printtags = python:hooktests.printtags' >> .hg/hgrc |
|
632 | $ echo 'pretxncommit.printtags = python:hooktests.printtags' >> .hg/hgrc | |
631 | $ hg tag -f foo |
|
633 | $ hg tag -f foo | |
632 | ['a', 'foo', 'tip'] |
|
634 | ['a', 'foo', 'tip'] | |
633 |
|
635 | |||
634 | new commits must be visible in pretxnchangegroup (issue3428) |
|
636 | new commits must be visible in pretxnchangegroup (issue3428) | |
635 |
|
637 | |||
636 | $ cd .. |
|
638 | $ cd .. | |
637 | $ hg init to |
|
639 | $ hg init to | |
638 | $ echo '[hooks]' >> to/.hg/hgrc |
|
640 | $ echo '[hooks]' >> to/.hg/hgrc | |
639 | $ echo 'pretxnchangegroup = hg --traceback tip' >> to/.hg/hgrc |
|
641 | $ echo 'pretxnchangegroup = hg --traceback tip' >> to/.hg/hgrc | |
640 | $ echo a >> to/a |
|
642 | $ echo a >> to/a | |
641 | $ hg --cwd to ci -Ama |
|
643 | $ hg --cwd to ci -Ama | |
642 | adding a |
|
644 | adding a | |
643 | $ hg clone to from |
|
645 | $ hg clone to from | |
644 | updating to branch default |
|
646 | updating to branch default | |
645 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
647 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved | |
646 | $ echo aa >> from/a |
|
648 | $ echo aa >> from/a | |
647 | $ hg --cwd from ci -mb |
|
649 | $ hg --cwd from ci -mb | |
648 | $ hg --cwd from push |
|
650 | $ hg --cwd from push | |
649 | pushing to $TESTTMP/to (glob) |
|
651 | pushing to $TESTTMP/to (glob) | |
650 | searching for changes |
|
652 | searching for changes | |
651 | adding changesets |
|
653 | adding changesets | |
652 | adding manifests |
|
654 | adding manifests | |
653 | adding file changes |
|
655 | adding file changes | |
654 | added 1 changesets with 1 changes to 1 files |
|
656 | added 1 changesets with 1 changes to 1 files | |
655 | changeset: 1:9836a07b9b9d |
|
657 | changeset: 1:9836a07b9b9d | |
656 | tag: tip |
|
658 | tag: tip | |
657 | user: test |
|
659 | user: test | |
658 | date: Thu Jan 01 00:00:00 1970 +0000 |
|
660 | date: Thu Jan 01 00:00:00 1970 +0000 | |
659 | summary: b |
|
661 | summary: b | |
660 |
|
662 |
@@ -1,154 +1,156 | |||||
1 | #require unix-permissions |
|
1 | #require unix-permissions | |
2 |
|
2 | |||
3 | test that new files created in .hg inherit the permissions from .hg/store |
|
3 | test that new files created in .hg inherit the permissions from .hg/store | |
4 |
|
4 | |||
5 | $ mkdir dir |
|
5 | $ mkdir dir | |
6 |
|
6 | |||
7 | just in case somebody has a strange $TMPDIR |
|
7 | just in case somebody has a strange $TMPDIR | |
8 |
|
8 | |||
9 | $ chmod g-s dir |
|
9 | $ chmod g-s dir | |
10 | $ cd dir |
|
10 | $ cd dir | |
11 |
|
11 | |||
12 | $ cat >printmodes.py <<EOF |
|
12 | $ cat >printmodes.py <<EOF | |
13 | > import os, sys |
|
13 | > import os, sys | |
14 | > |
|
14 | > | |
15 | > allnames = [] |
|
15 | > allnames = [] | |
16 | > isdir = {} |
|
16 | > isdir = {} | |
17 | > for root, dirs, files in os.walk(sys.argv[1]): |
|
17 | > for root, dirs, files in os.walk(sys.argv[1]): | |
18 | > for d in dirs: |
|
18 | > for d in dirs: | |
19 | > name = os.path.join(root, d) |
|
19 | > name = os.path.join(root, d) | |
20 | > isdir[name] = 1 |
|
20 | > isdir[name] = 1 | |
21 | > allnames.append(name) |
|
21 | > allnames.append(name) | |
22 | > for f in files: |
|
22 | > for f in files: | |
23 | > name = os.path.join(root, f) |
|
23 | > name = os.path.join(root, f) | |
24 | > allnames.append(name) |
|
24 | > allnames.append(name) | |
25 | > allnames.sort() |
|
25 | > allnames.sort() | |
26 | > for name in allnames: |
|
26 | > for name in allnames: | |
27 | > suffix = name in isdir and '/' or '' |
|
27 | > suffix = name in isdir and '/' or '' | |
28 | > print '%05o %s%s' % (os.lstat(name).st_mode & 07777, name, suffix) |
|
28 | > print '%05o %s%s' % (os.lstat(name).st_mode & 07777, name, suffix) | |
29 | > EOF |
|
29 | > EOF | |
30 |
|
30 | |||
31 | $ cat >mode.py <<EOF |
|
31 | $ cat >mode.py <<EOF | |
32 | > import sys |
|
32 | > import sys | |
33 | > import os |
|
33 | > import os | |
34 | > print '%05o' % os.lstat(sys.argv[1]).st_mode |
|
34 | > print '%05o' % os.lstat(sys.argv[1]).st_mode | |
35 | > EOF |
|
35 | > EOF | |
36 |
|
36 | |||
37 | $ umask 077 |
|
37 | $ umask 077 | |
38 |
|
38 | |||
39 | $ hg init repo |
|
39 | $ hg init repo | |
40 | $ cd repo |
|
40 | $ cd repo | |
41 |
|
41 | |||
42 | $ chmod 0770 .hg/store |
|
42 | $ chmod 0770 .hg/store | |
43 |
|
43 | |||
44 | before commit |
|
44 | before commit | |
45 | store can be written by the group, other files cannot |
|
45 | store can be written by the group, other files cannot | |
46 | store is setgid |
|
46 | store is setgid | |
47 |
|
47 | |||
48 | $ python ../printmodes.py . |
|
48 | $ python ../printmodes.py . | |
49 | 00700 ./.hg/ |
|
49 | 00700 ./.hg/ | |
50 | 00600 ./.hg/00changelog.i |
|
50 | 00600 ./.hg/00changelog.i | |
51 | 00600 ./.hg/requires |
|
51 | 00600 ./.hg/requires | |
52 | 00770 ./.hg/store/ |
|
52 | 00770 ./.hg/store/ | |
53 |
|
53 | |||
54 | $ mkdir dir |
|
54 | $ mkdir dir | |
55 | $ touch foo dir/bar |
|
55 | $ touch foo dir/bar | |
56 | $ hg ci -qAm 'add files' |
|
56 | $ hg ci -qAm 'add files' | |
57 |
|
57 | |||
58 | after commit |
|
58 | after commit | |
59 | working dir files can only be written by the owner |
|
59 | working dir files can only be written by the owner | |
60 | files created in .hg can be written by the group |
|
60 | files created in .hg can be written by the group | |
61 | (in particular, store/**, dirstate, branch cache file, undo files) |
|
61 | (in particular, store/**, dirstate, branch cache file, undo files) | |
62 | new directories are setgid |
|
62 | new directories are setgid | |
63 |
|
63 | |||
64 | $ python ../printmodes.py . |
|
64 | $ python ../printmodes.py . | |
65 | 00700 ./.hg/ |
|
65 | 00700 ./.hg/ | |
66 | 00600 ./.hg/00changelog.i |
|
66 | 00600 ./.hg/00changelog.i | |
67 | 00770 ./.hg/cache/ |
|
67 | 00770 ./.hg/cache/ | |
68 | 00660 ./.hg/cache/branch2-served |
|
68 | 00660 ./.hg/cache/branch2-served | |
69 | 00660 ./.hg/cache/rbc-names-v1 |
|
69 | 00660 ./.hg/cache/rbc-names-v1 | |
70 | 00660 ./.hg/cache/rbc-revs-v1 |
|
70 | 00660 ./.hg/cache/rbc-revs-v1 | |
71 | 00660 ./.hg/dirstate |
|
71 | 00660 ./.hg/dirstate | |
72 | 00660 ./.hg/last-message.txt |
|
72 | 00660 ./.hg/last-message.txt | |
73 | 00600 ./.hg/requires |
|
73 | 00600 ./.hg/requires | |
74 | 00770 ./.hg/store/ |
|
74 | 00770 ./.hg/store/ | |
75 | 00660 ./.hg/store/00changelog.i |
|
75 | 00660 ./.hg/store/00changelog.i | |
76 | 00660 ./.hg/store/00manifest.i |
|
76 | 00660 ./.hg/store/00manifest.i | |
77 | 00770 ./.hg/store/data/ |
|
77 | 00770 ./.hg/store/data/ | |
78 | 00770 ./.hg/store/data/dir/ |
|
78 | 00770 ./.hg/store/data/dir/ | |
79 | 00660 ./.hg/store/data/dir/bar.i |
|
79 | 00660 ./.hg/store/data/dir/bar.i | |
80 | 00660 ./.hg/store/data/foo.i |
|
80 | 00660 ./.hg/store/data/foo.i | |
81 | 00660 ./.hg/store/fncache |
|
81 | 00660 ./.hg/store/fncache | |
82 | 00660 ./.hg/store/phaseroots |
|
82 | 00660 ./.hg/store/phaseroots | |
83 | 00660 ./.hg/store/undo |
|
83 | 00660 ./.hg/store/undo | |
|
84 | 00660 ./.hg/store/undo.backupfiles | |||
84 | 00660 ./.hg/store/undo.phaseroots |
|
85 | 00660 ./.hg/store/undo.phaseroots | |
85 | 00660 ./.hg/undo.bookmarks |
|
86 | 00660 ./.hg/undo.bookmarks | |
86 | 00660 ./.hg/undo.branch |
|
87 | 00660 ./.hg/undo.branch | |
87 | 00660 ./.hg/undo.desc |
|
88 | 00660 ./.hg/undo.desc | |
88 | 00660 ./.hg/undo.dirstate |
|
89 | 00660 ./.hg/undo.dirstate | |
89 | 00700 ./dir/ |
|
90 | 00700 ./dir/ | |
90 | 00600 ./dir/bar |
|
91 | 00600 ./dir/bar | |
91 | 00600 ./foo |
|
92 | 00600 ./foo | |
92 |
|
93 | |||
93 | $ umask 007 |
|
94 | $ umask 007 | |
94 | $ hg init ../push |
|
95 | $ hg init ../push | |
95 |
|
96 | |||
96 | before push |
|
97 | before push | |
97 | group can write everything |
|
98 | group can write everything | |
98 |
|
99 | |||
99 | $ python ../printmodes.py ../push |
|
100 | $ python ../printmodes.py ../push | |
100 | 00770 ../push/.hg/ |
|
101 | 00770 ../push/.hg/ | |
101 | 00660 ../push/.hg/00changelog.i |
|
102 | 00660 ../push/.hg/00changelog.i | |
102 | 00660 ../push/.hg/requires |
|
103 | 00660 ../push/.hg/requires | |
103 | 00770 ../push/.hg/store/ |
|
104 | 00770 ../push/.hg/store/ | |
104 |
|
105 | |||
105 | $ umask 077 |
|
106 | $ umask 077 | |
106 | $ hg -q push ../push |
|
107 | $ hg -q push ../push | |
107 |
|
108 | |||
108 | after push |
|
109 | after push | |
109 | group can still write everything |
|
110 | group can still write everything | |
110 |
|
111 | |||
111 | $ python ../printmodes.py ../push |
|
112 | $ python ../printmodes.py ../push | |
112 | 00770 ../push/.hg/ |
|
113 | 00770 ../push/.hg/ | |
113 | 00660 ../push/.hg/00changelog.i |
|
114 | 00660 ../push/.hg/00changelog.i | |
114 | 00770 ../push/.hg/cache/ |
|
115 | 00770 ../push/.hg/cache/ | |
115 | 00660 ../push/.hg/cache/branch2-base |
|
116 | 00660 ../push/.hg/cache/branch2-base | |
116 | 00660 ../push/.hg/cache/rbc-names-v1 |
|
117 | 00660 ../push/.hg/cache/rbc-names-v1 | |
117 | 00660 ../push/.hg/cache/rbc-revs-v1 |
|
118 | 00660 ../push/.hg/cache/rbc-revs-v1 | |
118 | 00660 ../push/.hg/requires |
|
119 | 00660 ../push/.hg/requires | |
119 | 00770 ../push/.hg/store/ |
|
120 | 00770 ../push/.hg/store/ | |
120 | 00660 ../push/.hg/store/00changelog.i |
|
121 | 00660 ../push/.hg/store/00changelog.i | |
121 | 00660 ../push/.hg/store/00manifest.i |
|
122 | 00660 ../push/.hg/store/00manifest.i | |
122 | 00770 ../push/.hg/store/data/ |
|
123 | 00770 ../push/.hg/store/data/ | |
123 | 00770 ../push/.hg/store/data/dir/ |
|
124 | 00770 ../push/.hg/store/data/dir/ | |
124 | 00660 ../push/.hg/store/data/dir/bar.i |
|
125 | 00660 ../push/.hg/store/data/dir/bar.i | |
125 | 00660 ../push/.hg/store/data/foo.i |
|
126 | 00660 ../push/.hg/store/data/foo.i | |
126 | 00660 ../push/.hg/store/fncache |
|
127 | 00660 ../push/.hg/store/fncache | |
127 | 00660 ../push/.hg/store/undo |
|
128 | 00660 ../push/.hg/store/undo | |
|
129 | 00660 ../push/.hg/store/undo.backupfiles | |||
128 | 00660 ../push/.hg/store/undo.phaseroots |
|
130 | 00660 ../push/.hg/store/undo.phaseroots | |
129 | 00660 ../push/.hg/undo.bookmarks |
|
131 | 00660 ../push/.hg/undo.bookmarks | |
130 | 00660 ../push/.hg/undo.branch |
|
132 | 00660 ../push/.hg/undo.branch | |
131 | 00660 ../push/.hg/undo.desc |
|
133 | 00660 ../push/.hg/undo.desc | |
132 | 00660 ../push/.hg/undo.dirstate |
|
134 | 00660 ../push/.hg/undo.dirstate | |
133 |
|
135 | |||
134 |
|
136 | |||
135 | Test that we don't lose the setgid bit when we call chmod. |
|
137 | Test that we don't lose the setgid bit when we call chmod. | |
136 | Not all systems support setgid directories (e.g. HFS+), so |
|
138 | Not all systems support setgid directories (e.g. HFS+), so | |
137 | just check that directories have the same mode. |
|
139 | just check that directories have the same mode. | |
138 |
|
140 | |||
139 | $ cd .. |
|
141 | $ cd .. | |
140 | $ hg init setgid |
|
142 | $ hg init setgid | |
141 | $ cd setgid |
|
143 | $ cd setgid | |
142 | $ chmod g+rwx .hg/store |
|
144 | $ chmod g+rwx .hg/store | |
143 | $ chmod g+s .hg/store 2> /dev/null || true |
|
145 | $ chmod g+s .hg/store 2> /dev/null || true | |
144 | $ mkdir dir |
|
146 | $ mkdir dir | |
145 | $ touch dir/file |
|
147 | $ touch dir/file | |
146 | $ hg ci -qAm 'add dir/file' |
|
148 | $ hg ci -qAm 'add dir/file' | |
147 | $ storemode=`python ../mode.py .hg/store` |
|
149 | $ storemode=`python ../mode.py .hg/store` | |
148 | $ dirmode=`python ../mode.py .hg/store/data/dir` |
|
150 | $ dirmode=`python ../mode.py .hg/store/data/dir` | |
149 | $ if [ "$storemode" != "$dirmode" ]; then |
|
151 | $ if [ "$storemode" != "$dirmode" ]; then | |
150 | > echo "$storemode != $dirmode" |
|
152 | > echo "$storemode != $dirmode" | |
151 | > fi |
|
153 | > fi | |
152 | $ cd .. |
|
154 | $ cd .. | |
153 |
|
155 | |||
154 | $ cd .. # g-s dir |
|
156 | $ cd .. # g-s dir |
General Comments 0
You need to be logged in to leave comments.
Login now