##// END OF EJS Templates
statecheck: shifted defaults to addunfinished()...
Taapas Agrawal -
r42734:faec09d8 default
parent child Browse files
Show More
@@ -1,223 +1,225 b''
1 # state.py - writing and reading state files in Mercurial
1 # state.py - writing and reading state files in Mercurial
2 #
2 #
3 # Copyright 2018 Pulkit Goyal <pulkitmgoyal@gmail.com>
3 # Copyright 2018 Pulkit Goyal <pulkitmgoyal@gmail.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 """
8 """
9 This file contains class to wrap the state for commands and other
9 This file contains class to wrap the state for commands and other
10 related logic.
10 related logic.
11
11
12 All the data related to the command state is stored as dictionary in the object.
12 All the data related to the command state is stored as dictionary in the object.
13 The class has methods using which the data can be stored to disk in a file under
13 The class has methods using which the data can be stored to disk in a file under
14 .hg/ directory.
14 .hg/ directory.
15
15
16 We store the data on disk in cbor, for which we use the CBOR format to encode
16 We store the data on disk in cbor, for which we use the CBOR format to encode
17 the data.
17 the data.
18 """
18 """
19
19
20 from __future__ import absolute_import
20 from __future__ import absolute_import
21
21
22 from .i18n import _
22 from .i18n import _
23
23
24 from . import (
24 from . import (
25 error,
25 error,
26 util,
26 util,
27 )
27 )
28 from .utils import (
28 from .utils import (
29 cborutil,
29 cborutil,
30 )
30 )
31
31
32 class cmdstate(object):
32 class cmdstate(object):
33 """a wrapper class to store the state of commands like `rebase`, `graft`,
33 """a wrapper class to store the state of commands like `rebase`, `graft`,
34 `histedit`, `shelve` etc. Extensions can also use this to write state files.
34 `histedit`, `shelve` etc. Extensions can also use this to write state files.
35
35
36 All the data for the state is stored in the form of key-value pairs in a
36 All the data for the state is stored in the form of key-value pairs in a
37 dictionary.
37 dictionary.
38
38
39 The class object can write all the data to a file in .hg/ directory and
39 The class object can write all the data to a file in .hg/ directory and
40 can populate the object data reading that file.
40 can populate the object data reading that file.
41
41
42 Uses cbor to serialize and deserialize data while writing and reading from
42 Uses cbor to serialize and deserialize data while writing and reading from
43 disk.
43 disk.
44 """
44 """
45
45
46 def __init__(self, repo, fname):
46 def __init__(self, repo, fname):
47 """ repo is the repo object
47 """ repo is the repo object
48 fname is the file name in which data should be stored in .hg directory
48 fname is the file name in which data should be stored in .hg directory
49 """
49 """
50 self._repo = repo
50 self._repo = repo
51 self.fname = fname
51 self.fname = fname
52
52
53 def read(self):
53 def read(self):
54 """read the existing state file and return a dict of data stored"""
54 """read the existing state file and return a dict of data stored"""
55 return self._read()
55 return self._read()
56
56
57 def save(self, version, data):
57 def save(self, version, data):
58 """write all the state data stored to .hg/<filename> file
58 """write all the state data stored to .hg/<filename> file
59
59
60 we use third-party library cbor to serialize data to write in the file.
60 we use third-party library cbor to serialize data to write in the file.
61 """
61 """
62 if not isinstance(version, int):
62 if not isinstance(version, int):
63 raise error.ProgrammingError("version of state file should be"
63 raise error.ProgrammingError("version of state file should be"
64 " an integer")
64 " an integer")
65
65
66 with self._repo.vfs(self.fname, 'wb', atomictemp=True) as fp:
66 with self._repo.vfs(self.fname, 'wb', atomictemp=True) as fp:
67 fp.write('%d\n' % version)
67 fp.write('%d\n' % version)
68 for chunk in cborutil.streamencode(data):
68 for chunk in cborutil.streamencode(data):
69 fp.write(chunk)
69 fp.write(chunk)
70
70
71 def _read(self):
71 def _read(self):
72 """reads the state file and returns a dictionary which contain
72 """reads the state file and returns a dictionary which contain
73 data in the same format as it was before storing"""
73 data in the same format as it was before storing"""
74 with self._repo.vfs(self.fname, 'rb') as fp:
74 with self._repo.vfs(self.fname, 'rb') as fp:
75 try:
75 try:
76 int(fp.readline())
76 int(fp.readline())
77 except ValueError:
77 except ValueError:
78 raise error.CorruptedState("unknown version of state file"
78 raise error.CorruptedState("unknown version of state file"
79 " found")
79 " found")
80
80
81 return cborutil.decodeall(fp.read())[0]
81 return cborutil.decodeall(fp.read())[0]
82
82
83 def delete(self):
83 def delete(self):
84 """drop the state file if exists"""
84 """drop the state file if exists"""
85 util.unlinkpath(self._repo.vfs.join(self.fname), ignoremissing=True)
85 util.unlinkpath(self._repo.vfs.join(self.fname), ignoremissing=True)
86
86
87 def exists(self):
87 def exists(self):
88 """check whether the state file exists or not"""
88 """check whether the state file exists or not"""
89 return self._repo.vfs.exists(self.fname)
89 return self._repo.vfs.exists(self.fname)
90
90
91 class _statecheck(object):
91 class _statecheck(object):
92 """a utility class that deals with multistep operations like graft,
92 """a utility class that deals with multistep operations like graft,
93 histedit, bisect, update etc and check whether such commands
93 histedit, bisect, update etc and check whether such commands
94 are in an unfinished conditition or not and return appropriate message
94 are in an unfinished conditition or not and return appropriate message
95 and hint.
95 and hint.
96 It also has the ability to register and determine the states of any new
96 It also has the ability to register and determine the states of any new
97 multistep operation or multistep command extension.
97 multistep operation or multistep command extension.
98 """
98 """
99
99
100 def __init__(self, opname, fname, clearable=False, allowcommit=False,
100 def __init__(self, opname, fname, clearable, allowcommit, reportonly,
101 reportonly=False, continueflag=False, stopflag=False ,
101 continueflag, stopflag, cmdmsg, cmdhint, statushint):
102 cmdmsg="", cmdhint="", statushint=""):
103 """opname is the name the command or operation
104 fname is the file name in which data should be stored in .hg directory.
105 It is None for merge command.
106 clearable boolean determines whether or not interrupted states can be
107 cleared by running `hg update -C .` which in turn deletes the
108 state file.
109 allowcommit boolean decides whether commit is allowed during interrupted
110 state or not.
111 reportonly flag is used for operations like bisect where we just
112 need to detect the operation using 'hg status --verbose'
113 continueflag is a boolean determines whether or not a command supports
114 `--continue` option or not.
115 stopflag is a boolean that determines whether or not a command supports
116 --stop flag
117 cmdmsg is used to pass a different status message in case standard
118 message of the format "abort: cmdname in progress" is not desired.
119 cmdhint is used to pass a different hint message in case standard
120 message of the format "To continue: hg cmdname --continue
121 To abort: hg cmdname --abort" is not desired.
122 statushint is used to pass a different status message in case standard
123 message of the format ('To continue: hg cmdname --continue'
124 'To abort: hg cmdname --abort') is not desired
125 """
126 self._opname = opname
102 self._opname = opname
127 self._fname = fname
103 self._fname = fname
128 self._clearable = clearable
104 self._clearable = clearable
129 self._allowcommit = allowcommit
105 self._allowcommit = allowcommit
106 self._reportonly = reportonly
107 self._continueflag = continueflag
108 self._stopflag = stopflag
109 self._cmdmsg = cmdmsg
130 self._cmdhint = cmdhint
110 self._cmdhint = cmdhint
131 self._statushint = statushint
111 self._statushint = statushint
132 self._cmdmsg = cmdmsg
133 self._stopflag = stopflag
134 self._reportonly = reportonly
135 self._continueflag = continueflag
136
112
137 def statusmsg(self):
113 def statusmsg(self):
138 """returns the hint message corresponding to the command for
114 """returns the hint message corresponding to the command for
139 hg status --verbose
115 hg status --verbose
140 """
116 """
141 if not self._statushint:
117 if not self._statushint:
142 hint = (_('To continue: hg %s --continue\n'
118 hint = (_('To continue: hg %s --continue\n'
143 'To abort: hg %s --abort') % (self._opname,
119 'To abort: hg %s --abort') % (self._opname,
144 self._opname))
120 self._opname))
145 if self._stopflag:
121 if self._stopflag:
146 hint = hint + (_('\nTo stop: hg %s --stop') %
122 hint = hint + (_('\nTo stop: hg %s --stop') %
147 (self._opname))
123 (self._opname))
148 return hint
124 return hint
149 return self._statushint
125 return self._statushint
150
126
151 def hint(self):
127 def hint(self):
152 """returns the hint message corresponding to an interrupted
128 """returns the hint message corresponding to an interrupted
153 operation
129 operation
154 """
130 """
155 if not self._cmdhint:
131 if not self._cmdhint:
156 return (_("use 'hg %s --continue' or 'hg %s --abort'") %
132 return (_("use 'hg %s --continue' or 'hg %s --abort'") %
157 (self._opname, self._opname))
133 (self._opname, self._opname))
158 return self._cmdhint
134 return self._cmdhint
159
135
160 def msg(self):
136 def msg(self):
161 """returns the status message corresponding to the command"""
137 """returns the status message corresponding to the command"""
162 if not self._cmdmsg:
138 if not self._cmdmsg:
163 return _('%s in progress') % (self._opname)
139 return _('%s in progress') % (self._opname)
164 return self._cmdmsg
140 return self._cmdmsg
165
141
166 def continuemsg(self):
142 def continuemsg(self):
167 """ returns appropriate continue message corresponding to command"""
143 """ returns appropriate continue message corresponding to command"""
168 return _('hg %s --continue') % (self._opname)
144 return _('hg %s --continue') % (self._opname)
169
145
170 def isunfinished(self, repo):
146 def isunfinished(self, repo):
171 """determines whether a multi-step operation is in progress
147 """determines whether a multi-step operation is in progress
172 or not
148 or not
173 """
149 """
174 if self._opname == 'merge':
150 if self._opname == 'merge':
175 return len(repo[None].parents()) > 1
151 return len(repo[None].parents()) > 1
176 else:
152 else:
177 return repo.vfs.exists(self._fname)
153 return repo.vfs.exists(self._fname)
178
154
179 # A list of statecheck objects for multistep operations like graft.
155 # A list of statecheck objects for multistep operations like graft.
180 _unfinishedstates = []
156 _unfinishedstates = []
181
157
182 def addunfinished(opname, **kwargs):
158 def addunfinished(opname, fname, clearable=False, allowcommit=False,
159 reportonly=False, continueflag=False, stopflag=False,
160 cmdmsg="", cmdhint="", statushint=""):
183 """this registers a new command or operation to unfinishedstates
161 """this registers a new command or operation to unfinishedstates
162 opname is the name the command or operation
163 fname is the file name in which data should be stored in .hg directory.
164 It is None for merge command.
165 clearable boolean determines whether or not interrupted states can be
166 cleared by running `hg update -C .` which in turn deletes the
167 state file.
168 allowcommit boolean decides whether commit is allowed during interrupted
169 state or not.
170 reportonly flag is used for operations like bisect where we just
171 need to detect the operation using 'hg status --verbose'
172 continueflag is a boolean determines whether or not a command supports
173 `--continue` option or not.
174 stopflag is a boolean that determines whether or not a command supports
175 --stop flag
176 cmdmsg is used to pass a different status message in case standard
177 message of the format "abort: cmdname in progress" is not desired.
178 cmdhint is used to pass a different hint message in case standard
179 message of the format "To continue: hg cmdname --continue
180 To abort: hg cmdname --abort" is not desired.
181 statushint is used to pass a different status message in case standard
182 message of the format ('To continue: hg cmdname --continue'
183 'To abort: hg cmdname --abort') is not desired
184 """
184 """
185 statecheckobj = _statecheck(opname, **kwargs)
185 statecheckobj = _statecheck(opname, fname, clearable, allowcommit,
186 reportonly, continueflag, stopflag, cmdmsg,
187 cmdhint, statushint)
186 if opname == 'merge':
188 if opname == 'merge':
187 _unfinishedstates.append(statecheckobj)
189 _unfinishedstates.append(statecheckobj)
188 else:
190 else:
189 _unfinishedstates.insert(0, statecheckobj)
191 _unfinishedstates.insert(0, statecheckobj)
190
192
191 addunfinished(
193 addunfinished(
192 'graft', fname='graftstate', clearable=True, stopflag=True,
194 'graft', fname='graftstate', clearable=True, stopflag=True,
193 continueflag=True,
195 continueflag=True,
194 cmdhint=_("use 'hg graft --continue' or 'hg graft --stop' to stop")
196 cmdhint=_("use 'hg graft --continue' or 'hg graft --stop' to stop")
195 )
197 )
196 addunfinished(
198 addunfinished(
197 'update', fname='updatestate', clearable=True,
199 'update', fname='updatestate', clearable=True,
198 cmdmsg=_('last update was interrupted'),
200 cmdmsg=_('last update was interrupted'),
199 cmdhint=_("use 'hg update' to get a consistent checkout"),
201 cmdhint=_("use 'hg update' to get a consistent checkout"),
200 statushint=_("To continue: hg update")
202 statushint=_("To continue: hg update")
201 )
203 )
202 addunfinished(
204 addunfinished(
203 'bisect', fname='bisect.state', allowcommit=True, reportonly=True,
205 'bisect', fname='bisect.state', allowcommit=True, reportonly=True,
204 statushint=_('To mark the changeset good: hg bisect --good\n'
206 statushint=_('To mark the changeset good: hg bisect --good\n'
205 'To mark the changeset bad: hg bisect --bad\n'
207 'To mark the changeset bad: hg bisect --bad\n'
206 'To abort: hg bisect --reset\n')
208 'To abort: hg bisect --reset\n')
207 )
209 )
208 addunfinished(
210 addunfinished(
209 'merge', fname=None, clearable=True, allowcommit=True,
211 'merge', fname=None, clearable=True, allowcommit=True,
210 cmdmsg=_('outstanding uncommitted merge'),
212 cmdmsg=_('outstanding uncommitted merge'),
211 statushint=_('To continue: hg commit\n'
213 statushint=_('To continue: hg commit\n'
212 'To abort: hg merge --abort'),
214 'To abort: hg merge --abort'),
213 cmdhint=_("use 'hg commit' or 'hg merge --abort'")
215 cmdhint=_("use 'hg commit' or 'hg merge --abort'")
214 )
216 )
215
217
216 def getrepostate(repo):
218 def getrepostate(repo):
217 # experimental config: commands.status.skipstates
219 # experimental config: commands.status.skipstates
218 skip = set(repo.ui.configlist('commands', 'status.skipstates'))
220 skip = set(repo.ui.configlist('commands', 'status.skipstates'))
219 for state in _unfinishedstates:
221 for state in _unfinishedstates:
220 if state._opname in skip:
222 if state._opname in skip:
221 continue
223 continue
222 if state.isunfinished(repo):
224 if state.isunfinished(repo):
223 return (state._opname, state.statusmsg())
225 return (state._opname, state.statusmsg())
General Comments 0
You need to be logged in to leave comments. Login now