annotate hgext/journal.py @ 29443:cf092a3d202a

journal: new experimental extension Records bookmark locations and shows you where bookmarks were located in the past. This is the first in a planned series of locations to be recorded; a future patch will add working copy (dirstate) tracking, and remote bookmarks will be supported as well, so the journal storage format should be fairly generic to support those use-cases.
author Martijn Pieters <mjpieters@fb.com>
date Fri, 24 Jun 2016 16:12:05 +0100
parents
children 8361131b4768
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
29443
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
1 # journal.py
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
2 #
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
3 # Copyright 2014-2016 Facebook, Inc.
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
4 #
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
5 # This software may be used and distributed according to the terms of the
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
6 # GNU General Public License version 2 or any later version.
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
7 """Track previous positions of bookmarks (EXPERIMENTAL)
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
8
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
9 This extension adds a new command: `hg journal`, which shows you where
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
10 bookmarks were previously located.
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
11
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
12 """
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
13
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
14 from __future__ import absolute_import
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
15
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
16 import collections
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
17 import os
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
18
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
19 from mercurial.i18n import _
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
20
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
21 from mercurial import (
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
22 bookmarks,
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
23 cmdutil,
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
24 commands,
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
25 dispatch,
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
26 error,
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
27 extensions,
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
28 node,
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
29 util,
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
30 )
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
31
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
32 cmdtable = {}
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
33 command = cmdutil.command(cmdtable)
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
34
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
35 # Note for extension authors: ONLY specify testedwith = 'internal' for
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
36 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
37 # be specifying the version(s) of Mercurial they are tested with, or
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
38 # leave the attribute unspecified.
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
39 testedwith = 'internal'
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
40
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
41 # storage format version; increment when the format changes
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
42 storageversion = 0
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
43
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
44 # namespaces
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
45 bookmarktype = 'bookmark'
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
46
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
47 # Journal recording, register hooks and storage object
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
48 def extsetup(ui):
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
49 extensions.wrapfunction(dispatch, 'runcommand', runcommand)
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
50 extensions.wrapfunction(bookmarks.bmstore, '_write', recordbookmarks)
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
51
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
52 def reposetup(ui, repo):
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
53 if repo.local():
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
54 repo.journal = journalstorage(repo)
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
55
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
56 def runcommand(orig, lui, repo, cmd, fullargs, *args):
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
57 """Track the command line options for recording in the journal"""
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
58 journalstorage.recordcommand(*fullargs)
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
59 return orig(lui, repo, cmd, fullargs, *args)
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
60
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
61 def recordbookmarks(orig, store, fp):
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
62 """Records all bookmark changes in the journal."""
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
63 repo = store._repo
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
64 if util.safehasattr(repo, 'journal'):
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
65 oldmarks = bookmarks.bmstore(repo)
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
66 for mark, value in store.iteritems():
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
67 oldvalue = oldmarks.get(mark, node.nullid)
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
68 if value != oldvalue:
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
69 repo.journal.record(bookmarktype, mark, oldvalue, value)
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
70 return orig(store, fp)
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
71
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
72 class journalentry(collections.namedtuple(
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
73 'journalentry',
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
74 'timestamp user command namespace name oldhashes newhashes')):
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
75 """Individual journal entry
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
76
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
77 * timestamp: a mercurial (time, timezone) tuple
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
78 * user: the username that ran the command
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
79 * namespace: the entry namespace, an opaque string
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
80 * name: the name of the changed item, opaque string with meaning in the
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
81 namespace
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
82 * command: the hg command that triggered this record
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
83 * oldhashes: a tuple of one or more binary hashes for the old location
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
84 * newhashes: a tuple of one or more binary hashes for the new location
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
85
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
86 Handles serialisation from and to the storage format. Fields are
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
87 separated by newlines, hashes are written out in hex separated by commas,
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
88 timestamp and timezone are separated by a space.
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
89
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
90 """
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
91 @classmethod
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
92 def fromstorage(cls, line):
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
93 (time, user, command, namespace, name,
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
94 oldhashes, newhashes) = line.split('\n')
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
95 timestamp, tz = time.split()
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
96 timestamp, tz = float(timestamp), int(tz)
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
97 oldhashes = tuple(node.bin(hash) for hash in oldhashes.split(','))
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
98 newhashes = tuple(node.bin(hash) for hash in newhashes.split(','))
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
99 return cls(
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
100 (timestamp, tz), user, command, namespace, name,
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
101 oldhashes, newhashes)
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
102
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
103 def __str__(self):
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
104 """String representation for storage"""
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
105 time = ' '.join(map(str, self.timestamp))
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
106 oldhashes = ','.join([node.hex(hash) for hash in self.oldhashes])
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
107 newhashes = ','.join([node.hex(hash) for hash in self.newhashes])
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
108 return '\n'.join((
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
109 time, self.user, self.command, self.namespace, self.name,
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
110 oldhashes, newhashes))
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
111
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
112 class journalstorage(object):
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
113 """Storage for journal entries
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
114
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
115 Entries are stored with NUL bytes as separators. See the journalentry
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
116 class for the per-entry structure.
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
117
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
118 The file format starts with an integer version, delimited by a NUL.
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
119
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
120 """
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
121 _currentcommand = ()
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
122
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
123 def __init__(self, repo):
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
124 self.repo = repo
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
125 self.user = util.getuser()
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
126 self.vfs = repo.vfs
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
127
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
128 # track the current command for recording in journal entries
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
129 @property
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
130 def command(self):
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
131 commandstr = ' '.join(
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
132 map(util.shellquote, journalstorage._currentcommand))
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
133 if '\n' in commandstr:
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
134 # truncate multi-line commands
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
135 commandstr = commandstr.partition('\n')[0] + ' ...'
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
136 return commandstr
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
137
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
138 @classmethod
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
139 def recordcommand(cls, *fullargs):
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
140 """Set the current hg arguments, stored with recorded entries"""
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
141 # Set the current command on the class because we may have started
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
142 # with a non-local repo (cloning for example).
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
143 cls._currentcommand = fullargs
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
144
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
145 def record(self, namespace, name, oldhashes, newhashes):
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
146 """Record a new journal entry
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
147
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
148 * namespace: an opaque string; this can be used to filter on the type
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
149 of recorded entries.
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
150 * name: the name defining this entry; for bookmarks, this is the
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
151 bookmark name. Can be filtered on when retrieving entries.
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
152 * oldhashes and newhashes: each a single binary hash, or a list of
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
153 binary hashes. These represent the old and new position of the named
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
154 item.
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
155
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
156 """
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
157 if not isinstance(oldhashes, list):
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
158 oldhashes = [oldhashes]
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
159 if not isinstance(newhashes, list):
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
160 newhashes = [newhashes]
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
161
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
162 entry = journalentry(
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
163 util.makedate(), self.user, self.command, namespace, name,
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
164 oldhashes, newhashes)
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
165
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
166 with self.repo.wlock():
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
167 version = None
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
168 # open file in amend mode to ensure it is created if missing
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
169 with self.vfs('journal', mode='a+b', atomictemp=True) as f:
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
170 f.seek(0, os.SEEK_SET)
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
171 # Read just enough bytes to get a version number (up to 2
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
172 # digits plus separator)
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
173 version = f.read(3).partition('\0')[0]
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
174 if version and version != str(storageversion):
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
175 # different version of the storage. Exit early (and not
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
176 # write anything) if this is not a version we can handle or
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
177 # the file is corrupt. In future, perhaps rotate the file
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
178 # instead?
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
179 self.repo.ui.warn(
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
180 _("unsupported journal file version '%s'\n") % version)
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
181 return
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
182 if not version:
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
183 # empty file, write version first
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
184 f.write(str(storageversion) + '\0')
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
185 f.seek(0, os.SEEK_END)
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
186 f.write(str(entry) + '\0')
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
187
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
188 def filtered(self, namespace=None, name=None):
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
189 """Yield all journal entries with the given namespace or name
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
190
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
191 Both the namespace and the name are optional; if neither is given all
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
192 entries in the journal are produced.
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
193
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
194 """
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
195 for entry in self:
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
196 if namespace is not None and entry.namespace != namespace:
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
197 continue
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
198 if name is not None and entry.name != name:
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
199 continue
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
200 yield entry
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
201
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
202 def __iter__(self):
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
203 """Iterate over the storage
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
204
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
205 Yields journalentry instances for each contained journal record.
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
206
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
207 """
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
208 if not self.vfs.exists('journal'):
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
209 return
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
210
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
211 with self.vfs('journal') as f:
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
212 raw = f.read()
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
213
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
214 lines = raw.split('\0')
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
215 version = lines and lines[0]
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
216 if version != str(storageversion):
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
217 version = version or _('not available')
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
218 raise error.Abort(_("unknown journal file version '%s'") % version)
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
219
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
220 # Skip the first line, it's a version number. Reverse the rest.
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
221 lines = reversed(lines[1:])
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
222 for line in lines:
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
223 if not line:
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
224 continue
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
225 yield journalentry.fromstorage(line)
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
226
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
227 # journal reading
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
228 # log options that don't make sense for journal
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
229 _ignoreopts = ('no-merges', 'graph')
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
230 @command(
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
231 'journal', [
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
232 ('c', 'commits', None, 'show commit metadata'),
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
233 ] + [opt for opt in commands.logopts if opt[1] not in _ignoreopts],
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
234 '[OPTION]... [BOOKMARKNAME]')
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
235 def journal(ui, repo, *args, **opts):
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
236 """show the previous position of bookmarks
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
237
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
238 The journal is used to see the previous commits of bookmarks. By default
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
239 the previous locations for all bookmarks are shown. Passing a bookmark
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
240 name will show all the previous positions of that bookmark.
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
241
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
242 By default hg journal only shows the commit hash and the command that was
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
243 running at that time. -v/--verbose will show the prior hash, the user, and
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
244 the time at which it happened.
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
245
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
246 Use -c/--commits to output log information on each commit hash; at this
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
247 point you can use the usual `--patch`, `--git`, `--stat` and `--template`
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
248 switches to alter the log output for these.
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
249
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
250 `hg journal -T json` can be used to produce machine readable output.
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
251
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
252 """
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
253 bookmarkname = None
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
254 if args:
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
255 bookmarkname = args[0]
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
256
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
257 fm = ui.formatter('journal', opts)
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
258
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
259 if opts.get("template") != "json":
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
260 if bookmarkname is None:
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
261 name = _('all bookmarks')
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
262 else:
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
263 name = "'%s'" % bookmarkname
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
264 ui.status(_("previous locations of %s:\n") % name)
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
265
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
266 limit = cmdutil.loglimit(opts)
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
267 entry = None
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
268 for count, entry in enumerate(repo.journal.filtered(name=bookmarkname)):
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
269 if count == limit:
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
270 break
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
271 newhashesstr = ','.join([node.short(hash) for hash in entry.newhashes])
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
272 oldhashesstr = ','.join([node.short(hash) for hash in entry.oldhashes])
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
273
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
274 fm.startitem()
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
275 fm.condwrite(ui.verbose, 'oldhashes', '%s -> ', oldhashesstr)
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
276 fm.write('newhashes', '%s', newhashesstr)
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
277 fm.condwrite(ui.verbose, 'user', ' %s', entry.user.ljust(8))
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
278
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
279 timestring = util.datestr(entry.timestamp, '%Y-%m-%d %H:%M %1%2')
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
280 fm.condwrite(ui.verbose, 'date', ' %s', timestring)
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
281 fm.write('command', ' %s\n', entry.command)
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
282
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
283 if opts.get("commits"):
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
284 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=False)
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
285 for hash in entry.newhashes:
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
286 try:
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
287 ctx = repo[hash]
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
288 displayer.show(ctx)
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
289 except error.RepoLookupError as e:
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
290 fm.write('repolookuperror', "%s\n\n", str(e))
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
291 displayer.close()
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
292
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
293 fm.end()
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
294
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
295 if entry is None:
cf092a3d202a journal: new experimental extension
Martijn Pieters <mjpieters@fb.com>
parents:
diff changeset
296 ui.status(_("no recorded locations\n"))