##// END OF EJS Templates
journal: add dirstate tracking...
Martijn Pieters -
r29502:8361131b default
parent child Browse files
Show More
@@ -15,6 +15,7 b' from __future__ import absolute_import'
15
15
16 import collections
16 import collections
17 import os
17 import os
18 import weakref
18
19
19 from mercurial.i18n import _
20 from mercurial.i18n import _
20
21
@@ -22,9 +23,12 b' from mercurial import ('
22 bookmarks,
23 bookmarks,
23 cmdutil,
24 cmdutil,
24 commands,
25 commands,
26 dirstate,
25 dispatch,
27 dispatch,
26 error,
28 error,
27 extensions,
29 extensions,
30 localrepo,
31 lock,
28 node,
32 node,
29 util,
33 util,
30 )
34 )
@@ -43,11 +47,16 b' storageversion = 0'
43
47
44 # namespaces
48 # namespaces
45 bookmarktype = 'bookmark'
49 bookmarktype = 'bookmark'
50 wdirparenttype = 'wdirparent'
46
51
47 # Journal recording, register hooks and storage object
52 # Journal recording, register hooks and storage object
48 def extsetup(ui):
53 def extsetup(ui):
49 extensions.wrapfunction(dispatch, 'runcommand', runcommand)
54 extensions.wrapfunction(dispatch, 'runcommand', runcommand)
50 extensions.wrapfunction(bookmarks.bmstore, '_write', recordbookmarks)
55 extensions.wrapfunction(bookmarks.bmstore, '_write', recordbookmarks)
56 extensions.wrapfunction(
57 dirstate.dirstate, '_writedirstate', recorddirstateparents)
58 extensions.wrapfunction(
59 localrepo.localrepository.dirstate, 'func', wrapdirstate)
51
60
52 def reposetup(ui, repo):
61 def reposetup(ui, repo):
53 if repo.local():
62 if repo.local():
@@ -58,6 +67,42 b' def runcommand(orig, lui, repo, cmd, ful'
58 journalstorage.recordcommand(*fullargs)
67 journalstorage.recordcommand(*fullargs)
59 return orig(lui, repo, cmd, fullargs, *args)
68 return orig(lui, repo, cmd, fullargs, *args)
60
69
70 # hooks to record dirstate changes
71 def wrapdirstate(orig, repo):
72 """Make journal storage available to the dirstate object"""
73 dirstate = orig(repo)
74 if util.safehasattr(repo, 'journal'):
75 dirstate.journalstorage = repo.journal
76 return dirstate
77
78 def recorddirstateparents(orig, dirstate, dirstatefp):
79 """Records all dirstate parent changes in the journal."""
80 if util.safehasattr(dirstate, 'journalstorage'):
81 old = [node.nullid, node.nullid]
82 nodesize = len(node.nullid)
83 try:
84 # The only source for the old state is in the dirstate file still
85 # on disk; the in-memory dirstate object only contains the new
86 # state. dirstate._opendirstatefile() switches beteen .hg/dirstate
87 # and .hg/dirstate.pending depending on the transaction state.
88 with dirstate._opendirstatefile() as fp:
89 state = fp.read(2 * nodesize)
90 if len(state) == 2 * nodesize:
91 old = [state[:nodesize], state[nodesize:]]
92 except IOError:
93 pass
94
95 new = dirstate.parents()
96 if old != new:
97 # only record two hashes if there was a merge
98 oldhashes = old[:1] if old[1] == node.nullid else old
99 newhashes = new[:1] if new[1] == node.nullid else new
100 dirstate.journalstorage.record(
101 wdirparenttype, '.', oldhashes, newhashes)
102
103 return orig(dirstate, dirstatefp)
104
105 # hooks to record bookmark changes (both local and remote)
61 def recordbookmarks(orig, store, fp):
106 def recordbookmarks(orig, store, fp):
62 """Records all bookmark changes in the journal."""
107 """Records all bookmark changes in the journal."""
63 repo = store._repo
108 repo = store._repo
@@ -117,12 +162,17 b' class journalstorage(object):'
117
162
118 The file format starts with an integer version, delimited by a NUL.
163 The file format starts with an integer version, delimited by a NUL.
119
164
165 This storage uses a dedicated lock; this makes it easier to avoid issues
166 with adding entries that added when the regular wlock is unlocked (e.g.
167 the dirstate).
168
120 """
169 """
121 _currentcommand = ()
170 _currentcommand = ()
171 _lockref = None
122
172
123 def __init__(self, repo):
173 def __init__(self, repo):
124 self.repo = repo
125 self.user = util.getuser()
174 self.user = util.getuser()
175 self.ui = repo.ui
126 self.vfs = repo.vfs
176 self.vfs = repo.vfs
127
177
128 # track the current command for recording in journal entries
178 # track the current command for recording in journal entries
@@ -142,6 +192,24 b' class journalstorage(object):'
142 # with a non-local repo (cloning for example).
192 # with a non-local repo (cloning for example).
143 cls._currentcommand = fullargs
193 cls._currentcommand = fullargs
144
194
195 def jlock(self):
196 """Create a lock for the journal file"""
197 if self._lockref and self._lockref():
198 raise error.Abort(_('journal lock does not support nesting'))
199 desc = _('journal of %s') % self.vfs.base
200 try:
201 l = lock.lock(self.vfs, 'journal.lock', 0, desc=desc)
202 except error.LockHeld as inst:
203 self.ui.warn(
204 _("waiting for lock on %s held by %r\n") % (desc, inst.locker))
205 # default to 600 seconds timeout
206 l = lock.lock(
207 self.vfs, 'journal.lock',
208 int(self.ui.config("ui", "timeout", "600")), desc=desc)
209 self.ui.warn(_("got lock after %s seconds\n") % l.delay)
210 self._lockref = weakref.ref(l)
211 return l
212
145 def record(self, namespace, name, oldhashes, newhashes):
213 def record(self, namespace, name, oldhashes, newhashes):
146 """Record a new journal entry
214 """Record a new journal entry
147
215
@@ -163,7 +231,7 b' class journalstorage(object):'
163 util.makedate(), self.user, self.command, namespace, name,
231 util.makedate(), self.user, self.command, namespace, name,
164 oldhashes, newhashes)
232 oldhashes, newhashes)
165
233
166 with self.repo.wlock():
234 with self.jlock():
167 version = None
235 version = None
168 # open file in amend mode to ensure it is created if missing
236 # open file in amend mode to ensure it is created if missing
169 with self.vfs('journal', mode='a+b', atomictemp=True) as f:
237 with self.vfs('journal', mode='a+b', atomictemp=True) as f:
@@ -176,7 +244,7 b' class journalstorage(object):'
176 # write anything) if this is not a version we can handle or
244 # write anything) if this is not a version we can handle or
177 # the file is corrupt. In future, perhaps rotate the file
245 # the file is corrupt. In future, perhaps rotate the file
178 # instead?
246 # instead?
179 self.repo.ui.warn(
247 self.ui.warn(
180 _("unsupported journal file version '%s'\n") % version)
248 _("unsupported journal file version '%s'\n") % version)
181 return
249 return
182 if not version:
250 if not version:
@@ -229,15 +297,19 b' class journalstorage(object):'
229 _ignoreopts = ('no-merges', 'graph')
297 _ignoreopts = ('no-merges', 'graph')
230 @command(
298 @command(
231 'journal', [
299 'journal', [
300 ('', 'all', None, 'show history for all names'),
232 ('c', 'commits', None, 'show commit metadata'),
301 ('c', 'commits', None, 'show commit metadata'),
233 ] + [opt for opt in commands.logopts if opt[1] not in _ignoreopts],
302 ] + [opt for opt in commands.logopts if opt[1] not in _ignoreopts],
234 '[OPTION]... [BOOKMARKNAME]')
303 '[OPTION]... [BOOKMARKNAME]')
235 def journal(ui, repo, *args, **opts):
304 def journal(ui, repo, *args, **opts):
236 """show the previous position of bookmarks
305 """show the previous position of bookmarks and the working copy
237
306
238 The journal is used to see the previous commits of bookmarks. By default
307 The journal is used to see the previous commits that bookmarks and the
239 the previous locations for all bookmarks are shown. Passing a bookmark
308 working copy pointed to. By default the previous locations for the working
240 name will show all the previous positions of that bookmark.
309 copy. Passing a bookmark name will show all the previous positions of
310 that bookmark. Use the --all switch to show previous locations for all
311 bookmarks and the working copy; each line will then include the bookmark
312 name, or '.' for the working copy, as well.
241
313
242 By default hg journal only shows the commit hash and the command that was
314 By default hg journal only shows the commit hash and the command that was
243 running at that time. -v/--verbose will show the prior hash, the user, and
315 running at that time. -v/--verbose will show the prior hash, the user, and
@@ -250,22 +322,27 b' def journal(ui, repo, *args, **opts):'
250 `hg journal -T json` can be used to produce machine readable output.
322 `hg journal -T json` can be used to produce machine readable output.
251
323
252 """
324 """
253 bookmarkname = None
325 name = '.'
326 if opts.get('all'):
327 if args:
328 raise error.Abort(
329 _("You can't combine --all and filtering on a name"))
330 name = None
254 if args:
331 if args:
255 bookmarkname = args[0]
332 name = args[0]
256
333
257 fm = ui.formatter('journal', opts)
334 fm = ui.formatter('journal', opts)
258
335
259 if opts.get("template") != "json":
336 if opts.get("template") != "json":
260 if bookmarkname is None:
337 if name is None:
261 name = _('all bookmarks')
338 displayname = _('the working copy and bookmarks')
262 else:
339 else:
263 name = "'%s'" % bookmarkname
340 displayname = "'%s'" % name
264 ui.status(_("previous locations of %s:\n") % name)
341 ui.status(_("previous locations of %s:\n") % displayname)
265
342
266 limit = cmdutil.loglimit(opts)
343 limit = cmdutil.loglimit(opts)
267 entry = None
344 entry = None
268 for count, entry in enumerate(repo.journal.filtered(name=bookmarkname)):
345 for count, entry in enumerate(repo.journal.filtered(name=name)):
269 if count == limit:
346 if count == limit:
270 break
347 break
271 newhashesstr = ','.join([node.short(hash) for hash in entry.newhashes])
348 newhashesstr = ','.join([node.short(hash) for hash in entry.newhashes])
@@ -274,7 +351,8 b' def journal(ui, repo, *args, **opts):'
274 fm.startitem()
351 fm.startitem()
275 fm.condwrite(ui.verbose, 'oldhashes', '%s -> ', oldhashesstr)
352 fm.condwrite(ui.verbose, 'oldhashes', '%s -> ', oldhashesstr)
276 fm.write('newhashes', '%s', newhashesstr)
353 fm.write('newhashes', '%s', newhashesstr)
277 fm.condwrite(ui.verbose, 'user', ' %s', entry.user.ljust(8))
354 fm.condwrite(ui.verbose, 'user', ' %-8s', entry.user)
355 fm.condwrite(opts.get('all'), 'name', ' %-8s', entry.name)
278
356
279 timestring = util.datestr(entry.timestamp, '%Y-%m-%d %H:%M %1%2')
357 timestring = util.datestr(entry.timestamp, '%Y-%m-%d %H:%M %1%2')
280 fm.condwrite(ui.verbose, 'date', ' %s', timestring)
358 fm.condwrite(ui.verbose, 'date', ' %s', timestring)
@@ -32,22 +32,37 b' Setup repo'
32
32
33 $ hg init repo
33 $ hg init repo
34 $ cd repo
34 $ cd repo
35 $ echo a > a
36 $ hg commit -Aqm a
37 $ echo b > a
38 $ hg commit -Aqm b
39 $ hg up 0
40 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
41
35
42 Test empty journal
36 Test empty journal
43
37
44 $ hg journal
38 $ hg journal
45 previous locations of all bookmarks:
39 previous locations of '.':
46 no recorded locations
40 no recorded locations
47 $ hg journal foo
41 $ hg journal foo
48 previous locations of 'foo':
42 previous locations of 'foo':
49 no recorded locations
43 no recorded locations
50
44
45 Test that working copy changes are tracked
46
47 $ echo a > a
48 $ hg commit -Aqm a
49 $ hg journal
50 previous locations of '.':
51 cb9a9f314b8b commit -Aqm a
52 $ echo b > a
53 $ hg commit -Aqm b
54 $ hg journal
55 previous locations of '.':
56 1e6c11564562 commit -Aqm b
57 cb9a9f314b8b commit -Aqm a
58 $ hg up 0
59 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
60 $ hg journal
61 previous locations of '.':
62 cb9a9f314b8b up 0
63 1e6c11564562 commit -Aqm b
64 cb9a9f314b8b commit -Aqm a
65
51 Test that bookmarks are tracked
66 Test that bookmarks are tracked
52
67
53 $ hg book -r tip bar
68 $ hg book -r tip bar
@@ -68,22 +83,32 b' Test that bookmarks are tracked'
68 cb9a9f314b8b book -f bar
83 cb9a9f314b8b book -f bar
69 1e6c11564562 book -r tip bar
84 1e6c11564562 book -r tip bar
70
85
71 Test that you can list all bookmarks as well as limit the list or filter on them
86 Test that bookmarks and working copy tracking is not mixed
87
88 $ hg journal
89 previous locations of '.':
90 1e6c11564562 up
91 cb9a9f314b8b up 0
92 1e6c11564562 commit -Aqm b
93 cb9a9f314b8b commit -Aqm a
94
95 Test that you can list all entries as well as limit the list or filter on them
72
96
73 $ hg book -r tip baz
97 $ hg book -r tip baz
74 $ hg journal
98 $ hg journal --all
75 previous locations of all bookmarks:
99 previous locations of the working copy and bookmarks:
76 1e6c11564562 book -r tip baz
100 1e6c11564562 baz book -r tip baz
77 1e6c11564562 up
101 1e6c11564562 bar up
78 cb9a9f314b8b book -f bar
102 1e6c11564562 . up
79 1e6c11564562 book -r tip bar
103 cb9a9f314b8b bar book -f bar
104 1e6c11564562 bar book -r tip bar
105 cb9a9f314b8b . up 0
106 1e6c11564562 . commit -Aqm b
107 cb9a9f314b8b . commit -Aqm a
80 $ hg journal --limit 2
108 $ hg journal --limit 2
81 previous locations of all bookmarks:
109 previous locations of '.':
82 1e6c11564562 book -r tip baz
83 1e6c11564562 up
110 1e6c11564562 up
84 $ hg journal baz
111 cb9a9f314b8b up 0
85 previous locations of 'baz':
86 1e6c11564562 book -r tip baz
87 $ hg journal bar
112 $ hg journal bar
88 previous locations of 'bar':
113 previous locations of 'bar':
89 1e6c11564562 up
114 1e6c11564562 up
@@ -92,26 +117,62 b' Test that you can list all bookmarks as '
92 $ hg journal foo
117 $ hg journal foo
93 previous locations of 'foo':
118 previous locations of 'foo':
94 no recorded locations
119 no recorded locations
120 $ hg journal .
121 previous locations of '.':
122 1e6c11564562 up
123 cb9a9f314b8b up 0
124 1e6c11564562 commit -Aqm b
125 cb9a9f314b8b commit -Aqm a
95
126
96 Test that verbose and commit output work
127 Test that verbose, JSON and commit output work
97
128
98 $ hg journal --verbose
129 $ hg journal --verbose --all
99 previous locations of all bookmarks:
130 previous locations of the working copy and bookmarks:
100 000000000000 -> 1e6c11564562 foobar 1970-01-01 00:00 +0000 book -r tip baz
131 000000000000 -> 1e6c11564562 foobar baz 1970-01-01 00:00 +0000 book -r tip baz
101 cb9a9f314b8b -> 1e6c11564562 foobar 1970-01-01 00:00 +0000 up
132 cb9a9f314b8b -> 1e6c11564562 foobar bar 1970-01-01 00:00 +0000 up
102 1e6c11564562 -> cb9a9f314b8b foobar 1970-01-01 00:00 +0000 book -f bar
133 cb9a9f314b8b -> 1e6c11564562 foobar . 1970-01-01 00:00 +0000 up
103 000000000000 -> 1e6c11564562 foobar 1970-01-01 00:00 +0000 book -r tip bar
134 1e6c11564562 -> cb9a9f314b8b foobar bar 1970-01-01 00:00 +0000 book -f bar
135 000000000000 -> 1e6c11564562 foobar bar 1970-01-01 00:00 +0000 book -r tip bar
136 1e6c11564562 -> cb9a9f314b8b foobar . 1970-01-01 00:00 +0000 up 0
137 cb9a9f314b8b -> 1e6c11564562 foobar . 1970-01-01 00:00 +0000 commit -Aqm b
138 000000000000 -> cb9a9f314b8b foobar . 1970-01-01 00:00 +0000 commit -Aqm a
139 $ hg journal --verbose -Tjson
140 [
141 {
142 "command": "up",
143 "date": "1970-01-01 00:00 +0000",
144 "name": ".",
145 "newhashes": "1e6c11564562",
146 "oldhashes": "cb9a9f314b8b",
147 "user": "foobar"
148 },
149 {
150 "command": "up 0",
151 "date": "1970-01-01 00:00 +0000",
152 "name": ".",
153 "newhashes": "cb9a9f314b8b",
154 "oldhashes": "1e6c11564562",
155 "user": "foobar"
156 },
157 {
158 "command": "commit -Aqm b",
159 "date": "1970-01-01 00:00 +0000",
160 "name": ".",
161 "newhashes": "1e6c11564562",
162 "oldhashes": "cb9a9f314b8b",
163 "user": "foobar"
164 },
165 {
166 "command": "commit -Aqm a",
167 "date": "1970-01-01 00:00 +0000",
168 "name": ".",
169 "newhashes": "cb9a9f314b8b",
170 "oldhashes": "000000000000",
171 "user": "foobar"
172 }
173 ]
104 $ hg journal --commit
174 $ hg journal --commit
105 previous locations of all bookmarks:
175 previous locations of '.':
106 1e6c11564562 book -r tip baz
107 changeset: 1:1e6c11564562
108 bookmark: bar
109 bookmark: baz
110 tag: tip
111 user: test
112 date: Thu Jan 01 00:00:00 1970 +0000
113 summary: b
114
115 1e6c11564562 up
176 1e6c11564562 up
116 changeset: 1:1e6c11564562
177 changeset: 1:1e6c11564562
117 bookmark: bar
178 bookmark: bar
@@ -121,13 +182,13 b' Test that verbose and commit output work'
121 date: Thu Jan 01 00:00:00 1970 +0000
182 date: Thu Jan 01 00:00:00 1970 +0000
122 summary: b
183 summary: b
123
184
124 cb9a9f314b8b book -f bar
185 cb9a9f314b8b up 0
125 changeset: 0:cb9a9f314b8b
186 changeset: 0:cb9a9f314b8b
126 user: test
187 user: test
127 date: Thu Jan 01 00:00:00 1970 +0000
188 date: Thu Jan 01 00:00:00 1970 +0000
128 summary: a
189 summary: a
129
190
130 1e6c11564562 book -r tip bar
191 1e6c11564562 commit -Aqm b
131 changeset: 1:1e6c11564562
192 changeset: 1:1e6c11564562
132 bookmark: bar
193 bookmark: bar
133 bookmark: baz
194 bookmark: baz
@@ -136,12 +197,18 b' Test that verbose and commit output work'
136 date: Thu Jan 01 00:00:00 1970 +0000
197 date: Thu Jan 01 00:00:00 1970 +0000
137 summary: b
198 summary: b
138
199
200 cb9a9f314b8b commit -Aqm a
201 changeset: 0:cb9a9f314b8b
202 user: test
203 date: Thu Jan 01 00:00:00 1970 +0000
204 summary: a
205
139
206
140 Test for behaviour on unexpected storage version information
207 Test for behaviour on unexpected storage version information
141
208
142 $ printf '42\0' > .hg/journal
209 $ printf '42\0' > .hg/journal
143 $ hg journal
210 $ hg journal
144 previous locations of all bookmarks:
211 previous locations of '.':
145 abort: unknown journal file version '42'
212 abort: unknown journal file version '42'
146 [255]
213 [255]
147 $ hg book -r tip doomed
214 $ hg book -r tip doomed
General Comments 0
You need to be logged in to leave comments. Login now