##// END OF EJS Templates
walk: remove remaining users of cmdutils.matchpats
Matt Mackall -
r6582:5acbdd39 default
parent child Browse files
Show More
@@ -1,251 +1,251 b''
1 1 # extdiff.py - external diff program support for mercurial
2 2 #
3 3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 '''
9 9 The `extdiff' Mercurial extension allows you to use external programs
10 10 to compare revisions, or revision with working dir. The external diff
11 11 programs are called with a configurable set of options and two
12 12 non-option arguments: paths to directories containing snapshots of
13 13 files to compare.
14 14
15 15 To enable this extension:
16 16
17 17 [extensions]
18 18 hgext.extdiff =
19 19
20 20 The `extdiff' extension also allows to configure new diff commands, so
21 21 you do not need to type "hg extdiff -p kdiff3" always.
22 22
23 23 [extdiff]
24 24 # add new command that runs GNU diff(1) in 'context diff' mode
25 25 cdiff = gdiff -Nprc5
26 26 ## or the old way:
27 27 #cmd.cdiff = gdiff
28 28 #opts.cdiff = -Nprc5
29 29
30 30 # add new command called vdiff, runs kdiff3
31 31 vdiff = kdiff3
32 32
33 33 # add new command called meld, runs meld (no need to name twice)
34 34 meld =
35 35
36 36 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
37 37 #(see http://www.vim.org/scripts/script.php?script_id=102)
38 38 # Non english user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
39 39 # your .vimrc
40 40 vimdiff = gvim -f '+next' '+execute "DirDiff" argv(0) argv(1)'
41 41
42 42 You can use -I/-X and list of file or directory names like normal
43 43 "hg diff" command. The `extdiff' extension makes snapshots of only
44 44 needed files, so running the external diff program will actually be
45 45 pretty fast (at least faster than having to compare the entire tree).
46 46 '''
47 47
48 48 from mercurial.i18n import _
49 49 from mercurial.node import short
50 50 from mercurial import cmdutil, util, commands
51 51 import os, shlex, shutil, tempfile
52 52
53 53 def snapshot_node(ui, repo, files, node, tmproot):
54 54 '''snapshot files as of some revision'''
55 55 mf = repo.changectx(node).manifest()
56 56 dirname = os.path.basename(repo.root)
57 57 if dirname == "":
58 58 dirname = "root"
59 59 dirname = '%s.%s' % (dirname, short(node))
60 60 base = os.path.join(tmproot, dirname)
61 61 os.mkdir(base)
62 62 ui.note(_('making snapshot of %d files from rev %s\n') %
63 63 (len(files), short(node)))
64 64 for fn in files:
65 65 if not fn in mf:
66 66 # skipping new file after a merge ?
67 67 continue
68 68 wfn = util.pconvert(fn)
69 69 ui.note(' %s\n' % wfn)
70 70 dest = os.path.join(base, wfn)
71 71 destdir = os.path.dirname(dest)
72 72 if not os.path.isdir(destdir):
73 73 os.makedirs(destdir)
74 74 data = repo.wwritedata(wfn, repo.file(wfn).read(mf[wfn]))
75 75 open(dest, 'wb').write(data)
76 76 return dirname
77 77
78 78
79 79 def snapshot_wdir(ui, repo, files, tmproot):
80 80 '''snapshot files from working directory.
81 81 if not using snapshot, -I/-X does not work and recursive diff
82 82 in tools like kdiff3 and meld displays too many files.'''
83 83 repo_root = repo.root
84 84
85 85 dirname = os.path.basename(repo_root)
86 86 if dirname == "":
87 87 dirname = "root"
88 88 base = os.path.join(tmproot, dirname)
89 89 os.mkdir(base)
90 90 ui.note(_('making snapshot of %d files from working dir\n') %
91 91 (len(files)))
92 92
93 93 fns_and_mtime = []
94 94
95 95 for fn in files:
96 96 wfn = util.pconvert(fn)
97 97 ui.note(' %s\n' % wfn)
98 98 dest = os.path.join(base, wfn)
99 99 destdir = os.path.dirname(dest)
100 100 if not os.path.isdir(destdir):
101 101 os.makedirs(destdir)
102 102
103 103 fp = open(dest, 'wb')
104 104 for chunk in util.filechunkiter(repo.wopener(wfn)):
105 105 fp.write(chunk)
106 106 fp.close()
107 107
108 108 fns_and_mtime.append((dest, os.path.join(repo_root, fn),
109 109 os.path.getmtime(dest)))
110 110
111 111
112 112 return dirname, fns_and_mtime
113 113
114 114
115 115 def dodiff(ui, repo, diffcmd, diffopts, pats, opts):
116 116 '''Do the actuall diff:
117 117
118 118 - copy to a temp structure if diffing 2 internal revisions
119 119 - copy to a temp structure if diffing working revision with
120 120 another one and more than 1 file is changed
121 121 - just invoke the diff for a single file in the working dir
122 122 '''
123 123 node1, node2 = cmdutil.revpair(repo, opts['rev'])
124 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
124 matcher = cmdutil.match(repo, pats, opts)
125 125 modified, added, removed, deleted, unknown = repo.status(
126 node1, node2, files, match=matchfn)[:5]
126 node1, node2, matcher.files(), match=matcher)[:5]
127 127 if not (modified or added or removed):
128 128 return 0
129 129
130 130 tmproot = tempfile.mkdtemp(prefix='extdiff.')
131 131 dir2root = ''
132 132 try:
133 133 # Always make a copy of node1
134 134 dir1 = snapshot_node(ui, repo, modified + removed, node1, tmproot)
135 135 changes = len(modified) + len(removed) + len(added)
136 136
137 137 fns_and_mtime = []
138 138
139 139 # If node2 in not the wc or there is >1 change, copy it
140 140 if node2:
141 141 dir2 = snapshot_node(ui, repo, modified + added, node2, tmproot)
142 142 elif changes > 1:
143 143 #we only actually need to get the files to copy back to the working
144 144 #dir in this case (because the other cases are: diffing 2 revisions
145 145 #or single file -- in which case the file is already directly passed
146 146 #to the diff tool).
147 147 dir2, fns_and_mtime = snapshot_wdir(ui, repo, modified + added, tmproot)
148 148 else:
149 149 # This lets the diff tool open the changed file directly
150 150 dir2 = ''
151 151 dir2root = repo.root
152 152
153 153 # If only one change, diff the files instead of the directories
154 154 if changes == 1 :
155 155 if len(modified):
156 156 dir1 = os.path.join(dir1, util.localpath(modified[0]))
157 157 dir2 = os.path.join(dir2root, dir2, util.localpath(modified[0]))
158 158 elif len(removed) :
159 159 dir1 = os.path.join(dir1, util.localpath(removed[0]))
160 160 dir2 = os.devnull
161 161 else:
162 162 dir1 = os.devnull
163 163 dir2 = os.path.join(dir2root, dir2, util.localpath(added[0]))
164 164
165 165 cmdline = ('%s %s %s %s' %
166 166 (util.shellquote(diffcmd), ' '.join(diffopts),
167 167 util.shellquote(dir1), util.shellquote(dir2)))
168 168 ui.debug('running %r in %s\n' % (cmdline, tmproot))
169 169 util.system(cmdline, cwd=tmproot)
170 170
171 171 for copy_fn, working_fn, mtime in fns_and_mtime:
172 172 if os.path.getmtime(copy_fn) != mtime:
173 173 ui.debug('File changed while diffing. '
174 174 'Overwriting: %s (src: %s)\n' % (working_fn, copy_fn))
175 175 util.copyfile(copy_fn, working_fn)
176 176
177 177 return 1
178 178 finally:
179 179 ui.note(_('cleaning up temp directory\n'))
180 180 shutil.rmtree(tmproot)
181 181
182 182 def extdiff(ui, repo, *pats, **opts):
183 183 '''use external program to diff repository (or selected files)
184 184
185 185 Show differences between revisions for the specified files, using
186 186 an external program. The default program used is diff, with
187 187 default options "-Npru".
188 188
189 189 To select a different program, use the -p option. The program
190 190 will be passed the names of two directories to compare. To pass
191 191 additional options to the program, use the -o option. These will
192 192 be passed before the names of the directories to compare.
193 193
194 194 When two revision arguments are given, then changes are
195 195 shown between those revisions. If only one revision is
196 196 specified then that revision is compared to the working
197 197 directory, and, when no revisions are specified, the
198 198 working directory files are compared to its parent.'''
199 199 program = opts['program'] or 'diff'
200 200 if opts['program']:
201 201 option = opts['option']
202 202 else:
203 203 option = opts['option'] or ['-Npru']
204 204 return dodiff(ui, repo, program, option, pats, opts)
205 205
206 206 cmdtable = {
207 207 "extdiff":
208 208 (extdiff,
209 209 [('p', 'program', '', _('comparison program to run')),
210 210 ('o', 'option', [], _('pass option to comparison program')),
211 211 ('r', 'rev', [], _('revision')),
212 212 ] + commands.walkopts,
213 213 _('hg extdiff [OPT]... [FILE]...')),
214 214 }
215 215
216 216 def uisetup(ui):
217 217 for cmd, path in ui.configitems('extdiff'):
218 218 if cmd.startswith('cmd.'):
219 219 cmd = cmd[4:]
220 220 if not path: path = cmd
221 221 diffopts = ui.config('extdiff', 'opts.' + cmd, '')
222 222 diffopts = diffopts and [diffopts] or []
223 223 elif cmd.startswith('opts.'):
224 224 continue
225 225 else:
226 226 # command = path opts
227 227 if path:
228 228 diffopts = shlex.split(path)
229 229 path = diffopts.pop(0)
230 230 else:
231 231 path, diffopts = cmd, []
232 232 def save(cmd, path, diffopts):
233 233 '''use closure to save diff command to use'''
234 234 def mydiff(ui, repo, *pats, **opts):
235 235 return dodiff(ui, repo, path, diffopts, pats, opts)
236 236 mydiff.__doc__ = '''use %(path)s to diff repository (or selected files)
237 237
238 238 Show differences between revisions for the specified
239 239 files, using the %(path)s program.
240 240
241 241 When two revision arguments are given, then changes are
242 242 shown between those revisions. If only one revision is
243 243 specified then that revision is compared to the working
244 244 directory, and, when no revisions are specified, the
245 245 working directory files are compared to its parent.''' % {
246 246 'path': util.uirepr(path),
247 247 }
248 248 return mydiff
249 249 cmdtable[cmd] = (save(cmd, path, diffopts),
250 250 cmdtable['extdiff'][1][1:],
251 251 _('hg %s [OPTION]... [FILE]...') % cmd)
@@ -1,556 +1,556 b''
1 1 # keyword.py - $Keyword$ expansion for Mercurial
2 2 #
3 3 # Copyright 2007, 2008 Christian Ebert <blacktrash@gmx.net>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7 #
8 8 # $Id$
9 9 #
10 10 # Keyword expansion hack against the grain of a DSCM
11 11 #
12 12 # There are many good reasons why this is not needed in a distributed
13 13 # SCM, still it may be useful in very small projects based on single
14 14 # files (like LaTeX packages), that are mostly addressed to an audience
15 15 # not running a version control system.
16 16 #
17 17 # For in-depth discussion refer to
18 18 # <http://www.selenic.com/mercurial/wiki/index.cgi/KeywordPlan>.
19 19 #
20 20 # Keyword expansion is based on Mercurial's changeset template mappings.
21 21 #
22 22 # Binary files are not touched.
23 23 #
24 24 # Setup in hgrc:
25 25 #
26 26 # [extensions]
27 27 # # enable extension
28 28 # hgext.keyword =
29 29 #
30 30 # Files to act upon/ignore are specified in the [keyword] section.
31 31 # Customized keyword template mappings in the [keywordmaps] section.
32 32 #
33 33 # Run "hg help keyword" and "hg kwdemo" to get info on configuration.
34 34
35 35 '''keyword expansion in local repositories
36 36
37 37 This extension expands RCS/CVS-like or self-customized $Keywords$
38 38 in tracked text files selected by your configuration.
39 39
40 40 Keywords are only expanded in local repositories and not stored in
41 41 the change history. The mechanism can be regarded as a convenience
42 42 for the current user or for archive distribution.
43 43
44 44 Configuration is done in the [keyword] and [keywordmaps] sections
45 45 of hgrc files.
46 46
47 47 Example:
48 48
49 49 [keyword]
50 50 # expand keywords in every python file except those matching "x*"
51 51 **.py =
52 52 x* = ignore
53 53
54 54 Note: the more specific you are in your filename patterns
55 55 the less you lose speed in huge repos.
56 56
57 57 For [keywordmaps] template mapping and expansion demonstration and
58 58 control run "hg kwdemo".
59 59
60 60 An additional date template filter {date|utcdate} is provided.
61 61
62 62 The default template mappings (view with "hg kwdemo -d") can be replaced
63 63 with customized keywords and templates.
64 64 Again, run "hg kwdemo" to control the results of your config changes.
65 65
66 66 Before changing/disabling active keywords, run "hg kwshrink" to avoid
67 67 the risk of inadvertedly storing expanded keywords in the change history.
68 68
69 69 To force expansion after enabling it, or a configuration change, run
70 70 "hg kwexpand".
71 71
72 72 Also, when committing with the record extension or using mq's qrecord, be aware
73 73 that keywords cannot be updated. Again, run "hg kwexpand" on the files in
74 74 question to update keyword expansions after all changes have been checked in.
75 75
76 76 Expansions spanning more than one line and incremental expansions,
77 77 like CVS' $Log$, are not supported. A keyword template map
78 78 "Log = {desc}" expands to the first line of the changeset description.
79 79 '''
80 80
81 81 from mercurial import commands, cmdutil, context, dispatch, filelog, revlog
82 82 from mercurial import patch, localrepo, templater, templatefilters, util
83 83 from mercurial.hgweb import webcommands
84 84 from mercurial.node import nullid, hex
85 85 from mercurial.i18n import _
86 86 import re, shutil, tempfile, time
87 87
88 88 commands.optionalrepo += ' kwdemo'
89 89
90 90 # hg commands that do not act on keywords
91 91 nokwcommands = ('add addremove bundle copy export grep incoming init'
92 92 ' log outgoing push rename rollback tip'
93 93 ' convert email glog')
94 94
95 95 # hg commands that trigger expansion only when writing to working dir,
96 96 # not when reading filelog, and unexpand when reading from working dir
97 97 restricted = 'record qfold qimport qnew qpush qrefresh qrecord'
98 98
99 99 def utcdate(date):
100 100 '''Returns hgdate in cvs-like UTC format.'''
101 101 return time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(date[0]))
102 102
103 103 # make keyword tools accessible
104 104 kwtools = {'templater': None, 'hgcmd': '', 'inc': [], 'exc': ['.hg*']}
105 105
106 106
107 107 class kwtemplater(object):
108 108 '''
109 109 Sets up keyword templates, corresponding keyword regex, and
110 110 provides keyword substitution functions.
111 111 '''
112 112 templates = {
113 113 'Revision': '{node|short}',
114 114 'Author': '{author|user}',
115 115 'Date': '{date|utcdate}',
116 116 'RCSFile': '{file|basename},v',
117 117 'Source': '{root}/{file},v',
118 118 'Id': '{file|basename},v {node|short} {date|utcdate} {author|user}',
119 119 'Header': '{root}/{file},v {node|short} {date|utcdate} {author|user}',
120 120 }
121 121
122 122 def __init__(self, ui, repo):
123 123 self.ui = ui
124 124 self.repo = repo
125 125 self.matcher = util.matcher(repo.root,
126 126 inc=kwtools['inc'], exc=kwtools['exc'])[1]
127 127 self.restrict = kwtools['hgcmd'] in restricted.split()
128 128
129 129 kwmaps = self.ui.configitems('keywordmaps')
130 130 if kwmaps: # override default templates
131 131 kwmaps = [(k, templater.parsestring(v, False))
132 132 for (k, v) in kwmaps]
133 133 self.templates = dict(kwmaps)
134 134 escaped = map(re.escape, self.templates.keys())
135 135 kwpat = r'\$(%s)(: [^$\n\r]*? )??\$' % '|'.join(escaped)
136 136 self.re_kw = re.compile(kwpat)
137 137
138 138 templatefilters.filters['utcdate'] = utcdate
139 139 self.ct = cmdutil.changeset_templater(self.ui, self.repo,
140 140 False, '', False)
141 141
142 142 def getnode(self, path, fnode):
143 143 '''Derives changenode from file path and filenode.'''
144 144 # used by kwfilelog.read and kwexpand
145 145 c = context.filectx(self.repo, path, fileid=fnode)
146 146 return c.node()
147 147
148 148 def substitute(self, data, path, node, subfunc):
149 149 '''Replaces keywords in data with expanded template.'''
150 150 def kwsub(mobj):
151 151 kw = mobj.group(1)
152 152 self.ct.use_template(self.templates[kw])
153 153 self.ui.pushbuffer()
154 154 self.ct.show(changenode=node, root=self.repo.root, file=path)
155 155 ekw = templatefilters.firstline(self.ui.popbuffer())
156 156 return '$%s: %s $' % (kw, ekw)
157 157 return subfunc(kwsub, data)
158 158
159 159 def expand(self, path, node, data):
160 160 '''Returns data with keywords expanded.'''
161 161 if not self.restrict and self.matcher(path) and not util.binary(data):
162 162 changenode = self.getnode(path, node)
163 163 return self.substitute(data, path, changenode, self.re_kw.sub)
164 164 return data
165 165
166 166 def iskwfile(self, path, islink):
167 167 '''Returns true if path matches [keyword] pattern
168 168 and is not a symbolic link.
169 169 Caveat: localrepository._link fails on Windows.'''
170 170 return self.matcher(path) and not islink(path)
171 171
172 172 def overwrite(self, node, expand, files):
173 173 '''Overwrites selected files expanding/shrinking keywords.'''
174 174 ctx = self.repo.changectx(node)
175 175 mf = ctx.manifest()
176 176 if node is not None: # commit
177 177 files = [f for f in ctx.files() if f in mf]
178 178 notify = self.ui.debug
179 179 else: # kwexpand/kwshrink
180 180 notify = self.ui.note
181 181 candidates = [f for f in files if self.iskwfile(f, mf.linkf)]
182 182 if candidates:
183 183 self.restrict = True # do not expand when reading
184 184 candidates.sort()
185 185 action = expand and 'expanding' or 'shrinking'
186 186 for f in candidates:
187 187 fp = self.repo.file(f)
188 188 data = fp.read(mf[f])
189 189 if util.binary(data):
190 190 continue
191 191 if expand:
192 192 changenode = node or self.getnode(f, mf[f])
193 193 data, found = self.substitute(data, f, changenode,
194 194 self.re_kw.subn)
195 195 else:
196 196 found = self.re_kw.search(data)
197 197 if found:
198 198 notify(_('overwriting %s %s keywords\n') % (f, action))
199 199 self.repo.wwrite(f, data, mf.flags(f))
200 200 self.repo.dirstate.normal(f)
201 201 self.restrict = False
202 202
203 203 def shrinktext(self, text):
204 204 '''Unconditionally removes all keyword substitutions from text.'''
205 205 return self.re_kw.sub(r'$\1$', text)
206 206
207 207 def shrink(self, fname, text):
208 208 '''Returns text with all keyword substitutions removed.'''
209 209 if self.matcher(fname) and not util.binary(text):
210 210 return self.shrinktext(text)
211 211 return text
212 212
213 213 def shrinklines(self, fname, lines):
214 214 '''Returns lines with keyword substitutions removed.'''
215 215 if self.matcher(fname):
216 216 text = ''.join(lines)
217 217 if not util.binary(text):
218 218 return self.shrinktext(text).splitlines(True)
219 219 return lines
220 220
221 221 def wread(self, fname, data):
222 222 '''If in restricted mode returns data read from wdir with
223 223 keyword substitutions removed.'''
224 224 return self.restrict and self.shrink(fname, data) or data
225 225
226 226 class kwfilelog(filelog.filelog):
227 227 '''
228 228 Subclass of filelog to hook into its read, add, cmp methods.
229 229 Keywords are "stored" unexpanded, and processed on reading.
230 230 '''
231 231 def __init__(self, opener, kwt, path):
232 232 super(kwfilelog, self).__init__(opener, path)
233 233 self.kwt = kwt
234 234 self.path = path
235 235
236 236 def read(self, node):
237 237 '''Expands keywords when reading filelog.'''
238 238 data = super(kwfilelog, self).read(node)
239 239 return self.kwt.expand(self.path, node, data)
240 240
241 241 def add(self, text, meta, tr, link, p1=None, p2=None):
242 242 '''Removes keyword substitutions when adding to filelog.'''
243 243 text = self.kwt.shrink(self.path, text)
244 244 return super(kwfilelog, self).add(text, meta, tr, link, p1, p2)
245 245
246 246 def cmp(self, node, text):
247 247 '''Removes keyword substitutions for comparison.'''
248 248 text = self.kwt.shrink(self.path, text)
249 249 if self.renamed(node):
250 250 t2 = super(kwfilelog, self).read(node)
251 251 return t2 != text
252 252 return revlog.revlog.cmp(self, node, text)
253 253
254 254 def _status(ui, repo, kwt, *pats, **opts):
255 255 '''Bails out if [keyword] configuration is not active.
256 256 Returns status of working directory.'''
257 257 if kwt:
258 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
259 return repo.status(files=files, match=match, list_clean=True)
258 matcher = cmdutil.match(repo, pats, opts)
259 return repo.status(files=matcher.files(), match=matcher, list_clean=True)
260 260 if ui.configitems('keyword'):
261 261 raise util.Abort(_('[keyword] patterns cannot match'))
262 262 raise util.Abort(_('no [keyword] patterns configured'))
263 263
264 264 def _kwfwrite(ui, repo, expand, *pats, **opts):
265 265 '''Selects files and passes them to kwtemplater.overwrite.'''
266 266 kwt = kwtools['templater']
267 267 status = _status(ui, repo, kwt, *pats, **opts)
268 268 modified, added, removed, deleted, unknown, ignored, clean = status
269 269 if modified or added or removed or deleted:
270 270 raise util.Abort(_('outstanding uncommitted changes in given files'))
271 271 wlock = lock = None
272 272 try:
273 273 wlock = repo.wlock()
274 274 lock = repo.lock()
275 275 kwt.overwrite(None, expand, clean)
276 276 finally:
277 277 del wlock, lock
278 278
279 279
280 280 def demo(ui, repo, *args, **opts):
281 281 '''print [keywordmaps] configuration and an expansion example
282 282
283 283 Show current, custom, or default keyword template maps
284 284 and their expansion.
285 285
286 286 Extend current configuration by specifying maps as arguments
287 287 and optionally by reading from an additional hgrc file.
288 288
289 289 Override current keyword template maps with "default" option.
290 290 '''
291 291 def demostatus(stat):
292 292 ui.status(_('\n\t%s\n') % stat)
293 293
294 294 def demoitems(section, items):
295 295 ui.write('[%s]\n' % section)
296 296 for k, v in items:
297 297 ui.write('%s = %s\n' % (k, v))
298 298
299 299 msg = 'hg keyword config and expansion example'
300 300 kwstatus = 'current'
301 301 fn = 'demo.txt'
302 302 branchname = 'demobranch'
303 303 tmpdir = tempfile.mkdtemp('', 'kwdemo.')
304 304 ui.note(_('creating temporary repo at %s\n') % tmpdir)
305 305 repo = localrepo.localrepository(ui, tmpdir, True)
306 306 ui.setconfig('keyword', fn, '')
307 307 if args or opts.get('rcfile'):
308 308 kwstatus = 'custom'
309 309 if opts.get('rcfile'):
310 310 ui.readconfig(opts.get('rcfile'))
311 311 if opts.get('default'):
312 312 kwstatus = 'default'
313 313 kwmaps = kwtemplater.templates
314 314 if ui.configitems('keywordmaps'):
315 315 # override maps from optional rcfile
316 316 for k, v in kwmaps.iteritems():
317 317 ui.setconfig('keywordmaps', k, v)
318 318 elif args:
319 319 # simulate hgrc parsing
320 320 rcmaps = ['[keywordmaps]\n'] + [a + '\n' for a in args]
321 321 fp = repo.opener('hgrc', 'w')
322 322 fp.writelines(rcmaps)
323 323 fp.close()
324 324 ui.readconfig(repo.join('hgrc'))
325 325 if not opts.get('default'):
326 326 kwmaps = dict(ui.configitems('keywordmaps')) or kwtemplater.templates
327 327 uisetup(ui)
328 328 reposetup(ui, repo)
329 329 for k, v in ui.configitems('extensions'):
330 330 if k.endswith('keyword'):
331 331 extension = '%s = %s' % (k, v)
332 332 break
333 333 demostatus('config using %s keyword template maps' % kwstatus)
334 334 ui.write('[extensions]\n%s\n' % extension)
335 335 demoitems('keyword', ui.configitems('keyword'))
336 336 demoitems('keywordmaps', kwmaps.iteritems())
337 337 keywords = '$' + '$\n$'.join(kwmaps.keys()) + '$\n'
338 338 repo.wopener(fn, 'w').write(keywords)
339 339 repo.add([fn])
340 340 path = repo.wjoin(fn)
341 341 ui.note(_('\n%s keywords written to %s:\n') % (kwstatus, path))
342 342 ui.note(keywords)
343 343 ui.note('\nhg -R "%s" branch "%s"\n' % (tmpdir, branchname))
344 344 # silence branch command if not verbose
345 345 quiet = ui.quiet
346 346 ui.quiet = not ui.verbose
347 347 commands.branch(ui, repo, branchname)
348 348 ui.quiet = quiet
349 349 for name, cmd in ui.configitems('hooks'):
350 350 if name.split('.', 1)[0].find('commit') > -1:
351 351 repo.ui.setconfig('hooks', name, '')
352 352 ui.note(_('unhooked all commit hooks\n'))
353 353 ui.note('hg -R "%s" ci -m "%s"\n' % (tmpdir, msg))
354 354 repo.commit(text=msg)
355 355 format = ui.verbose and ' in %s' % path or ''
356 356 demostatus('%s keywords expanded%s' % (kwstatus, format))
357 357 ui.write(repo.wread(fn))
358 358 ui.debug(_('\nremoving temporary repo %s\n') % tmpdir)
359 359 shutil.rmtree(tmpdir, ignore_errors=True)
360 360
361 361 def expand(ui, repo, *pats, **opts):
362 362 '''expand keywords in working directory
363 363
364 364 Run after (re)enabling keyword expansion.
365 365
366 366 kwexpand refuses to run if given files contain local changes.
367 367 '''
368 368 # 3rd argument sets expansion to True
369 369 _kwfwrite(ui, repo, True, *pats, **opts)
370 370
371 371 def files(ui, repo, *pats, **opts):
372 372 '''print files currently configured for keyword expansion
373 373
374 374 Crosscheck which files in working directory are potential targets for
375 375 keyword expansion.
376 376 That is, files matched by [keyword] config patterns but not symlinks.
377 377 '''
378 378 kwt = kwtools['templater']
379 379 status = _status(ui, repo, kwt, *pats, **opts)
380 380 modified, added, removed, deleted, unknown, ignored, clean = status
381 381 files = modified + added + clean
382 382 if opts.get('untracked'):
383 383 files += unknown
384 384 files.sort()
385 385 wctx = repo.workingctx()
386 386 islink = lambda p: 'l' in wctx.fileflags(p)
387 387 kwfiles = [f for f in files if kwt.iskwfile(f, islink)]
388 388 cwd = pats and repo.getcwd() or ''
389 389 kwfstats = not opts.get('ignore') and (('K', kwfiles),) or ()
390 390 if opts.get('all') or opts.get('ignore'):
391 391 kwfstats += (('I', [f for f in files if f not in kwfiles]),)
392 392 for char, filenames in kwfstats:
393 393 format = (opts.get('all') or ui.verbose) and '%s %%s\n' % char or '%s\n'
394 394 for f in filenames:
395 395 ui.write(format % repo.pathto(f, cwd))
396 396
397 397 def shrink(ui, repo, *pats, **opts):
398 398 '''revert expanded keywords in working directory
399 399
400 400 Run before changing/disabling active keywords
401 401 or if you experience problems with "hg import" or "hg merge".
402 402
403 403 kwshrink refuses to run if given files contain local changes.
404 404 '''
405 405 # 3rd argument sets expansion to False
406 406 _kwfwrite(ui, repo, False, *pats, **opts)
407 407
408 408
409 409 def uisetup(ui):
410 410 '''Collects [keyword] config in kwtools.
411 411 Monkeypatches dispatch._parse if needed.'''
412 412
413 413 for pat, opt in ui.configitems('keyword'):
414 414 if opt != 'ignore':
415 415 kwtools['inc'].append(pat)
416 416 else:
417 417 kwtools['exc'].append(pat)
418 418
419 419 if kwtools['inc']:
420 420 def kwdispatch_parse(ui, args):
421 421 '''Monkeypatch dispatch._parse to obtain running hg command.'''
422 422 cmd, func, args, options, cmdoptions = dispatch_parse(ui, args)
423 423 kwtools['hgcmd'] = cmd
424 424 return cmd, func, args, options, cmdoptions
425 425
426 426 dispatch_parse = dispatch._parse
427 427 dispatch._parse = kwdispatch_parse
428 428
429 429 def reposetup(ui, repo):
430 430 '''Sets up repo as kwrepo for keyword substitution.
431 431 Overrides file method to return kwfilelog instead of filelog
432 432 if file matches user configuration.
433 433 Wraps commit to overwrite configured files with updated
434 434 keyword substitutions.
435 435 Monkeypatches patch and webcommands.'''
436 436
437 437 try:
438 438 if (not repo.local() or not kwtools['inc']
439 439 or kwtools['hgcmd'] in nokwcommands.split()
440 440 or '.hg' in util.splitpath(repo.root)
441 441 or repo._url.startswith('bundle:')):
442 442 return
443 443 except AttributeError:
444 444 pass
445 445
446 446 kwtools['templater'] = kwt = kwtemplater(ui, repo)
447 447
448 448 class kwrepo(repo.__class__):
449 449 def file(self, f):
450 450 if f[0] == '/':
451 451 f = f[1:]
452 452 return kwfilelog(self.sopener, kwt, f)
453 453
454 454 def wread(self, filename):
455 455 data = super(kwrepo, self).wread(filename)
456 456 return kwt.wread(filename, data)
457 457
458 458 def commit(self, files=None, text='', user=None, date=None,
459 459 match=util.always, force=False, force_editor=False,
460 460 p1=None, p2=None, extra={}, empty_ok=False):
461 461 wlock = lock = None
462 462 _p1 = _p2 = None
463 463 try:
464 464 wlock = self.wlock()
465 465 lock = self.lock()
466 466 # store and postpone commit hooks
467 467 commithooks = {}
468 468 for name, cmd in ui.configitems('hooks'):
469 469 if name.split('.', 1)[0] == 'commit':
470 470 commithooks[name] = cmd
471 471 ui.setconfig('hooks', name, None)
472 472 if commithooks:
473 473 # store parents for commit hook environment
474 474 if p1 is None:
475 475 _p1, _p2 = repo.dirstate.parents()
476 476 else:
477 477 _p1, _p2 = p1, p2 or nullid
478 478 _p1 = hex(_p1)
479 479 if _p2 == nullid:
480 480 _p2 = ''
481 481 else:
482 482 _p2 = hex(_p2)
483 483
484 484 n = super(kwrepo, self).commit(files, text, user, date, match,
485 485 force, force_editor, p1, p2,
486 486 extra, empty_ok)
487 487
488 488 # restore commit hooks
489 489 for name, cmd in commithooks.iteritems():
490 490 ui.setconfig('hooks', name, cmd)
491 491 if n is not None:
492 492 kwt.overwrite(n, True, None)
493 493 repo.hook('commit', node=n, parent1=_p1, parent2=_p2)
494 494 return n
495 495 finally:
496 496 del wlock, lock
497 497
498 498 # monkeypatches
499 499 def kwpatchfile_init(self, ui, fname, missing=False):
500 500 '''Monkeypatch/wrap patch.patchfile.__init__ to avoid
501 501 rejects or conflicts due to expanded keywords in working dir.'''
502 502 patchfile_init(self, ui, fname, missing)
503 503 # shrink keywords read from working dir
504 504 self.lines = kwt.shrinklines(self.fname, self.lines)
505 505
506 506 def kw_diff(repo, node1=None, node2=None, files=None, match=util.always,
507 507 fp=None, changes=None, opts=None):
508 508 '''Monkeypatch patch.diff to avoid expansion except when
509 509 comparing against working dir.'''
510 510 if node2 is not None:
511 511 kwt.matcher = util.never
512 512 elif node1 is not None and node1 != repo.changectx().node():
513 513 kwt.restrict = True
514 514 patch_diff(repo, node1, node2, files, match, fp, changes, opts)
515 515
516 516 def kwweb_changeset(web, req, tmpl):
517 517 '''Wraps webcommands.changeset turning off keyword expansion.'''
518 518 kwt.matcher = util.never
519 519 return webcommands_changeset(web, req, tmpl)
520 520
521 521 def kwweb_filediff(web, req, tmpl):
522 522 '''Wraps webcommands.filediff turning off keyword expansion.'''
523 523 kwt.matcher = util.never
524 524 return webcommands_filediff(web, req, tmpl)
525 525
526 526 repo.__class__ = kwrepo
527 527
528 528 patchfile_init = patch.patchfile.__init__
529 529 patch_diff = patch.diff
530 530 webcommands_changeset = webcommands.changeset
531 531 webcommands_filediff = webcommands.filediff
532 532
533 533 patch.patchfile.__init__ = kwpatchfile_init
534 534 patch.diff = kw_diff
535 535 webcommands.changeset = webcommands.rev = kwweb_changeset
536 536 webcommands.filediff = webcommands.diff = kwweb_filediff
537 537
538 538
539 539 cmdtable = {
540 540 'kwdemo':
541 541 (demo,
542 542 [('d', 'default', None, _('show default keyword template maps')),
543 543 ('f', 'rcfile', [], _('read maps from rcfile'))],
544 544 _('hg kwdemo [-d] [-f RCFILE] [TEMPLATEMAP]...')),
545 545 'kwexpand': (expand, commands.walkopts,
546 546 _('hg kwexpand [OPTION]... [FILE]...')),
547 547 'kwfiles':
548 548 (files,
549 549 [('a', 'all', None, _('show keyword status flags of all files')),
550 550 ('i', 'ignore', None, _('show files excluded from expansion')),
551 551 ('u', 'untracked', None, _('additionally show untracked files')),
552 552 ] + commands.walkopts,
553 553 _('hg kwfiles [OPTION]... [FILE]...')),
554 554 'kwshrink': (shrink, commands.walkopts,
555 555 _('hg kwshrink [OPTION]... [FILE]...')),
556 556 }
@@ -1,2367 +1,2366 b''
1 1 # mq.py - patch queues for mercurial
2 2 #
3 3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 '''patch management and development
9 9
10 10 This extension lets you work with a stack of patches in a Mercurial
11 11 repository. It manages two stacks of patches - all known patches, and
12 12 applied patches (subset of known patches).
13 13
14 14 Known patches are represented as patch files in the .hg/patches
15 15 directory. Applied patches are both patch files and changesets.
16 16
17 17 Common tasks (use "hg help command" for more details):
18 18
19 19 prepare repository to work with patches qinit
20 20 create new patch qnew
21 21 import existing patch qimport
22 22
23 23 print patch series qseries
24 24 print applied patches qapplied
25 25 print name of top applied patch qtop
26 26
27 27 add known patch to applied stack qpush
28 28 remove patch from applied stack qpop
29 29 refresh contents of top applied patch qrefresh
30 30 '''
31 31
32 32 from mercurial.i18n import _
33 33 from mercurial.node import bin, hex, short
34 34 from mercurial.repo import RepoError
35 35 from mercurial import commands, cmdutil, hg, patch, revlog, util
36 36 from mercurial import repair
37 37 import os, sys, re, errno
38 38
39 39 commands.norepo += " qclone"
40 40
41 41 # Patch names looks like unix-file names.
42 42 # They must be joinable with queue directory and result in the patch path.
43 43 normname = util.normpath
44 44
45 45 class statusentry:
46 46 def __init__(self, rev, name=None):
47 47 if not name:
48 48 fields = rev.split(':', 1)
49 49 if len(fields) == 2:
50 50 self.rev, self.name = fields
51 51 else:
52 52 self.rev, self.name = None, None
53 53 else:
54 54 self.rev, self.name = rev, name
55 55
56 56 def __str__(self):
57 57 return self.rev + ':' + self.name
58 58
59 59 class queue:
60 60 def __init__(self, ui, path, patchdir=None):
61 61 self.basepath = path
62 62 self.path = patchdir or os.path.join(path, "patches")
63 63 self.opener = util.opener(self.path)
64 64 self.ui = ui
65 65 self.applied = []
66 66 self.full_series = []
67 67 self.applied_dirty = 0
68 68 self.series_dirty = 0
69 69 self.series_path = "series"
70 70 self.status_path = "status"
71 71 self.guards_path = "guards"
72 72 self.active_guards = None
73 73 self.guards_dirty = False
74 74 self._diffopts = None
75 75
76 76 if os.path.exists(self.join(self.series_path)):
77 77 self.full_series = self.opener(self.series_path).read().splitlines()
78 78 self.parse_series()
79 79
80 80 if os.path.exists(self.join(self.status_path)):
81 81 lines = self.opener(self.status_path).read().splitlines()
82 82 self.applied = [statusentry(l) for l in lines]
83 83
84 84 def diffopts(self):
85 85 if self._diffopts is None:
86 86 self._diffopts = patch.diffopts(self.ui)
87 87 return self._diffopts
88 88
89 89 def join(self, *p):
90 90 return os.path.join(self.path, *p)
91 91
92 92 def find_series(self, patch):
93 93 pre = re.compile("(\s*)([^#]+)")
94 94 index = 0
95 95 for l in self.full_series:
96 96 m = pre.match(l)
97 97 if m:
98 98 s = m.group(2)
99 99 s = s.rstrip()
100 100 if s == patch:
101 101 return index
102 102 index += 1
103 103 return None
104 104
105 105 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
106 106
107 107 def parse_series(self):
108 108 self.series = []
109 109 self.series_guards = []
110 110 for l in self.full_series:
111 111 h = l.find('#')
112 112 if h == -1:
113 113 patch = l
114 114 comment = ''
115 115 elif h == 0:
116 116 continue
117 117 else:
118 118 patch = l[:h]
119 119 comment = l[h:]
120 120 patch = patch.strip()
121 121 if patch:
122 122 if patch in self.series:
123 123 raise util.Abort(_('%s appears more than once in %s') %
124 124 (patch, self.join(self.series_path)))
125 125 self.series.append(patch)
126 126 self.series_guards.append(self.guard_re.findall(comment))
127 127
128 128 def check_guard(self, guard):
129 129 bad_chars = '# \t\r\n\f'
130 130 first = guard[0]
131 131 for c in '-+':
132 132 if first == c:
133 133 return (_('guard %r starts with invalid character: %r') %
134 134 (guard, c))
135 135 for c in bad_chars:
136 136 if c in guard:
137 137 return _('invalid character in guard %r: %r') % (guard, c)
138 138
139 139 def set_active(self, guards):
140 140 for guard in guards:
141 141 bad = self.check_guard(guard)
142 142 if bad:
143 143 raise util.Abort(bad)
144 144 guards = dict.fromkeys(guards).keys()
145 145 guards.sort()
146 146 self.ui.debug('active guards: %s\n' % ' '.join(guards))
147 147 self.active_guards = guards
148 148 self.guards_dirty = True
149 149
150 150 def active(self):
151 151 if self.active_guards is None:
152 152 self.active_guards = []
153 153 try:
154 154 guards = self.opener(self.guards_path).read().split()
155 155 except IOError, err:
156 156 if err.errno != errno.ENOENT: raise
157 157 guards = []
158 158 for i, guard in enumerate(guards):
159 159 bad = self.check_guard(guard)
160 160 if bad:
161 161 self.ui.warn('%s:%d: %s\n' %
162 162 (self.join(self.guards_path), i + 1, bad))
163 163 else:
164 164 self.active_guards.append(guard)
165 165 return self.active_guards
166 166
167 167 def set_guards(self, idx, guards):
168 168 for g in guards:
169 169 if len(g) < 2:
170 170 raise util.Abort(_('guard %r too short') % g)
171 171 if g[0] not in '-+':
172 172 raise util.Abort(_('guard %r starts with invalid char') % g)
173 173 bad = self.check_guard(g[1:])
174 174 if bad:
175 175 raise util.Abort(bad)
176 176 drop = self.guard_re.sub('', self.full_series[idx])
177 177 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
178 178 self.parse_series()
179 179 self.series_dirty = True
180 180
181 181 def pushable(self, idx):
182 182 if isinstance(idx, str):
183 183 idx = self.series.index(idx)
184 184 patchguards = self.series_guards[idx]
185 185 if not patchguards:
186 186 return True, None
187 187 default = False
188 188 guards = self.active()
189 189 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
190 190 if exactneg:
191 191 return False, exactneg[0]
192 192 pos = [g for g in patchguards if g[0] == '+']
193 193 exactpos = [g for g in pos if g[1:] in guards]
194 194 if pos:
195 195 if exactpos:
196 196 return True, exactpos[0]
197 197 return False, pos
198 198 return True, ''
199 199
200 200 def explain_pushable(self, idx, all_patches=False):
201 201 write = all_patches and self.ui.write or self.ui.warn
202 202 if all_patches or self.ui.verbose:
203 203 if isinstance(idx, str):
204 204 idx = self.series.index(idx)
205 205 pushable, why = self.pushable(idx)
206 206 if all_patches and pushable:
207 207 if why is None:
208 208 write(_('allowing %s - no guards in effect\n') %
209 209 self.series[idx])
210 210 else:
211 211 if not why:
212 212 write(_('allowing %s - no matching negative guards\n') %
213 213 self.series[idx])
214 214 else:
215 215 write(_('allowing %s - guarded by %r\n') %
216 216 (self.series[idx], why))
217 217 if not pushable:
218 218 if why:
219 219 write(_('skipping %s - guarded by %r\n') %
220 220 (self.series[idx], why))
221 221 else:
222 222 write(_('skipping %s - no matching guards\n') %
223 223 self.series[idx])
224 224
225 225 def save_dirty(self):
226 226 def write_list(items, path):
227 227 fp = self.opener(path, 'w')
228 228 for i in items:
229 229 fp.write("%s\n" % i)
230 230 fp.close()
231 231 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
232 232 if self.series_dirty: write_list(self.full_series, self.series_path)
233 233 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
234 234
235 235 def readheaders(self, patch):
236 236 def eatdiff(lines):
237 237 while lines:
238 238 l = lines[-1]
239 239 if (l.startswith("diff -") or
240 240 l.startswith("Index:") or
241 241 l.startswith("===========")):
242 242 del lines[-1]
243 243 else:
244 244 break
245 245 def eatempty(lines):
246 246 while lines:
247 247 l = lines[-1]
248 248 if re.match('\s*$', l):
249 249 del lines[-1]
250 250 else:
251 251 break
252 252
253 253 pf = self.join(patch)
254 254 message = []
255 255 comments = []
256 256 user = None
257 257 date = None
258 258 format = None
259 259 subject = None
260 260 diffstart = 0
261 261
262 262 for line in file(pf):
263 263 line = line.rstrip()
264 264 if line.startswith('diff --git'):
265 265 diffstart = 2
266 266 break
267 267 if diffstart:
268 268 if line.startswith('+++ '):
269 269 diffstart = 2
270 270 break
271 271 if line.startswith("--- "):
272 272 diffstart = 1
273 273 continue
274 274 elif format == "hgpatch":
275 275 # parse values when importing the result of an hg export
276 276 if line.startswith("# User "):
277 277 user = line[7:]
278 278 elif line.startswith("# Date "):
279 279 date = line[7:]
280 280 elif not line.startswith("# ") and line:
281 281 message.append(line)
282 282 format = None
283 283 elif line == '# HG changeset patch':
284 284 format = "hgpatch"
285 285 elif (format != "tagdone" and (line.startswith("Subject: ") or
286 286 line.startswith("subject: "))):
287 287 subject = line[9:]
288 288 format = "tag"
289 289 elif (format != "tagdone" and (line.startswith("From: ") or
290 290 line.startswith("from: "))):
291 291 user = line[6:]
292 292 format = "tag"
293 293 elif format == "tag" and line == "":
294 294 # when looking for tags (subject: from: etc) they
295 295 # end once you find a blank line in the source
296 296 format = "tagdone"
297 297 elif message or line:
298 298 message.append(line)
299 299 comments.append(line)
300 300
301 301 eatdiff(message)
302 302 eatdiff(comments)
303 303 eatempty(message)
304 304 eatempty(comments)
305 305
306 306 # make sure message isn't empty
307 307 if format and format.startswith("tag") and subject:
308 308 message.insert(0, "")
309 309 message.insert(0, subject)
310 310 return (message, comments, user, date, diffstart > 1)
311 311
312 312 def removeundo(self, repo):
313 313 undo = repo.sjoin('undo')
314 314 if not os.path.exists(undo):
315 315 return
316 316 try:
317 317 os.unlink(undo)
318 318 except OSError, inst:
319 319 self.ui.warn('error removing undo: %s\n' % str(inst))
320 320
321 321 def printdiff(self, repo, node1, node2=None, files=None,
322 322 fp=None, changes=None, opts={}):
323 fns, matchfn, anypats = cmdutil.matchpats(repo, files, opts)
324
325 patch.diff(repo, node1, node2, fns, match=matchfn,
323 m = cmdutil.match(repo, files, opts)
324 patch.diff(repo, node1, node2, m.files(), match=m,
326 325 fp=fp, changes=changes, opts=self.diffopts())
327 326
328 327 def mergeone(self, repo, mergeq, head, patch, rev):
329 328 # first try just applying the patch
330 329 (err, n) = self.apply(repo, [ patch ], update_status=False,
331 330 strict=True, merge=rev)
332 331
333 332 if err == 0:
334 333 return (err, n)
335 334
336 335 if n is None:
337 336 raise util.Abort(_("apply failed for patch %s") % patch)
338 337
339 338 self.ui.warn("patch didn't work out, merging %s\n" % patch)
340 339
341 340 # apply failed, strip away that rev and merge.
342 341 hg.clean(repo, head)
343 342 self.strip(repo, n, update=False, backup='strip')
344 343
345 344 ctx = repo.changectx(rev)
346 345 ret = hg.merge(repo, rev)
347 346 if ret:
348 347 raise util.Abort(_("update returned %d") % ret)
349 348 n = repo.commit(None, ctx.description(), ctx.user(), force=1)
350 349 if n == None:
351 350 raise util.Abort(_("repo commit failed"))
352 351 try:
353 352 message, comments, user, date, patchfound = mergeq.readheaders(patch)
354 353 except:
355 354 raise util.Abort(_("unable to read %s") % patch)
356 355
357 356 patchf = self.opener(patch, "w")
358 357 if comments:
359 358 comments = "\n".join(comments) + '\n\n'
360 359 patchf.write(comments)
361 360 self.printdiff(repo, head, n, fp=patchf)
362 361 patchf.close()
363 362 self.removeundo(repo)
364 363 return (0, n)
365 364
366 365 def qparents(self, repo, rev=None):
367 366 if rev is None:
368 367 (p1, p2) = repo.dirstate.parents()
369 368 if p2 == revlog.nullid:
370 369 return p1
371 370 if len(self.applied) == 0:
372 371 return None
373 372 return revlog.bin(self.applied[-1].rev)
374 373 pp = repo.changelog.parents(rev)
375 374 if pp[1] != revlog.nullid:
376 375 arevs = [ x.rev for x in self.applied ]
377 376 p0 = revlog.hex(pp[0])
378 377 p1 = revlog.hex(pp[1])
379 378 if p0 in arevs:
380 379 return pp[0]
381 380 if p1 in arevs:
382 381 return pp[1]
383 382 return pp[0]
384 383
385 384 def mergepatch(self, repo, mergeq, series):
386 385 if len(self.applied) == 0:
387 386 # each of the patches merged in will have two parents. This
388 387 # can confuse the qrefresh, qdiff, and strip code because it
389 388 # needs to know which parent is actually in the patch queue.
390 389 # so, we insert a merge marker with only one parent. This way
391 390 # the first patch in the queue is never a merge patch
392 391 #
393 392 pname = ".hg.patches.merge.marker"
394 393 n = repo.commit(None, '[mq]: merge marker', user=None, force=1)
395 394 self.removeundo(repo)
396 395 self.applied.append(statusentry(revlog.hex(n), pname))
397 396 self.applied_dirty = 1
398 397
399 398 head = self.qparents(repo)
400 399
401 400 for patch in series:
402 401 patch = mergeq.lookup(patch, strict=True)
403 402 if not patch:
404 403 self.ui.warn("patch %s does not exist\n" % patch)
405 404 return (1, None)
406 405 pushable, reason = self.pushable(patch)
407 406 if not pushable:
408 407 self.explain_pushable(patch, all_patches=True)
409 408 continue
410 409 info = mergeq.isapplied(patch)
411 410 if not info:
412 411 self.ui.warn("patch %s is not applied\n" % patch)
413 412 return (1, None)
414 413 rev = revlog.bin(info[1])
415 414 (err, head) = self.mergeone(repo, mergeq, head, patch, rev)
416 415 if head:
417 416 self.applied.append(statusentry(revlog.hex(head), patch))
418 417 self.applied_dirty = 1
419 418 if err:
420 419 return (err, head)
421 420 self.save_dirty()
422 421 return (0, head)
423 422
424 423 def patch(self, repo, patchfile):
425 424 '''Apply patchfile to the working directory.
426 425 patchfile: file name of patch'''
427 426 files = {}
428 427 try:
429 428 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
430 429 files=files)
431 430 except Exception, inst:
432 431 self.ui.note(str(inst) + '\n')
433 432 if not self.ui.verbose:
434 433 self.ui.warn("patch failed, unable to continue (try -v)\n")
435 434 return (False, files, False)
436 435
437 436 return (True, files, fuzz)
438 437
439 438 def apply(self, repo, series, list=False, update_status=True,
440 439 strict=False, patchdir=None, merge=None, all_files={}):
441 440 wlock = lock = tr = None
442 441 try:
443 442 wlock = repo.wlock()
444 443 lock = repo.lock()
445 444 tr = repo.transaction()
446 445 try:
447 446 ret = self._apply(repo, series, list, update_status,
448 447 strict, patchdir, merge, all_files=all_files)
449 448 tr.close()
450 449 self.save_dirty()
451 450 return ret
452 451 except:
453 452 try:
454 453 tr.abort()
455 454 finally:
456 455 repo.invalidate()
457 456 repo.dirstate.invalidate()
458 457 raise
459 458 finally:
460 459 del tr, lock, wlock
461 460 self.removeundo(repo)
462 461
463 462 def _apply(self, repo, series, list=False, update_status=True,
464 463 strict=False, patchdir=None, merge=None, all_files={}):
465 464 # TODO unify with commands.py
466 465 if not patchdir:
467 466 patchdir = self.path
468 467 err = 0
469 468 n = None
470 469 for patchname in series:
471 470 pushable, reason = self.pushable(patchname)
472 471 if not pushable:
473 472 self.explain_pushable(patchname, all_patches=True)
474 473 continue
475 474 self.ui.warn("applying %s\n" % patchname)
476 475 pf = os.path.join(patchdir, patchname)
477 476
478 477 try:
479 478 message, comments, user, date, patchfound = self.readheaders(patchname)
480 479 except:
481 480 self.ui.warn("Unable to read %s\n" % patchname)
482 481 err = 1
483 482 break
484 483
485 484 if not message:
486 485 message = "imported patch %s\n" % patchname
487 486 else:
488 487 if list:
489 488 message.append("\nimported patch %s" % patchname)
490 489 message = '\n'.join(message)
491 490
492 491 (patcherr, files, fuzz) = self.patch(repo, pf)
493 492 all_files.update(files)
494 493 patcherr = not patcherr
495 494
496 495 if merge and files:
497 496 # Mark as removed/merged and update dirstate parent info
498 497 removed = []
499 498 merged = []
500 499 for f in files:
501 500 if os.path.exists(repo.wjoin(f)):
502 501 merged.append(f)
503 502 else:
504 503 removed.append(f)
505 504 for f in removed:
506 505 repo.dirstate.remove(f)
507 506 for f in merged:
508 507 repo.dirstate.merge(f)
509 508 p1, p2 = repo.dirstate.parents()
510 509 repo.dirstate.setparents(p1, merge)
511 510 files = patch.updatedir(self.ui, repo, files)
512 511 n = repo.commit(files, message, user, date, match=util.never,
513 512 force=True)
514 513
515 514 if n == None:
516 515 raise util.Abort(_("repo commit failed"))
517 516
518 517 if update_status:
519 518 self.applied.append(statusentry(revlog.hex(n), patchname))
520 519
521 520 if patcherr:
522 521 if not patchfound:
523 522 self.ui.warn("patch %s is empty\n" % patchname)
524 523 err = 0
525 524 else:
526 525 self.ui.warn("patch failed, rejects left in working dir\n")
527 526 err = 1
528 527 break
529 528
530 529 if fuzz and strict:
531 530 self.ui.warn("fuzz found when applying patch, stopping\n")
532 531 err = 1
533 532 break
534 533 return (err, n)
535 534
536 535 def delete(self, repo, patches, opts):
537 536 if not patches and not opts.get('rev'):
538 537 raise util.Abort(_('qdelete requires at least one revision or '
539 538 'patch name'))
540 539
541 540 realpatches = []
542 541 for patch in patches:
543 542 patch = self.lookup(patch, strict=True)
544 543 info = self.isapplied(patch)
545 544 if info:
546 545 raise util.Abort(_("cannot delete applied patch %s") % patch)
547 546 if patch not in self.series:
548 547 raise util.Abort(_("patch %s not in series file") % patch)
549 548 realpatches.append(patch)
550 549
551 550 appliedbase = 0
552 551 if opts.get('rev'):
553 552 if not self.applied:
554 553 raise util.Abort(_('no patches applied'))
555 554 revs = cmdutil.revrange(repo, opts['rev'])
556 555 if len(revs) > 1 and revs[0] > revs[1]:
557 556 revs.reverse()
558 557 for rev in revs:
559 558 if appliedbase >= len(self.applied):
560 559 raise util.Abort(_("revision %d is not managed") % rev)
561 560
562 561 base = revlog.bin(self.applied[appliedbase].rev)
563 562 node = repo.changelog.node(rev)
564 563 if node != base:
565 564 raise util.Abort(_("cannot delete revision %d above "
566 565 "applied patches") % rev)
567 566 realpatches.append(self.applied[appliedbase].name)
568 567 appliedbase += 1
569 568
570 569 if not opts.get('keep'):
571 570 r = self.qrepo()
572 571 if r:
573 572 r.remove(realpatches, True)
574 573 else:
575 574 for p in realpatches:
576 575 os.unlink(self.join(p))
577 576
578 577 if appliedbase:
579 578 del self.applied[:appliedbase]
580 579 self.applied_dirty = 1
581 580 indices = [self.find_series(p) for p in realpatches]
582 581 indices.sort()
583 582 for i in indices[-1::-1]:
584 583 del self.full_series[i]
585 584 self.parse_series()
586 585 self.series_dirty = 1
587 586
588 587 def check_toppatch(self, repo):
589 588 if len(self.applied) > 0:
590 589 top = revlog.bin(self.applied[-1].rev)
591 590 pp = repo.dirstate.parents()
592 591 if top not in pp:
593 592 raise util.Abort(_("working directory revision is not qtip"))
594 593 return top
595 594 return None
596 595 def check_localchanges(self, repo, force=False, refresh=True):
597 596 m, a, r, d = repo.status()[:4]
598 597 if m or a or r or d:
599 598 if not force:
600 599 if refresh:
601 600 raise util.Abort(_("local changes found, refresh first"))
602 601 else:
603 602 raise util.Abort(_("local changes found"))
604 603 return m, a, r, d
605 604
606 605 _reserved = ('series', 'status', 'guards')
607 606 def check_reserved_name(self, name):
608 607 if (name in self._reserved or name.startswith('.hg')
609 608 or name.startswith('.mq')):
610 609 raise util.Abort(_('"%s" cannot be used as the name of a patch')
611 610 % name)
612 611
613 612 def new(self, repo, patch, *pats, **opts):
614 613 msg = opts.get('msg')
615 614 force = opts.get('force')
616 615 user = opts.get('user')
617 616 date = opts.get('date')
618 617 if date:
619 618 date = util.parsedate(date)
620 619 self.check_reserved_name(patch)
621 620 if os.path.exists(self.join(patch)):
622 621 raise util.Abort(_('patch "%s" already exists') % patch)
623 622 if opts.get('include') or opts.get('exclude') or pats:
624 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
625 m, a, r, d = repo.status(files=fns, match=match)[:4]
623 match = cmdutil.match(repo, pats, opts)
624 m, a, r, d = repo.status(files=match.files(), match=match)[:4]
626 625 else:
627 626 m, a, r, d = self.check_localchanges(repo, force)
628 fns, match, anypats = cmdutil.matchpats(repo, m + a + r)
627 match = cmdutil.match(repo, m + a + r)
629 628 commitfiles = m + a + r
630 629 self.check_toppatch(repo)
631 630 wlock = repo.wlock()
632 631 try:
633 632 insert = self.full_series_end()
634 633 commitmsg = msg and msg or ("[mq]: %s" % patch)
635 634 n = repo.commit(commitfiles, commitmsg, user, date, match=match, force=True)
636 635 if n == None:
637 636 raise util.Abort(_("repo commit failed"))
638 637 self.full_series[insert:insert] = [patch]
639 638 self.applied.append(statusentry(revlog.hex(n), patch))
640 639 self.parse_series()
641 640 self.series_dirty = 1
642 641 self.applied_dirty = 1
643 642 p = self.opener(patch, "w")
644 643 if date:
645 644 p.write("# HG changeset patch\n")
646 645 if user:
647 646 p.write("# User " + user + "\n")
648 647 p.write("# Date %d %d\n" % date)
649 648 p.write("\n")
650 649 elif user:
651 650 p.write("From: " + user + "\n")
652 651 p.write("\n")
653 652 if msg:
654 653 msg = msg + "\n"
655 654 p.write(msg)
656 655 p.close()
657 656 wlock = None
658 657 r = self.qrepo()
659 658 if r: r.add([patch])
660 659 if commitfiles:
661 660 self.refresh(repo, short=True, git=opts.get('git'))
662 661 self.removeundo(repo)
663 662 finally:
664 663 del wlock
665 664
666 665 def strip(self, repo, rev, update=True, backup="all", force=None):
667 666 wlock = lock = None
668 667 try:
669 668 wlock = repo.wlock()
670 669 lock = repo.lock()
671 670
672 671 if update:
673 672 self.check_localchanges(repo, force=force, refresh=False)
674 673 urev = self.qparents(repo, rev)
675 674 hg.clean(repo, urev)
676 675 repo.dirstate.write()
677 676
678 677 self.removeundo(repo)
679 678 repair.strip(self.ui, repo, rev, backup)
680 679 # strip may have unbundled a set of backed up revisions after
681 680 # the actual strip
682 681 self.removeundo(repo)
683 682 finally:
684 683 del lock, wlock
685 684
686 685 def isapplied(self, patch):
687 686 """returns (index, rev, patch)"""
688 687 for i in xrange(len(self.applied)):
689 688 a = self.applied[i]
690 689 if a.name == patch:
691 690 return (i, a.rev, a.name)
692 691 return None
693 692
694 693 # if the exact patch name does not exist, we try a few
695 694 # variations. If strict is passed, we try only #1
696 695 #
697 696 # 1) a number to indicate an offset in the series file
698 697 # 2) a unique substring of the patch name was given
699 698 # 3) patchname[-+]num to indicate an offset in the series file
700 699 def lookup(self, patch, strict=False):
701 700 patch = patch and str(patch)
702 701
703 702 def partial_name(s):
704 703 if s in self.series:
705 704 return s
706 705 matches = [x for x in self.series if s in x]
707 706 if len(matches) > 1:
708 707 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
709 708 for m in matches:
710 709 self.ui.warn(' %s\n' % m)
711 710 return None
712 711 if matches:
713 712 return matches[0]
714 713 if len(self.series) > 0 and len(self.applied) > 0:
715 714 if s == 'qtip':
716 715 return self.series[self.series_end(True)-1]
717 716 if s == 'qbase':
718 717 return self.series[0]
719 718 return None
720 719 if patch == None:
721 720 return None
722 721
723 722 # we don't want to return a partial match until we make
724 723 # sure the file name passed in does not exist (checked below)
725 724 res = partial_name(patch)
726 725 if res and res == patch:
727 726 return res
728 727
729 728 if not os.path.isfile(self.join(patch)):
730 729 try:
731 730 sno = int(patch)
732 731 except(ValueError, OverflowError):
733 732 pass
734 733 else:
735 734 if sno < len(self.series):
736 735 return self.series[sno]
737 736 if not strict:
738 737 # return any partial match made above
739 738 if res:
740 739 return res
741 740 minus = patch.rfind('-')
742 741 if minus >= 0:
743 742 res = partial_name(patch[:minus])
744 743 if res:
745 744 i = self.series.index(res)
746 745 try:
747 746 off = int(patch[minus+1:] or 1)
748 747 except(ValueError, OverflowError):
749 748 pass
750 749 else:
751 750 if i - off >= 0:
752 751 return self.series[i - off]
753 752 plus = patch.rfind('+')
754 753 if plus >= 0:
755 754 res = partial_name(patch[:plus])
756 755 if res:
757 756 i = self.series.index(res)
758 757 try:
759 758 off = int(patch[plus+1:] or 1)
760 759 except(ValueError, OverflowError):
761 760 pass
762 761 else:
763 762 if i + off < len(self.series):
764 763 return self.series[i + off]
765 764 raise util.Abort(_("patch %s not in series") % patch)
766 765
767 766 def push(self, repo, patch=None, force=False, list=False,
768 767 mergeq=None):
769 768 wlock = repo.wlock()
770 769 if repo.dirstate.parents()[0] != repo.changelog.tip():
771 770 self.ui.status(_("(working directory not at tip)\n"))
772 771
773 772 try:
774 773 patch = self.lookup(patch)
775 774 # Suppose our series file is: A B C and the current 'top'
776 775 # patch is B. qpush C should be performed (moving forward)
777 776 # qpush B is a NOP (no change) qpush A is an error (can't
778 777 # go backwards with qpush)
779 778 if patch:
780 779 info = self.isapplied(patch)
781 780 if info:
782 781 if info[0] < len(self.applied) - 1:
783 782 raise util.Abort(
784 783 _("cannot push to a previous patch: %s") % patch)
785 784 if info[0] < len(self.series) - 1:
786 785 self.ui.warn(
787 786 _('qpush: %s is already at the top\n') % patch)
788 787 else:
789 788 self.ui.warn(_('all patches are currently applied\n'))
790 789 return
791 790
792 791 # Following the above example, starting at 'top' of B:
793 792 # qpush should be performed (pushes C), but a subsequent
794 793 # qpush without an argument is an error (nothing to
795 794 # apply). This allows a loop of "...while hg qpush..." to
796 795 # work as it detects an error when done
797 796 if self.series_end() == len(self.series):
798 797 self.ui.warn(_('patch series already fully applied\n'))
799 798 return 1
800 799 if not force:
801 800 self.check_localchanges(repo)
802 801
803 802 self.applied_dirty = 1;
804 803 start = self.series_end()
805 804 if start > 0:
806 805 self.check_toppatch(repo)
807 806 if not patch:
808 807 patch = self.series[start]
809 808 end = start + 1
810 809 else:
811 810 end = self.series.index(patch, start) + 1
812 811 s = self.series[start:end]
813 812 all_files = {}
814 813 try:
815 814 if mergeq:
816 815 ret = self.mergepatch(repo, mergeq, s)
817 816 else:
818 817 ret = self.apply(repo, s, list, all_files=all_files)
819 818 except:
820 819 self.ui.warn(_('cleaning up working directory...'))
821 820 node = repo.dirstate.parents()[0]
822 821 hg.revert(repo, node, None)
823 822 unknown = repo.status()[4]
824 823 # only remove unknown files that we know we touched or
825 824 # created while patching
826 825 for f in unknown:
827 826 if f in all_files:
828 827 util.unlink(repo.wjoin(f))
829 828 self.ui.warn(_('done\n'))
830 829 raise
831 830 top = self.applied[-1].name
832 831 if ret[0]:
833 832 self.ui.write(
834 833 "Errors during apply, please fix and refresh %s\n" % top)
835 834 else:
836 835 self.ui.write("Now at: %s\n" % top)
837 836 return ret[0]
838 837 finally:
839 838 del wlock
840 839
841 840 def pop(self, repo, patch=None, force=False, update=True, all=False):
842 841 def getfile(f, rev, flags):
843 842 t = repo.file(f).read(rev)
844 843 repo.wwrite(f, t, flags)
845 844
846 845 wlock = repo.wlock()
847 846 try:
848 847 if patch:
849 848 # index, rev, patch
850 849 info = self.isapplied(patch)
851 850 if not info:
852 851 patch = self.lookup(patch)
853 852 info = self.isapplied(patch)
854 853 if not info:
855 854 raise util.Abort(_("patch %s is not applied") % patch)
856 855
857 856 if len(self.applied) == 0:
858 857 # Allow qpop -a to work repeatedly,
859 858 # but not qpop without an argument
860 859 self.ui.warn(_("no patches applied\n"))
861 860 return not all
862 861
863 862 if not update:
864 863 parents = repo.dirstate.parents()
865 864 rr = [ revlog.bin(x.rev) for x in self.applied ]
866 865 for p in parents:
867 866 if p in rr:
868 867 self.ui.warn("qpop: forcing dirstate update\n")
869 868 update = True
870 869
871 870 if not force and update:
872 871 self.check_localchanges(repo)
873 872
874 873 self.applied_dirty = 1;
875 874 end = len(self.applied)
876 875 if not patch:
877 876 if all:
878 877 popi = 0
879 878 else:
880 879 popi = len(self.applied) - 1
881 880 else:
882 881 popi = info[0] + 1
883 882 if popi >= end:
884 883 self.ui.warn("qpop: %s is already at the top\n" % patch)
885 884 return
886 885 info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name]
887 886
888 887 start = info[0]
889 888 rev = revlog.bin(info[1])
890 889
891 890 if update:
892 891 top = self.check_toppatch(repo)
893 892
894 893 if repo.changelog.heads(rev) != [revlog.bin(self.applied[-1].rev)]:
895 894 raise util.Abort("popping would remove a revision not "
896 895 "managed by this patch queue")
897 896
898 897 # we know there are no local changes, so we can make a simplified
899 898 # form of hg.update.
900 899 if update:
901 900 qp = self.qparents(repo, rev)
902 901 changes = repo.changelog.read(qp)
903 902 mmap = repo.manifest.read(changes[0])
904 903 m, a, r, d, u = repo.status(qp, top)[:5]
905 904 if d:
906 905 raise util.Abort("deletions found between repo revs")
907 906 for f in m:
908 907 getfile(f, mmap[f], mmap.flags(f))
909 908 for f in r:
910 909 getfile(f, mmap[f], mmap.flags(f))
911 910 for f in m + r:
912 911 repo.dirstate.normal(f)
913 912 for f in a:
914 913 try:
915 914 os.unlink(repo.wjoin(f))
916 915 except OSError, e:
917 916 if e.errno != errno.ENOENT:
918 917 raise
919 918 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
920 919 except: pass
921 920 repo.dirstate.forget(f)
922 921 repo.dirstate.setparents(qp, revlog.nullid)
923 922 del self.applied[start:end]
924 923 self.strip(repo, rev, update=False, backup='strip')
925 924 if len(self.applied):
926 925 self.ui.write("Now at: %s\n" % self.applied[-1].name)
927 926 else:
928 927 self.ui.write("Patch queue now empty\n")
929 928 finally:
930 929 del wlock
931 930
932 931 def diff(self, repo, pats, opts):
933 932 top = self.check_toppatch(repo)
934 933 if not top:
935 934 self.ui.write("No patches applied\n")
936 935 return
937 936 qp = self.qparents(repo, top)
938 937 if opts.get('git'):
939 938 self.diffopts().git = True
940 939 if opts.get('unified') is not None:
941 940 self.diffopts().context = opts['unified']
942 941 self.printdiff(repo, qp, files=pats, opts=opts)
943 942
944 943 def refresh(self, repo, pats=None, **opts):
945 944 if len(self.applied) == 0:
946 945 self.ui.write("No patches applied\n")
947 946 return 1
948 947 newdate = opts.get('date')
949 948 if newdate:
950 949 newdate = '%d %d' % util.parsedate(newdate)
951 950 wlock = repo.wlock()
952 951 try:
953 952 self.check_toppatch(repo)
954 953 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
955 954 top = revlog.bin(top)
956 955 if repo.changelog.heads(top) != [top]:
957 956 raise util.Abort("cannot refresh a revision with children")
958 957 cparents = repo.changelog.parents(top)
959 958 patchparent = self.qparents(repo, top)
960 959 message, comments, user, date, patchfound = self.readheaders(patchfn)
961 960
962 961 patchf = self.opener(patchfn, 'r+')
963 962
964 963 # if the patch was a git patch, refresh it as a git patch
965 964 for line in patchf:
966 965 if line.startswith('diff --git'):
967 966 self.diffopts().git = True
968 967 break
969 968
970 969 msg = opts.get('msg', '').rstrip()
971 970 if msg and comments:
972 971 # Remove existing message, keeping the rest of the comments
973 972 # fields.
974 973 # If comments contains 'subject: ', message will prepend
975 974 # the field and a blank line.
976 975 if message:
977 976 subj = 'subject: ' + message[0].lower()
978 977 for i in xrange(len(comments)):
979 978 if subj == comments[i].lower():
980 979 del comments[i]
981 980 message = message[2:]
982 981 break
983 982 ci = 0
984 983 for mi in xrange(len(message)):
985 984 while message[mi] != comments[ci]:
986 985 ci += 1
987 986 del comments[ci]
988 987
989 988 def setheaderfield(comments, prefixes, new):
990 989 # Update all references to a field in the patch header.
991 990 # If none found, add it email style.
992 991 res = False
993 992 for prefix in prefixes:
994 993 for i in xrange(len(comments)):
995 994 if comments[i].startswith(prefix):
996 995 comments[i] = prefix + new
997 996 res = True
998 997 break
999 998 return res
1000 999
1001 1000 newuser = opts.get('user')
1002 1001 if newuser:
1003 1002 if not setheaderfield(comments, ['From: ', '# User '], newuser):
1004 1003 try:
1005 1004 patchheaderat = comments.index('# HG changeset patch')
1006 1005 comments.insert(patchheaderat + 1,'# User ' + newuser)
1007 1006 except ValueError:
1008 1007 comments = ['From: ' + newuser, ''] + comments
1009 1008 user = newuser
1010 1009
1011 1010 if newdate:
1012 1011 if setheaderfield(comments, ['# Date '], newdate):
1013 1012 date = newdate
1014 1013
1015 1014 if msg:
1016 1015 comments.append(msg)
1017 1016
1018 1017 patchf.seek(0)
1019 1018 patchf.truncate()
1020 1019
1021 1020 if comments:
1022 1021 comments = "\n".join(comments) + '\n\n'
1023 1022 patchf.write(comments)
1024 1023
1025 1024 if opts.get('git'):
1026 1025 self.diffopts().git = True
1027 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1026 matchfn = cmdutil.match(repo, pats, opts)
1028 1027 tip = repo.changelog.tip()
1029 1028 if top == tip:
1030 1029 # if the top of our patch queue is also the tip, there is an
1031 1030 # optimization here. We update the dirstate in place and strip
1032 1031 # off the tip commit. Then just commit the current directory
1033 1032 # tree. We can also send repo.commit the list of files
1034 1033 # changed to speed up the diff
1035 1034 #
1036 1035 # in short mode, we only diff the files included in the
1037 1036 # patch already
1038 1037 #
1039 1038 # this should really read:
1040 1039 # mm, dd, aa, aa2, uu = repo.status(tip, patchparent)[:5]
1041 1040 # but we do it backwards to take advantage of manifest/chlog
1042 1041 # caching against the next repo.status call
1043 1042 #
1044 1043 mm, aa, dd, aa2, uu = repo.status(patchparent, tip)[:5]
1045 1044 changes = repo.changelog.read(tip)
1046 1045 man = repo.manifest.read(changes[0])
1047 1046 aaa = aa[:]
1048 1047 if opts.get('short'):
1049 1048 filelist = mm + aa + dd
1050 1049 match = dict.fromkeys(filelist).__contains__
1051 1050 else:
1052 1051 filelist = None
1053 1052 match = util.always
1054 1053 m, a, r, d, u = repo.status(files=filelist, match=match)[:5]
1055 1054
1056 1055 # we might end up with files that were added between
1057 1056 # tip and the dirstate parent, but then changed in the
1058 1057 # local dirstate. in this case, we want them to only
1059 1058 # show up in the added section
1060 1059 for x in m:
1061 1060 if x not in aa:
1062 1061 mm.append(x)
1063 1062 # we might end up with files added by the local dirstate that
1064 1063 # were deleted by the patch. In this case, they should only
1065 1064 # show up in the changed section.
1066 1065 for x in a:
1067 1066 if x in dd:
1068 1067 del dd[dd.index(x)]
1069 1068 mm.append(x)
1070 1069 else:
1071 1070 aa.append(x)
1072 1071 # make sure any files deleted in the local dirstate
1073 1072 # are not in the add or change column of the patch
1074 1073 forget = []
1075 1074 for x in d + r:
1076 1075 if x in aa:
1077 1076 del aa[aa.index(x)]
1078 1077 forget.append(x)
1079 1078 continue
1080 1079 elif x in mm:
1081 1080 del mm[mm.index(x)]
1082 1081 dd.append(x)
1083 1082
1084 1083 m = util.unique(mm)
1085 1084 r = util.unique(dd)
1086 1085 a = util.unique(aa)
1087 1086 c = [filter(matchfn, l) for l in (m, a, r, [], u)]
1088 1087 filelist = util.unique(c[0] + c[1] + c[2])
1089 1088 patch.diff(repo, patchparent, files=filelist, match=matchfn,
1090 1089 fp=patchf, changes=c, opts=self.diffopts())
1091 1090 patchf.close()
1092 1091
1093 1092 repo.dirstate.setparents(*cparents)
1094 1093 copies = {}
1095 1094 for dst in a:
1096 1095 src = repo.dirstate.copied(dst)
1097 1096 if src is not None:
1098 1097 copies.setdefault(src, []).append(dst)
1099 1098 repo.dirstate.add(dst)
1100 1099 # remember the copies between patchparent and tip
1101 1100 # this may be slow, so don't do it if we're not tracking copies
1102 1101 if self.diffopts().git:
1103 1102 for dst in aaa:
1104 1103 f = repo.file(dst)
1105 1104 src = f.renamed(man[dst])
1106 1105 if src:
1107 1106 copies[src[0]] = copies.get(dst, [])
1108 1107 if dst in a:
1109 1108 copies[src[0]].append(dst)
1110 1109 # we can't copy a file created by the patch itself
1111 1110 if dst in copies:
1112 1111 del copies[dst]
1113 1112 for src, dsts in copies.iteritems():
1114 1113 for dst in dsts:
1115 1114 repo.dirstate.copy(src, dst)
1116 1115 for f in r:
1117 1116 repo.dirstate.remove(f)
1118 1117 # if the patch excludes a modified file, mark that
1119 1118 # file with mtime=0 so status can see it.
1120 1119 mm = []
1121 1120 for i in xrange(len(m)-1, -1, -1):
1122 1121 if not matchfn(m[i]):
1123 1122 mm.append(m[i])
1124 1123 del m[i]
1125 1124 for f in m:
1126 1125 repo.dirstate.normal(f)
1127 1126 for f in mm:
1128 1127 repo.dirstate.normallookup(f)
1129 1128 for f in forget:
1130 1129 repo.dirstate.forget(f)
1131 1130
1132 1131 if not msg:
1133 1132 if not message:
1134 1133 message = "[mq]: %s\n" % patchfn
1135 1134 else:
1136 1135 message = "\n".join(message)
1137 1136 else:
1138 1137 message = msg
1139 1138
1140 1139 if not user:
1141 1140 user = changes[1]
1142 1141
1143 1142 self.applied.pop()
1144 1143 self.applied_dirty = 1
1145 1144 self.strip(repo, top, update=False,
1146 1145 backup='strip')
1147 1146 n = repo.commit(filelist, message, user, date, match=matchfn,
1148 1147 force=1)
1149 1148 self.applied.append(statusentry(revlog.hex(n), patchfn))
1150 1149 self.removeundo(repo)
1151 1150 else:
1152 1151 self.printdiff(repo, patchparent, fp=patchf)
1153 1152 patchf.close()
1154 1153 added = repo.status()[1]
1155 1154 for a in added:
1156 1155 f = repo.wjoin(a)
1157 1156 try:
1158 1157 os.unlink(f)
1159 1158 except OSError, e:
1160 1159 if e.errno != errno.ENOENT:
1161 1160 raise
1162 1161 try: os.removedirs(os.path.dirname(f))
1163 1162 except: pass
1164 1163 # forget the file copies in the dirstate
1165 1164 # push should readd the files later on
1166 1165 repo.dirstate.forget(a)
1167 1166 self.pop(repo, force=True)
1168 1167 self.push(repo, force=True)
1169 1168 finally:
1170 1169 del wlock
1171 1170
1172 1171 def init(self, repo, create=False):
1173 1172 if not create and os.path.isdir(self.path):
1174 1173 raise util.Abort(_("patch queue directory already exists"))
1175 1174 try:
1176 1175 os.mkdir(self.path)
1177 1176 except OSError, inst:
1178 1177 if inst.errno != errno.EEXIST or not create:
1179 1178 raise
1180 1179 if create:
1181 1180 return self.qrepo(create=True)
1182 1181
1183 1182 def unapplied(self, repo, patch=None):
1184 1183 if patch and patch not in self.series:
1185 1184 raise util.Abort(_("patch %s is not in series file") % patch)
1186 1185 if not patch:
1187 1186 start = self.series_end()
1188 1187 else:
1189 1188 start = self.series.index(patch) + 1
1190 1189 unapplied = []
1191 1190 for i in xrange(start, len(self.series)):
1192 1191 pushable, reason = self.pushable(i)
1193 1192 if pushable:
1194 1193 unapplied.append((i, self.series[i]))
1195 1194 self.explain_pushable(i)
1196 1195 return unapplied
1197 1196
1198 1197 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1199 1198 summary=False):
1200 1199 def displayname(patchname):
1201 1200 if summary:
1202 1201 msg = self.readheaders(patchname)[0]
1203 1202 msg = msg and ': ' + msg[0] or ': '
1204 1203 else:
1205 1204 msg = ''
1206 1205 return '%s%s' % (patchname, msg)
1207 1206
1208 1207 applied = dict.fromkeys([p.name for p in self.applied])
1209 1208 if length is None:
1210 1209 length = len(self.series) - start
1211 1210 if not missing:
1212 1211 for i in xrange(start, start+length):
1213 1212 patch = self.series[i]
1214 1213 if patch in applied:
1215 1214 stat = 'A'
1216 1215 elif self.pushable(i)[0]:
1217 1216 stat = 'U'
1218 1217 else:
1219 1218 stat = 'G'
1220 1219 pfx = ''
1221 1220 if self.ui.verbose:
1222 1221 pfx = '%d %s ' % (i, stat)
1223 1222 elif status and status != stat:
1224 1223 continue
1225 1224 self.ui.write('%s%s\n' % (pfx, displayname(patch)))
1226 1225 else:
1227 1226 msng_list = []
1228 1227 for root, dirs, files in os.walk(self.path):
1229 1228 d = root[len(self.path) + 1:]
1230 1229 for f in files:
1231 1230 fl = os.path.join(d, f)
1232 1231 if (fl not in self.series and
1233 1232 fl not in (self.status_path, self.series_path,
1234 1233 self.guards_path)
1235 1234 and not fl.startswith('.')):
1236 1235 msng_list.append(fl)
1237 1236 msng_list.sort()
1238 1237 for x in msng_list:
1239 1238 pfx = self.ui.verbose and ('D ') or ''
1240 1239 self.ui.write("%s%s\n" % (pfx, displayname(x)))
1241 1240
1242 1241 def issaveline(self, l):
1243 1242 if l.name == '.hg.patches.save.line':
1244 1243 return True
1245 1244
1246 1245 def qrepo(self, create=False):
1247 1246 if create or os.path.isdir(self.join(".hg")):
1248 1247 return hg.repository(self.ui, path=self.path, create=create)
1249 1248
1250 1249 def restore(self, repo, rev, delete=None, qupdate=None):
1251 1250 c = repo.changelog.read(rev)
1252 1251 desc = c[4].strip()
1253 1252 lines = desc.splitlines()
1254 1253 i = 0
1255 1254 datastart = None
1256 1255 series = []
1257 1256 applied = []
1258 1257 qpp = None
1259 1258 for i in xrange(0, len(lines)):
1260 1259 if lines[i] == 'Patch Data:':
1261 1260 datastart = i + 1
1262 1261 elif lines[i].startswith('Dirstate:'):
1263 1262 l = lines[i].rstrip()
1264 1263 l = l[10:].split(' ')
1265 1264 qpp = [ bin(x) for x in l ]
1266 1265 elif datastart != None:
1267 1266 l = lines[i].rstrip()
1268 1267 se = statusentry(l)
1269 1268 file_ = se.name
1270 1269 if se.rev:
1271 1270 applied.append(se)
1272 1271 else:
1273 1272 series.append(file_)
1274 1273 if datastart == None:
1275 1274 self.ui.warn("No saved patch data found\n")
1276 1275 return 1
1277 1276 self.ui.warn("restoring status: %s\n" % lines[0])
1278 1277 self.full_series = series
1279 1278 self.applied = applied
1280 1279 self.parse_series()
1281 1280 self.series_dirty = 1
1282 1281 self.applied_dirty = 1
1283 1282 heads = repo.changelog.heads()
1284 1283 if delete:
1285 1284 if rev not in heads:
1286 1285 self.ui.warn("save entry has children, leaving it alone\n")
1287 1286 else:
1288 1287 self.ui.warn("removing save entry %s\n" % short(rev))
1289 1288 pp = repo.dirstate.parents()
1290 1289 if rev in pp:
1291 1290 update = True
1292 1291 else:
1293 1292 update = False
1294 1293 self.strip(repo, rev, update=update, backup='strip')
1295 1294 if qpp:
1296 1295 self.ui.warn("saved queue repository parents: %s %s\n" %
1297 1296 (short(qpp[0]), short(qpp[1])))
1298 1297 if qupdate:
1299 1298 self.ui.status(_("queue directory updating\n"))
1300 1299 r = self.qrepo()
1301 1300 if not r:
1302 1301 self.ui.warn("Unable to load queue repository\n")
1303 1302 return 1
1304 1303 hg.clean(r, qpp[0])
1305 1304
1306 1305 def save(self, repo, msg=None):
1307 1306 if len(self.applied) == 0:
1308 1307 self.ui.warn("save: no patches applied, exiting\n")
1309 1308 return 1
1310 1309 if self.issaveline(self.applied[-1]):
1311 1310 self.ui.warn("status is already saved\n")
1312 1311 return 1
1313 1312
1314 1313 ar = [ ':' + x for x in self.full_series ]
1315 1314 if not msg:
1316 1315 msg = "hg patches saved state"
1317 1316 else:
1318 1317 msg = "hg patches: " + msg.rstrip('\r\n')
1319 1318 r = self.qrepo()
1320 1319 if r:
1321 1320 pp = r.dirstate.parents()
1322 1321 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1323 1322 msg += "\n\nPatch Data:\n"
1324 1323 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1325 1324 "\n".join(ar) + '\n' or "")
1326 1325 n = repo.commit(None, text, user=None, force=1)
1327 1326 if not n:
1328 1327 self.ui.warn("repo commit failed\n")
1329 1328 return 1
1330 1329 self.applied.append(statusentry(revlog.hex(n),'.hg.patches.save.line'))
1331 1330 self.applied_dirty = 1
1332 1331 self.removeundo(repo)
1333 1332
1334 1333 def full_series_end(self):
1335 1334 if len(self.applied) > 0:
1336 1335 p = self.applied[-1].name
1337 1336 end = self.find_series(p)
1338 1337 if end == None:
1339 1338 return len(self.full_series)
1340 1339 return end + 1
1341 1340 return 0
1342 1341
1343 1342 def series_end(self, all_patches=False):
1344 1343 """If all_patches is False, return the index of the next pushable patch
1345 1344 in the series, or the series length. If all_patches is True, return the
1346 1345 index of the first patch past the last applied one.
1347 1346 """
1348 1347 end = 0
1349 1348 def next(start):
1350 1349 if all_patches:
1351 1350 return start
1352 1351 i = start
1353 1352 while i < len(self.series):
1354 1353 p, reason = self.pushable(i)
1355 1354 if p:
1356 1355 break
1357 1356 self.explain_pushable(i)
1358 1357 i += 1
1359 1358 return i
1360 1359 if len(self.applied) > 0:
1361 1360 p = self.applied[-1].name
1362 1361 try:
1363 1362 end = self.series.index(p)
1364 1363 except ValueError:
1365 1364 return 0
1366 1365 return next(end + 1)
1367 1366 return next(end)
1368 1367
1369 1368 def appliedname(self, index):
1370 1369 pname = self.applied[index].name
1371 1370 if not self.ui.verbose:
1372 1371 p = pname
1373 1372 else:
1374 1373 p = str(self.series.index(pname)) + " " + pname
1375 1374 return p
1376 1375
1377 1376 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1378 1377 force=None, git=False):
1379 1378 def checkseries(patchname):
1380 1379 if patchname in self.series:
1381 1380 raise util.Abort(_('patch %s is already in the series file')
1382 1381 % patchname)
1383 1382 def checkfile(patchname):
1384 1383 if not force and os.path.exists(self.join(patchname)):
1385 1384 raise util.Abort(_('patch "%s" already exists')
1386 1385 % patchname)
1387 1386
1388 1387 if rev:
1389 1388 if files:
1390 1389 raise util.Abort(_('option "-r" not valid when importing '
1391 1390 'files'))
1392 1391 rev = cmdutil.revrange(repo, rev)
1393 1392 rev.sort(lambda x, y: cmp(y, x))
1394 1393 if (len(files) > 1 or len(rev) > 1) and patchname:
1395 1394 raise util.Abort(_('option "-n" not valid when importing multiple '
1396 1395 'patches'))
1397 1396 i = 0
1398 1397 added = []
1399 1398 if rev:
1400 1399 # If mq patches are applied, we can only import revisions
1401 1400 # that form a linear path to qbase.
1402 1401 # Otherwise, they should form a linear path to a head.
1403 1402 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1404 1403 if len(heads) > 1:
1405 1404 raise util.Abort(_('revision %d is the root of more than one '
1406 1405 'branch') % rev[-1])
1407 1406 if self.applied:
1408 1407 base = revlog.hex(repo.changelog.node(rev[0]))
1409 1408 if base in [n.rev for n in self.applied]:
1410 1409 raise util.Abort(_('revision %d is already managed')
1411 1410 % rev[0])
1412 1411 if heads != [revlog.bin(self.applied[-1].rev)]:
1413 1412 raise util.Abort(_('revision %d is not the parent of '
1414 1413 'the queue') % rev[0])
1415 1414 base = repo.changelog.rev(revlog.bin(self.applied[0].rev))
1416 1415 lastparent = repo.changelog.parentrevs(base)[0]
1417 1416 else:
1418 1417 if heads != [repo.changelog.node(rev[0])]:
1419 1418 raise util.Abort(_('revision %d has unmanaged children')
1420 1419 % rev[0])
1421 1420 lastparent = None
1422 1421
1423 1422 if git:
1424 1423 self.diffopts().git = True
1425 1424
1426 1425 for r in rev:
1427 1426 p1, p2 = repo.changelog.parentrevs(r)
1428 1427 n = repo.changelog.node(r)
1429 1428 if p2 != revlog.nullrev:
1430 1429 raise util.Abort(_('cannot import merge revision %d') % r)
1431 1430 if lastparent and lastparent != r:
1432 1431 raise util.Abort(_('revision %d is not the parent of %d')
1433 1432 % (r, lastparent))
1434 1433 lastparent = p1
1435 1434
1436 1435 if not patchname:
1437 1436 patchname = normname('%d.diff' % r)
1438 1437 self.check_reserved_name(patchname)
1439 1438 checkseries(patchname)
1440 1439 checkfile(patchname)
1441 1440 self.full_series.insert(0, patchname)
1442 1441
1443 1442 patchf = self.opener(patchname, "w")
1444 1443 patch.export(repo, [n], fp=patchf, opts=self.diffopts())
1445 1444 patchf.close()
1446 1445
1447 1446 se = statusentry(revlog.hex(n), patchname)
1448 1447 self.applied.insert(0, se)
1449 1448
1450 1449 added.append(patchname)
1451 1450 patchname = None
1452 1451 self.parse_series()
1453 1452 self.applied_dirty = 1
1454 1453
1455 1454 for filename in files:
1456 1455 if existing:
1457 1456 if filename == '-':
1458 1457 raise util.Abort(_('-e is incompatible with import from -'))
1459 1458 if not patchname:
1460 1459 patchname = normname(filename)
1461 1460 self.check_reserved_name(patchname)
1462 1461 if not os.path.isfile(self.join(patchname)):
1463 1462 raise util.Abort(_("patch %s does not exist") % patchname)
1464 1463 else:
1465 1464 try:
1466 1465 if filename == '-':
1467 1466 if not patchname:
1468 1467 raise util.Abort(_('need --name to import a patch from -'))
1469 1468 text = sys.stdin.read()
1470 1469 else:
1471 1470 text = file(filename, 'rb').read()
1472 1471 except IOError:
1473 1472 raise util.Abort(_("unable to read %s") % patchname)
1474 1473 if not patchname:
1475 1474 patchname = normname(os.path.basename(filename))
1476 1475 self.check_reserved_name(patchname)
1477 1476 checkfile(patchname)
1478 1477 patchf = self.opener(patchname, "w")
1479 1478 patchf.write(text)
1480 1479 checkseries(patchname)
1481 1480 index = self.full_series_end() + i
1482 1481 self.full_series[index:index] = [patchname]
1483 1482 self.parse_series()
1484 1483 self.ui.warn("adding %s to series file\n" % patchname)
1485 1484 i += 1
1486 1485 added.append(patchname)
1487 1486 patchname = None
1488 1487 self.series_dirty = 1
1489 1488 qrepo = self.qrepo()
1490 1489 if qrepo:
1491 1490 qrepo.add(added)
1492 1491
1493 1492 def delete(ui, repo, *patches, **opts):
1494 1493 """remove patches from queue
1495 1494
1496 1495 The patches must not be applied, unless they are arguments to
1497 1496 the --rev parameter. At least one patch or revision is required.
1498 1497
1499 1498 With --rev, mq will stop managing the named revisions (converting
1500 1499 them to regular mercurial changesets). The patches must be applied
1501 1500 and at the base of the stack. This option is useful when the patches
1502 1501 have been applied upstream.
1503 1502
1504 1503 With --keep, the patch files are preserved in the patch directory."""
1505 1504 q = repo.mq
1506 1505 q.delete(repo, patches, opts)
1507 1506 q.save_dirty()
1508 1507 return 0
1509 1508
1510 1509 def applied(ui, repo, patch=None, **opts):
1511 1510 """print the patches already applied"""
1512 1511 q = repo.mq
1513 1512 if patch:
1514 1513 if patch not in q.series:
1515 1514 raise util.Abort(_("patch %s is not in series file") % patch)
1516 1515 end = q.series.index(patch) + 1
1517 1516 else:
1518 1517 end = q.series_end(True)
1519 1518 return q.qseries(repo, length=end, status='A', summary=opts.get('summary'))
1520 1519
1521 1520 def unapplied(ui, repo, patch=None, **opts):
1522 1521 """print the patches not yet applied"""
1523 1522 q = repo.mq
1524 1523 if patch:
1525 1524 if patch not in q.series:
1526 1525 raise util.Abort(_("patch %s is not in series file") % patch)
1527 1526 start = q.series.index(patch) + 1
1528 1527 else:
1529 1528 start = q.series_end(True)
1530 1529 q.qseries(repo, start=start, status='U', summary=opts.get('summary'))
1531 1530
1532 1531 def qimport(ui, repo, *filename, **opts):
1533 1532 """import a patch
1534 1533
1535 1534 The patch will have the same name as its source file unless you
1536 1535 give it a new one with --name.
1537 1536
1538 1537 You can register an existing patch inside the patch directory
1539 1538 with the --existing flag.
1540 1539
1541 1540 With --force, an existing patch of the same name will be overwritten.
1542 1541
1543 1542 An existing changeset may be placed under mq control with --rev
1544 1543 (e.g. qimport --rev tip -n patch will place tip under mq control).
1545 1544 With --git, patches imported with --rev will use the git diff
1546 1545 format.
1547 1546 """
1548 1547 q = repo.mq
1549 1548 q.qimport(repo, filename, patchname=opts['name'],
1550 1549 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1551 1550 git=opts['git'])
1552 1551 q.save_dirty()
1553 1552 return 0
1554 1553
1555 1554 def init(ui, repo, **opts):
1556 1555 """init a new queue repository
1557 1556
1558 1557 The queue repository is unversioned by default. If -c is
1559 1558 specified, qinit will create a separate nested repository
1560 1559 for patches (qinit -c may also be run later to convert
1561 1560 an unversioned patch repository into a versioned one).
1562 1561 You can use qcommit to commit changes to this queue repository."""
1563 1562 q = repo.mq
1564 1563 r = q.init(repo, create=opts['create_repo'])
1565 1564 q.save_dirty()
1566 1565 if r:
1567 1566 if not os.path.exists(r.wjoin('.hgignore')):
1568 1567 fp = r.wopener('.hgignore', 'w')
1569 1568 fp.write('^\\.hg\n')
1570 1569 fp.write('^\\.mq\n')
1571 1570 fp.write('syntax: glob\n')
1572 1571 fp.write('status\n')
1573 1572 fp.write('guards\n')
1574 1573 fp.close()
1575 1574 if not os.path.exists(r.wjoin('series')):
1576 1575 r.wopener('series', 'w').close()
1577 1576 r.add(['.hgignore', 'series'])
1578 1577 commands.add(ui, r)
1579 1578 return 0
1580 1579
1581 1580 def clone(ui, source, dest=None, **opts):
1582 1581 '''clone main and patch repository at same time
1583 1582
1584 1583 If source is local, destination will have no patches applied. If
1585 1584 source is remote, this command can not check if patches are
1586 1585 applied in source, so cannot guarantee that patches are not
1587 1586 applied in destination. If you clone remote repository, be sure
1588 1587 before that it has no patches applied.
1589 1588
1590 1589 Source patch repository is looked for in <src>/.hg/patches by
1591 1590 default. Use -p <url> to change.
1592 1591
1593 1592 The patch directory must be a nested mercurial repository, as
1594 1593 would be created by qinit -c.
1595 1594 '''
1596 1595 def patchdir(repo):
1597 1596 url = repo.url()
1598 1597 if url.endswith('/'):
1599 1598 url = url[:-1]
1600 1599 return url + '/.hg/patches'
1601 1600 cmdutil.setremoteconfig(ui, opts)
1602 1601 if dest is None:
1603 1602 dest = hg.defaultdest(source)
1604 1603 sr = hg.repository(ui, ui.expandpath(source))
1605 1604 patchespath = opts['patches'] or patchdir(sr)
1606 1605 try:
1607 1606 pr = hg.repository(ui, patchespath)
1608 1607 except RepoError:
1609 1608 raise util.Abort(_('versioned patch repository not found'
1610 1609 ' (see qinit -c)'))
1611 1610 qbase, destrev = None, None
1612 1611 if sr.local():
1613 1612 if sr.mq.applied:
1614 1613 qbase = revlog.bin(sr.mq.applied[0].rev)
1615 1614 if not hg.islocal(dest):
1616 1615 heads = dict.fromkeys(sr.heads())
1617 1616 for h in sr.heads(qbase):
1618 1617 del heads[h]
1619 1618 destrev = heads.keys()
1620 1619 destrev.append(sr.changelog.parents(qbase)[0])
1621 1620 elif sr.capable('lookup'):
1622 1621 try:
1623 1622 qbase = sr.lookup('qbase')
1624 1623 except RepoError:
1625 1624 pass
1626 1625 ui.note(_('cloning main repo\n'))
1627 1626 sr, dr = hg.clone(ui, sr.url(), dest,
1628 1627 pull=opts['pull'],
1629 1628 rev=destrev,
1630 1629 update=False,
1631 1630 stream=opts['uncompressed'])
1632 1631 ui.note(_('cloning patch repo\n'))
1633 1632 spr, dpr = hg.clone(ui, opts['patches'] or patchdir(sr), patchdir(dr),
1634 1633 pull=opts['pull'], update=not opts['noupdate'],
1635 1634 stream=opts['uncompressed'])
1636 1635 if dr.local():
1637 1636 if qbase:
1638 1637 ui.note(_('stripping applied patches from destination repo\n'))
1639 1638 dr.mq.strip(dr, qbase, update=False, backup=None)
1640 1639 if not opts['noupdate']:
1641 1640 ui.note(_('updating destination repo\n'))
1642 1641 hg.update(dr, dr.changelog.tip())
1643 1642
1644 1643 def commit(ui, repo, *pats, **opts):
1645 1644 """commit changes in the queue repository"""
1646 1645 q = repo.mq
1647 1646 r = q.qrepo()
1648 1647 if not r: raise util.Abort('no queue repository')
1649 1648 commands.commit(r.ui, r, *pats, **opts)
1650 1649
1651 1650 def series(ui, repo, **opts):
1652 1651 """print the entire series file"""
1653 1652 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1654 1653 return 0
1655 1654
1656 1655 def top(ui, repo, **opts):
1657 1656 """print the name of the current patch"""
1658 1657 q = repo.mq
1659 1658 t = q.applied and q.series_end(True) or 0
1660 1659 if t:
1661 1660 return q.qseries(repo, start=t-1, length=1, status='A',
1662 1661 summary=opts.get('summary'))
1663 1662 else:
1664 1663 ui.write("No patches applied\n")
1665 1664 return 1
1666 1665
1667 1666 def next(ui, repo, **opts):
1668 1667 """print the name of the next patch"""
1669 1668 q = repo.mq
1670 1669 end = q.series_end()
1671 1670 if end == len(q.series):
1672 1671 ui.write("All patches applied\n")
1673 1672 return 1
1674 1673 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1675 1674
1676 1675 def prev(ui, repo, **opts):
1677 1676 """print the name of the previous patch"""
1678 1677 q = repo.mq
1679 1678 l = len(q.applied)
1680 1679 if l == 1:
1681 1680 ui.write("Only one patch applied\n")
1682 1681 return 1
1683 1682 if not l:
1684 1683 ui.write("No patches applied\n")
1685 1684 return 1
1686 1685 return q.qseries(repo, start=l-2, length=1, status='A',
1687 1686 summary=opts.get('summary'))
1688 1687
1689 1688 def setupheaderopts(ui, opts):
1690 1689 def do(opt,val):
1691 1690 if not opts[opt] and opts['current' + opt]:
1692 1691 opts[opt] = val
1693 1692 do('user', ui.username())
1694 1693 do('date', "%d %d" % util.makedate())
1695 1694
1696 1695 def new(ui, repo, patch, *args, **opts):
1697 1696 """create a new patch
1698 1697
1699 1698 qnew creates a new patch on top of the currently-applied patch
1700 1699 (if any). It will refuse to run if there are any outstanding
1701 1700 changes unless -f is specified, in which case the patch will
1702 1701 be initialised with them. You may also use -I, -X, and/or a list of
1703 1702 files after the patch name to add only changes to matching files
1704 1703 to the new patch, leaving the rest as uncommitted modifications.
1705 1704
1706 1705 -e, -m or -l set the patch header as well as the commit message.
1707 1706 If none is specified, the patch header is empty and the
1708 1707 commit message is '[mq]: PATCH'"""
1709 1708 q = repo.mq
1710 1709 message = cmdutil.logmessage(opts)
1711 1710 if opts['edit']:
1712 1711 message = ui.edit(message, ui.username())
1713 1712 opts['msg'] = message
1714 1713 setupheaderopts(ui, opts)
1715 1714 q.new(repo, patch, *args, **opts)
1716 1715 q.save_dirty()
1717 1716 return 0
1718 1717
1719 1718 def refresh(ui, repo, *pats, **opts):
1720 1719 """update the current patch
1721 1720
1722 1721 If any file patterns are provided, the refreshed patch will contain only
1723 1722 the modifications that match those patterns; the remaining modifications
1724 1723 will remain in the working directory.
1725 1724
1726 1725 hg add/remove/copy/rename work as usual, though you might want to use
1727 1726 git-style patches (--git or [diff] git=1) to track copies and renames.
1728 1727 """
1729 1728 q = repo.mq
1730 1729 message = cmdutil.logmessage(opts)
1731 1730 if opts['edit']:
1732 1731 if not q.applied:
1733 1732 ui.write(_("No patches applied\n"))
1734 1733 return 1
1735 1734 if message:
1736 1735 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1737 1736 patch = q.applied[-1].name
1738 1737 (message, comment, user, date, hasdiff) = q.readheaders(patch)
1739 1738 message = ui.edit('\n'.join(message), user or ui.username())
1740 1739 setupheaderopts(ui, opts)
1741 1740 ret = q.refresh(repo, pats, msg=message, **opts)
1742 1741 q.save_dirty()
1743 1742 return ret
1744 1743
1745 1744 def diff(ui, repo, *pats, **opts):
1746 1745 """diff of the current patch"""
1747 1746 repo.mq.diff(repo, pats, opts)
1748 1747 return 0
1749 1748
1750 1749 def fold(ui, repo, *files, **opts):
1751 1750 """fold the named patches into the current patch
1752 1751
1753 1752 Patches must not yet be applied. Each patch will be successively
1754 1753 applied to the current patch in the order given. If all the
1755 1754 patches apply successfully, the current patch will be refreshed
1756 1755 with the new cumulative patch, and the folded patches will
1757 1756 be deleted. With -k/--keep, the folded patch files will not
1758 1757 be removed afterwards.
1759 1758
1760 1759 The header for each folded patch will be concatenated with
1761 1760 the current patch header, separated by a line of '* * *'."""
1762 1761
1763 1762 q = repo.mq
1764 1763
1765 1764 if not files:
1766 1765 raise util.Abort(_('qfold requires at least one patch name'))
1767 1766 if not q.check_toppatch(repo):
1768 1767 raise util.Abort(_('No patches applied'))
1769 1768
1770 1769 message = cmdutil.logmessage(opts)
1771 1770 if opts['edit']:
1772 1771 if message:
1773 1772 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1774 1773
1775 1774 parent = q.lookup('qtip')
1776 1775 patches = []
1777 1776 messages = []
1778 1777 for f in files:
1779 1778 p = q.lookup(f)
1780 1779 if p in patches or p == parent:
1781 1780 ui.warn(_('Skipping already folded patch %s') % p)
1782 1781 if q.isapplied(p):
1783 1782 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
1784 1783 patches.append(p)
1785 1784
1786 1785 for p in patches:
1787 1786 if not message:
1788 1787 messages.append(q.readheaders(p)[0])
1789 1788 pf = q.join(p)
1790 1789 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1791 1790 if not patchsuccess:
1792 1791 raise util.Abort(_('Error folding patch %s') % p)
1793 1792 patch.updatedir(ui, repo, files)
1794 1793
1795 1794 if not message:
1796 1795 message, comments, user = q.readheaders(parent)[0:3]
1797 1796 for msg in messages:
1798 1797 message.append('* * *')
1799 1798 message.extend(msg)
1800 1799 message = '\n'.join(message)
1801 1800
1802 1801 if opts['edit']:
1803 1802 message = ui.edit(message, user or ui.username())
1804 1803
1805 1804 q.refresh(repo, msg=message)
1806 1805 q.delete(repo, patches, opts)
1807 1806 q.save_dirty()
1808 1807
1809 1808 def goto(ui, repo, patch, **opts):
1810 1809 '''push or pop patches until named patch is at top of stack'''
1811 1810 q = repo.mq
1812 1811 patch = q.lookup(patch)
1813 1812 if q.isapplied(patch):
1814 1813 ret = q.pop(repo, patch, force=opts['force'])
1815 1814 else:
1816 1815 ret = q.push(repo, patch, force=opts['force'])
1817 1816 q.save_dirty()
1818 1817 return ret
1819 1818
1820 1819 def guard(ui, repo, *args, **opts):
1821 1820 '''set or print guards for a patch
1822 1821
1823 1822 Guards control whether a patch can be pushed. A patch with no
1824 1823 guards is always pushed. A patch with a positive guard ("+foo") is
1825 1824 pushed only if the qselect command has activated it. A patch with
1826 1825 a negative guard ("-foo") is never pushed if the qselect command
1827 1826 has activated it.
1828 1827
1829 1828 With no arguments, print the currently active guards.
1830 1829 With arguments, set guards for the named patch.
1831 1830
1832 1831 To set a negative guard "-foo" on topmost patch ("--" is needed so
1833 1832 hg will not interpret "-foo" as an option):
1834 1833 hg qguard -- -foo
1835 1834
1836 1835 To set guards on another patch:
1837 1836 hg qguard other.patch +2.6.17 -stable
1838 1837 '''
1839 1838 def status(idx):
1840 1839 guards = q.series_guards[idx] or ['unguarded']
1841 1840 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
1842 1841 q = repo.mq
1843 1842 patch = None
1844 1843 args = list(args)
1845 1844 if opts['list']:
1846 1845 if args or opts['none']:
1847 1846 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
1848 1847 for i in xrange(len(q.series)):
1849 1848 status(i)
1850 1849 return
1851 1850 if not args or args[0][0:1] in '-+':
1852 1851 if not q.applied:
1853 1852 raise util.Abort(_('no patches applied'))
1854 1853 patch = q.applied[-1].name
1855 1854 if patch is None and args[0][0:1] not in '-+':
1856 1855 patch = args.pop(0)
1857 1856 if patch is None:
1858 1857 raise util.Abort(_('no patch to work with'))
1859 1858 if args or opts['none']:
1860 1859 idx = q.find_series(patch)
1861 1860 if idx is None:
1862 1861 raise util.Abort(_('no patch named %s') % patch)
1863 1862 q.set_guards(idx, args)
1864 1863 q.save_dirty()
1865 1864 else:
1866 1865 status(q.series.index(q.lookup(patch)))
1867 1866
1868 1867 def header(ui, repo, patch=None):
1869 1868 """Print the header of the topmost or specified patch"""
1870 1869 q = repo.mq
1871 1870
1872 1871 if patch:
1873 1872 patch = q.lookup(patch)
1874 1873 else:
1875 1874 if not q.applied:
1876 1875 ui.write('No patches applied\n')
1877 1876 return 1
1878 1877 patch = q.lookup('qtip')
1879 1878 message = repo.mq.readheaders(patch)[0]
1880 1879
1881 1880 ui.write('\n'.join(message) + '\n')
1882 1881
1883 1882 def lastsavename(path):
1884 1883 (directory, base) = os.path.split(path)
1885 1884 names = os.listdir(directory)
1886 1885 namere = re.compile("%s.([0-9]+)" % base)
1887 1886 maxindex = None
1888 1887 maxname = None
1889 1888 for f in names:
1890 1889 m = namere.match(f)
1891 1890 if m:
1892 1891 index = int(m.group(1))
1893 1892 if maxindex == None or index > maxindex:
1894 1893 maxindex = index
1895 1894 maxname = f
1896 1895 if maxname:
1897 1896 return (os.path.join(directory, maxname), maxindex)
1898 1897 return (None, None)
1899 1898
1900 1899 def savename(path):
1901 1900 (last, index) = lastsavename(path)
1902 1901 if last is None:
1903 1902 index = 0
1904 1903 newpath = path + ".%d" % (index + 1)
1905 1904 return newpath
1906 1905
1907 1906 def push(ui, repo, patch=None, **opts):
1908 1907 """push the next patch onto the stack
1909 1908
1910 1909 When --force is applied, all local changes in patched files will be lost.
1911 1910 """
1912 1911 q = repo.mq
1913 1912 mergeq = None
1914 1913
1915 1914 if opts['all']:
1916 1915 if not q.series:
1917 1916 ui.warn(_('no patches in series\n'))
1918 1917 return 0
1919 1918 patch = q.series[-1]
1920 1919 if opts['merge']:
1921 1920 if opts['name']:
1922 1921 newpath = opts['name']
1923 1922 else:
1924 1923 newpath, i = lastsavename(q.path)
1925 1924 if not newpath:
1926 1925 ui.warn("no saved queues found, please use -n\n")
1927 1926 return 1
1928 1927 mergeq = queue(ui, repo.join(""), newpath)
1929 1928 ui.warn("merging with queue at: %s\n" % mergeq.path)
1930 1929 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1931 1930 mergeq=mergeq)
1932 1931 return ret
1933 1932
1934 1933 def pop(ui, repo, patch=None, **opts):
1935 1934 """pop the current patch off the stack"""
1936 1935 localupdate = True
1937 1936 if opts['name']:
1938 1937 q = queue(ui, repo.join(""), repo.join(opts['name']))
1939 1938 ui.warn('using patch queue: %s\n' % q.path)
1940 1939 localupdate = False
1941 1940 else:
1942 1941 q = repo.mq
1943 1942 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
1944 1943 all=opts['all'])
1945 1944 q.save_dirty()
1946 1945 return ret
1947 1946
1948 1947 def rename(ui, repo, patch, name=None, **opts):
1949 1948 """rename a patch
1950 1949
1951 1950 With one argument, renames the current patch to PATCH1.
1952 1951 With two arguments, renames PATCH1 to PATCH2."""
1953 1952
1954 1953 q = repo.mq
1955 1954
1956 1955 if not name:
1957 1956 name = patch
1958 1957 patch = None
1959 1958
1960 1959 if patch:
1961 1960 patch = q.lookup(patch)
1962 1961 else:
1963 1962 if not q.applied:
1964 1963 ui.write(_('No patches applied\n'))
1965 1964 return
1966 1965 patch = q.lookup('qtip')
1967 1966 absdest = q.join(name)
1968 1967 if os.path.isdir(absdest):
1969 1968 name = normname(os.path.join(name, os.path.basename(patch)))
1970 1969 absdest = q.join(name)
1971 1970 if os.path.exists(absdest):
1972 1971 raise util.Abort(_('%s already exists') % absdest)
1973 1972
1974 1973 if name in q.series:
1975 1974 raise util.Abort(_('A patch named %s already exists in the series file') % name)
1976 1975
1977 1976 if ui.verbose:
1978 1977 ui.write('Renaming %s to %s\n' % (patch, name))
1979 1978 i = q.find_series(patch)
1980 1979 guards = q.guard_re.findall(q.full_series[i])
1981 1980 q.full_series[i] = name + ''.join([' #' + g for g in guards])
1982 1981 q.parse_series()
1983 1982 q.series_dirty = 1
1984 1983
1985 1984 info = q.isapplied(patch)
1986 1985 if info:
1987 1986 q.applied[info[0]] = statusentry(info[1], name)
1988 1987 q.applied_dirty = 1
1989 1988
1990 1989 util.rename(q.join(patch), absdest)
1991 1990 r = q.qrepo()
1992 1991 if r:
1993 1992 wlock = r.wlock()
1994 1993 try:
1995 1994 if r.dirstate[name] == 'r':
1996 1995 r.undelete([name])
1997 1996 r.copy(patch, name)
1998 1997 r.remove([patch], False)
1999 1998 finally:
2000 1999 del wlock
2001 2000
2002 2001 q.save_dirty()
2003 2002
2004 2003 def restore(ui, repo, rev, **opts):
2005 2004 """restore the queue state saved by a rev"""
2006 2005 rev = repo.lookup(rev)
2007 2006 q = repo.mq
2008 2007 q.restore(repo, rev, delete=opts['delete'],
2009 2008 qupdate=opts['update'])
2010 2009 q.save_dirty()
2011 2010 return 0
2012 2011
2013 2012 def save(ui, repo, **opts):
2014 2013 """save current queue state"""
2015 2014 q = repo.mq
2016 2015 message = cmdutil.logmessage(opts)
2017 2016 ret = q.save(repo, msg=message)
2018 2017 if ret:
2019 2018 return ret
2020 2019 q.save_dirty()
2021 2020 if opts['copy']:
2022 2021 path = q.path
2023 2022 if opts['name']:
2024 2023 newpath = os.path.join(q.basepath, opts['name'])
2025 2024 if os.path.exists(newpath):
2026 2025 if not os.path.isdir(newpath):
2027 2026 raise util.Abort(_('destination %s exists and is not '
2028 2027 'a directory') % newpath)
2029 2028 if not opts['force']:
2030 2029 raise util.Abort(_('destination %s exists, '
2031 2030 'use -f to force') % newpath)
2032 2031 else:
2033 2032 newpath = savename(path)
2034 2033 ui.warn("copy %s to %s\n" % (path, newpath))
2035 2034 util.copyfiles(path, newpath)
2036 2035 if opts['empty']:
2037 2036 try:
2038 2037 os.unlink(q.join(q.status_path))
2039 2038 except:
2040 2039 pass
2041 2040 return 0
2042 2041
2043 2042 def strip(ui, repo, rev, **opts):
2044 2043 """strip a revision and all later revs on the same branch"""
2045 2044 rev = repo.lookup(rev)
2046 2045 backup = 'all'
2047 2046 if opts['backup']:
2048 2047 backup = 'strip'
2049 2048 elif opts['nobackup']:
2050 2049 backup = 'none'
2051 2050 update = repo.dirstate.parents()[0] != revlog.nullid
2052 2051 repo.mq.strip(repo, rev, backup=backup, update=update, force=opts['force'])
2053 2052 return 0
2054 2053
2055 2054 def select(ui, repo, *args, **opts):
2056 2055 '''set or print guarded patches to push
2057 2056
2058 2057 Use the qguard command to set or print guards on patch, then use
2059 2058 qselect to tell mq which guards to use. A patch will be pushed if it
2060 2059 has no guards or any positive guards match the currently selected guard,
2061 2060 but will not be pushed if any negative guards match the current guard.
2062 2061 For example:
2063 2062
2064 2063 qguard foo.patch -stable (negative guard)
2065 2064 qguard bar.patch +stable (positive guard)
2066 2065 qselect stable
2067 2066
2068 2067 This activates the "stable" guard. mq will skip foo.patch (because
2069 2068 it has a negative match) but push bar.patch (because it
2070 2069 has a positive match).
2071 2070
2072 2071 With no arguments, prints the currently active guards.
2073 2072 With one argument, sets the active guard.
2074 2073
2075 2074 Use -n/--none to deactivate guards (no other arguments needed).
2076 2075 When no guards are active, patches with positive guards are skipped
2077 2076 and patches with negative guards are pushed.
2078 2077
2079 2078 qselect can change the guards on applied patches. It does not pop
2080 2079 guarded patches by default. Use --pop to pop back to the last applied
2081 2080 patch that is not guarded. Use --reapply (which implies --pop) to push
2082 2081 back to the current patch afterwards, but skip guarded patches.
2083 2082
2084 2083 Use -s/--series to print a list of all guards in the series file (no
2085 2084 other arguments needed). Use -v for more information.'''
2086 2085
2087 2086 q = repo.mq
2088 2087 guards = q.active()
2089 2088 if args or opts['none']:
2090 2089 old_unapplied = q.unapplied(repo)
2091 2090 old_guarded = [i for i in xrange(len(q.applied)) if
2092 2091 not q.pushable(i)[0]]
2093 2092 q.set_active(args)
2094 2093 q.save_dirty()
2095 2094 if not args:
2096 2095 ui.status(_('guards deactivated\n'))
2097 2096 if not opts['pop'] and not opts['reapply']:
2098 2097 unapplied = q.unapplied(repo)
2099 2098 guarded = [i for i in xrange(len(q.applied))
2100 2099 if not q.pushable(i)[0]]
2101 2100 if len(unapplied) != len(old_unapplied):
2102 2101 ui.status(_('number of unguarded, unapplied patches has '
2103 2102 'changed from %d to %d\n') %
2104 2103 (len(old_unapplied), len(unapplied)))
2105 2104 if len(guarded) != len(old_guarded):
2106 2105 ui.status(_('number of guarded, applied patches has changed '
2107 2106 'from %d to %d\n') %
2108 2107 (len(old_guarded), len(guarded)))
2109 2108 elif opts['series']:
2110 2109 guards = {}
2111 2110 noguards = 0
2112 2111 for gs in q.series_guards:
2113 2112 if not gs:
2114 2113 noguards += 1
2115 2114 for g in gs:
2116 2115 guards.setdefault(g, 0)
2117 2116 guards[g] += 1
2118 2117 if ui.verbose:
2119 2118 guards['NONE'] = noguards
2120 2119 guards = guards.items()
2121 2120 guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
2122 2121 if guards:
2123 2122 ui.note(_('guards in series file:\n'))
2124 2123 for guard, count in guards:
2125 2124 ui.note('%2d ' % count)
2126 2125 ui.write(guard, '\n')
2127 2126 else:
2128 2127 ui.note(_('no guards in series file\n'))
2129 2128 else:
2130 2129 if guards:
2131 2130 ui.note(_('active guards:\n'))
2132 2131 for g in guards:
2133 2132 ui.write(g, '\n')
2134 2133 else:
2135 2134 ui.write(_('no active guards\n'))
2136 2135 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2137 2136 popped = False
2138 2137 if opts['pop'] or opts['reapply']:
2139 2138 for i in xrange(len(q.applied)):
2140 2139 pushable, reason = q.pushable(i)
2141 2140 if not pushable:
2142 2141 ui.status(_('popping guarded patches\n'))
2143 2142 popped = True
2144 2143 if i == 0:
2145 2144 q.pop(repo, all=True)
2146 2145 else:
2147 2146 q.pop(repo, i-1)
2148 2147 break
2149 2148 if popped:
2150 2149 try:
2151 2150 if reapply:
2152 2151 ui.status(_('reapplying unguarded patches\n'))
2153 2152 q.push(repo, reapply)
2154 2153 finally:
2155 2154 q.save_dirty()
2156 2155
2157 2156 def reposetup(ui, repo):
2158 2157 class mqrepo(repo.__class__):
2159 2158 def abort_if_wdir_patched(self, errmsg, force=False):
2160 2159 if self.mq.applied and not force:
2161 2160 parent = revlog.hex(self.dirstate.parents()[0])
2162 2161 if parent in [s.rev for s in self.mq.applied]:
2163 2162 raise util.Abort(errmsg)
2164 2163
2165 2164 def commit(self, *args, **opts):
2166 2165 if len(args) >= 6:
2167 2166 force = args[5]
2168 2167 else:
2169 2168 force = opts.get('force')
2170 2169 self.abort_if_wdir_patched(
2171 2170 _('cannot commit over an applied mq patch'),
2172 2171 force)
2173 2172
2174 2173 return super(mqrepo, self).commit(*args, **opts)
2175 2174
2176 2175 def push(self, remote, force=False, revs=None):
2177 2176 if self.mq.applied and not force and not revs:
2178 2177 raise util.Abort(_('source has mq patches applied'))
2179 2178 return super(mqrepo, self).push(remote, force, revs)
2180 2179
2181 2180 def tags(self):
2182 2181 if self.tagscache:
2183 2182 return self.tagscache
2184 2183
2185 2184 tagscache = super(mqrepo, self).tags()
2186 2185
2187 2186 q = self.mq
2188 2187 if not q.applied:
2189 2188 return tagscache
2190 2189
2191 2190 mqtags = [(revlog.bin(patch.rev), patch.name) for patch in q.applied]
2192 2191
2193 2192 if mqtags[-1][0] not in self.changelog.nodemap:
2194 2193 self.ui.warn('mq status file refers to unknown node %s\n'
2195 2194 % revlog.short(mqtags[-1][0]))
2196 2195 return tagscache
2197 2196
2198 2197 mqtags.append((mqtags[-1][0], 'qtip'))
2199 2198 mqtags.append((mqtags[0][0], 'qbase'))
2200 2199 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2201 2200 for patch in mqtags:
2202 2201 if patch[1] in tagscache:
2203 2202 self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
2204 2203 else:
2205 2204 tagscache[patch[1]] = patch[0]
2206 2205
2207 2206 return tagscache
2208 2207
2209 2208 def _branchtags(self, partial, lrev):
2210 2209 q = self.mq
2211 2210 if not q.applied:
2212 2211 return super(mqrepo, self)._branchtags(partial, lrev)
2213 2212
2214 2213 cl = self.changelog
2215 2214 qbasenode = revlog.bin(q.applied[0].rev)
2216 2215 if qbasenode not in cl.nodemap:
2217 2216 self.ui.warn('mq status file refers to unknown node %s\n'
2218 2217 % revlog.short(qbasenode))
2219 2218 return super(mqrepo, self)._branchtags(partial, lrev)
2220 2219
2221 2220 qbase = cl.rev(qbasenode)
2222 2221 start = lrev + 1
2223 2222 if start < qbase:
2224 2223 # update the cache (excluding the patches) and save it
2225 2224 self._updatebranchcache(partial, lrev+1, qbase)
2226 2225 self._writebranchcache(partial, cl.node(qbase-1), qbase-1)
2227 2226 start = qbase
2228 2227 # if start = qbase, the cache is as updated as it should be.
2229 2228 # if start > qbase, the cache includes (part of) the patches.
2230 2229 # we might as well use it, but we won't save it.
2231 2230
2232 2231 # update the cache up to the tip
2233 2232 self._updatebranchcache(partial, start, cl.count())
2234 2233
2235 2234 return partial
2236 2235
2237 2236 if repo.local():
2238 2237 repo.__class__ = mqrepo
2239 2238 repo.mq = queue(ui, repo.join(""))
2240 2239
2241 2240 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2242 2241
2243 2242 headeropts = [
2244 2243 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2245 2244 ('u', 'user', '', _('add "From: <given user>" to patch')),
2246 2245 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2247 2246 ('d', 'date', '', _('add "Date: <given date>" to patch'))]
2248 2247
2249 2248 cmdtable = {
2250 2249 "qapplied": (applied, [] + seriesopts, _('hg qapplied [-s] [PATCH]')),
2251 2250 "qclone":
2252 2251 (clone,
2253 2252 [('', 'pull', None, _('use pull protocol to copy metadata')),
2254 2253 ('U', 'noupdate', None, _('do not update the new working directories')),
2255 2254 ('', 'uncompressed', None,
2256 2255 _('use uncompressed transfer (fast over LAN)')),
2257 2256 ('p', 'patches', '', _('location of source patch repo')),
2258 2257 ] + commands.remoteopts,
2259 2258 _('hg qclone [OPTION]... SOURCE [DEST]')),
2260 2259 "qcommit|qci":
2261 2260 (commit,
2262 2261 commands.table["^commit|ci"][1],
2263 2262 _('hg qcommit [OPTION]... [FILE]...')),
2264 2263 "^qdiff":
2265 2264 (diff,
2266 2265 [('g', 'git', None, _('use git extended diff format')),
2267 2266 ('U', 'unified', 3, _('number of lines of context to show')),
2268 2267 ] + commands.walkopts,
2269 2268 _('hg qdiff [-I] [-X] [-U NUM] [-g] [FILE]...')),
2270 2269 "qdelete|qremove|qrm":
2271 2270 (delete,
2272 2271 [('k', 'keep', None, _('keep patch file')),
2273 2272 ('r', 'rev', [], _('stop managing a revision'))],
2274 2273 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2275 2274 'qfold':
2276 2275 (fold,
2277 2276 [('e', 'edit', None, _('edit patch header')),
2278 2277 ('k', 'keep', None, _('keep folded patch files')),
2279 2278 ] + commands.commitopts,
2280 2279 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2281 2280 'qgoto':
2282 2281 (goto,
2283 2282 [('f', 'force', None, _('overwrite any local changes'))],
2284 2283 _('hg qgoto [OPTION]... PATCH')),
2285 2284 'qguard':
2286 2285 (guard,
2287 2286 [('l', 'list', None, _('list all patches and guards')),
2288 2287 ('n', 'none', None, _('drop all guards'))],
2289 2288 _('hg qguard [-l] [-n] [PATCH] [+GUARD]... [-GUARD]...')),
2290 2289 'qheader': (header, [], _('hg qheader [PATCH]')),
2291 2290 "^qimport":
2292 2291 (qimport,
2293 2292 [('e', 'existing', None, 'import file in patch dir'),
2294 2293 ('n', 'name', '', 'patch file name'),
2295 2294 ('f', 'force', None, 'overwrite existing files'),
2296 2295 ('r', 'rev', [], 'place existing revisions under mq control'),
2297 2296 ('g', 'git', None, _('use git extended diff format'))],
2298 2297 _('hg qimport [-e] [-n NAME] [-f] [-g] [-r REV]... FILE...')),
2299 2298 "^qinit":
2300 2299 (init,
2301 2300 [('c', 'create-repo', None, 'create queue repository')],
2302 2301 _('hg qinit [-c]')),
2303 2302 "qnew":
2304 2303 (new,
2305 2304 [('e', 'edit', None, _('edit commit message')),
2306 2305 ('f', 'force', None, _('import uncommitted changes into patch')),
2307 2306 ('g', 'git', None, _('use git extended diff format')),
2308 2307 ] + commands.walkopts + commands.commitopts + headeropts,
2309 2308 _('hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH [FILE]...')),
2310 2309 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2311 2310 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2312 2311 "^qpop":
2313 2312 (pop,
2314 2313 [('a', 'all', None, _('pop all patches')),
2315 2314 ('n', 'name', '', _('queue name to pop')),
2316 2315 ('f', 'force', None, _('forget any local changes'))],
2317 2316 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2318 2317 "^qpush":
2319 2318 (push,
2320 2319 [('f', 'force', None, _('apply if the patch has rejects')),
2321 2320 ('l', 'list', None, _('list patch name in commit text')),
2322 2321 ('a', 'all', None, _('apply all patches')),
2323 2322 ('m', 'merge', None, _('merge from another queue')),
2324 2323 ('n', 'name', '', _('merge queue name'))],
2325 2324 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]')),
2326 2325 "^qrefresh":
2327 2326 (refresh,
2328 2327 [('e', 'edit', None, _('edit commit message')),
2329 2328 ('g', 'git', None, _('use git extended diff format')),
2330 2329 ('s', 'short', None, _('refresh only files already in the patch')),
2331 2330 ] + commands.walkopts + commands.commitopts + headeropts,
2332 2331 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2333 2332 'qrename|qmv':
2334 2333 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2335 2334 "qrestore":
2336 2335 (restore,
2337 2336 [('d', 'delete', None, _('delete save entry')),
2338 2337 ('u', 'update', None, _('update queue working dir'))],
2339 2338 _('hg qrestore [-d] [-u] REV')),
2340 2339 "qsave":
2341 2340 (save,
2342 2341 [('c', 'copy', None, _('copy patch directory')),
2343 2342 ('n', 'name', '', _('copy directory name')),
2344 2343 ('e', 'empty', None, _('clear queue status file')),
2345 2344 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2346 2345 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2347 2346 "qselect":
2348 2347 (select,
2349 2348 [('n', 'none', None, _('disable all guards')),
2350 2349 ('s', 'series', None, _('list all guards in series file')),
2351 2350 ('', 'pop', None, _('pop to before first guarded applied patch')),
2352 2351 ('', 'reapply', None, _('pop, then reapply patches'))],
2353 2352 _('hg qselect [OPTION]... [GUARD]...')),
2354 2353 "qseries":
2355 2354 (series,
2356 2355 [('m', 'missing', None, _('print patches not in series')),
2357 2356 ] + seriesopts,
2358 2357 _('hg qseries [-ms]')),
2359 2358 "^strip":
2360 2359 (strip,
2361 2360 [('f', 'force', None, _('force removal with local changes')),
2362 2361 ('b', 'backup', None, _('bundle unrelated changesets')),
2363 2362 ('n', 'nobackup', None, _('no backups'))],
2364 2363 _('hg strip [-f] [-b] [-n] REV')),
2365 2364 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
2366 2365 "qunapplied": (unapplied, [] + seriesopts, _('hg qunapplied [-s] [PATCH]')),
2367 2366 }
@@ -1,142 +1,142 b''
1 1 # Copyright (C) 2006 - Marco Barisione <marco@barisione.org>
2 2 #
3 3 # This is a small extension for Mercurial (http://www.selenic.com/mercurial)
4 4 # that removes files not known to mercurial
5 5 #
6 6 # This program was inspired by the "cvspurge" script contained in CVS utilities
7 7 # (http://www.red-bean.com/cvsutils/).
8 8 #
9 9 # To enable the "purge" extension put these lines in your ~/.hgrc:
10 10 # [extensions]
11 11 # hgext.purge =
12 12 #
13 13 # For help on the usage of "hg purge" use:
14 14 # hg help purge
15 15 #
16 16 # This program is free software; you can redistribute it and/or modify
17 17 # it under the terms of the GNU General Public License as published by
18 18 # the Free Software Foundation; either version 2 of the License, or
19 19 # (at your option) any later version.
20 20 #
21 21 # This program is distributed in the hope that it will be useful,
22 22 # but WITHOUT ANY WARRANTY; without even the implied warranty of
23 23 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 24 # GNU General Public License for more details.
25 25 #
26 26 # You should have received a copy of the GNU General Public License
27 27 # along with this program; if not, write to the Free Software
28 28 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 29
30 30 from mercurial import util, commands, cmdutil
31 31 from mercurial.i18n import _
32 32 import os
33 33
34 34 def purge(ui, repo, *dirs, **opts):
35 35 '''removes files not tracked by mercurial
36 36
37 37 Delete files not known to mercurial, this is useful to test local and
38 38 uncommitted changes in the otherwise clean source tree.
39 39
40 40 This means that purge will delete:
41 41 - Unknown files: files marked with "?" by "hg status"
42 42 - Ignored files: files usually ignored by Mercurial because they match
43 43 a pattern in a ".hgignore" file
44 44 - Empty directories: in fact Mercurial ignores directories unless they
45 45 contain files under source control managment
46 46 But it will leave untouched:
47 47 - Unmodified tracked files
48 48 - Modified tracked files
49 49 - New files added to the repository (with "hg add")
50 50
51 51 If directories are given on the command line, only files in these
52 52 directories are considered.
53 53
54 54 Be careful with purge, you could irreversibly delete some files you
55 55 forgot to add to the repository. If you only want to print the list of
56 56 files that this program would delete use the --print option.
57 57 '''
58 58 act = not opts['print']
59 59 ignored = bool(opts['all'])
60 60 abort_on_err = bool(opts['abort_on_err'])
61 61 eol = opts['print0'] and '\0' or '\n'
62 62 if eol == '\0':
63 63 # --print0 implies --print
64 64 act = False
65 65 force = bool(opts['force'])
66 66
67 67 def error(msg):
68 68 if abort_on_err:
69 69 raise util.Abort(msg)
70 70 else:
71 71 ui.warn(_('warning: %s\n') % msg)
72 72
73 73 def remove(remove_func, name):
74 74 if act:
75 75 try:
76 76 remove_func(os.path.join(repo.root, name))
77 77 except OSError, e:
78 78 error(_('%s cannot be removed') % name)
79 79 else:
80 80 ui.write('%s%s' % (name, eol))
81 81
82 82 if not force:
83 83 _check_fs(ui, repo)
84 84
85 85 directories = []
86 86 files = []
87 87 missing = []
88 roots, match, anypats = cmdutil.matchpats(repo, dirs, opts)
89 for src, f, st in repo.dirstate.statwalk(roots, match,
88 match = cmdutil.match(repo, dirs, opts)
89 for src, f, st in repo.dirstate.statwalk(match.files(), match,
90 90 ignored=ignored, directories=True):
91 91 if src == 'd':
92 92 directories.append(f)
93 93 elif src == 'm':
94 94 missing.append(f)
95 95 elif src == 'f' and f not in repo.dirstate:
96 96 files.append(f)
97 97
98 98 directories.sort()
99 99
100 100 for f in files:
101 101 if f not in repo.dirstate:
102 102 ui.note(_('Removing file %s\n') % f)
103 103 remove(os.remove, f)
104 104
105 105 for f in directories[::-1]:
106 106 if match(f) and not os.listdir(repo.wjoin(f)):
107 107 ui.note(_('Removing directory %s\n') % f)
108 108 remove(os.rmdir, f)
109 109
110 110 def _check_fs(ui, repo):
111 111 """Abort if there is the chance of having problems with name-mangling fs
112 112
113 113 In a name mangling filesystem (e.g. a case insensitive one)
114 114 dirstate.walk() can yield filenames different from the ones
115 115 stored in the dirstate. This already confuses the status and
116 116 add commands, but with purge this may cause data loss.
117 117
118 118 To prevent this, this function will abort if there are uncommitted
119 119 changes.
120 120 """
121 121
122 122 # We can't use (files, match) to do a partial walk here - we wouldn't
123 123 # notice a modified README file if the user ran "hg purge readme"
124 124 modified, added, removed, deleted = repo.status()[:4]
125 125 if modified or added or removed or deleted:
126 126 if not util.checkfolding(repo.path) and not ui.quiet:
127 127 ui.warn(_("Purging on name mangling filesystems is not "
128 128 "fully supported.\n"))
129 129 raise util.Abort(_("outstanding uncommitted changes"))
130 130
131 131 cmdtable = {
132 132 'purge|clean':
133 133 (purge,
134 134 [('a', 'abort-on-err', None, _('abort if an error occurs')),
135 135 ('', 'all', None, _('purge ignored files too')),
136 136 ('f', 'force', None, _('purge even when there are uncommitted changes')),
137 137 ('p', 'print', None, _('print the file names instead of deleting them')),
138 138 ('0', 'print0', None, _('end filenames with NUL, for use with xargs'
139 139 ' (implies -p)')),
140 140 ] + commands.walkopts,
141 141 _('hg purge [OPTION]... [DIR]...'))
142 142 }
@@ -1,1187 +1,1183 b''
1 1 # cmdutil.py - help for command processing in mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from node import hex, nullid, nullrev, short
9 9 from i18n import _
10 10 import os, sys, bisect, stat
11 11 import mdiff, bdiff, util, templater, templatefilters, patch, errno
12 12 import match as _match
13 13
14 14 revrangesep = ':'
15 15
16 16 class UnknownCommand(Exception):
17 17 """Exception raised if command is not in the command table."""
18 18 class AmbiguousCommand(Exception):
19 19 """Exception raised if command shortcut matches more than one command."""
20 20
21 21 def findpossible(ui, cmd, table):
22 22 """
23 23 Return cmd -> (aliases, command table entry)
24 24 for each matching command.
25 25 Return debug commands (or their aliases) only if no normal command matches.
26 26 """
27 27 choice = {}
28 28 debugchoice = {}
29 29 for e in table.keys():
30 30 aliases = e.lstrip("^").split("|")
31 31 found = None
32 32 if cmd in aliases:
33 33 found = cmd
34 34 elif not ui.config("ui", "strict"):
35 35 for a in aliases:
36 36 if a.startswith(cmd):
37 37 found = a
38 38 break
39 39 if found is not None:
40 40 if aliases[0].startswith("debug") or found.startswith("debug"):
41 41 debugchoice[found] = (aliases, table[e])
42 42 else:
43 43 choice[found] = (aliases, table[e])
44 44
45 45 if not choice and debugchoice:
46 46 choice = debugchoice
47 47
48 48 return choice
49 49
50 50 def findcmd(ui, cmd, table):
51 51 """Return (aliases, command table entry) for command string."""
52 52 choice = findpossible(ui, cmd, table)
53 53
54 54 if cmd in choice:
55 55 return choice[cmd]
56 56
57 57 if len(choice) > 1:
58 58 clist = choice.keys()
59 59 clist.sort()
60 60 raise AmbiguousCommand(cmd, clist)
61 61
62 62 if choice:
63 63 return choice.values()[0]
64 64
65 65 raise UnknownCommand(cmd)
66 66
67 67 def bail_if_changed(repo):
68 68 if repo.dirstate.parents()[1] != nullid:
69 69 raise util.Abort(_('outstanding uncommitted merge'))
70 70 modified, added, removed, deleted = repo.status()[:4]
71 71 if modified or added or removed or deleted:
72 72 raise util.Abort(_("outstanding uncommitted changes"))
73 73
74 74 def logmessage(opts):
75 75 """ get the log message according to -m and -l option """
76 76 message = opts['message']
77 77 logfile = opts['logfile']
78 78
79 79 if message and logfile:
80 80 raise util.Abort(_('options --message and --logfile are mutually '
81 81 'exclusive'))
82 82 if not message and logfile:
83 83 try:
84 84 if logfile == '-':
85 85 message = sys.stdin.read()
86 86 else:
87 87 message = open(logfile).read()
88 88 except IOError, inst:
89 89 raise util.Abort(_("can't read commit message '%s': %s") %
90 90 (logfile, inst.strerror))
91 91 return message
92 92
93 93 def loglimit(opts):
94 94 """get the log limit according to option -l/--limit"""
95 95 limit = opts.get('limit')
96 96 if limit:
97 97 try:
98 98 limit = int(limit)
99 99 except ValueError:
100 100 raise util.Abort(_('limit must be a positive integer'))
101 101 if limit <= 0: raise util.Abort(_('limit must be positive'))
102 102 else:
103 103 limit = sys.maxint
104 104 return limit
105 105
106 106 def setremoteconfig(ui, opts):
107 107 "copy remote options to ui tree"
108 108 if opts.get('ssh'):
109 109 ui.setconfig("ui", "ssh", opts['ssh'])
110 110 if opts.get('remotecmd'):
111 111 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
112 112
113 113 def revpair(repo, revs):
114 114 '''return pair of nodes, given list of revisions. second item can
115 115 be None, meaning use working dir.'''
116 116
117 117 def revfix(repo, val, defval):
118 118 if not val and val != 0 and defval is not None:
119 119 val = defval
120 120 return repo.lookup(val)
121 121
122 122 if not revs:
123 123 return repo.dirstate.parents()[0], None
124 124 end = None
125 125 if len(revs) == 1:
126 126 if revrangesep in revs[0]:
127 127 start, end = revs[0].split(revrangesep, 1)
128 128 start = revfix(repo, start, 0)
129 129 end = revfix(repo, end, repo.changelog.count() - 1)
130 130 else:
131 131 start = revfix(repo, revs[0], None)
132 132 elif len(revs) == 2:
133 133 if revrangesep in revs[0] or revrangesep in revs[1]:
134 134 raise util.Abort(_('too many revisions specified'))
135 135 start = revfix(repo, revs[0], None)
136 136 end = revfix(repo, revs[1], None)
137 137 else:
138 138 raise util.Abort(_('too many revisions specified'))
139 139 return start, end
140 140
141 141 def revrange(repo, revs):
142 142 """Yield revision as strings from a list of revision specifications."""
143 143
144 144 def revfix(repo, val, defval):
145 145 if not val and val != 0 and defval is not None:
146 146 return defval
147 147 return repo.changelog.rev(repo.lookup(val))
148 148
149 149 seen, l = {}, []
150 150 for spec in revs:
151 151 if revrangesep in spec:
152 152 start, end = spec.split(revrangesep, 1)
153 153 start = revfix(repo, start, 0)
154 154 end = revfix(repo, end, repo.changelog.count() - 1)
155 155 step = start > end and -1 or 1
156 156 for rev in xrange(start, end+step, step):
157 157 if rev in seen:
158 158 continue
159 159 seen[rev] = 1
160 160 l.append(rev)
161 161 else:
162 162 rev = revfix(repo, spec, None)
163 163 if rev in seen:
164 164 continue
165 165 seen[rev] = 1
166 166 l.append(rev)
167 167
168 168 return l
169 169
170 170 def make_filename(repo, pat, node,
171 171 total=None, seqno=None, revwidth=None, pathname=None):
172 172 node_expander = {
173 173 'H': lambda: hex(node),
174 174 'R': lambda: str(repo.changelog.rev(node)),
175 175 'h': lambda: short(node),
176 176 }
177 177 expander = {
178 178 '%': lambda: '%',
179 179 'b': lambda: os.path.basename(repo.root),
180 180 }
181 181
182 182 try:
183 183 if node:
184 184 expander.update(node_expander)
185 185 if node:
186 186 expander['r'] = (lambda:
187 187 str(repo.changelog.rev(node)).zfill(revwidth or 0))
188 188 if total is not None:
189 189 expander['N'] = lambda: str(total)
190 190 if seqno is not None:
191 191 expander['n'] = lambda: str(seqno)
192 192 if total is not None and seqno is not None:
193 193 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
194 194 if pathname is not None:
195 195 expander['s'] = lambda: os.path.basename(pathname)
196 196 expander['d'] = lambda: os.path.dirname(pathname) or '.'
197 197 expander['p'] = lambda: pathname
198 198
199 199 newname = []
200 200 patlen = len(pat)
201 201 i = 0
202 202 while i < patlen:
203 203 c = pat[i]
204 204 if c == '%':
205 205 i += 1
206 206 c = pat[i]
207 207 c = expander[c]()
208 208 newname.append(c)
209 209 i += 1
210 210 return ''.join(newname)
211 211 except KeyError, inst:
212 212 raise util.Abort(_("invalid format spec '%%%s' in output file name") %
213 213 inst.args[0])
214 214
215 215 def make_file(repo, pat, node=None,
216 216 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
217 217 if not pat or pat == '-':
218 218 return 'w' in mode and sys.stdout or sys.stdin
219 219 if hasattr(pat, 'write') and 'w' in mode:
220 220 return pat
221 221 if hasattr(pat, 'read') and 'r' in mode:
222 222 return pat
223 223 return open(make_filename(repo, pat, node, total, seqno, revwidth,
224 224 pathname),
225 225 mode)
226 226
227 227 def match(repo, pats=[], opts={}, globbed=False, default='relpath'):
228 228 if not globbed and default == 'relpath':
229 229 pats = util.expand_glob(pats or [])
230 230 m = _match.match(repo.root, repo.getcwd(), pats,
231 231 opts.get('include'), opts.get('exclude'), default)
232 232 def badfn(f, msg):
233 233 repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
234 234 return False
235 235 m.bad = badfn
236 236 return m
237 237
238 def matchpats(repo, pats=[], opts={}, globbed=False, default='relpath'):
239 m = match(repo, pats, opts, globbed, default)
240 return m.files(), m, m.anypats()
241
242 238 def walk(repo, match, node=None):
243 239 for src, fn in repo.walk(node, match):
244 240 yield src, fn, match.rel(fn), match.exact(fn)
245 241
246 242 def findrenames(repo, added=None, removed=None, threshold=0.5):
247 243 '''find renamed files -- yields (before, after, score) tuples'''
248 244 if added is None or removed is None:
249 245 added, removed = repo.status()[1:3]
250 246 ctx = repo.changectx()
251 247 for a in added:
252 248 aa = repo.wread(a)
253 249 bestname, bestscore = None, threshold
254 250 for r in removed:
255 251 rr = ctx.filectx(r).data()
256 252
257 253 # bdiff.blocks() returns blocks of matching lines
258 254 # count the number of bytes in each
259 255 equal = 0
260 256 alines = mdiff.splitnewlines(aa)
261 257 matches = bdiff.blocks(aa, rr)
262 258 for x1,x2,y1,y2 in matches:
263 259 for line in alines[x1:x2]:
264 260 equal += len(line)
265 261
266 262 lengths = len(aa) + len(rr)
267 263 if lengths:
268 264 myscore = equal*2.0 / lengths
269 265 if myscore >= bestscore:
270 266 bestname, bestscore = r, myscore
271 267 if bestname:
272 268 yield bestname, a, bestscore
273 269
274 270 def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
275 271 if dry_run is None:
276 272 dry_run = opts.get('dry_run')
277 273 if similarity is None:
278 274 similarity = float(opts.get('similarity') or 0)
279 275 add, remove = [], []
280 276 mapping = {}
281 277 m = match(repo, pats, opts)
282 278 for src, abs, rel, exact in walk(repo, m):
283 279 target = repo.wjoin(abs)
284 280 if src == 'f' and abs not in repo.dirstate:
285 281 add.append(abs)
286 282 mapping[abs] = rel, exact
287 283 if repo.ui.verbose or not exact:
288 284 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
289 285 if repo.dirstate[abs] != 'r' and (not util.lexists(target)
290 286 or (os.path.isdir(target) and not os.path.islink(target))):
291 287 remove.append(abs)
292 288 mapping[abs] = rel, exact
293 289 if repo.ui.verbose or not exact:
294 290 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
295 291 if not dry_run:
296 292 repo.remove(remove)
297 293 repo.add(add)
298 294 if similarity > 0:
299 295 for old, new, score in findrenames(repo, add, remove, similarity):
300 296 oldrel, oldexact = mapping[old]
301 297 newrel, newexact = mapping[new]
302 298 if repo.ui.verbose or not oldexact or not newexact:
303 299 repo.ui.status(_('recording removal of %s as rename to %s '
304 300 '(%d%% similar)\n') %
305 301 (oldrel, newrel, score * 100))
306 302 if not dry_run:
307 303 repo.copy(old, new)
308 304
309 305 def copy(ui, repo, pats, opts, rename=False):
310 306 # called with the repo lock held
311 307 #
312 308 # hgsep => pathname that uses "/" to separate directories
313 309 # ossep => pathname that uses os.sep to separate directories
314 310 cwd = repo.getcwd()
315 311 targets = {}
316 312 after = opts.get("after")
317 313 dryrun = opts.get("dry_run")
318 314
319 315 def walkpat(pat):
320 316 srcs = []
321 317 m = match(repo, [pat], opts, globbed=True)
322 318 for tag, abs, rel, exact in walk(repo, m):
323 319 state = repo.dirstate[abs]
324 320 if state in '?r':
325 321 if exact and state == '?':
326 322 ui.warn(_('%s: not copying - file is not managed\n') % rel)
327 323 if exact and state == 'r':
328 324 ui.warn(_('%s: not copying - file has been marked for'
329 325 ' remove\n') % rel)
330 326 continue
331 327 # abs: hgsep
332 328 # rel: ossep
333 329 srcs.append((abs, rel, exact))
334 330 return srcs
335 331
336 332 # abssrc: hgsep
337 333 # relsrc: ossep
338 334 # otarget: ossep
339 335 def copyfile(abssrc, relsrc, otarget, exact):
340 336 abstarget = util.canonpath(repo.root, cwd, otarget)
341 337 reltarget = repo.pathto(abstarget, cwd)
342 338 target = repo.wjoin(abstarget)
343 339 src = repo.wjoin(abssrc)
344 340 state = repo.dirstate[abstarget]
345 341
346 342 # check for collisions
347 343 prevsrc = targets.get(abstarget)
348 344 if prevsrc is not None:
349 345 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
350 346 (reltarget, repo.pathto(abssrc, cwd),
351 347 repo.pathto(prevsrc, cwd)))
352 348 return
353 349
354 350 # check for overwrites
355 351 exists = os.path.exists(target)
356 352 if (not after and exists or after and state in 'mn'):
357 353 if not opts['force']:
358 354 ui.warn(_('%s: not overwriting - file exists\n') %
359 355 reltarget)
360 356 return
361 357
362 358 if after:
363 359 if not exists:
364 360 return
365 361 elif not dryrun:
366 362 try:
367 363 if exists:
368 364 os.unlink(target)
369 365 targetdir = os.path.dirname(target) or '.'
370 366 if not os.path.isdir(targetdir):
371 367 os.makedirs(targetdir)
372 368 util.copyfile(src, target)
373 369 except IOError, inst:
374 370 if inst.errno == errno.ENOENT:
375 371 ui.warn(_('%s: deleted in working copy\n') % relsrc)
376 372 else:
377 373 ui.warn(_('%s: cannot copy - %s\n') %
378 374 (relsrc, inst.strerror))
379 375 return True # report a failure
380 376
381 377 if ui.verbose or not exact:
382 378 action = rename and "moving" or "copying"
383 379 ui.status(_('%s %s to %s\n') % (action, relsrc, reltarget))
384 380
385 381 targets[abstarget] = abssrc
386 382
387 383 # fix up dirstate
388 384 origsrc = repo.dirstate.copied(abssrc) or abssrc
389 385 if abstarget == origsrc: # copying back a copy?
390 386 if state not in 'mn' and not dryrun:
391 387 repo.dirstate.normallookup(abstarget)
392 388 else:
393 389 if repo.dirstate[origsrc] == 'a':
394 390 if not ui.quiet:
395 391 ui.warn(_("%s has not been committed yet, so no copy "
396 392 "data will be stored for %s.\n")
397 393 % (repo.pathto(origsrc, cwd), reltarget))
398 394 if abstarget not in repo.dirstate and not dryrun:
399 395 repo.add([abstarget])
400 396 elif not dryrun:
401 397 repo.copy(origsrc, abstarget)
402 398
403 399 if rename and not dryrun:
404 400 repo.remove([abssrc], not after)
405 401
406 402 # pat: ossep
407 403 # dest ossep
408 404 # srcs: list of (hgsep, hgsep, ossep, bool)
409 405 # return: function that takes hgsep and returns ossep
410 406 def targetpathfn(pat, dest, srcs):
411 407 if os.path.isdir(pat):
412 408 abspfx = util.canonpath(repo.root, cwd, pat)
413 409 abspfx = util.localpath(abspfx)
414 410 if destdirexists:
415 411 striplen = len(os.path.split(abspfx)[0])
416 412 else:
417 413 striplen = len(abspfx)
418 414 if striplen:
419 415 striplen += len(os.sep)
420 416 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
421 417 elif destdirexists:
422 418 res = lambda p: os.path.join(dest,
423 419 os.path.basename(util.localpath(p)))
424 420 else:
425 421 res = lambda p: dest
426 422 return res
427 423
428 424 # pat: ossep
429 425 # dest ossep
430 426 # srcs: list of (hgsep, hgsep, ossep, bool)
431 427 # return: function that takes hgsep and returns ossep
432 428 def targetpathafterfn(pat, dest, srcs):
433 429 if util.patkind(pat, None)[0]:
434 430 # a mercurial pattern
435 431 res = lambda p: os.path.join(dest,
436 432 os.path.basename(util.localpath(p)))
437 433 else:
438 434 abspfx = util.canonpath(repo.root, cwd, pat)
439 435 if len(abspfx) < len(srcs[0][0]):
440 436 # A directory. Either the target path contains the last
441 437 # component of the source path or it does not.
442 438 def evalpath(striplen):
443 439 score = 0
444 440 for s in srcs:
445 441 t = os.path.join(dest, util.localpath(s[0])[striplen:])
446 442 if os.path.exists(t):
447 443 score += 1
448 444 return score
449 445
450 446 abspfx = util.localpath(abspfx)
451 447 striplen = len(abspfx)
452 448 if striplen:
453 449 striplen += len(os.sep)
454 450 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
455 451 score = evalpath(striplen)
456 452 striplen1 = len(os.path.split(abspfx)[0])
457 453 if striplen1:
458 454 striplen1 += len(os.sep)
459 455 if evalpath(striplen1) > score:
460 456 striplen = striplen1
461 457 res = lambda p: os.path.join(dest,
462 458 util.localpath(p)[striplen:])
463 459 else:
464 460 # a file
465 461 if destdirexists:
466 462 res = lambda p: os.path.join(dest,
467 463 os.path.basename(util.localpath(p)))
468 464 else:
469 465 res = lambda p: dest
470 466 return res
471 467
472 468
473 469 pats = util.expand_glob(pats)
474 470 if not pats:
475 471 raise util.Abort(_('no source or destination specified'))
476 472 if len(pats) == 1:
477 473 raise util.Abort(_('no destination specified'))
478 474 dest = pats.pop()
479 475 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
480 476 if not destdirexists:
481 477 if len(pats) > 1 or util.patkind(pats[0], None)[0]:
482 478 raise util.Abort(_('with multiple sources, destination must be an '
483 479 'existing directory'))
484 480 if util.endswithsep(dest):
485 481 raise util.Abort(_('destination %s is not a directory') % dest)
486 482
487 483 tfn = targetpathfn
488 484 if after:
489 485 tfn = targetpathafterfn
490 486 copylist = []
491 487 for pat in pats:
492 488 srcs = walkpat(pat)
493 489 if not srcs:
494 490 continue
495 491 copylist.append((tfn(pat, dest, srcs), srcs))
496 492 if not copylist:
497 493 raise util.Abort(_('no files to copy'))
498 494
499 495 errors = 0
500 496 for targetpath, srcs in copylist:
501 497 for abssrc, relsrc, exact in srcs:
502 498 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
503 499 errors += 1
504 500
505 501 if errors:
506 502 ui.warn(_('(consider using --after)\n'))
507 503
508 504 return errors
509 505
510 506 def service(opts, parentfn=None, initfn=None, runfn=None):
511 507 '''Run a command as a service.'''
512 508
513 509 if opts['daemon'] and not opts['daemon_pipefds']:
514 510 rfd, wfd = os.pipe()
515 511 args = sys.argv[:]
516 512 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
517 513 # Don't pass --cwd to the child process, because we've already
518 514 # changed directory.
519 515 for i in xrange(1,len(args)):
520 516 if args[i].startswith('--cwd='):
521 517 del args[i]
522 518 break
523 519 elif args[i].startswith('--cwd'):
524 520 del args[i:i+2]
525 521 break
526 522 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
527 523 args[0], args)
528 524 os.close(wfd)
529 525 os.read(rfd, 1)
530 526 if parentfn:
531 527 return parentfn(pid)
532 528 else:
533 529 os._exit(0)
534 530
535 531 if initfn:
536 532 initfn()
537 533
538 534 if opts['pid_file']:
539 535 fp = open(opts['pid_file'], 'w')
540 536 fp.write(str(os.getpid()) + '\n')
541 537 fp.close()
542 538
543 539 if opts['daemon_pipefds']:
544 540 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
545 541 os.close(rfd)
546 542 try:
547 543 os.setsid()
548 544 except AttributeError:
549 545 pass
550 546 os.write(wfd, 'y')
551 547 os.close(wfd)
552 548 sys.stdout.flush()
553 549 sys.stderr.flush()
554 550 fd = os.open(util.nulldev, os.O_RDWR)
555 551 if fd != 0: os.dup2(fd, 0)
556 552 if fd != 1: os.dup2(fd, 1)
557 553 if fd != 2: os.dup2(fd, 2)
558 554 if fd not in (0, 1, 2): os.close(fd)
559 555
560 556 if runfn:
561 557 return runfn()
562 558
563 559 class changeset_printer(object):
564 560 '''show changeset information when templating not requested.'''
565 561
566 562 def __init__(self, ui, repo, patch, buffered):
567 563 self.ui = ui
568 564 self.repo = repo
569 565 self.buffered = buffered
570 566 self.patch = patch
571 567 self.header = {}
572 568 self.hunk = {}
573 569 self.lastheader = None
574 570
575 571 def flush(self, rev):
576 572 if rev in self.header:
577 573 h = self.header[rev]
578 574 if h != self.lastheader:
579 575 self.lastheader = h
580 576 self.ui.write(h)
581 577 del self.header[rev]
582 578 if rev in self.hunk:
583 579 self.ui.write(self.hunk[rev])
584 580 del self.hunk[rev]
585 581 return 1
586 582 return 0
587 583
588 584 def show(self, rev=0, changenode=None, copies=(), **props):
589 585 if self.buffered:
590 586 self.ui.pushbuffer()
591 587 self._show(rev, changenode, copies, props)
592 588 self.hunk[rev] = self.ui.popbuffer()
593 589 else:
594 590 self._show(rev, changenode, copies, props)
595 591
596 592 def _show(self, rev, changenode, copies, props):
597 593 '''show a single changeset or file revision'''
598 594 log = self.repo.changelog
599 595 if changenode is None:
600 596 changenode = log.node(rev)
601 597 elif not rev:
602 598 rev = log.rev(changenode)
603 599
604 600 if self.ui.quiet:
605 601 self.ui.write("%d:%s\n" % (rev, short(changenode)))
606 602 return
607 603
608 604 changes = log.read(changenode)
609 605 date = util.datestr(changes[2])
610 606 extra = changes[5]
611 607 branch = extra.get("branch")
612 608
613 609 hexfunc = self.ui.debugflag and hex or short
614 610
615 611 parents = [(p, hexfunc(log.node(p)))
616 612 for p in self._meaningful_parentrevs(log, rev)]
617 613
618 614 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
619 615
620 616 # don't show the default branch name
621 617 if branch != 'default':
622 618 branch = util.tolocal(branch)
623 619 self.ui.write(_("branch: %s\n") % branch)
624 620 for tag in self.repo.nodetags(changenode):
625 621 self.ui.write(_("tag: %s\n") % tag)
626 622 for parent in parents:
627 623 self.ui.write(_("parent: %d:%s\n") % parent)
628 624
629 625 if self.ui.debugflag:
630 626 self.ui.write(_("manifest: %d:%s\n") %
631 627 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
632 628 self.ui.write(_("user: %s\n") % changes[1])
633 629 self.ui.write(_("date: %s\n") % date)
634 630
635 631 if self.ui.debugflag:
636 632 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
637 633 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
638 634 files):
639 635 if value:
640 636 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
641 637 elif changes[3] and self.ui.verbose:
642 638 self.ui.write(_("files: %s\n") % " ".join(changes[3]))
643 639 if copies and self.ui.verbose:
644 640 copies = ['%s (%s)' % c for c in copies]
645 641 self.ui.write(_("copies: %s\n") % ' '.join(copies))
646 642
647 643 if extra and self.ui.debugflag:
648 644 extraitems = extra.items()
649 645 extraitems.sort()
650 646 for key, value in extraitems:
651 647 self.ui.write(_("extra: %s=%s\n")
652 648 % (key, value.encode('string_escape')))
653 649
654 650 description = changes[4].strip()
655 651 if description:
656 652 if self.ui.verbose:
657 653 self.ui.write(_("description:\n"))
658 654 self.ui.write(description)
659 655 self.ui.write("\n\n")
660 656 else:
661 657 self.ui.write(_("summary: %s\n") %
662 658 description.splitlines()[0])
663 659 self.ui.write("\n")
664 660
665 661 self.showpatch(changenode)
666 662
667 663 def showpatch(self, node):
668 664 if self.patch:
669 665 prev = self.repo.changelog.parents(node)[0]
670 666 patch.diff(self.repo, prev, node, match=self.patch, fp=self.ui,
671 667 opts=patch.diffopts(self.ui))
672 668 self.ui.write("\n")
673 669
674 670 def _meaningful_parentrevs(self, log, rev):
675 671 """Return list of meaningful (or all if debug) parentrevs for rev.
676 672
677 673 For merges (two non-nullrev revisions) both parents are meaningful.
678 674 Otherwise the first parent revision is considered meaningful if it
679 675 is not the preceding revision.
680 676 """
681 677 parents = log.parentrevs(rev)
682 678 if not self.ui.debugflag and parents[1] == nullrev:
683 679 if parents[0] >= rev - 1:
684 680 parents = []
685 681 else:
686 682 parents = [parents[0]]
687 683 return parents
688 684
689 685
690 686 class changeset_templater(changeset_printer):
691 687 '''format changeset information.'''
692 688
693 689 def __init__(self, ui, repo, patch, mapfile, buffered):
694 690 changeset_printer.__init__(self, ui, repo, patch, buffered)
695 691 filters = templatefilters.filters.copy()
696 692 filters['formatnode'] = (ui.debugflag and (lambda x: x)
697 693 or (lambda x: x[:12]))
698 694 self.t = templater.templater(mapfile, filters,
699 695 cache={
700 696 'parent': '{rev}:{node|formatnode} ',
701 697 'manifest': '{rev}:{node|formatnode}',
702 698 'filecopy': '{name} ({source})'})
703 699
704 700 def use_template(self, t):
705 701 '''set template string to use'''
706 702 self.t.cache['changeset'] = t
707 703
708 704 def _show(self, rev, changenode, copies, props):
709 705 '''show a single changeset or file revision'''
710 706 log = self.repo.changelog
711 707 if changenode is None:
712 708 changenode = log.node(rev)
713 709 elif not rev:
714 710 rev = log.rev(changenode)
715 711
716 712 changes = log.read(changenode)
717 713
718 714 def showlist(name, values, plural=None, **args):
719 715 '''expand set of values.
720 716 name is name of key in template map.
721 717 values is list of strings or dicts.
722 718 plural is plural of name, if not simply name + 's'.
723 719
724 720 expansion works like this, given name 'foo'.
725 721
726 722 if values is empty, expand 'no_foos'.
727 723
728 724 if 'foo' not in template map, return values as a string,
729 725 joined by space.
730 726
731 727 expand 'start_foos'.
732 728
733 729 for each value, expand 'foo'. if 'last_foo' in template
734 730 map, expand it instead of 'foo' for last key.
735 731
736 732 expand 'end_foos'.
737 733 '''
738 734 if plural: names = plural
739 735 else: names = name + 's'
740 736 if not values:
741 737 noname = 'no_' + names
742 738 if noname in self.t:
743 739 yield self.t(noname, **args)
744 740 return
745 741 if name not in self.t:
746 742 if isinstance(values[0], str):
747 743 yield ' '.join(values)
748 744 else:
749 745 for v in values:
750 746 yield dict(v, **args)
751 747 return
752 748 startname = 'start_' + names
753 749 if startname in self.t:
754 750 yield self.t(startname, **args)
755 751 vargs = args.copy()
756 752 def one(v, tag=name):
757 753 try:
758 754 vargs.update(v)
759 755 except (AttributeError, ValueError):
760 756 try:
761 757 for a, b in v:
762 758 vargs[a] = b
763 759 except ValueError:
764 760 vargs[name] = v
765 761 return self.t(tag, **vargs)
766 762 lastname = 'last_' + name
767 763 if lastname in self.t:
768 764 last = values.pop()
769 765 else:
770 766 last = None
771 767 for v in values:
772 768 yield one(v)
773 769 if last is not None:
774 770 yield one(last, tag=lastname)
775 771 endname = 'end_' + names
776 772 if endname in self.t:
777 773 yield self.t(endname, **args)
778 774
779 775 def showbranches(**args):
780 776 branch = changes[5].get("branch")
781 777 if branch != 'default':
782 778 branch = util.tolocal(branch)
783 779 return showlist('branch', [branch], plural='branches', **args)
784 780
785 781 def showparents(**args):
786 782 parents = [[('rev', p), ('node', hex(log.node(p)))]
787 783 for p in self._meaningful_parentrevs(log, rev)]
788 784 return showlist('parent', parents, **args)
789 785
790 786 def showtags(**args):
791 787 return showlist('tag', self.repo.nodetags(changenode), **args)
792 788
793 789 def showextras(**args):
794 790 extras = changes[5].items()
795 791 extras.sort()
796 792 for key, value in extras:
797 793 args = args.copy()
798 794 args.update(dict(key=key, value=value))
799 795 yield self.t('extra', **args)
800 796
801 797 def showcopies(**args):
802 798 c = [{'name': x[0], 'source': x[1]} for x in copies]
803 799 return showlist('file_copy', c, plural='file_copies', **args)
804 800
805 801 files = []
806 802 def getfiles():
807 803 if not files:
808 804 files[:] = self.repo.status(
809 805 log.parents(changenode)[0], changenode)[:3]
810 806 return files
811 807 def showfiles(**args):
812 808 return showlist('file', changes[3], **args)
813 809 def showmods(**args):
814 810 return showlist('file_mod', getfiles()[0], **args)
815 811 def showadds(**args):
816 812 return showlist('file_add', getfiles()[1], **args)
817 813 def showdels(**args):
818 814 return showlist('file_del', getfiles()[2], **args)
819 815 def showmanifest(**args):
820 816 args = args.copy()
821 817 args.update(dict(rev=self.repo.manifest.rev(changes[0]),
822 818 node=hex(changes[0])))
823 819 return self.t('manifest', **args)
824 820
825 821 defprops = {
826 822 'author': changes[1],
827 823 'branches': showbranches,
828 824 'date': changes[2],
829 825 'desc': changes[4].strip(),
830 826 'file_adds': showadds,
831 827 'file_dels': showdels,
832 828 'file_mods': showmods,
833 829 'files': showfiles,
834 830 'file_copies': showcopies,
835 831 'manifest': showmanifest,
836 832 'node': hex(changenode),
837 833 'parents': showparents,
838 834 'rev': rev,
839 835 'tags': showtags,
840 836 'extras': showextras,
841 837 }
842 838 props = props.copy()
843 839 props.update(defprops)
844 840
845 841 try:
846 842 if self.ui.debugflag and 'header_debug' in self.t:
847 843 key = 'header_debug'
848 844 elif self.ui.quiet and 'header_quiet' in self.t:
849 845 key = 'header_quiet'
850 846 elif self.ui.verbose and 'header_verbose' in self.t:
851 847 key = 'header_verbose'
852 848 elif 'header' in self.t:
853 849 key = 'header'
854 850 else:
855 851 key = ''
856 852 if key:
857 853 h = templater.stringify(self.t(key, **props))
858 854 if self.buffered:
859 855 self.header[rev] = h
860 856 else:
861 857 self.ui.write(h)
862 858 if self.ui.debugflag and 'changeset_debug' in self.t:
863 859 key = 'changeset_debug'
864 860 elif self.ui.quiet and 'changeset_quiet' in self.t:
865 861 key = 'changeset_quiet'
866 862 elif self.ui.verbose and 'changeset_verbose' in self.t:
867 863 key = 'changeset_verbose'
868 864 else:
869 865 key = 'changeset'
870 866 self.ui.write(templater.stringify(self.t(key, **props)))
871 867 self.showpatch(changenode)
872 868 except KeyError, inst:
873 869 raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
874 870 inst.args[0]))
875 871 except SyntaxError, inst:
876 872 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
877 873
878 874 def show_changeset(ui, repo, opts, buffered=False, matchfn=False):
879 875 """show one changeset using template or regular display.
880 876
881 877 Display format will be the first non-empty hit of:
882 878 1. option 'template'
883 879 2. option 'style'
884 880 3. [ui] setting 'logtemplate'
885 881 4. [ui] setting 'style'
886 882 If all of these values are either the unset or the empty string,
887 883 regular display via changeset_printer() is done.
888 884 """
889 885 # options
890 886 patch = False
891 887 if opts.get('patch'):
892 888 patch = matchfn or util.always
893 889
894 890 tmpl = opts.get('template')
895 891 mapfile = None
896 892 if tmpl:
897 893 tmpl = templater.parsestring(tmpl, quoted=False)
898 894 else:
899 895 mapfile = opts.get('style')
900 896 # ui settings
901 897 if not mapfile:
902 898 tmpl = ui.config('ui', 'logtemplate')
903 899 if tmpl:
904 900 tmpl = templater.parsestring(tmpl)
905 901 else:
906 902 mapfile = ui.config('ui', 'style')
907 903
908 904 if tmpl or mapfile:
909 905 if mapfile:
910 906 if not os.path.split(mapfile)[0]:
911 907 mapname = (templater.templatepath('map-cmdline.' + mapfile)
912 908 or templater.templatepath(mapfile))
913 909 if mapname: mapfile = mapname
914 910 try:
915 911 t = changeset_templater(ui, repo, patch, mapfile, buffered)
916 912 except SyntaxError, inst:
917 913 raise util.Abort(inst.args[0])
918 914 if tmpl: t.use_template(tmpl)
919 915 return t
920 916 return changeset_printer(ui, repo, patch, buffered)
921 917
922 918 def finddate(ui, repo, date):
923 919 """Find the tipmost changeset that matches the given date spec"""
924 920 df = util.matchdate(date)
925 921 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
926 922 changeiter, matchfn = walkchangerevs(ui, repo, [], get, {'rev':None})
927 923 results = {}
928 924 for st, rev, fns in changeiter:
929 925 if st == 'add':
930 926 d = get(rev)[2]
931 927 if df(d[0]):
932 928 results[rev] = d
933 929 elif st == 'iter':
934 930 if rev in results:
935 931 ui.status("Found revision %s from %s\n" %
936 932 (rev, util.datestr(results[rev])))
937 933 return str(rev)
938 934
939 935 raise util.Abort(_("revision matching date not found"))
940 936
941 937 def walkchangerevs(ui, repo, pats, change, opts):
942 938 '''Iterate over files and the revs they changed in.
943 939
944 940 Callers most commonly need to iterate backwards over the history
945 941 it is interested in. Doing so has awful (quadratic-looking)
946 942 performance, so we use iterators in a "windowed" way.
947 943
948 944 We walk a window of revisions in the desired order. Within the
949 945 window, we first walk forwards to gather data, then in the desired
950 946 order (usually backwards) to display it.
951 947
952 948 This function returns an (iterator, matchfn) tuple. The iterator
953 949 yields 3-tuples. They will be of one of the following forms:
954 950
955 951 "window", incrementing, lastrev: stepping through a window,
956 952 positive if walking forwards through revs, last rev in the
957 953 sequence iterated over - use to reset state for the current window
958 954
959 955 "add", rev, fns: out-of-order traversal of the given file names
960 956 fns, which changed during revision rev - use to gather data for
961 957 possible display
962 958
963 959 "iter", rev, None: in-order traversal of the revs earlier iterated
964 960 over with "add" - use to display data'''
965 961
966 962 def increasing_windows(start, end, windowsize=8, sizelimit=512):
967 963 if start < end:
968 964 while start < end:
969 965 yield start, min(windowsize, end-start)
970 966 start += windowsize
971 967 if windowsize < sizelimit:
972 968 windowsize *= 2
973 969 else:
974 970 while start > end:
975 971 yield start, min(windowsize, start-end-1)
976 972 start -= windowsize
977 973 if windowsize < sizelimit:
978 974 windowsize *= 2
979 975
980 976 m = match(repo, pats, opts)
981 977 follow = opts.get('follow') or opts.get('follow_first')
982 978
983 979 if repo.changelog.count() == 0:
984 980 return [], m
985 981
986 982 if follow:
987 983 defrange = '%s:0' % repo.changectx().rev()
988 984 else:
989 985 defrange = '-1:0'
990 986 revs = revrange(repo, opts['rev'] or [defrange])
991 987 wanted = {}
992 988 slowpath = m.anypats() or opts.get('removed')
993 989 fncache = {}
994 990
995 991 if not slowpath and not m.files():
996 992 # No files, no patterns. Display all revs.
997 993 wanted = dict.fromkeys(revs)
998 994 copies = []
999 995 if not slowpath:
1000 996 # Only files, no patterns. Check the history of each file.
1001 997 def filerevgen(filelog, node):
1002 998 cl_count = repo.changelog.count()
1003 999 if node is None:
1004 1000 last = filelog.count() - 1
1005 1001 else:
1006 1002 last = filelog.rev(node)
1007 1003 for i, window in increasing_windows(last, nullrev):
1008 1004 revs = []
1009 1005 for j in xrange(i - window, i + 1):
1010 1006 n = filelog.node(j)
1011 1007 revs.append((filelog.linkrev(n),
1012 1008 follow and filelog.renamed(n)))
1013 1009 revs.reverse()
1014 1010 for rev in revs:
1015 1011 # only yield rev for which we have the changelog, it can
1016 1012 # happen while doing "hg log" during a pull or commit
1017 1013 if rev[0] < cl_count:
1018 1014 yield rev
1019 1015 def iterfiles():
1020 1016 for filename in m.files():
1021 1017 yield filename, None
1022 1018 for filename_node in copies:
1023 1019 yield filename_node
1024 1020 minrev, maxrev = min(revs), max(revs)
1025 1021 for file_, node in iterfiles():
1026 1022 filelog = repo.file(file_)
1027 1023 if filelog.count() == 0:
1028 1024 if node is None:
1029 1025 # A zero count may be a directory or deleted file, so
1030 1026 # try to find matching entries on the slow path.
1031 1027 slowpath = True
1032 1028 break
1033 1029 else:
1034 1030 ui.warn(_('%s:%s copy source revision cannot be found!\n')
1035 1031 % (file_, short(node)))
1036 1032 continue
1037 1033 for rev, copied in filerevgen(filelog, node):
1038 1034 if rev <= maxrev:
1039 1035 if rev < minrev:
1040 1036 break
1041 1037 fncache.setdefault(rev, [])
1042 1038 fncache[rev].append(file_)
1043 1039 wanted[rev] = 1
1044 1040 if follow and copied:
1045 1041 copies.append(copied)
1046 1042 if slowpath:
1047 1043 if follow:
1048 1044 raise util.Abort(_('can only follow copies/renames for explicit '
1049 1045 'file names'))
1050 1046
1051 1047 # The slow path checks files modified in every changeset.
1052 1048 def changerevgen():
1053 1049 for i, window in increasing_windows(repo.changelog.count()-1,
1054 1050 nullrev):
1055 1051 for j in xrange(i - window, i + 1):
1056 1052 yield j, change(j)[3]
1057 1053
1058 1054 for rev, changefiles in changerevgen():
1059 1055 matches = filter(m, changefiles)
1060 1056 if matches:
1061 1057 fncache[rev] = matches
1062 1058 wanted[rev] = 1
1063 1059
1064 1060 class followfilter:
1065 1061 def __init__(self, onlyfirst=False):
1066 1062 self.startrev = nullrev
1067 1063 self.roots = []
1068 1064 self.onlyfirst = onlyfirst
1069 1065
1070 1066 def match(self, rev):
1071 1067 def realparents(rev):
1072 1068 if self.onlyfirst:
1073 1069 return repo.changelog.parentrevs(rev)[0:1]
1074 1070 else:
1075 1071 return filter(lambda x: x != nullrev,
1076 1072 repo.changelog.parentrevs(rev))
1077 1073
1078 1074 if self.startrev == nullrev:
1079 1075 self.startrev = rev
1080 1076 return True
1081 1077
1082 1078 if rev > self.startrev:
1083 1079 # forward: all descendants
1084 1080 if not self.roots:
1085 1081 self.roots.append(self.startrev)
1086 1082 for parent in realparents(rev):
1087 1083 if parent in self.roots:
1088 1084 self.roots.append(rev)
1089 1085 return True
1090 1086 else:
1091 1087 # backwards: all parents
1092 1088 if not self.roots:
1093 1089 self.roots.extend(realparents(self.startrev))
1094 1090 if rev in self.roots:
1095 1091 self.roots.remove(rev)
1096 1092 self.roots.extend(realparents(rev))
1097 1093 return True
1098 1094
1099 1095 return False
1100 1096
1101 1097 # it might be worthwhile to do this in the iterator if the rev range
1102 1098 # is descending and the prune args are all within that range
1103 1099 for rev in opts.get('prune', ()):
1104 1100 rev = repo.changelog.rev(repo.lookup(rev))
1105 1101 ff = followfilter()
1106 1102 stop = min(revs[0], revs[-1])
1107 1103 for x in xrange(rev, stop-1, -1):
1108 1104 if ff.match(x) and x in wanted:
1109 1105 del wanted[x]
1110 1106
1111 1107 def iterate():
1112 1108 if follow and not m.files():
1113 1109 ff = followfilter(onlyfirst=opts.get('follow_first'))
1114 1110 def want(rev):
1115 1111 if ff.match(rev) and rev in wanted:
1116 1112 return True
1117 1113 return False
1118 1114 else:
1119 1115 def want(rev):
1120 1116 return rev in wanted
1121 1117
1122 1118 for i, window in increasing_windows(0, len(revs)):
1123 1119 yield 'window', revs[0] < revs[-1], revs[-1]
1124 1120 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
1125 1121 srevs = list(nrevs)
1126 1122 srevs.sort()
1127 1123 for rev in srevs:
1128 1124 fns = fncache.get(rev)
1129 1125 if not fns:
1130 1126 def fns_generator():
1131 1127 for f in change(rev)[3]:
1132 1128 if m(f):
1133 1129 yield f
1134 1130 fns = fns_generator()
1135 1131 yield 'add', rev, fns
1136 1132 for rev in nrevs:
1137 1133 yield 'iter', rev, None
1138 1134 return iterate(), m
1139 1135
1140 1136 def commit(ui, repo, commitfunc, pats, opts):
1141 1137 '''commit the specified files or all outstanding changes'''
1142 1138 date = opts.get('date')
1143 1139 if date:
1144 1140 opts['date'] = util.parsedate(date)
1145 1141 message = logmessage(opts)
1146 1142
1147 1143 # extract addremove carefully -- this function can be called from a command
1148 1144 # that doesn't support addremove
1149 1145 if opts.get('addremove'):
1150 1146 addremove(repo, pats, opts)
1151 1147
1152 1148 m = match(repo, pats, opts)
1153 1149 if pats:
1154 1150 status = repo.status(files=m.files(), match=m)
1155 1151 modified, added, removed, deleted, unknown = status[:5]
1156 1152 files = modified + added + removed
1157 1153 slist = None
1158 1154 for f in m.files():
1159 1155 if f == '.':
1160 1156 continue
1161 1157 if f not in files:
1162 1158 rf = repo.wjoin(f)
1163 1159 rel = repo.pathto(f)
1164 1160 try:
1165 1161 mode = os.lstat(rf)[stat.ST_MODE]
1166 1162 except OSError:
1167 1163 raise util.Abort(_("file %s not found!") % rel)
1168 1164 if stat.S_ISDIR(mode):
1169 1165 name = f + '/'
1170 1166 if slist is None:
1171 1167 slist = list(files)
1172 1168 slist.sort()
1173 1169 i = bisect.bisect(slist, name)
1174 1170 if i >= len(slist) or not slist[i].startswith(name):
1175 1171 raise util.Abort(_("no match under directory %s!")
1176 1172 % rel)
1177 1173 elif not (stat.S_ISREG(mode) or stat.S_ISLNK(mode)):
1178 1174 raise util.Abort(_("can't commit %s: "
1179 1175 "unsupported file type!") % rel)
1180 1176 elif f not in repo.dirstate:
1181 1177 raise util.Abort(_("file %s not tracked!") % rel)
1182 1178 else:
1183 1179 files = []
1184 1180 try:
1185 return commitfunc(ui, repo, files, message, match, opts)
1181 return commitfunc(ui, repo, files, message, m, opts)
1186 1182 except ValueError, inst:
1187 1183 raise util.Abort(str(inst))
@@ -1,3335 +1,3332 b''
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from node import hex, nullid, nullrev, short
9 9 from repo import RepoError, NoCapability
10 10 from i18n import _
11 11 import os, re, sys, urllib
12 12 import hg, util, revlog, bundlerepo, extensions, copies
13 13 import difflib, patch, time, help, mdiff, tempfile
14 14 import version, socket
15 15 import archival, changegroup, cmdutil, hgweb.server, sshserver, hbisect
16 16 import merge as merge_
17 17
18 18 # Commands start here, listed alphabetically
19 19
20 20 def add(ui, repo, *pats, **opts):
21 21 """add the specified files on the next commit
22 22
23 23 Schedule files to be version controlled and added to the repository.
24 24
25 25 The files will be added to the repository at the next commit. To
26 26 undo an add before that, see hg revert.
27 27
28 28 If no names are given, add all files in the repository.
29 29 """
30 30
31 31 rejected = None
32 32 exacts = {}
33 33 names = []
34 34 m = cmdutil.match(repo, pats, opts)
35 35 m.bad = lambda x,y: True
36 36 for src, abs, rel, exact in cmdutil.walk(repo, m):
37 37 if exact:
38 38 if ui.verbose:
39 39 ui.status(_('adding %s\n') % rel)
40 40 names.append(abs)
41 41 exacts[abs] = 1
42 42 elif abs not in repo.dirstate:
43 43 ui.status(_('adding %s\n') % rel)
44 44 names.append(abs)
45 45 if not opts.get('dry_run'):
46 46 rejected = repo.add(names)
47 47 rejected = [p for p in rejected if p in exacts]
48 48 return rejected and 1 or 0
49 49
50 50 def addremove(ui, repo, *pats, **opts):
51 51 """add all new files, delete all missing files
52 52
53 53 Add all new files and remove all missing files from the repository.
54 54
55 55 New files are ignored if they match any of the patterns in .hgignore. As
56 56 with add, these changes take effect at the next commit.
57 57
58 58 Use the -s option to detect renamed files. With a parameter > 0,
59 59 this compares every removed file with every added file and records
60 60 those similar enough as renames. This option takes a percentage
61 61 between 0 (disabled) and 100 (files must be identical) as its
62 62 parameter. Detecting renamed files this way can be expensive.
63 63 """
64 64 try:
65 65 sim = float(opts.get('similarity') or 0)
66 66 except ValueError:
67 67 raise util.Abort(_('similarity must be a number'))
68 68 if sim < 0 or sim > 100:
69 69 raise util.Abort(_('similarity must be between 0 and 100'))
70 70 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
71 71
72 72 def annotate(ui, repo, *pats, **opts):
73 73 """show changeset information per file line
74 74
75 75 List changes in files, showing the revision id responsible for each line
76 76
77 77 This command is useful to discover who did a change or when a change took
78 78 place.
79 79
80 80 Without the -a option, annotate will avoid processing files it
81 81 detects as binary. With -a, annotate will generate an annotation
82 82 anyway, probably with undesirable results.
83 83 """
84 84 datefunc = ui.quiet and util.shortdate or util.datestr
85 85 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
86 86
87 87 if not pats:
88 88 raise util.Abort(_('at least one file name or pattern required'))
89 89
90 90 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
91 91 ('number', lambda x: str(x[0].rev())),
92 92 ('changeset', lambda x: short(x[0].node())),
93 93 ('date', getdate),
94 94 ('follow', lambda x: x[0].path()),
95 95 ]
96 96
97 97 if (not opts['user'] and not opts['changeset'] and not opts['date']
98 98 and not opts['follow']):
99 99 opts['number'] = 1
100 100
101 101 linenumber = opts.get('line_number') is not None
102 102 if (linenumber and (not opts['changeset']) and (not opts['number'])):
103 103 raise util.Abort(_('at least one of -n/-c is required for -l'))
104 104
105 105 funcmap = [func for op, func in opmap if opts.get(op)]
106 106 if linenumber:
107 107 lastfunc = funcmap[-1]
108 108 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
109 109
110 110 ctx = repo.changectx(opts['rev'])
111 111
112 112 m = cmdutil.match(repo, pats, opts)
113 113 for src, abs, rel, exact in cmdutil.walk(repo, m, ctx.node()):
114 114 fctx = ctx.filectx(abs)
115 115 if not opts['text'] and util.binary(fctx.data()):
116 116 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
117 117 continue
118 118
119 119 lines = fctx.annotate(follow=opts.get('follow'),
120 120 linenumber=linenumber)
121 121 pieces = []
122 122
123 123 for f in funcmap:
124 124 l = [f(n) for n, dummy in lines]
125 125 if l:
126 126 m = max(map(len, l))
127 127 pieces.append(["%*s" % (m, x) for x in l])
128 128
129 129 if pieces:
130 130 for p, l in zip(zip(*pieces), lines):
131 131 ui.write("%s: %s" % (" ".join(p), l[1]))
132 132
133 133 def archive(ui, repo, dest, **opts):
134 134 '''create unversioned archive of a repository revision
135 135
136 136 By default, the revision used is the parent of the working
137 137 directory; use "-r" to specify a different revision.
138 138
139 139 To specify the type of archive to create, use "-t". Valid
140 140 types are:
141 141
142 142 "files" (default): a directory full of files
143 143 "tar": tar archive, uncompressed
144 144 "tbz2": tar archive, compressed using bzip2
145 145 "tgz": tar archive, compressed using gzip
146 146 "uzip": zip archive, uncompressed
147 147 "zip": zip archive, compressed using deflate
148 148
149 149 The exact name of the destination archive or directory is given
150 150 using a format string; see "hg help export" for details.
151 151
152 152 Each member added to an archive file has a directory prefix
153 153 prepended. Use "-p" to specify a format string for the prefix.
154 154 The default is the basename of the archive, with suffixes removed.
155 155 '''
156 156
157 157 ctx = repo.changectx(opts['rev'])
158 158 if not ctx:
159 159 raise util.Abort(_('repository has no revisions'))
160 160 node = ctx.node()
161 161 dest = cmdutil.make_filename(repo, dest, node)
162 162 if os.path.realpath(dest) == repo.root:
163 163 raise util.Abort(_('repository root cannot be destination'))
164 164 matchfn = cmdutil.match(repo, [], opts)
165 165 kind = opts.get('type') or 'files'
166 166 prefix = opts['prefix']
167 167 if dest == '-':
168 168 if kind == 'files':
169 169 raise util.Abort(_('cannot archive plain files to stdout'))
170 170 dest = sys.stdout
171 171 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
172 172 prefix = cmdutil.make_filename(repo, prefix, node)
173 173 archival.archive(repo, dest, node, kind, not opts['no_decode'],
174 174 matchfn, prefix)
175 175
176 176 def backout(ui, repo, node=None, rev=None, **opts):
177 177 '''reverse effect of earlier changeset
178 178
179 179 Commit the backed out changes as a new changeset. The new
180 180 changeset is a child of the backed out changeset.
181 181
182 182 If you back out a changeset other than the tip, a new head is
183 183 created. This head will be the new tip and you should merge this
184 184 backout changeset with another head (current one by default).
185 185
186 186 The --merge option remembers the parent of the working directory
187 187 before starting the backout, then merges the new head with that
188 188 changeset afterwards. This saves you from doing the merge by
189 189 hand. The result of this merge is not committed, as for a normal
190 190 merge.
191 191
192 192 See 'hg help dates' for a list of formats valid for -d/--date.
193 193 '''
194 194 if rev and node:
195 195 raise util.Abort(_("please specify just one revision"))
196 196
197 197 if not rev:
198 198 rev = node
199 199
200 200 if not rev:
201 201 raise util.Abort(_("please specify a revision to backout"))
202 202
203 203 date = opts.get('date')
204 204 if date:
205 205 opts['date'] = util.parsedate(date)
206 206
207 207 cmdutil.bail_if_changed(repo)
208 208 node = repo.lookup(rev)
209 209
210 210 op1, op2 = repo.dirstate.parents()
211 211 a = repo.changelog.ancestor(op1, node)
212 212 if a != node:
213 213 raise util.Abort(_('cannot back out change on a different branch'))
214 214
215 215 p1, p2 = repo.changelog.parents(node)
216 216 if p1 == nullid:
217 217 raise util.Abort(_('cannot back out a change with no parents'))
218 218 if p2 != nullid:
219 219 if not opts['parent']:
220 220 raise util.Abort(_('cannot back out a merge changeset without '
221 221 '--parent'))
222 222 p = repo.lookup(opts['parent'])
223 223 if p not in (p1, p2):
224 224 raise util.Abort(_('%s is not a parent of %s') %
225 225 (short(p), short(node)))
226 226 parent = p
227 227 else:
228 228 if opts['parent']:
229 229 raise util.Abort(_('cannot use --parent on non-merge changeset'))
230 230 parent = p1
231 231
232 232 # the backout should appear on the same branch
233 233 branch = repo.dirstate.branch()
234 234 hg.clean(repo, node, show_stats=False)
235 235 repo.dirstate.setbranch(branch)
236 236 revert_opts = opts.copy()
237 237 revert_opts['date'] = None
238 238 revert_opts['all'] = True
239 239 revert_opts['rev'] = hex(parent)
240 240 revert_opts['no_backup'] = None
241 241 revert(ui, repo, **revert_opts)
242 242 commit_opts = opts.copy()
243 243 commit_opts['addremove'] = False
244 244 if not commit_opts['message'] and not commit_opts['logfile']:
245 245 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
246 246 commit_opts['force_editor'] = True
247 247 commit(ui, repo, **commit_opts)
248 248 def nice(node):
249 249 return '%d:%s' % (repo.changelog.rev(node), short(node))
250 250 ui.status(_('changeset %s backs out changeset %s\n') %
251 251 (nice(repo.changelog.tip()), nice(node)))
252 252 if op1 != node:
253 253 hg.clean(repo, op1, show_stats=False)
254 254 if opts['merge']:
255 255 ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
256 256 hg.merge(repo, hex(repo.changelog.tip()))
257 257 else:
258 258 ui.status(_('the backout changeset is a new head - '
259 259 'do not forget to merge\n'))
260 260 ui.status(_('(use "backout --merge" '
261 261 'if you want to auto-merge)\n'))
262 262
263 263 def bisect(ui, repo, rev=None, extra=None,
264 264 reset=None, good=None, bad=None, skip=None, noupdate=None):
265 265 """subdivision search of changesets
266 266
267 267 This command helps to find changesets which introduce problems.
268 268 To use, mark the earliest changeset you know exhibits the problem
269 269 as bad, then mark the latest changeset which is free from the
270 270 problem as good. Bisect will update your working directory to a
271 271 revision for testing. Once you have performed tests, mark the
272 272 working directory as bad or good and bisect will either update to
273 273 another candidate changeset or announce that it has found the bad
274 274 revision.
275 275 """
276 276 # backward compatibility
277 277 if rev in "good bad reset init".split():
278 278 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
279 279 cmd, rev, extra = rev, extra, None
280 280 if cmd == "good":
281 281 good = True
282 282 elif cmd == "bad":
283 283 bad = True
284 284 else:
285 285 reset = True
286 286 elif extra or good + bad + skip + reset > 1:
287 287 raise util.Abort("Incompatible arguments")
288 288
289 289 if reset:
290 290 p = repo.join("bisect.state")
291 291 if os.path.exists(p):
292 292 os.unlink(p)
293 293 return
294 294
295 295 # load state
296 296 state = {'good': [], 'bad': [], 'skip': []}
297 297 if os.path.exists(repo.join("bisect.state")):
298 298 for l in repo.opener("bisect.state"):
299 299 kind, node = l[:-1].split()
300 300 node = repo.lookup(node)
301 301 if kind not in state:
302 302 raise util.Abort(_("unknown bisect kind %s") % kind)
303 303 state[kind].append(node)
304 304
305 305 # update state
306 306 node = repo.lookup(rev or '.')
307 307 if good:
308 308 state['good'].append(node)
309 309 elif bad:
310 310 state['bad'].append(node)
311 311 elif skip:
312 312 state['skip'].append(node)
313 313
314 314 # save state
315 315 f = repo.opener("bisect.state", "w", atomictemp=True)
316 316 wlock = repo.wlock()
317 317 try:
318 318 for kind in state:
319 319 for node in state[kind]:
320 320 f.write("%s %s\n" % (kind, hex(node)))
321 321 f.rename()
322 322 finally:
323 323 del wlock
324 324
325 325 if not state['good'] or not state['bad']:
326 326 return
327 327
328 328 # actually bisect
329 329 node, changesets, good = hbisect.bisect(repo.changelog, state)
330 330 if changesets == 0:
331 331 ui.write(_("The first %s revision is:\n") % (good and "good" or "bad"))
332 332 displayer = cmdutil.show_changeset(ui, repo, {})
333 333 displayer.show(changenode=node)
334 334 elif node is not None:
335 335 # compute the approximate number of remaining tests
336 336 tests, size = 0, 2
337 337 while size <= changesets:
338 338 tests, size = tests + 1, size * 2
339 339 rev = repo.changelog.rev(node)
340 340 ui.write(_("Testing changeset %s:%s "
341 341 "(%s changesets remaining, ~%s tests)\n")
342 342 % (rev, short(node), changesets, tests))
343 343 if not noupdate:
344 344 cmdutil.bail_if_changed(repo)
345 345 return hg.clean(repo, node)
346 346
347 347 def branch(ui, repo, label=None, **opts):
348 348 """set or show the current branch name
349 349
350 350 With no argument, show the current branch name. With one argument,
351 351 set the working directory branch name (the branch does not exist in
352 352 the repository until the next commit).
353 353
354 354 Unless --force is specified, branch will not let you set a
355 355 branch name that shadows an existing branch.
356 356
357 357 Use the command 'hg update' to switch to an existing branch.
358 358 """
359 359
360 360 if label:
361 361 if not opts.get('force') and label in repo.branchtags():
362 362 if label not in [p.branch() for p in repo.workingctx().parents()]:
363 363 raise util.Abort(_('a branch of the same name already exists'
364 364 ' (use --force to override)'))
365 365 repo.dirstate.setbranch(util.fromlocal(label))
366 366 ui.status(_('marked working directory as branch %s\n') % label)
367 367 else:
368 368 ui.write("%s\n" % util.tolocal(repo.dirstate.branch()))
369 369
370 370 def branches(ui, repo, active=False):
371 371 """list repository named branches
372 372
373 373 List the repository's named branches, indicating which ones are
374 374 inactive. If active is specified, only show active branches.
375 375
376 376 A branch is considered active if it contains unmerged heads.
377 377
378 378 Use the command 'hg update' to switch to an existing branch.
379 379 """
380 380 b = repo.branchtags()
381 381 heads = dict.fromkeys(repo.heads(), 1)
382 382 l = [((n in heads), repo.changelog.rev(n), n, t) for t, n in b.items()]
383 383 l.sort()
384 384 l.reverse()
385 385 for ishead, r, n, t in l:
386 386 if active and not ishead:
387 387 # If we're only displaying active branches, abort the loop on
388 388 # encountering the first inactive head
389 389 break
390 390 else:
391 391 hexfunc = ui.debugflag and hex or short
392 392 if ui.quiet:
393 393 ui.write("%s\n" % t)
394 394 else:
395 395 spaces = " " * (30 - util.locallen(t))
396 396 # The code only gets here if inactive branches are being
397 397 # displayed or the branch is active.
398 398 isinactive = ((not ishead) and " (inactive)") or ''
399 399 ui.write("%s%s %s:%s%s\n" % (t, spaces, r, hexfunc(n), isinactive))
400 400
401 401 def bundle(ui, repo, fname, dest=None, **opts):
402 402 """create a changegroup file
403 403
404 404 Generate a compressed changegroup file collecting changesets not
405 405 found in the other repository.
406 406
407 407 If no destination repository is specified the destination is
408 408 assumed to have all the nodes specified by one or more --base
409 409 parameters. To create a bundle containing all changesets, use
410 410 --all (or --base null). To change the compression method applied,
411 411 use the -t option (by default, bundles are compressed using bz2).
412 412
413 413 The bundle file can then be transferred using conventional means and
414 414 applied to another repository with the unbundle or pull command.
415 415 This is useful when direct push and pull are not available or when
416 416 exporting an entire repository is undesirable.
417 417
418 418 Applying bundles preserves all changeset contents including
419 419 permissions, copy/rename information, and revision history.
420 420 """
421 421 revs = opts.get('rev') or None
422 422 if revs:
423 423 revs = [repo.lookup(rev) for rev in revs]
424 424 if opts.get('all'):
425 425 base = ['null']
426 426 else:
427 427 base = opts.get('base')
428 428 if base:
429 429 if dest:
430 430 raise util.Abort(_("--base is incompatible with specifiying "
431 431 "a destination"))
432 432 base = [repo.lookup(rev) for rev in base]
433 433 # create the right base
434 434 # XXX: nodesbetween / changegroup* should be "fixed" instead
435 435 o = []
436 436 has = {nullid: None}
437 437 for n in base:
438 438 has.update(repo.changelog.reachable(n))
439 439 if revs:
440 440 visit = list(revs)
441 441 else:
442 442 visit = repo.changelog.heads()
443 443 seen = {}
444 444 while visit:
445 445 n = visit.pop(0)
446 446 parents = [p for p in repo.changelog.parents(n) if p not in has]
447 447 if len(parents) == 0:
448 448 o.insert(0, n)
449 449 else:
450 450 for p in parents:
451 451 if p not in seen:
452 452 seen[p] = 1
453 453 visit.append(p)
454 454 else:
455 455 cmdutil.setremoteconfig(ui, opts)
456 456 dest, revs, checkout = hg.parseurl(
457 457 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
458 458 other = hg.repository(ui, dest)
459 459 o = repo.findoutgoing(other, force=opts['force'])
460 460
461 461 if revs:
462 462 cg = repo.changegroupsubset(o, revs, 'bundle')
463 463 else:
464 464 cg = repo.changegroup(o, 'bundle')
465 465
466 466 bundletype = opts.get('type', 'bzip2').lower()
467 467 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
468 468 bundletype = btypes.get(bundletype)
469 469 if bundletype not in changegroup.bundletypes:
470 470 raise util.Abort(_('unknown bundle type specified with --type'))
471 471
472 472 changegroup.writebundle(cg, fname, bundletype)
473 473
474 474 def cat(ui, repo, file1, *pats, **opts):
475 475 """output the current or given revision of files
476 476
477 477 Print the specified files as they were at the given revision.
478 478 If no revision is given, the parent of the working directory is used,
479 479 or tip if no revision is checked out.
480 480
481 481 Output may be to a file, in which case the name of the file is
482 482 given using a format string. The formatting rules are the same as
483 483 for the export command, with the following additions:
484 484
485 485 %s basename of file being printed
486 486 %d dirname of file being printed, or '.' if in repo root
487 487 %p root-relative path name of file being printed
488 488 """
489 489 ctx = repo.changectx(opts['rev'])
490 490 err = 1
491 491 m = cmdutil.match(repo, (file1,) + pats, opts)
492 492 for src, abs, rel, exact in cmdutil.walk(repo, m, ctx.node()):
493 493 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
494 494 data = ctx.filectx(abs).data()
495 495 if opts.get('decode'):
496 496 data = repo.wwritedata(abs, data)
497 497 fp.write(data)
498 498 err = 0
499 499 return err
500 500
501 501 def clone(ui, source, dest=None, **opts):
502 502 """make a copy of an existing repository
503 503
504 504 Create a copy of an existing repository in a new directory.
505 505
506 506 If no destination directory name is specified, it defaults to the
507 507 basename of the source.
508 508
509 509 The location of the source is added to the new repository's
510 510 .hg/hgrc file, as the default to be used for future pulls.
511 511
512 512 For efficiency, hardlinks are used for cloning whenever the source
513 513 and destination are on the same filesystem (note this applies only
514 514 to the repository data, not to the checked out files). Some
515 515 filesystems, such as AFS, implement hardlinking incorrectly, but
516 516 do not report errors. In these cases, use the --pull option to
517 517 avoid hardlinking.
518 518
519 519 In some cases, you can clone repositories and checked out files
520 520 using full hardlinks with
521 521
522 522 $ cp -al REPO REPOCLONE
523 523
524 524 This is the fastest way to clone, but it is not always safe. The
525 525 operation is not atomic (making sure REPO is not modified during
526 526 the operation is up to you) and you have to make sure your editor
527 527 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
528 528 this is not compatible with certain extensions that place their
529 529 metadata under the .hg directory, such as mq.
530 530
531 531 If you use the -r option to clone up to a specific revision, no
532 532 subsequent revisions will be present in the cloned repository.
533 533 This option implies --pull, even on local repositories.
534 534
535 535 See pull for valid source format details.
536 536
537 537 It is possible to specify an ssh:// URL as the destination, but no
538 538 .hg/hgrc and working directory will be created on the remote side.
539 539 Look at the help text for the pull command for important details
540 540 about ssh:// URLs.
541 541 """
542 542 cmdutil.setremoteconfig(ui, opts)
543 543 hg.clone(ui, source, dest,
544 544 pull=opts['pull'],
545 545 stream=opts['uncompressed'],
546 546 rev=opts['rev'],
547 547 update=not opts['noupdate'])
548 548
549 549 def commit(ui, repo, *pats, **opts):
550 550 """commit the specified files or all outstanding changes
551 551
552 552 Commit changes to the given files into the repository.
553 553
554 554 If a list of files is omitted, all changes reported by "hg status"
555 555 will be committed.
556 556
557 557 If you are committing the result of a merge, do not provide any
558 558 file names or -I/-X filters.
559 559
560 560 If no commit message is specified, the configured editor is started to
561 561 enter a message.
562 562
563 563 See 'hg help dates' for a list of formats valid for -d/--date.
564 564 """
565 565 def commitfunc(ui, repo, files, message, match, opts):
566 566 return repo.commit(files, message, opts['user'], opts['date'], match,
567 567 force_editor=opts.get('force_editor'))
568 568
569 569 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
570 570 if not node:
571 571 return
572 572 cl = repo.changelog
573 573 rev = cl.rev(node)
574 574 parents = cl.parentrevs(rev)
575 575 if rev - 1 in parents:
576 576 # one of the parents was the old tip
577 577 return
578 578 if (parents == (nullrev, nullrev) or
579 579 len(cl.heads(cl.node(parents[0]))) > 1 and
580 580 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
581 581 ui.status(_('created new head\n'))
582 582
583 583 def copy(ui, repo, *pats, **opts):
584 584 """mark files as copied for the next commit
585 585
586 586 Mark dest as having copies of source files. If dest is a
587 587 directory, copies are put in that directory. If dest is a file,
588 588 there can only be one source.
589 589
590 590 By default, this command copies the contents of files as they
591 591 stand in the working directory. If invoked with --after, the
592 592 operation is recorded, but no copying is performed.
593 593
594 594 This command takes effect in the next commit. To undo a copy
595 595 before that, see hg revert.
596 596 """
597 597 wlock = repo.wlock(False)
598 598 try:
599 599 return cmdutil.copy(ui, repo, pats, opts)
600 600 finally:
601 601 del wlock
602 602
603 603 def debugancestor(ui, repo, *args):
604 604 """find the ancestor revision of two revisions in a given index"""
605 605 if len(args) == 3:
606 606 index, rev1, rev2 = args
607 607 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
608 608 lookup = r.lookup
609 609 elif len(args) == 2:
610 610 if not repo:
611 611 raise util.Abort(_("There is no Mercurial repository here "
612 612 "(.hg not found)"))
613 613 rev1, rev2 = args
614 614 r = repo.changelog
615 615 lookup = repo.lookup
616 616 else:
617 617 raise util.Abort(_('either two or three arguments required'))
618 618 a = r.ancestor(lookup(rev1), lookup(rev2))
619 619 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
620 620
621 621 def debugcomplete(ui, cmd='', **opts):
622 622 """returns the completion list associated with the given command"""
623 623
624 624 if opts['options']:
625 625 options = []
626 626 otables = [globalopts]
627 627 if cmd:
628 628 aliases, entry = cmdutil.findcmd(ui, cmd, table)
629 629 otables.append(entry[1])
630 630 for t in otables:
631 631 for o in t:
632 632 if o[0]:
633 633 options.append('-%s' % o[0])
634 634 options.append('--%s' % o[1])
635 635 ui.write("%s\n" % "\n".join(options))
636 636 return
637 637
638 638 clist = cmdutil.findpossible(ui, cmd, table).keys()
639 639 clist.sort()
640 640 ui.write("%s\n" % "\n".join(clist))
641 641
642 642 def debugfsinfo(ui, path = "."):
643 643 file('.debugfsinfo', 'w').write('')
644 644 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
645 645 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
646 646 ui.write('case-sensitive: %s\n' % (util.checkfolding('.debugfsinfo')
647 647 and 'yes' or 'no'))
648 648 os.unlink('.debugfsinfo')
649 649
650 650 def debugrebuildstate(ui, repo, rev=""):
651 651 """rebuild the dirstate as it would look like for the given revision"""
652 652 if rev == "":
653 653 rev = repo.changelog.tip()
654 654 ctx = repo.changectx(rev)
655 655 files = ctx.manifest()
656 656 wlock = repo.wlock()
657 657 try:
658 658 repo.dirstate.rebuild(rev, files)
659 659 finally:
660 660 del wlock
661 661
662 662 def debugcheckstate(ui, repo):
663 663 """validate the correctness of the current dirstate"""
664 664 parent1, parent2 = repo.dirstate.parents()
665 665 m1 = repo.changectx(parent1).manifest()
666 666 m2 = repo.changectx(parent2).manifest()
667 667 errors = 0
668 668 for f in repo.dirstate:
669 669 state = repo.dirstate[f]
670 670 if state in "nr" and f not in m1:
671 671 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
672 672 errors += 1
673 673 if state in "a" and f in m1:
674 674 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
675 675 errors += 1
676 676 if state in "m" and f not in m1 and f not in m2:
677 677 ui.warn(_("%s in state %s, but not in either manifest\n") %
678 678 (f, state))
679 679 errors += 1
680 680 for f in m1:
681 681 state = repo.dirstate[f]
682 682 if state not in "nrm":
683 683 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
684 684 errors += 1
685 685 if errors:
686 686 error = _(".hg/dirstate inconsistent with current parent's manifest")
687 687 raise util.Abort(error)
688 688
689 689 def showconfig(ui, repo, *values, **opts):
690 690 """show combined config settings from all hgrc files
691 691
692 692 With no args, print names and values of all config items.
693 693
694 694 With one arg of the form section.name, print just the value of
695 695 that config item.
696 696
697 697 With multiple args, print names and values of all config items
698 698 with matching section names."""
699 699
700 700 untrusted = bool(opts.get('untrusted'))
701 701 if values:
702 702 if len([v for v in values if '.' in v]) > 1:
703 703 raise util.Abort(_('only one config item permitted'))
704 704 for section, name, value in ui.walkconfig(untrusted=untrusted):
705 705 sectname = section + '.' + name
706 706 if values:
707 707 for v in values:
708 708 if v == section:
709 709 ui.write('%s=%s\n' % (sectname, value))
710 710 elif v == sectname:
711 711 ui.write(value, '\n')
712 712 else:
713 713 ui.write('%s=%s\n' % (sectname, value))
714 714
715 715 def debugsetparents(ui, repo, rev1, rev2=None):
716 716 """manually set the parents of the current working directory
717 717
718 718 This is useful for writing repository conversion tools, but should
719 719 be used with care.
720 720 """
721 721
722 722 if not rev2:
723 723 rev2 = hex(nullid)
724 724
725 725 wlock = repo.wlock()
726 726 try:
727 727 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
728 728 finally:
729 729 del wlock
730 730
731 731 def debugstate(ui, repo, nodates=None):
732 732 """show the contents of the current dirstate"""
733 733 k = repo.dirstate._map.items()
734 734 k.sort()
735 735 timestr = ""
736 736 showdate = not nodates
737 737 for file_, ent in k:
738 738 if showdate:
739 739 if ent[3] == -1:
740 740 # Pad or slice to locale representation
741 741 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0)))
742 742 timestr = 'unset'
743 743 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
744 744 else:
745 745 timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3]))
746 746 if ent[1] & 020000:
747 747 mode = 'lnk'
748 748 else:
749 749 mode = '%3o' % (ent[1] & 0777)
750 750 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
751 751 for f in repo.dirstate.copies():
752 752 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
753 753
754 754 def debugdata(ui, file_, rev):
755 755 """dump the contents of a data file revision"""
756 756 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
757 757 try:
758 758 ui.write(r.revision(r.lookup(rev)))
759 759 except KeyError:
760 760 raise util.Abort(_('invalid revision identifier %s') % rev)
761 761
762 762 def debugdate(ui, date, range=None, **opts):
763 763 """parse and display a date"""
764 764 if opts["extended"]:
765 765 d = util.parsedate(date, util.extendeddateformats)
766 766 else:
767 767 d = util.parsedate(date)
768 768 ui.write("internal: %s %s\n" % d)
769 769 ui.write("standard: %s\n" % util.datestr(d))
770 770 if range:
771 771 m = util.matchdate(range)
772 772 ui.write("match: %s\n" % m(d[0]))
773 773
774 774 def debugindex(ui, file_):
775 775 """dump the contents of an index file"""
776 776 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
777 777 ui.write(" rev offset length base linkrev" +
778 778 " nodeid p1 p2\n")
779 779 for i in xrange(r.count()):
780 780 node = r.node(i)
781 781 try:
782 782 pp = r.parents(node)
783 783 except:
784 784 pp = [nullid, nullid]
785 785 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
786 786 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
787 787 short(node), short(pp[0]), short(pp[1])))
788 788
789 789 def debugindexdot(ui, file_):
790 790 """dump an index DAG as a .dot file"""
791 791 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
792 792 ui.write("digraph G {\n")
793 793 for i in xrange(r.count()):
794 794 node = r.node(i)
795 795 pp = r.parents(node)
796 796 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
797 797 if pp[1] != nullid:
798 798 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
799 799 ui.write("}\n")
800 800
801 801 def debuginstall(ui):
802 802 '''test Mercurial installation'''
803 803
804 804 def writetemp(contents):
805 805 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
806 806 f = os.fdopen(fd, "wb")
807 807 f.write(contents)
808 808 f.close()
809 809 return name
810 810
811 811 problems = 0
812 812
813 813 # encoding
814 814 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
815 815 try:
816 816 util.fromlocal("test")
817 817 except util.Abort, inst:
818 818 ui.write(" %s\n" % inst)
819 819 ui.write(_(" (check that your locale is properly set)\n"))
820 820 problems += 1
821 821
822 822 # compiled modules
823 823 ui.status(_("Checking extensions...\n"))
824 824 try:
825 825 import bdiff, mpatch, base85
826 826 except Exception, inst:
827 827 ui.write(" %s\n" % inst)
828 828 ui.write(_(" One or more extensions could not be found"))
829 829 ui.write(_(" (check that you compiled the extensions)\n"))
830 830 problems += 1
831 831
832 832 # templates
833 833 ui.status(_("Checking templates...\n"))
834 834 try:
835 835 import templater
836 836 t = templater.templater(templater.templatepath("map-cmdline.default"))
837 837 except Exception, inst:
838 838 ui.write(" %s\n" % inst)
839 839 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
840 840 problems += 1
841 841
842 842 # patch
843 843 ui.status(_("Checking patch...\n"))
844 844 patchproblems = 0
845 845 a = "1\n2\n3\n4\n"
846 846 b = "1\n2\n3\ninsert\n4\n"
847 847 fa = writetemp(a)
848 848 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
849 849 os.path.basename(fa))
850 850 fd = writetemp(d)
851 851
852 852 files = {}
853 853 try:
854 854 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
855 855 except util.Abort, e:
856 856 ui.write(_(" patch call failed:\n"))
857 857 ui.write(" " + str(e) + "\n")
858 858 patchproblems += 1
859 859 else:
860 860 if list(files) != [os.path.basename(fa)]:
861 861 ui.write(_(" unexpected patch output!\n"))
862 862 patchproblems += 1
863 863 a = file(fa).read()
864 864 if a != b:
865 865 ui.write(_(" patch test failed!\n"))
866 866 patchproblems += 1
867 867
868 868 if patchproblems:
869 869 if ui.config('ui', 'patch'):
870 870 ui.write(_(" (Current patch tool may be incompatible with patch,"
871 871 " or misconfigured. Please check your .hgrc file)\n"))
872 872 else:
873 873 ui.write(_(" Internal patcher failure, please report this error"
874 874 " to http://www.selenic.com/mercurial/bts\n"))
875 875 problems += patchproblems
876 876
877 877 os.unlink(fa)
878 878 os.unlink(fd)
879 879
880 880 # editor
881 881 ui.status(_("Checking commit editor...\n"))
882 882 editor = ui.geteditor()
883 883 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
884 884 if not cmdpath:
885 885 if editor == 'vi':
886 886 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
887 887 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
888 888 else:
889 889 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
890 890 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
891 891 problems += 1
892 892
893 893 # check username
894 894 ui.status(_("Checking username...\n"))
895 895 user = os.environ.get("HGUSER")
896 896 if user is None:
897 897 user = ui.config("ui", "username")
898 898 if user is None:
899 899 user = os.environ.get("EMAIL")
900 900 if not user:
901 901 ui.warn(" ")
902 902 ui.username()
903 903 ui.write(_(" (specify a username in your .hgrc file)\n"))
904 904
905 905 if not problems:
906 906 ui.status(_("No problems detected\n"))
907 907 else:
908 908 ui.write(_("%s problems detected,"
909 909 " please check your install!\n") % problems)
910 910
911 911 return problems
912 912
913 913 def debugrename(ui, repo, file1, *pats, **opts):
914 914 """dump rename information"""
915 915
916 916 ctx = repo.changectx(opts.get('rev', 'tip'))
917 917 m = cmdutil.match(repo, (file1,) + pats, opts)
918 918 for src, abs, rel, exact in cmdutil.walk(repo, m, ctx.node()):
919 919 fctx = ctx.filectx(abs)
920 920 o = fctx.filelog().renamed(fctx.filenode())
921 921 if o:
922 922 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
923 923 else:
924 924 ui.write(_("%s not renamed\n") % rel)
925 925
926 926 def debugwalk(ui, repo, *pats, **opts):
927 927 """show how files match on given patterns"""
928 928 m = cmdutil.match(repo, pats, opts)
929 929 items = list(cmdutil.walk(repo, m))
930 930 if not items:
931 931 return
932 932 fmt = '%%s %%-%ds %%-%ds %%s' % (
933 933 max([len(abs) for (src, abs, rel, exact) in items]),
934 934 max([len(rel) for (src, abs, rel, exact) in items]))
935 935 for src, abs, rel, exact in items:
936 936 line = fmt % (src, abs, rel, exact and 'exact' or '')
937 937 ui.write("%s\n" % line.rstrip())
938 938
939 939 def diff(ui, repo, *pats, **opts):
940 940 """diff repository (or selected files)
941 941
942 942 Show differences between revisions for the specified files.
943 943
944 944 Differences between files are shown using the unified diff format.
945 945
946 946 NOTE: diff may generate unexpected results for merges, as it will
947 947 default to comparing against the working directory's first parent
948 948 changeset if no revisions are specified.
949 949
950 950 When two revision arguments are given, then changes are shown
951 951 between those revisions. If only one revision is specified then
952 952 that revision is compared to the working directory, and, when no
953 953 revisions are specified, the working directory files are compared
954 954 to its parent.
955 955
956 956 Without the -a option, diff will avoid generating diffs of files
957 957 it detects as binary. With -a, diff will generate a diff anyway,
958 958 probably with undesirable results.
959 959 """
960 960 node1, node2 = cmdutil.revpair(repo, opts['rev'])
961 961
962 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
963
964 patch.diff(repo, node1, node2, fns, match=matchfn,
962 m = cmdutil.match(repo, pats, opts)
963 patch.diff(repo, node1, node2, m.files(), match=m,
965 964 opts=patch.diffopts(ui, opts))
966 965
967 966 def export(ui, repo, *changesets, **opts):
968 967 """dump the header and diffs for one or more changesets
969 968
970 969 Print the changeset header and diffs for one or more revisions.
971 970
972 971 The information shown in the changeset header is: author,
973 972 changeset hash, parent(s) and commit comment.
974 973
975 974 NOTE: export may generate unexpected diff output for merge changesets,
976 975 as it will compare the merge changeset against its first parent only.
977 976
978 977 Output may be to a file, in which case the name of the file is
979 978 given using a format string. The formatting rules are as follows:
980 979
981 980 %% literal "%" character
982 981 %H changeset hash (40 bytes of hexadecimal)
983 982 %N number of patches being generated
984 983 %R changeset revision number
985 984 %b basename of the exporting repository
986 985 %h short-form changeset hash (12 bytes of hexadecimal)
987 986 %n zero-padded sequence number, starting at 1
988 987 %r zero-padded changeset revision number
989 988
990 989 Without the -a option, export will avoid generating diffs of files
991 990 it detects as binary. With -a, export will generate a diff anyway,
992 991 probably with undesirable results.
993 992
994 993 With the --switch-parent option, the diff will be against the second
995 994 parent. It can be useful to review a merge.
996 995 """
997 996 if not changesets:
998 997 raise util.Abort(_("export requires at least one changeset"))
999 998 revs = cmdutil.revrange(repo, changesets)
1000 999 if len(revs) > 1:
1001 1000 ui.note(_('exporting patches:\n'))
1002 1001 else:
1003 1002 ui.note(_('exporting patch:\n'))
1004 1003 patch.export(repo, revs, template=opts['output'],
1005 1004 switch_parent=opts['switch_parent'],
1006 1005 opts=patch.diffopts(ui, opts))
1007 1006
1008 1007 def grep(ui, repo, pattern, *pats, **opts):
1009 1008 """search for a pattern in specified files and revisions
1010 1009
1011 1010 Search revisions of files for a regular expression.
1012 1011
1013 1012 This command behaves differently than Unix grep. It only accepts
1014 1013 Python/Perl regexps. It searches repository history, not the
1015 1014 working directory. It always prints the revision number in which
1016 1015 a match appears.
1017 1016
1018 1017 By default, grep only prints output for the first revision of a
1019 1018 file in which it finds a match. To get it to print every revision
1020 1019 that contains a change in match status ("-" for a match that
1021 1020 becomes a non-match, or "+" for a non-match that becomes a match),
1022 1021 use the --all flag.
1023 1022 """
1024 1023 reflags = 0
1025 1024 if opts['ignore_case']:
1026 1025 reflags |= re.I
1027 1026 try:
1028 1027 regexp = re.compile(pattern, reflags)
1029 1028 except Exception, inst:
1030 1029 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1031 1030 return None
1032 1031 sep, eol = ':', '\n'
1033 1032 if opts['print0']:
1034 1033 sep = eol = '\0'
1035 1034
1036 1035 fcache = {}
1037 1036 def getfile(fn):
1038 1037 if fn not in fcache:
1039 1038 fcache[fn] = repo.file(fn)
1040 1039 return fcache[fn]
1041 1040
1042 1041 def matchlines(body):
1043 1042 begin = 0
1044 1043 linenum = 0
1045 1044 while True:
1046 1045 match = regexp.search(body, begin)
1047 1046 if not match:
1048 1047 break
1049 1048 mstart, mend = match.span()
1050 1049 linenum += body.count('\n', begin, mstart) + 1
1051 1050 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1052 1051 lend = body.find('\n', mend)
1053 1052 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1054 1053 begin = lend + 1
1055 1054
1056 1055 class linestate(object):
1057 1056 def __init__(self, line, linenum, colstart, colend):
1058 1057 self.line = line
1059 1058 self.linenum = linenum
1060 1059 self.colstart = colstart
1061 1060 self.colend = colend
1062 1061
1063 1062 def __hash__(self):
1064 1063 return hash((self.linenum, self.line))
1065 1064
1066 1065 def __eq__(self, other):
1067 1066 return self.line == other.line
1068 1067
1069 1068 matches = {}
1070 1069 copies = {}
1071 1070 def grepbody(fn, rev, body):
1072 1071 matches[rev].setdefault(fn, [])
1073 1072 m = matches[rev][fn]
1074 1073 for lnum, cstart, cend, line in matchlines(body):
1075 1074 s = linestate(line, lnum, cstart, cend)
1076 1075 m.append(s)
1077 1076
1078 1077 def difflinestates(a, b):
1079 1078 sm = difflib.SequenceMatcher(None, a, b)
1080 1079 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1081 1080 if tag == 'insert':
1082 1081 for i in xrange(blo, bhi):
1083 1082 yield ('+', b[i])
1084 1083 elif tag == 'delete':
1085 1084 for i in xrange(alo, ahi):
1086 1085 yield ('-', a[i])
1087 1086 elif tag == 'replace':
1088 1087 for i in xrange(alo, ahi):
1089 1088 yield ('-', a[i])
1090 1089 for i in xrange(blo, bhi):
1091 1090 yield ('+', b[i])
1092 1091
1093 1092 prev = {}
1094 1093 def display(fn, rev, states, prevstates):
1095 1094 datefunc = ui.quiet and util.shortdate or util.datestr
1096 1095 found = False
1097 1096 filerevmatches = {}
1098 1097 r = prev.get(fn, -1)
1099 1098 if opts['all']:
1100 1099 iter = difflinestates(states, prevstates)
1101 1100 else:
1102 1101 iter = [('', l) for l in prevstates]
1103 1102 for change, l in iter:
1104 1103 cols = [fn, str(r)]
1105 1104 if opts['line_number']:
1106 1105 cols.append(str(l.linenum))
1107 1106 if opts['all']:
1108 1107 cols.append(change)
1109 1108 if opts['user']:
1110 1109 cols.append(ui.shortuser(get(r)[1]))
1111 1110 if opts.get('date'):
1112 1111 cols.append(datefunc(get(r)[2]))
1113 1112 if opts['files_with_matches']:
1114 1113 c = (fn, r)
1115 1114 if c in filerevmatches:
1116 1115 continue
1117 1116 filerevmatches[c] = 1
1118 1117 else:
1119 1118 cols.append(l.line)
1120 1119 ui.write(sep.join(cols), eol)
1121 1120 found = True
1122 1121 return found
1123 1122
1124 1123 fstate = {}
1125 1124 skip = {}
1126 1125 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1127 1126 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1128 1127 found = False
1129 1128 follow = opts.get('follow')
1130 1129 for st, rev, fns in changeiter:
1131 1130 if st == 'window':
1132 1131 matches.clear()
1133 1132 elif st == 'add':
1134 1133 ctx = repo.changectx(rev)
1135 1134 matches[rev] = {}
1136 1135 for fn in fns:
1137 1136 if fn in skip:
1138 1137 continue
1139 1138 try:
1140 1139 grepbody(fn, rev, getfile(fn).read(ctx.filenode(fn)))
1141 1140 fstate.setdefault(fn, [])
1142 1141 if follow:
1143 1142 copied = getfile(fn).renamed(ctx.filenode(fn))
1144 1143 if copied:
1145 1144 copies.setdefault(rev, {})[fn] = copied[0]
1146 1145 except revlog.LookupError:
1147 1146 pass
1148 1147 elif st == 'iter':
1149 1148 states = matches[rev].items()
1150 1149 states.sort()
1151 1150 for fn, m in states:
1152 1151 copy = copies.get(rev, {}).get(fn)
1153 1152 if fn in skip:
1154 1153 if copy:
1155 1154 skip[copy] = True
1156 1155 continue
1157 1156 if fn in prev or fstate[fn]:
1158 1157 r = display(fn, rev, m, fstate[fn])
1159 1158 found = found or r
1160 1159 if r and not opts['all']:
1161 1160 skip[fn] = True
1162 1161 if copy:
1163 1162 skip[copy] = True
1164 1163 fstate[fn] = m
1165 1164 if copy:
1166 1165 fstate[copy] = m
1167 1166 prev[fn] = rev
1168 1167
1169 1168 fstate = fstate.items()
1170 1169 fstate.sort()
1171 1170 for fn, state in fstate:
1172 1171 if fn in skip:
1173 1172 continue
1174 1173 if fn not in copies.get(prev[fn], {}):
1175 1174 found = display(fn, rev, {}, state) or found
1176 1175 return (not found and 1) or 0
1177 1176
1178 1177 def heads(ui, repo, *branchrevs, **opts):
1179 1178 """show current repository heads or show branch heads
1180 1179
1181 1180 With no arguments, show all repository head changesets.
1182 1181
1183 1182 If branch or revisions names are given this will show the heads of
1184 1183 the specified branches or the branches those revisions are tagged
1185 1184 with.
1186 1185
1187 1186 Repository "heads" are changesets that don't have child
1188 1187 changesets. They are where development generally takes place and
1189 1188 are the usual targets for update and merge operations.
1190 1189
1191 1190 Branch heads are changesets that have a given branch tag, but have
1192 1191 no child changesets with that tag. They are usually where
1193 1192 development on the given branch takes place.
1194 1193 """
1195 1194 if opts['rev']:
1196 1195 start = repo.lookup(opts['rev'])
1197 1196 else:
1198 1197 start = None
1199 1198 if not branchrevs:
1200 1199 # Assume we're looking repo-wide heads if no revs were specified.
1201 1200 heads = repo.heads(start)
1202 1201 else:
1203 1202 heads = []
1204 1203 visitedset = util.set()
1205 1204 for branchrev in branchrevs:
1206 1205 branch = repo.changectx(branchrev).branch()
1207 1206 if branch in visitedset:
1208 1207 continue
1209 1208 visitedset.add(branch)
1210 1209 bheads = repo.branchheads(branch, start)
1211 1210 if not bheads:
1212 1211 if branch != branchrev:
1213 1212 ui.warn(_("no changes on branch %s containing %s are "
1214 1213 "reachable from %s\n")
1215 1214 % (branch, branchrev, opts['rev']))
1216 1215 else:
1217 1216 ui.warn(_("no changes on branch %s are reachable from %s\n")
1218 1217 % (branch, opts['rev']))
1219 1218 heads.extend(bheads)
1220 1219 if not heads:
1221 1220 return 1
1222 1221 displayer = cmdutil.show_changeset(ui, repo, opts)
1223 1222 for n in heads:
1224 1223 displayer.show(changenode=n)
1225 1224
1226 1225 def help_(ui, name=None, with_version=False):
1227 1226 """show help for a command, extension, or list of commands
1228 1227
1229 1228 With no arguments, print a list of commands and short help.
1230 1229
1231 1230 Given a command name, print help for that command.
1232 1231
1233 1232 Given an extension name, print help for that extension, and the
1234 1233 commands it provides."""
1235 1234 option_lists = []
1236 1235
1237 1236 def addglobalopts(aliases):
1238 1237 if ui.verbose:
1239 1238 option_lists.append((_("global options:"), globalopts))
1240 1239 if name == 'shortlist':
1241 1240 option_lists.append((_('use "hg help" for the full list '
1242 1241 'of commands'), ()))
1243 1242 else:
1244 1243 if name == 'shortlist':
1245 1244 msg = _('use "hg help" for the full list of commands '
1246 1245 'or "hg -v" for details')
1247 1246 elif aliases:
1248 1247 msg = _('use "hg -v help%s" to show aliases and '
1249 1248 'global options') % (name and " " + name or "")
1250 1249 else:
1251 1250 msg = _('use "hg -v help %s" to show global options') % name
1252 1251 option_lists.append((msg, ()))
1253 1252
1254 1253 def helpcmd(name):
1255 1254 if with_version:
1256 1255 version_(ui)
1257 1256 ui.write('\n')
1258 1257 aliases, i = cmdutil.findcmd(ui, name, table)
1259 1258 # synopsis
1260 1259 ui.write("%s\n" % i[2])
1261 1260
1262 1261 # aliases
1263 1262 if not ui.quiet and len(aliases) > 1:
1264 1263 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1265 1264
1266 1265 # description
1267 1266 doc = i[0].__doc__
1268 1267 if not doc:
1269 1268 doc = _("(No help text available)")
1270 1269 if ui.quiet:
1271 1270 doc = doc.splitlines(0)[0]
1272 1271 ui.write("\n%s\n" % doc.rstrip())
1273 1272
1274 1273 if not ui.quiet:
1275 1274 # options
1276 1275 if i[1]:
1277 1276 option_lists.append((_("options:\n"), i[1]))
1278 1277
1279 1278 addglobalopts(False)
1280 1279
1281 1280 def helplist(header, select=None):
1282 1281 h = {}
1283 1282 cmds = {}
1284 1283 for c, e in table.items():
1285 1284 f = c.split("|", 1)[0]
1286 1285 if select and not select(f):
1287 1286 continue
1288 1287 if name == "shortlist" and not f.startswith("^"):
1289 1288 continue
1290 1289 f = f.lstrip("^")
1291 1290 if not ui.debugflag and f.startswith("debug"):
1292 1291 continue
1293 1292 doc = e[0].__doc__
1294 1293 if not doc:
1295 1294 doc = _("(No help text available)")
1296 1295 h[f] = doc.splitlines(0)[0].rstrip()
1297 1296 cmds[f] = c.lstrip("^")
1298 1297
1299 1298 if not h:
1300 1299 ui.status(_('no commands defined\n'))
1301 1300 return
1302 1301
1303 1302 ui.status(header)
1304 1303 fns = h.keys()
1305 1304 fns.sort()
1306 1305 m = max(map(len, fns))
1307 1306 for f in fns:
1308 1307 if ui.verbose:
1309 1308 commands = cmds[f].replace("|",", ")
1310 1309 ui.write(" %s:\n %s\n"%(commands, h[f]))
1311 1310 else:
1312 1311 ui.write(' %-*s %s\n' % (m, f, h[f]))
1313 1312
1314 1313 if not ui.quiet:
1315 1314 addglobalopts(True)
1316 1315
1317 1316 def helptopic(name):
1318 1317 v = None
1319 1318 for i in help.helptable:
1320 1319 l = i.split('|')
1321 1320 if name in l:
1322 1321 v = i
1323 1322 header = l[-1]
1324 1323 if not v:
1325 1324 raise cmdutil.UnknownCommand(name)
1326 1325
1327 1326 # description
1328 1327 doc = help.helptable[v]
1329 1328 if not doc:
1330 1329 doc = _("(No help text available)")
1331 1330 if callable(doc):
1332 1331 doc = doc()
1333 1332
1334 1333 ui.write("%s\n" % header)
1335 1334 ui.write("%s\n" % doc.rstrip())
1336 1335
1337 1336 def helpext(name):
1338 1337 try:
1339 1338 mod = extensions.find(name)
1340 1339 except KeyError:
1341 1340 raise cmdutil.UnknownCommand(name)
1342 1341
1343 1342 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1344 1343 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1345 1344 for d in doc[1:]:
1346 1345 ui.write(d, '\n')
1347 1346
1348 1347 ui.status('\n')
1349 1348
1350 1349 try:
1351 1350 ct = mod.cmdtable
1352 1351 except AttributeError:
1353 1352 ct = {}
1354 1353
1355 1354 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1356 1355 helplist(_('list of commands:\n\n'), modcmds.has_key)
1357 1356
1358 1357 if name and name != 'shortlist':
1359 1358 i = None
1360 1359 for f in (helpcmd, helptopic, helpext):
1361 1360 try:
1362 1361 f(name)
1363 1362 i = None
1364 1363 break
1365 1364 except cmdutil.UnknownCommand, inst:
1366 1365 i = inst
1367 1366 if i:
1368 1367 raise i
1369 1368
1370 1369 else:
1371 1370 # program name
1372 1371 if ui.verbose or with_version:
1373 1372 version_(ui)
1374 1373 else:
1375 1374 ui.status(_("Mercurial Distributed SCM\n"))
1376 1375 ui.status('\n')
1377 1376
1378 1377 # list of commands
1379 1378 if name == "shortlist":
1380 1379 header = _('basic commands:\n\n')
1381 1380 else:
1382 1381 header = _('list of commands:\n\n')
1383 1382
1384 1383 helplist(header)
1385 1384
1386 1385 # list all option lists
1387 1386 opt_output = []
1388 1387 for title, options in option_lists:
1389 1388 opt_output.append(("\n%s" % title, None))
1390 1389 for shortopt, longopt, default, desc in options:
1391 1390 if "DEPRECATED" in desc and not ui.verbose: continue
1392 1391 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1393 1392 longopt and " --%s" % longopt),
1394 1393 "%s%s" % (desc,
1395 1394 default
1396 1395 and _(" (default: %s)") % default
1397 1396 or "")))
1398 1397
1399 1398 if opt_output:
1400 1399 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1401 1400 for first, second in opt_output:
1402 1401 if second:
1403 1402 ui.write(" %-*s %s\n" % (opts_len, first, second))
1404 1403 else:
1405 1404 ui.write("%s\n" % first)
1406 1405
1407 1406 def identify(ui, repo, source=None,
1408 1407 rev=None, num=None, id=None, branch=None, tags=None):
1409 1408 """identify the working copy or specified revision
1410 1409
1411 1410 With no revision, print a summary of the current state of the repo.
1412 1411
1413 1412 With a path, do a lookup in another repository.
1414 1413
1415 1414 This summary identifies the repository state using one or two parent
1416 1415 hash identifiers, followed by a "+" if there are uncommitted changes
1417 1416 in the working directory, a list of tags for this revision and a branch
1418 1417 name for non-default branches.
1419 1418 """
1420 1419
1421 1420 if not repo and not source:
1422 1421 raise util.Abort(_("There is no Mercurial repository here "
1423 1422 "(.hg not found)"))
1424 1423
1425 1424 hexfunc = ui.debugflag and hex or short
1426 1425 default = not (num or id or branch or tags)
1427 1426 output = []
1428 1427
1429 1428 if source:
1430 1429 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1431 1430 srepo = hg.repository(ui, source)
1432 1431 if not rev and revs:
1433 1432 rev = revs[0]
1434 1433 if not rev:
1435 1434 rev = "tip"
1436 1435 if num or branch or tags:
1437 1436 raise util.Abort(
1438 1437 "can't query remote revision number, branch, or tags")
1439 1438 output = [hexfunc(srepo.lookup(rev))]
1440 1439 elif not rev:
1441 1440 ctx = repo.workingctx()
1442 1441 parents = ctx.parents()
1443 1442 changed = False
1444 1443 if default or id or num:
1445 1444 changed = ctx.files() + ctx.deleted()
1446 1445 if default or id:
1447 1446 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1448 1447 (changed) and "+" or "")]
1449 1448 if num:
1450 1449 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1451 1450 (changed) and "+" or ""))
1452 1451 else:
1453 1452 ctx = repo.changectx(rev)
1454 1453 if default or id:
1455 1454 output = [hexfunc(ctx.node())]
1456 1455 if num:
1457 1456 output.append(str(ctx.rev()))
1458 1457
1459 1458 if not source and default and not ui.quiet:
1460 1459 b = util.tolocal(ctx.branch())
1461 1460 if b != 'default':
1462 1461 output.append("(%s)" % b)
1463 1462
1464 1463 # multiple tags for a single parent separated by '/'
1465 1464 t = "/".join(ctx.tags())
1466 1465 if t:
1467 1466 output.append(t)
1468 1467
1469 1468 if branch:
1470 1469 output.append(util.tolocal(ctx.branch()))
1471 1470
1472 1471 if tags:
1473 1472 output.extend(ctx.tags())
1474 1473
1475 1474 ui.write("%s\n" % ' '.join(output))
1476 1475
1477 1476 def import_(ui, repo, patch1, *patches, **opts):
1478 1477 """import an ordered set of patches
1479 1478
1480 1479 Import a list of patches and commit them individually.
1481 1480
1482 1481 If there are outstanding changes in the working directory, import
1483 1482 will abort unless given the -f flag.
1484 1483
1485 1484 You can import a patch straight from a mail message. Even patches
1486 1485 as attachments work (body part must be type text/plain or
1487 1486 text/x-patch to be used). From and Subject headers of email
1488 1487 message are used as default committer and commit message. All
1489 1488 text/plain body parts before first diff are added to commit
1490 1489 message.
1491 1490
1492 1491 If the imported patch was generated by hg export, user and description
1493 1492 from patch override values from message headers and body. Values
1494 1493 given on command line with -m and -u override these.
1495 1494
1496 1495 If --exact is specified, import will set the working directory
1497 1496 to the parent of each patch before applying it, and will abort
1498 1497 if the resulting changeset has a different ID than the one
1499 1498 recorded in the patch. This may happen due to character set
1500 1499 problems or other deficiencies in the text patch format.
1501 1500
1502 1501 To read a patch from standard input, use patch name "-".
1503 1502 See 'hg help dates' for a list of formats valid for -d/--date.
1504 1503 """
1505 1504 patches = (patch1,) + patches
1506 1505
1507 1506 date = opts.get('date')
1508 1507 if date:
1509 1508 opts['date'] = util.parsedate(date)
1510 1509
1511 1510 if opts.get('exact') or not opts['force']:
1512 1511 cmdutil.bail_if_changed(repo)
1513 1512
1514 1513 d = opts["base"]
1515 1514 strip = opts["strip"]
1516 1515 wlock = lock = None
1517 1516 try:
1518 1517 wlock = repo.wlock()
1519 1518 lock = repo.lock()
1520 1519 for p in patches:
1521 1520 pf = os.path.join(d, p)
1522 1521
1523 1522 if pf == '-':
1524 1523 ui.status(_("applying patch from stdin\n"))
1525 1524 data = patch.extract(ui, sys.stdin)
1526 1525 else:
1527 1526 ui.status(_("applying %s\n") % p)
1528 1527 if os.path.exists(pf):
1529 1528 data = patch.extract(ui, file(pf, 'rb'))
1530 1529 else:
1531 1530 data = patch.extract(ui, urllib.urlopen(pf))
1532 1531 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1533 1532
1534 1533 if tmpname is None:
1535 1534 raise util.Abort(_('no diffs found'))
1536 1535
1537 1536 try:
1538 1537 cmdline_message = cmdutil.logmessage(opts)
1539 1538 if cmdline_message:
1540 1539 # pickup the cmdline msg
1541 1540 message = cmdline_message
1542 1541 elif message:
1543 1542 # pickup the patch msg
1544 1543 message = message.strip()
1545 1544 else:
1546 1545 # launch the editor
1547 1546 message = None
1548 1547 ui.debug(_('message:\n%s\n') % message)
1549 1548
1550 1549 wp = repo.workingctx().parents()
1551 1550 if opts.get('exact'):
1552 1551 if not nodeid or not p1:
1553 1552 raise util.Abort(_('not a mercurial patch'))
1554 1553 p1 = repo.lookup(p1)
1555 1554 p2 = repo.lookup(p2 or hex(nullid))
1556 1555
1557 1556 if p1 != wp[0].node():
1558 1557 hg.clean(repo, p1)
1559 1558 repo.dirstate.setparents(p1, p2)
1560 1559 elif p2:
1561 1560 try:
1562 1561 p1 = repo.lookup(p1)
1563 1562 p2 = repo.lookup(p2)
1564 1563 if p1 == wp[0].node():
1565 1564 repo.dirstate.setparents(p1, p2)
1566 1565 except RepoError:
1567 1566 pass
1568 1567 if opts.get('exact') or opts.get('import_branch'):
1569 1568 repo.dirstate.setbranch(branch or 'default')
1570 1569
1571 1570 files = {}
1572 1571 try:
1573 1572 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1574 1573 files=files)
1575 1574 finally:
1576 1575 files = patch.updatedir(ui, repo, files)
1577 1576 if not opts.get('no_commit'):
1578 1577 n = repo.commit(files, message, opts.get('user') or user,
1579 1578 opts.get('date') or date)
1580 1579 if opts.get('exact'):
1581 1580 if hex(n) != nodeid:
1582 1581 repo.rollback()
1583 1582 raise util.Abort(_('patch is damaged'
1584 1583 ' or loses information'))
1585 1584 # Force a dirstate write so that the next transaction
1586 1585 # backups an up-do-date file.
1587 1586 repo.dirstate.write()
1588 1587 finally:
1589 1588 os.unlink(tmpname)
1590 1589 finally:
1591 1590 del lock, wlock
1592 1591
1593 1592 def incoming(ui, repo, source="default", **opts):
1594 1593 """show new changesets found in source
1595 1594
1596 1595 Show new changesets found in the specified path/URL or the default
1597 1596 pull location. These are the changesets that would be pulled if a pull
1598 1597 was requested.
1599 1598
1600 1599 For remote repository, using --bundle avoids downloading the changesets
1601 1600 twice if the incoming is followed by a pull.
1602 1601
1603 1602 See pull for valid source format details.
1604 1603 """
1605 1604 limit = cmdutil.loglimit(opts)
1606 1605 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts['rev'])
1607 1606 cmdutil.setremoteconfig(ui, opts)
1608 1607
1609 1608 other = hg.repository(ui, source)
1610 1609 ui.status(_('comparing with %s\n') % util.hidepassword(source))
1611 1610 if revs:
1612 1611 revs = [other.lookup(rev) for rev in revs]
1613 1612 incoming = repo.findincoming(other, heads=revs, force=opts["force"])
1614 1613 if not incoming:
1615 1614 try:
1616 1615 os.unlink(opts["bundle"])
1617 1616 except:
1618 1617 pass
1619 1618 ui.status(_("no changes found\n"))
1620 1619 return 1
1621 1620
1622 1621 cleanup = None
1623 1622 try:
1624 1623 fname = opts["bundle"]
1625 1624 if fname or not other.local():
1626 1625 # create a bundle (uncompressed if other repo is not local)
1627 1626 if revs is None:
1628 1627 cg = other.changegroup(incoming, "incoming")
1629 1628 else:
1630 1629 cg = other.changegroupsubset(incoming, revs, 'incoming')
1631 1630 bundletype = other.local() and "HG10BZ" or "HG10UN"
1632 1631 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1633 1632 # keep written bundle?
1634 1633 if opts["bundle"]:
1635 1634 cleanup = None
1636 1635 if not other.local():
1637 1636 # use the created uncompressed bundlerepo
1638 1637 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1639 1638
1640 1639 o = other.changelog.nodesbetween(incoming, revs)[0]
1641 1640 if opts['newest_first']:
1642 1641 o.reverse()
1643 1642 displayer = cmdutil.show_changeset(ui, other, opts)
1644 1643 count = 0
1645 1644 for n in o:
1646 1645 if count >= limit:
1647 1646 break
1648 1647 parents = [p for p in other.changelog.parents(n) if p != nullid]
1649 1648 if opts['no_merges'] and len(parents) == 2:
1650 1649 continue
1651 1650 count += 1
1652 1651 displayer.show(changenode=n)
1653 1652 finally:
1654 1653 if hasattr(other, 'close'):
1655 1654 other.close()
1656 1655 if cleanup:
1657 1656 os.unlink(cleanup)
1658 1657
1659 1658 def init(ui, dest=".", **opts):
1660 1659 """create a new repository in the given directory
1661 1660
1662 1661 Initialize a new repository in the given directory. If the given
1663 1662 directory does not exist, it is created.
1664 1663
1665 1664 If no directory is given, the current directory is used.
1666 1665
1667 1666 It is possible to specify an ssh:// URL as the destination.
1668 1667 Look at the help text for the pull command for important details
1669 1668 about ssh:// URLs.
1670 1669 """
1671 1670 cmdutil.setremoteconfig(ui, opts)
1672 1671 hg.repository(ui, dest, create=1)
1673 1672
1674 1673 def locate(ui, repo, *pats, **opts):
1675 1674 """locate files matching specific patterns
1676 1675
1677 1676 Print all files under Mercurial control whose names match the
1678 1677 given patterns.
1679 1678
1680 1679 This command searches the entire repository by default. To search
1681 1680 just the current directory and its subdirectories, use
1682 1681 "--include .".
1683 1682
1684 1683 If no patterns are given to match, this command prints all file
1685 1684 names.
1686 1685
1687 1686 If you want to feed the output of this command into the "xargs"
1688 1687 command, use the "-0" option to both this command and "xargs".
1689 1688 This will avoid the problem of "xargs" treating single filenames
1690 1689 that contain white space as multiple filenames.
1691 1690 """
1692 1691 end = opts['print0'] and '\0' or '\n'
1693 1692 rev = opts['rev']
1694 1693 if rev:
1695 1694 node = repo.lookup(rev)
1696 1695 else:
1697 1696 node = None
1698 1697
1699 1698 ret = 1
1700 1699 m = cmdutil.match(repo, pats, opts, default='relglob')
1701 1700 m.bad = lambda x,y: True
1702 1701 for src, abs, rel, exact in cmdutil.walk(repo, m, node):
1703 1702 if src == 'b':
1704 1703 continue
1705 1704 if not node and abs not in repo.dirstate:
1706 1705 continue
1707 1706 if opts['fullpath']:
1708 1707 ui.write(os.path.join(repo.root, abs), end)
1709 1708 else:
1710 1709 ui.write(((pats and rel) or abs), end)
1711 1710 ret = 0
1712 1711
1713 1712 return ret
1714 1713
1715 1714 def log(ui, repo, *pats, **opts):
1716 1715 """show revision history of entire repository or files
1717 1716
1718 1717 Print the revision history of the specified files or the entire
1719 1718 project.
1720 1719
1721 1720 File history is shown without following rename or copy history of
1722 1721 files. Use -f/--follow with a file name to follow history across
1723 1722 renames and copies. --follow without a file name will only show
1724 1723 ancestors or descendants of the starting revision. --follow-first
1725 1724 only follows the first parent of merge revisions.
1726 1725
1727 1726 If no revision range is specified, the default is tip:0 unless
1728 1727 --follow is set, in which case the working directory parent is
1729 1728 used as the starting revision.
1730 1729
1731 1730 See 'hg help dates' for a list of formats valid for -d/--date.
1732 1731
1733 1732 By default this command outputs: changeset id and hash, tags,
1734 1733 non-trivial parents, user, date and time, and a summary for each
1735 1734 commit. When the -v/--verbose switch is used, the list of changed
1736 1735 files and full commit message is shown.
1737 1736
1738 1737 NOTE: log -p may generate unexpected diff output for merge
1739 1738 changesets, as it will compare the merge changeset against its
1740 1739 first parent only. Also, the files: list will only reflect files
1741 1740 that are different from BOTH parents.
1742 1741
1743 1742 """
1744 1743
1745 1744 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1746 1745 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1747 1746
1748 1747 limit = cmdutil.loglimit(opts)
1749 1748 count = 0
1750 1749
1751 1750 if opts['copies'] and opts['rev']:
1752 1751 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1753 1752 else:
1754 1753 endrev = repo.changelog.count()
1755 1754 rcache = {}
1756 1755 ncache = {}
1757 1756 def getrenamed(fn, rev):
1758 1757 '''looks up all renames for a file (up to endrev) the first
1759 1758 time the file is given. It indexes on the changerev and only
1760 1759 parses the manifest if linkrev != changerev.
1761 1760 Returns rename info for fn at changerev rev.'''
1762 1761 if fn not in rcache:
1763 1762 rcache[fn] = {}
1764 1763 ncache[fn] = {}
1765 1764 fl = repo.file(fn)
1766 1765 for i in xrange(fl.count()):
1767 1766 node = fl.node(i)
1768 1767 lr = fl.linkrev(node)
1769 1768 renamed = fl.renamed(node)
1770 1769 rcache[fn][lr] = renamed
1771 1770 if renamed:
1772 1771 ncache[fn][node] = renamed
1773 1772 if lr >= endrev:
1774 1773 break
1775 1774 if rev in rcache[fn]:
1776 1775 return rcache[fn][rev]
1777 1776
1778 1777 # If linkrev != rev (i.e. rev not found in rcache) fallback to
1779 1778 # filectx logic.
1780 1779
1781 1780 try:
1782 1781 return repo.changectx(rev).filectx(fn).renamed()
1783 1782 except revlog.LookupError:
1784 1783 pass
1785 1784 return None
1786 1785
1787 1786 df = False
1788 1787 if opts["date"]:
1789 1788 df = util.matchdate(opts["date"])
1790 1789
1791 1790 only_branches = opts['only_branch']
1792 1791
1793 1792 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1794 1793 for st, rev, fns in changeiter:
1795 1794 if st == 'add':
1796 1795 changenode = repo.changelog.node(rev)
1797 1796 parents = [p for p in repo.changelog.parentrevs(rev)
1798 1797 if p != nullrev]
1799 1798 if opts['no_merges'] and len(parents) == 2:
1800 1799 continue
1801 1800 if opts['only_merges'] and len(parents) != 2:
1802 1801 continue
1803 1802
1804 1803 if only_branches:
1805 1804 revbranch = get(rev)[5]['branch']
1806 1805 if revbranch not in only_branches:
1807 1806 continue
1808 1807
1809 1808 if df:
1810 1809 changes = get(rev)
1811 1810 if not df(changes[2][0]):
1812 1811 continue
1813 1812
1814 1813 if opts['keyword']:
1815 1814 changes = get(rev)
1816 1815 miss = 0
1817 1816 for k in [kw.lower() for kw in opts['keyword']]:
1818 1817 if not (k in changes[1].lower() or
1819 1818 k in changes[4].lower() or
1820 1819 k in " ".join(changes[3]).lower()):
1821 1820 miss = 1
1822 1821 break
1823 1822 if miss:
1824 1823 continue
1825 1824
1826 1825 copies = []
1827 1826 if opts.get('copies') and rev:
1828 1827 for fn in get(rev)[3]:
1829 1828 rename = getrenamed(fn, rev)
1830 1829 if rename:
1831 1830 copies.append((fn, rename[0]))
1832 1831 displayer.show(rev, changenode, copies=copies)
1833 1832 elif st == 'iter':
1834 1833 if count == limit: break
1835 1834 if displayer.flush(rev):
1836 1835 count += 1
1837 1836
1838 1837 def manifest(ui, repo, node=None, rev=None):
1839 1838 """output the current or given revision of the project manifest
1840 1839
1841 1840 Print a list of version controlled files for the given revision.
1842 1841 If no revision is given, the parent of the working directory is used,
1843 1842 or tip if no revision is checked out.
1844 1843
1845 1844 The manifest is the list of files being version controlled. If no revision
1846 1845 is given then the first parent of the working directory is used.
1847 1846
1848 1847 With -v flag, print file permissions, symlink and executable bits. With
1849 1848 --debug flag, print file revision hashes.
1850 1849 """
1851 1850
1852 1851 if rev and node:
1853 1852 raise util.Abort(_("please specify just one revision"))
1854 1853
1855 1854 if not node:
1856 1855 node = rev
1857 1856
1858 1857 m = repo.changectx(node).manifest()
1859 1858 files = m.keys()
1860 1859 files.sort()
1861 1860
1862 1861 for f in files:
1863 1862 if ui.debugflag:
1864 1863 ui.write("%40s " % hex(m[f]))
1865 1864 if ui.verbose:
1866 1865 type = m.execf(f) and "*" or m.linkf(f) and "@" or " "
1867 1866 perm = m.execf(f) and "755" or "644"
1868 1867 ui.write("%3s %1s " % (perm, type))
1869 1868 ui.write("%s\n" % f)
1870 1869
1871 1870 def merge(ui, repo, node=None, force=None, rev=None):
1872 1871 """merge working directory with another revision
1873 1872
1874 1873 Merge the contents of the current working directory and the
1875 1874 requested revision. Files that changed between either parent are
1876 1875 marked as changed for the next commit and a commit must be
1877 1876 performed before any further updates are allowed.
1878 1877
1879 1878 If no revision is specified, the working directory's parent is a
1880 1879 head revision, and the repository contains exactly one other head,
1881 1880 the other head is merged with by default. Otherwise, an explicit
1882 1881 revision to merge with must be provided.
1883 1882 """
1884 1883
1885 1884 if rev and node:
1886 1885 raise util.Abort(_("please specify just one revision"))
1887 1886 if not node:
1888 1887 node = rev
1889 1888
1890 1889 if not node:
1891 1890 heads = repo.heads()
1892 1891 if len(heads) > 2:
1893 1892 raise util.Abort(_('repo has %d heads - '
1894 1893 'please merge with an explicit rev') %
1895 1894 len(heads))
1896 1895 parent = repo.dirstate.parents()[0]
1897 1896 if len(heads) == 1:
1898 1897 msg = _('there is nothing to merge')
1899 1898 if parent != repo.lookup(repo.workingctx().branch()):
1900 1899 msg = _('%s - use "hg update" instead') % msg
1901 1900 raise util.Abort(msg)
1902 1901
1903 1902 if parent not in heads:
1904 1903 raise util.Abort(_('working dir not at a head rev - '
1905 1904 'use "hg update" or merge with an explicit rev'))
1906 1905 node = parent == heads[0] and heads[-1] or heads[0]
1907 1906 return hg.merge(repo, node, force=force)
1908 1907
1909 1908 def outgoing(ui, repo, dest=None, **opts):
1910 1909 """show changesets not found in destination
1911 1910
1912 1911 Show changesets not found in the specified destination repository or
1913 1912 the default push location. These are the changesets that would be pushed
1914 1913 if a push was requested.
1915 1914
1916 1915 See pull for valid destination format details.
1917 1916 """
1918 1917 limit = cmdutil.loglimit(opts)
1919 1918 dest, revs, checkout = hg.parseurl(
1920 1919 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
1921 1920 cmdutil.setremoteconfig(ui, opts)
1922 1921 if revs:
1923 1922 revs = [repo.lookup(rev) for rev in revs]
1924 1923
1925 1924 other = hg.repository(ui, dest)
1926 1925 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
1927 1926 o = repo.findoutgoing(other, force=opts['force'])
1928 1927 if not o:
1929 1928 ui.status(_("no changes found\n"))
1930 1929 return 1
1931 1930 o = repo.changelog.nodesbetween(o, revs)[0]
1932 1931 if opts['newest_first']:
1933 1932 o.reverse()
1934 1933 displayer = cmdutil.show_changeset(ui, repo, opts)
1935 1934 count = 0
1936 1935 for n in o:
1937 1936 if count >= limit:
1938 1937 break
1939 1938 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1940 1939 if opts['no_merges'] and len(parents) == 2:
1941 1940 continue
1942 1941 count += 1
1943 1942 displayer.show(changenode=n)
1944 1943
1945 1944 def parents(ui, repo, file_=None, **opts):
1946 1945 """show the parents of the working dir or revision
1947 1946
1948 1947 Print the working directory's parent revisions. If a
1949 1948 revision is given via --rev, the parent of that revision
1950 1949 will be printed. If a file argument is given, revision in
1951 1950 which the file was last changed (before the working directory
1952 1951 revision or the argument to --rev if given) is printed.
1953 1952 """
1954 1953 rev = opts.get('rev')
1955 1954 if rev:
1956 1955 ctx = repo.changectx(rev)
1957 1956 else:
1958 1957 ctx = repo.workingctx()
1959 1958
1960 1959 if file_:
1961 files, match, anypats = cmdutil.matchpats(repo, (file_,), opts)
1962 if anypats or len(files) != 1:
1960 m = cmdutil.match(repo, (file_,), opts)
1961 if m.anypats() or len(m.files()) != 1:
1963 1962 raise util.Abort(_('can only specify an explicit file name'))
1964 file_ = files[0]
1963 file_ = m.files()[0]
1965 1964 filenodes = []
1966 1965 for cp in ctx.parents():
1967 1966 if not cp:
1968 1967 continue
1969 1968 try:
1970 1969 filenodes.append(cp.filenode(file_))
1971 1970 except revlog.LookupError:
1972 1971 pass
1973 1972 if not filenodes:
1974 1973 raise util.Abort(_("'%s' not found in manifest!") % file_)
1975 1974 fl = repo.file(file_)
1976 1975 p = [repo.lookup(fl.linkrev(fn)) for fn in filenodes]
1977 1976 else:
1978 1977 p = [cp.node() for cp in ctx.parents()]
1979 1978
1980 1979 displayer = cmdutil.show_changeset(ui, repo, opts)
1981 1980 for n in p:
1982 1981 if n != nullid:
1983 1982 displayer.show(changenode=n)
1984 1983
1985 1984 def paths(ui, repo, search=None):
1986 1985 """show definition of symbolic path names
1987 1986
1988 1987 Show definition of symbolic path name NAME. If no name is given, show
1989 1988 definition of available names.
1990 1989
1991 1990 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1992 1991 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1993 1992 """
1994 1993 if search:
1995 1994 for name, path in ui.configitems("paths"):
1996 1995 if name == search:
1997 1996 ui.write("%s\n" % util.hidepassword(path))
1998 1997 return
1999 1998 ui.warn(_("not found!\n"))
2000 1999 return 1
2001 2000 else:
2002 2001 for name, path in ui.configitems("paths"):
2003 2002 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
2004 2003
2005 2004 def postincoming(ui, repo, modheads, optupdate, checkout):
2006 2005 if modheads == 0:
2007 2006 return
2008 2007 if optupdate:
2009 2008 if modheads <= 1 or checkout:
2010 2009 return hg.update(repo, checkout)
2011 2010 else:
2012 2011 ui.status(_("not updating, since new heads added\n"))
2013 2012 if modheads > 1:
2014 2013 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2015 2014 else:
2016 2015 ui.status(_("(run 'hg update' to get a working copy)\n"))
2017 2016
2018 2017 def pull(ui, repo, source="default", **opts):
2019 2018 """pull changes from the specified source
2020 2019
2021 2020 Pull changes from a remote repository to a local one.
2022 2021
2023 2022 This finds all changes from the repository at the specified path
2024 2023 or URL and adds them to the local repository. By default, this
2025 2024 does not update the copy of the project in the working directory.
2026 2025
2027 2026 Valid URLs are of the form:
2028 2027
2029 2028 local/filesystem/path (or file://local/filesystem/path)
2030 2029 http://[user@]host[:port]/[path]
2031 2030 https://[user@]host[:port]/[path]
2032 2031 ssh://[user@]host[:port]/[path]
2033 2032 static-http://host[:port]/[path]
2034 2033
2035 2034 Paths in the local filesystem can either point to Mercurial
2036 2035 repositories or to bundle files (as created by 'hg bundle' or
2037 2036 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
2038 2037 allows access to a Mercurial repository where you simply use a web
2039 2038 server to publish the .hg directory as static content.
2040 2039
2041 2040 An optional identifier after # indicates a particular branch, tag,
2042 2041 or changeset to pull.
2043 2042
2044 2043 Some notes about using SSH with Mercurial:
2045 2044 - SSH requires an accessible shell account on the destination machine
2046 2045 and a copy of hg in the remote path or specified with as remotecmd.
2047 2046 - path is relative to the remote user's home directory by default.
2048 2047 Use an extra slash at the start of a path to specify an absolute path:
2049 2048 ssh://example.com//tmp/repository
2050 2049 - Mercurial doesn't use its own compression via SSH; the right thing
2051 2050 to do is to configure it in your ~/.ssh/config, e.g.:
2052 2051 Host *.mylocalnetwork.example.com
2053 2052 Compression no
2054 2053 Host *
2055 2054 Compression yes
2056 2055 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2057 2056 with the --ssh command line option.
2058 2057 """
2059 2058 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts['rev'])
2060 2059 cmdutil.setremoteconfig(ui, opts)
2061 2060
2062 2061 other = hg.repository(ui, source)
2063 2062 ui.status(_('pulling from %s\n') % util.hidepassword(source))
2064 2063 if revs:
2065 2064 try:
2066 2065 revs = [other.lookup(rev) for rev in revs]
2067 2066 except NoCapability:
2068 2067 error = _("Other repository doesn't support revision lookup, "
2069 2068 "so a rev cannot be specified.")
2070 2069 raise util.Abort(error)
2071 2070
2072 2071 modheads = repo.pull(other, heads=revs, force=opts['force'])
2073 2072 return postincoming(ui, repo, modheads, opts['update'], checkout)
2074 2073
2075 2074 def push(ui, repo, dest=None, **opts):
2076 2075 """push changes to the specified destination
2077 2076
2078 2077 Push changes from the local repository to the given destination.
2079 2078
2080 2079 This is the symmetrical operation for pull. It helps to move
2081 2080 changes from the current repository to a different one. If the
2082 2081 destination is local this is identical to a pull in that directory
2083 2082 from the current one.
2084 2083
2085 2084 By default, push will refuse to run if it detects the result would
2086 2085 increase the number of remote heads. This generally indicates the
2087 2086 the client has forgotten to sync and merge before pushing.
2088 2087
2089 2088 Valid URLs are of the form:
2090 2089
2091 2090 local/filesystem/path (or file://local/filesystem/path)
2092 2091 ssh://[user@]host[:port]/[path]
2093 2092 http://[user@]host[:port]/[path]
2094 2093 https://[user@]host[:port]/[path]
2095 2094
2096 2095 An optional identifier after # indicates a particular branch, tag,
2097 2096 or changeset to push.
2098 2097
2099 2098 Look at the help text for the pull command for important details
2100 2099 about ssh:// URLs.
2101 2100
2102 2101 Pushing to http:// and https:// URLs is only possible, if this
2103 2102 feature is explicitly enabled on the remote Mercurial server.
2104 2103 """
2105 2104 dest, revs, checkout = hg.parseurl(
2106 2105 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
2107 2106 cmdutil.setremoteconfig(ui, opts)
2108 2107
2109 2108 other = hg.repository(ui, dest)
2110 2109 ui.status('pushing to %s\n' % util.hidepassword(dest))
2111 2110 if revs:
2112 2111 revs = [repo.lookup(rev) for rev in revs]
2113 2112 r = repo.push(other, opts['force'], revs=revs)
2114 2113 return r == 0
2115 2114
2116 2115 def rawcommit(ui, repo, *pats, **opts):
2117 2116 """raw commit interface (DEPRECATED)
2118 2117
2119 2118 (DEPRECATED)
2120 2119 Lowlevel commit, for use in helper scripts.
2121 2120
2122 2121 This command is not intended to be used by normal users, as it is
2123 2122 primarily useful for importing from other SCMs.
2124 2123
2125 2124 This command is now deprecated and will be removed in a future
2126 2125 release, please use debugsetparents and commit instead.
2127 2126 """
2128 2127
2129 2128 ui.warn(_("(the rawcommit command is deprecated)\n"))
2130 2129
2131 2130 message = cmdutil.logmessage(opts)
2132 2131
2133 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
2132 files = cmdutil.match(repo, pats, opts).files()
2134 2133 if opts['files']:
2135 2134 files += open(opts['files']).read().splitlines()
2136 2135
2137 2136 parents = [repo.lookup(p) for p in opts['parent']]
2138 2137
2139 2138 try:
2140 2139 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2141 2140 except ValueError, inst:
2142 2141 raise util.Abort(str(inst))
2143 2142
2144 2143 def recover(ui, repo):
2145 2144 """roll back an interrupted transaction
2146 2145
2147 2146 Recover from an interrupted commit or pull.
2148 2147
2149 2148 This command tries to fix the repository status after an interrupted
2150 2149 operation. It should only be necessary when Mercurial suggests it.
2151 2150 """
2152 2151 if repo.recover():
2153 2152 return hg.verify(repo)
2154 2153 return 1
2155 2154
2156 2155 def remove(ui, repo, *pats, **opts):
2157 2156 """remove the specified files on the next commit
2158 2157
2159 2158 Schedule the indicated files for removal from the repository.
2160 2159
2161 2160 This only removes files from the current branch, not from the entire
2162 2161 project history. -A can be used to remove only files that have already
2163 2162 been deleted, -f can be used to force deletion, and -Af can be used
2164 2163 to remove files from the next revision without deleting them.
2165 2164
2166 2165 The following table details the behavior of remove for different file
2167 2166 states (columns) and option combinations (rows). The file states are
2168 2167 Added, Clean, Modified and Missing (as reported by hg status). The
2169 2168 actions are Warn, Remove (from branch) and Delete (from disk).
2170 2169
2171 2170 A C M !
2172 2171 none W RD W R
2173 2172 -f R RD RD R
2174 2173 -A W W W R
2175 2174 -Af R R R R
2176 2175
2177 2176 This command schedules the files to be removed at the next commit.
2178 2177 To undo a remove before that, see hg revert.
2179 2178 """
2180 2179
2181 2180 after, force = opts.get('after'), opts.get('force')
2182 2181 if not pats and not after:
2183 2182 raise util.Abort(_('no files specified'))
2184 2183
2185 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2186 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2184 m = cmdutil.match(repo, pats, opts)
2185 mardu = map(dict.fromkeys, repo.status(files=m.files(), match=m))[:5]
2187 2186 modified, added, removed, deleted, unknown = mardu
2188 2187
2189 2188 remove, forget = [], []
2190 m = cmdutil.match(repo, pats, opts)
2191 2189 for src, abs, rel, exact in cmdutil.walk(repo, m):
2192 2190
2193 2191 reason = None
2194 2192 if abs in removed or abs in unknown:
2195 2193 continue
2196 2194
2197 2195 # last column
2198 2196 elif abs in deleted:
2199 2197 remove.append(abs)
2200 2198
2201 2199 # rest of the third row
2202 2200 elif after and not force:
2203 2201 reason = _('still exists (use -f to force removal)')
2204 2202
2205 2203 # rest of the first column
2206 2204 elif abs in added:
2207 2205 if not force:
2208 2206 reason = _('has been marked for add (use -f to force removal)')
2209 2207 else:
2210 2208 forget.append(abs)
2211 2209
2212 2210 # rest of the third column
2213 2211 elif abs in modified:
2214 2212 if not force:
2215 2213 reason = _('is modified (use -f to force removal)')
2216 2214 else:
2217 2215 remove.append(abs)
2218 2216
2219 2217 # rest of the second column
2220 2218 elif not reason:
2221 2219 remove.append(abs)
2222 2220
2223 2221 if reason:
2224 2222 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2225 2223 elif ui.verbose or not exact:
2226 2224 ui.status(_('removing %s\n') % rel)
2227 2225
2228 2226 repo.forget(forget)
2229 2227 repo.remove(remove, unlink=not after)
2230 2228
2231 2229 def rename(ui, repo, *pats, **opts):
2232 2230 """rename files; equivalent of copy + remove
2233 2231
2234 2232 Mark dest as copies of sources; mark sources for deletion. If
2235 2233 dest is a directory, copies are put in that directory. If dest is
2236 2234 a file, there can only be one source.
2237 2235
2238 2236 By default, this command copies the contents of files as they
2239 2237 stand in the working directory. If invoked with --after, the
2240 2238 operation is recorded, but no copying is performed.
2241 2239
2242 2240 This command takes effect in the next commit. To undo a rename
2243 2241 before that, see hg revert.
2244 2242 """
2245 2243 wlock = repo.wlock(False)
2246 2244 try:
2247 2245 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2248 2246 finally:
2249 2247 del wlock
2250 2248
2251 2249 def resolve(ui, repo, *pats, **opts):
2252 2250 """resolve file merges from a branch merge or update
2253 2251
2254 2252 This command will attempt to resolve unresolved merges from the
2255 2253 last update or merge command. This will use the local file
2256 2254 revision preserved at the last update or merge to cleanly retry
2257 2255 the file merge attempt. With no file or options specified, this
2258 2256 command will attempt to resolve all unresolved files.
2259 2257 """
2260 2258
2261 2259 if len([x for x in opts if opts[x]]) > 1:
2262 2260 raise util.Abort(_("too many options specified"))
2263 2261
2264 2262 ms = merge_.mergestate(repo)
2265 2263 mf = util.matcher(repo.root, "", pats, [], [])[1]
2266 2264
2267 2265 for f in ms:
2268 2266 if mf(f):
2269 2267 if opts.get("list"):
2270 2268 ui.write("%s %s\n" % (ms[f].upper(), f))
2271 2269 elif opts.get("mark"):
2272 2270 ms.mark(f, "r")
2273 2271 elif opts.get("unmark"):
2274 2272 ms.mark(f, "u")
2275 2273 else:
2276 2274 wctx = repo.workingctx()
2277 2275 mctx = wctx.parents()[-1]
2278 2276 ms.resolve(f, wctx, mctx)
2279 2277
2280 2278 def revert(ui, repo, *pats, **opts):
2281 2279 """restore individual files or dirs to an earlier state
2282 2280
2283 2281 (use update -r to check out earlier revisions, revert does not
2284 2282 change the working dir parents)
2285 2283
2286 2284 With no revision specified, revert the named files or directories
2287 2285 to the contents they had in the parent of the working directory.
2288 2286 This restores the contents of the affected files to an unmodified
2289 2287 state and unschedules adds, removes, copies, and renames. If the
2290 2288 working directory has two parents, you must explicitly specify the
2291 2289 revision to revert to.
2292 2290
2293 2291 Using the -r option, revert the given files or directories to their
2294 2292 contents as of a specific revision. This can be helpful to "roll
2295 2293 back" some or all of an earlier change.
2296 2294 See 'hg help dates' for a list of formats valid for -d/--date.
2297 2295
2298 2296 Revert modifies the working directory. It does not commit any
2299 2297 changes, or change the parent of the working directory. If you
2300 2298 revert to a revision other than the parent of the working
2301 2299 directory, the reverted files will thus appear modified
2302 2300 afterwards.
2303 2301
2304 2302 If a file has been deleted, it is restored. If the executable
2305 2303 mode of a file was changed, it is reset.
2306 2304
2307 2305 If names are given, all files matching the names are reverted.
2308 2306 If no arguments are given, no files are reverted.
2309 2307
2310 2308 Modified files are saved with a .orig suffix before reverting.
2311 2309 To disable these backups, use --no-backup.
2312 2310 """
2313 2311
2314 2312 if opts["date"]:
2315 2313 if opts["rev"]:
2316 2314 raise util.Abort(_("you can't specify a revision and a date"))
2317 2315 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2318 2316
2319 2317 if not pats and not opts['all']:
2320 2318 raise util.Abort(_('no files or directories specified; '
2321 2319 'use --all to revert the whole repo'))
2322 2320
2323 2321 parent, p2 = repo.dirstate.parents()
2324 2322 if not opts['rev'] and p2 != nullid:
2325 2323 raise util.Abort(_('uncommitted merge - please provide a '
2326 2324 'specific revision'))
2327 2325 ctx = repo.changectx(opts['rev'])
2328 2326 node = ctx.node()
2329 2327 mf = ctx.manifest()
2330 2328 if node == parent:
2331 2329 pmf = mf
2332 2330 else:
2333 2331 pmf = None
2334 2332
2335 2333 # need all matching names in dirstate and manifest of target rev,
2336 2334 # so have to walk both. do not print errors if files exist in one
2337 2335 # but not other.
2338 2336
2339 2337 names = {}
2340 2338
2341 2339 wlock = repo.wlock()
2342 2340 try:
2343 2341 # walk dirstate.
2344 2342 files = []
2345 2343
2346 2344 m = cmdutil.match(repo, pats, opts)
2347 2345 def bad(f, msg):
2348 2346 if f not in mf:
2349 2347 repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
2350 2348 return False
2351 2349 m.bad = bad
2352 2350 for src, abs, rel, exact in cmdutil.walk(repo, m):
2353 2351 names[abs] = (rel, exact)
2354 2352
2355 2353 # walk target manifest.
2356 2354
2357 2355 def badfn(path, msg):
2358 2356 if path in names:
2359 2357 return True
2360 2358 path_ = path + '/'
2361 2359 for f in names:
2362 2360 if f.startswith(path_):
2363 2361 return True
2364 2362 return False
2365 2363
2366 2364 m = cmdutil.match(repo, pats, opts)
2367 2365 m.bad = badfn
2368 2366 for src, abs, rel, exact in cmdutil.walk(repo, m, node=node):
2369 2367 if abs in names or src == 'b':
2370 2368 continue
2371 2369 names[abs] = (rel, exact)
2372 2370
2373 2371 changes = repo.status(files=files, match=names.has_key)[:4]
2374 2372 modified, added, removed, deleted = map(dict.fromkeys, changes)
2375 2373
2376 2374 # if f is a rename, also revert the source
2377 2375 cwd = repo.getcwd()
2378 2376 for f in added:
2379 2377 src = repo.dirstate.copied(f)
2380 2378 if src and src not in names and repo.dirstate[src] == 'r':
2381 2379 removed[src] = None
2382 2380 names[src] = (repo.pathto(src, cwd), True)
2383 2381
2384 2382 def removeforget(abs):
2385 2383 if repo.dirstate[abs] == 'a':
2386 2384 return _('forgetting %s\n')
2387 2385 return _('removing %s\n')
2388 2386
2389 2387 revert = ([], _('reverting %s\n'))
2390 2388 add = ([], _('adding %s\n'))
2391 2389 remove = ([], removeforget)
2392 2390 undelete = ([], _('undeleting %s\n'))
2393 2391
2394 2392 disptable = (
2395 2393 # dispatch table:
2396 2394 # file state
2397 2395 # action if in target manifest
2398 2396 # action if not in target manifest
2399 2397 # make backup if in target manifest
2400 2398 # make backup if not in target manifest
2401 2399 (modified, revert, remove, True, True),
2402 2400 (added, revert, remove, True, False),
2403 2401 (removed, undelete, None, False, False),
2404 2402 (deleted, revert, remove, False, False),
2405 2403 )
2406 2404
2407 2405 entries = names.items()
2408 2406 entries.sort()
2409 2407
2410 2408 for abs, (rel, exact) in entries:
2411 2409 mfentry = mf.get(abs)
2412 2410 target = repo.wjoin(abs)
2413 2411 def handle(xlist, dobackup):
2414 2412 xlist[0].append(abs)
2415 2413 if dobackup and not opts['no_backup'] and util.lexists(target):
2416 2414 bakname = "%s.orig" % rel
2417 2415 ui.note(_('saving current version of %s as %s\n') %
2418 2416 (rel, bakname))
2419 2417 if not opts.get('dry_run'):
2420 2418 util.copyfile(target, bakname)
2421 2419 if ui.verbose or not exact:
2422 2420 msg = xlist[1]
2423 2421 if not isinstance(msg, basestring):
2424 2422 msg = msg(abs)
2425 2423 ui.status(msg % rel)
2426 2424 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2427 2425 if abs not in table: continue
2428 2426 # file has changed in dirstate
2429 2427 if mfentry:
2430 2428 handle(hitlist, backuphit)
2431 2429 elif misslist is not None:
2432 2430 handle(misslist, backupmiss)
2433 2431 break
2434 2432 else:
2435 2433 if abs not in repo.dirstate:
2436 2434 if mfentry:
2437 2435 handle(add, True)
2438 2436 elif exact:
2439 2437 ui.warn(_('file not managed: %s\n') % rel)
2440 2438 continue
2441 2439 # file has not changed in dirstate
2442 2440 if node == parent:
2443 2441 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2444 2442 continue
2445 2443 if pmf is None:
2446 2444 # only need parent manifest in this unlikely case,
2447 2445 # so do not read by default
2448 2446 pmf = repo.changectx(parent).manifest()
2449 2447 if abs in pmf:
2450 2448 if mfentry:
2451 2449 # if version of file is same in parent and target
2452 2450 # manifests, do nothing
2453 2451 if (pmf[abs] != mfentry or
2454 2452 pmf.flags(abs) != mf.flags(abs)):
2455 2453 handle(revert, False)
2456 2454 else:
2457 2455 handle(remove, False)
2458 2456
2459 2457 if not opts.get('dry_run'):
2460 2458 def checkout(f):
2461 2459 fc = ctx[f]
2462 2460 repo.wwrite(f, fc.data(), fc.fileflags())
2463 2461
2464 2462 audit_path = util.path_auditor(repo.root)
2465 2463 for f in remove[0]:
2466 2464 if repo.dirstate[f] == 'a':
2467 2465 repo.dirstate.forget(f)
2468 2466 continue
2469 2467 audit_path(f)
2470 2468 try:
2471 2469 util.unlink(repo.wjoin(f))
2472 2470 except OSError:
2473 2471 pass
2474 2472 repo.dirstate.remove(f)
2475 2473
2476 2474 normal = None
2477 2475 if node == parent:
2478 2476 # We're reverting to our parent. If possible, we'd like status
2479 2477 # to report the file as clean. We have to use normallookup for
2480 2478 # merges to avoid losing information about merged/dirty files.
2481 2479 if p2 != nullid:
2482 2480 normal = repo.dirstate.normallookup
2483 2481 else:
2484 2482 normal = repo.dirstate.normal
2485 2483 for f in revert[0]:
2486 2484 checkout(f)
2487 2485 if normal:
2488 2486 normal(f)
2489 2487
2490 2488 for f in add[0]:
2491 2489 checkout(f)
2492 2490 repo.dirstate.add(f)
2493 2491
2494 2492 normal = repo.dirstate.normallookup
2495 2493 if node == parent and p2 == nullid:
2496 2494 normal = repo.dirstate.normal
2497 2495 for f in undelete[0]:
2498 2496 checkout(f)
2499 2497 normal(f)
2500 2498
2501 2499 finally:
2502 2500 del wlock
2503 2501
2504 2502 def rollback(ui, repo):
2505 2503 """roll back the last transaction
2506 2504
2507 2505 This command should be used with care. There is only one level of
2508 2506 rollback, and there is no way to undo a rollback. It will also
2509 2507 restore the dirstate at the time of the last transaction, losing
2510 2508 any dirstate changes since that time.
2511 2509
2512 2510 Transactions are used to encapsulate the effects of all commands
2513 2511 that create new changesets or propagate existing changesets into a
2514 2512 repository. For example, the following commands are transactional,
2515 2513 and their effects can be rolled back:
2516 2514
2517 2515 commit
2518 2516 import
2519 2517 pull
2520 2518 push (with this repository as destination)
2521 2519 unbundle
2522 2520
2523 2521 This command is not intended for use on public repositories. Once
2524 2522 changes are visible for pull by other users, rolling a transaction
2525 2523 back locally is ineffective (someone else may already have pulled
2526 2524 the changes). Furthermore, a race is possible with readers of the
2527 2525 repository; for example an in-progress pull from the repository
2528 2526 may fail if a rollback is performed.
2529 2527 """
2530 2528 repo.rollback()
2531 2529
2532 2530 def root(ui, repo):
2533 2531 """print the root (top) of the current working dir
2534 2532
2535 2533 Print the root directory of the current repository.
2536 2534 """
2537 2535 ui.write(repo.root + "\n")
2538 2536
2539 2537 def serve(ui, repo, **opts):
2540 2538 """export the repository via HTTP
2541 2539
2542 2540 Start a local HTTP repository browser and pull server.
2543 2541
2544 2542 By default, the server logs accesses to stdout and errors to
2545 2543 stderr. Use the "-A" and "-E" options to log to files.
2546 2544 """
2547 2545
2548 2546 if opts["stdio"]:
2549 2547 if repo is None:
2550 2548 raise RepoError(_("There is no Mercurial repository here"
2551 2549 " (.hg not found)"))
2552 2550 s = sshserver.sshserver(ui, repo)
2553 2551 s.serve_forever()
2554 2552
2555 2553 parentui = ui.parentui or ui
2556 2554 optlist = ("name templates style address port prefix ipv6"
2557 2555 " accesslog errorlog webdir_conf certificate")
2558 2556 for o in optlist.split():
2559 2557 if opts[o]:
2560 2558 parentui.setconfig("web", o, str(opts[o]))
2561 2559 if (repo is not None) and (repo.ui != parentui):
2562 2560 repo.ui.setconfig("web", o, str(opts[o]))
2563 2561
2564 2562 if repo is None and not ui.config("web", "webdir_conf"):
2565 2563 raise RepoError(_("There is no Mercurial repository here"
2566 2564 " (.hg not found)"))
2567 2565
2568 2566 class service:
2569 2567 def init(self):
2570 2568 util.set_signal_handler()
2571 2569 self.httpd = hgweb.server.create_server(parentui, repo)
2572 2570
2573 2571 if not ui.verbose: return
2574 2572
2575 2573 if self.httpd.prefix:
2576 2574 prefix = self.httpd.prefix.strip('/') + '/'
2577 2575 else:
2578 2576 prefix = ''
2579 2577
2580 2578 port = ':%d' % self.httpd.port
2581 2579 if port == ':80':
2582 2580 port = ''
2583 2581
2584 2582 bindaddr = self.httpd.addr
2585 2583 if bindaddr == '0.0.0.0':
2586 2584 bindaddr = '*'
2587 2585 elif ':' in bindaddr: # IPv6
2588 2586 bindaddr = '[%s]' % bindaddr
2589 2587
2590 2588 fqaddr = self.httpd.fqaddr
2591 2589 if ':' in fqaddr:
2592 2590 fqaddr = '[%s]' % fqaddr
2593 2591 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2594 2592 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2595 2593
2596 2594 def run(self):
2597 2595 self.httpd.serve_forever()
2598 2596
2599 2597 service = service()
2600 2598
2601 2599 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2602 2600
2603 2601 def status(ui, repo, *pats, **opts):
2604 2602 """show changed files in the working directory
2605 2603
2606 2604 Show status of files in the repository. If names are given, only
2607 2605 files that match are shown. Files that are clean or ignored or
2608 2606 source of a copy/move operation, are not listed unless -c (clean),
2609 2607 -i (ignored), -C (copies) or -A is given. Unless options described
2610 2608 with "show only ..." are given, the options -mardu are used.
2611 2609
2612 2610 Option -q/--quiet hides untracked (unknown and ignored) files
2613 2611 unless explicitly requested with -u/--unknown or -i/-ignored.
2614 2612
2615 2613 NOTE: status may appear to disagree with diff if permissions have
2616 2614 changed or a merge has occurred. The standard diff format does not
2617 2615 report permission changes and diff only reports changes relative
2618 2616 to one merge parent.
2619 2617
2620 2618 If one revision is given, it is used as the base revision.
2621 2619 If two revisions are given, the difference between them is shown.
2622 2620
2623 2621 The codes used to show the status of files are:
2624 2622 M = modified
2625 2623 A = added
2626 2624 R = removed
2627 2625 C = clean
2628 2626 ! = deleted, but still tracked
2629 2627 ? = not tracked
2630 2628 I = ignored
2631 2629 = the previous added file was copied from here
2632 2630 """
2633 2631
2634 2632 all = opts['all']
2635 2633 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2636 2634
2637 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2635 matcher = cmdutil.match(repo, pats, opts)
2638 2636 cwd = (pats and repo.getcwd()) or ''
2639 2637 modified, added, removed, deleted, unknown, ignored, clean = [
2640 n for n in repo.status(node1=node1, node2=node2, files=files,
2641 match=matchfn,
2638 n for n in repo.status(node1, node2, matcher.files(), matcher,
2642 2639 list_ignored=opts['ignored']
2643 2640 or all and not ui.quiet,
2644 2641 list_clean=opts['clean'] or all,
2645 2642 list_unknown=opts['unknown']
2646 2643 or not (ui.quiet or
2647 2644 opts['modified'] or
2648 2645 opts['added'] or
2649 2646 opts['removed'] or
2650 2647 opts['deleted'] or
2651 2648 opts['ignored']))]
2652 2649
2653 2650 changetypes = (('modified', 'M', modified),
2654 2651 ('added', 'A', added),
2655 2652 ('removed', 'R', removed),
2656 2653 ('deleted', '!', deleted),
2657 2654 ('unknown', '?', unknown),
2658 2655 ('ignored', 'I', ignored))
2659 2656
2660 2657 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2661 2658
2662 2659 copy = {}
2663 2660 showcopy = {}
2664 2661 if ((all or opts.get('copies')) and not opts.get('no_status')):
2665 2662 if opts.get('rev') == []:
2666 2663 # fast path, more correct with merge parents
2667 2664 showcopy = copy = repo.dirstate.copies().copy()
2668 2665 else:
2669 2666 ctxn = repo.changectx(nullid)
2670 2667 ctx1 = repo.changectx(node1)
2671 2668 ctx2 = repo.changectx(node2)
2672 2669 if node2 is None:
2673 2670 ctx2 = repo.workingctx()
2674 2671 copy, diverge = copies.copies(repo, ctx1, ctx2, ctxn)
2675 2672 for k, v in copy.items():
2676 2673 copy[v] = k
2677 2674
2678 2675 end = opts['print0'] and '\0' or '\n'
2679 2676
2680 2677 for opt, char, changes in ([ct for ct in explicit_changetypes
2681 2678 if all or opts[ct[0]]]
2682 2679 or changetypes):
2683 2680
2684 2681 if opts['no_status']:
2685 2682 format = "%%s%s" % end
2686 2683 else:
2687 2684 format = "%s %%s%s" % (char, end)
2688 2685
2689 2686 for f in changes:
2690 2687 ui.write(format % repo.pathto(f, cwd))
2691 2688 if f in copy and (f in added or f in showcopy):
2692 2689 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2693 2690
2694 2691 def tag(ui, repo, name1, *names, **opts):
2695 2692 """add one or more tags for the current or given revision
2696 2693
2697 2694 Name a particular revision using <name>.
2698 2695
2699 2696 Tags are used to name particular revisions of the repository and are
2700 2697 very useful to compare different revisions, to go back to significant
2701 2698 earlier versions or to mark branch points as releases, etc.
2702 2699
2703 2700 If no revision is given, the parent of the working directory is used,
2704 2701 or tip if no revision is checked out.
2705 2702
2706 2703 To facilitate version control, distribution, and merging of tags,
2707 2704 they are stored as a file named ".hgtags" which is managed
2708 2705 similarly to other project files and can be hand-edited if
2709 2706 necessary. The file '.hg/localtags' is used for local tags (not
2710 2707 shared among repositories).
2711 2708
2712 2709 See 'hg help dates' for a list of formats valid for -d/--date.
2713 2710 """
2714 2711
2715 2712 rev_ = None
2716 2713 names = (name1,) + names
2717 2714 if len(names) != len(dict.fromkeys(names)):
2718 2715 raise util.Abort(_('tag names must be unique'))
2719 2716 for n in names:
2720 2717 if n in ['tip', '.', 'null']:
2721 2718 raise util.Abort(_('the name \'%s\' is reserved') % n)
2722 2719 if opts['rev'] and opts['remove']:
2723 2720 raise util.Abort(_("--rev and --remove are incompatible"))
2724 2721 if opts['rev']:
2725 2722 rev_ = opts['rev']
2726 2723 message = opts['message']
2727 2724 if opts['remove']:
2728 2725 expectedtype = opts['local'] and 'local' or 'global'
2729 2726 for n in names:
2730 2727 if not repo.tagtype(n):
2731 2728 raise util.Abort(_('tag \'%s\' does not exist') % n)
2732 2729 if repo.tagtype(n) != expectedtype:
2733 2730 raise util.Abort(_('tag \'%s\' is not a %s tag') %
2734 2731 (n, expectedtype))
2735 2732 rev_ = nullid
2736 2733 if not message:
2737 2734 message = _('Removed tag %s') % ', '.join(names)
2738 2735 elif not opts['force']:
2739 2736 for n in names:
2740 2737 if n in repo.tags():
2741 2738 raise util.Abort(_('tag \'%s\' already exists '
2742 2739 '(use -f to force)') % n)
2743 2740 if not rev_ and repo.dirstate.parents()[1] != nullid:
2744 2741 raise util.Abort(_('uncommitted merge - please provide a '
2745 2742 'specific revision'))
2746 2743 r = repo.changectx(rev_).node()
2747 2744
2748 2745 if not message:
2749 2746 message = (_('Added tag %s for changeset %s') %
2750 2747 (', '.join(names), short(r)))
2751 2748
2752 2749 date = opts.get('date')
2753 2750 if date:
2754 2751 date = util.parsedate(date)
2755 2752
2756 2753 repo.tag(names, r, message, opts['local'], opts['user'], date)
2757 2754
2758 2755 def tags(ui, repo):
2759 2756 """list repository tags
2760 2757
2761 2758 List the repository tags.
2762 2759
2763 2760 This lists both regular and local tags. When the -v/--verbose switch
2764 2761 is used, a third column "local" is printed for local tags.
2765 2762 """
2766 2763
2767 2764 l = repo.tagslist()
2768 2765 l.reverse()
2769 2766 hexfunc = ui.debugflag and hex or short
2770 2767 tagtype = ""
2771 2768
2772 2769 for t, n in l:
2773 2770 if ui.quiet:
2774 2771 ui.write("%s\n" % t)
2775 2772 continue
2776 2773
2777 2774 try:
2778 2775 hn = hexfunc(n)
2779 2776 r = "%5d:%s" % (repo.changelog.rev(n), hn)
2780 2777 except revlog.LookupError:
2781 2778 r = " ?:%s" % hn
2782 2779 else:
2783 2780 spaces = " " * (30 - util.locallen(t))
2784 2781 if ui.verbose:
2785 2782 if repo.tagtype(t) == 'local':
2786 2783 tagtype = " local"
2787 2784 else:
2788 2785 tagtype = ""
2789 2786 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
2790 2787
2791 2788 def tip(ui, repo, **opts):
2792 2789 """show the tip revision
2793 2790
2794 2791 The tip revision (usually just called the tip) is the most
2795 2792 recently added changeset in the repository, the most recently
2796 2793 changed head.
2797 2794
2798 2795 If you have just made a commit, that commit will be the tip. If
2799 2796 you have just pulled changes from another repository, the tip of
2800 2797 that repository becomes the current tip. The "tip" tag is special
2801 2798 and cannot be renamed or assigned to a different changeset.
2802 2799 """
2803 2800 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2804 2801
2805 2802 def unbundle(ui, repo, fname1, *fnames, **opts):
2806 2803 """apply one or more changegroup files
2807 2804
2808 2805 Apply one or more compressed changegroup files generated by the
2809 2806 bundle command.
2810 2807 """
2811 2808 fnames = (fname1,) + fnames
2812 2809
2813 2810 lock = None
2814 2811 try:
2815 2812 lock = repo.lock()
2816 2813 for fname in fnames:
2817 2814 if os.path.exists(fname):
2818 2815 f = open(fname, "rb")
2819 2816 else:
2820 2817 f = urllib.urlopen(fname)
2821 2818 gen = changegroup.readbundle(f, fname)
2822 2819 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2823 2820 finally:
2824 2821 del lock
2825 2822
2826 2823 return postincoming(ui, repo, modheads, opts['update'], None)
2827 2824
2828 2825 def update(ui, repo, node=None, rev=None, clean=False, date=None):
2829 2826 """update working directory
2830 2827
2831 2828 Update the working directory to the specified revision, or the
2832 2829 tip of the current branch if none is specified.
2833 2830
2834 2831 If the requested revision is a descendant of the working
2835 2832 directory, any outstanding changes in the working directory will
2836 2833 be merged into the result. If it is not directly descended but is
2837 2834 on the same named branch, update aborts with a suggestion to use
2838 2835 merge or update -C instead.
2839 2836
2840 2837 If the requested revision is on a different named branch and the
2841 2838 working directory is clean, update quietly switches branches.
2842 2839
2843 2840 See 'hg help dates' for a list of formats valid for --date.
2844 2841 """
2845 2842 if rev and node:
2846 2843 raise util.Abort(_("please specify just one revision"))
2847 2844
2848 2845 if not rev:
2849 2846 rev = node
2850 2847
2851 2848 if date:
2852 2849 if rev:
2853 2850 raise util.Abort(_("you can't specify a revision and a date"))
2854 2851 rev = cmdutil.finddate(ui, repo, date)
2855 2852
2856 2853 if clean:
2857 2854 return hg.clean(repo, rev)
2858 2855 else:
2859 2856 return hg.update(repo, rev)
2860 2857
2861 2858 def verify(ui, repo):
2862 2859 """verify the integrity of the repository
2863 2860
2864 2861 Verify the integrity of the current repository.
2865 2862
2866 2863 This will perform an extensive check of the repository's
2867 2864 integrity, validating the hashes and checksums of each entry in
2868 2865 the changelog, manifest, and tracked files, as well as the
2869 2866 integrity of their crosslinks and indices.
2870 2867 """
2871 2868 return hg.verify(repo)
2872 2869
2873 2870 def version_(ui):
2874 2871 """output version and copyright information"""
2875 2872 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2876 2873 % version.get_version())
2877 2874 ui.status(_(
2878 2875 "\nCopyright (C) 2005-2008 Matt Mackall <mpm@selenic.com> and others\n"
2879 2876 "This is free software; see the source for copying conditions. "
2880 2877 "There is NO\nwarranty; "
2881 2878 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2882 2879 ))
2883 2880
2884 2881 # Command options and aliases are listed here, alphabetically
2885 2882
2886 2883 globalopts = [
2887 2884 ('R', 'repository', '',
2888 2885 _('repository root directory or symbolic path name')),
2889 2886 ('', 'cwd', '', _('change working directory')),
2890 2887 ('y', 'noninteractive', None,
2891 2888 _('do not prompt, assume \'yes\' for any required answers')),
2892 2889 ('q', 'quiet', None, _('suppress output')),
2893 2890 ('v', 'verbose', None, _('enable additional output')),
2894 2891 ('', 'config', [], _('set/override config option')),
2895 2892 ('', 'debug', None, _('enable debugging output')),
2896 2893 ('', 'debugger', None, _('start debugger')),
2897 2894 ('', 'encoding', util._encoding, _('set the charset encoding')),
2898 2895 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2899 2896 ('', 'lsprof', None, _('print improved command execution profile')),
2900 2897 ('', 'traceback', None, _('print traceback on exception')),
2901 2898 ('', 'time', None, _('time how long the command takes')),
2902 2899 ('', 'profile', None, _('print command execution profile')),
2903 2900 ('', 'version', None, _('output version information and exit')),
2904 2901 ('h', 'help', None, _('display help and exit')),
2905 2902 ]
2906 2903
2907 2904 dryrunopts = [('n', 'dry-run', None,
2908 2905 _('do not perform actions, just print output'))]
2909 2906
2910 2907 remoteopts = [
2911 2908 ('e', 'ssh', '', _('specify ssh command to use')),
2912 2909 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2913 2910 ]
2914 2911
2915 2912 walkopts = [
2916 2913 ('I', 'include', [], _('include names matching the given patterns')),
2917 2914 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2918 2915 ]
2919 2916
2920 2917 commitopts = [
2921 2918 ('m', 'message', '', _('use <text> as commit message')),
2922 2919 ('l', 'logfile', '', _('read commit message from <file>')),
2923 2920 ]
2924 2921
2925 2922 commitopts2 = [
2926 2923 ('d', 'date', '', _('record datecode as commit date')),
2927 2924 ('u', 'user', '', _('record user as committer')),
2928 2925 ]
2929 2926
2930 2927 templateopts = [
2931 2928 ('', 'style', '', _('display using template map file')),
2932 2929 ('', 'template', '', _('display with template')),
2933 2930 ]
2934 2931
2935 2932 logopts = [
2936 2933 ('p', 'patch', None, _('show patch')),
2937 2934 ('l', 'limit', '', _('limit number of changes displayed')),
2938 2935 ('M', 'no-merges', None, _('do not show merges')),
2939 2936 ] + templateopts
2940 2937
2941 2938 table = {
2942 2939 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2943 2940 "addremove":
2944 2941 (addremove,
2945 2942 [('s', 'similarity', '',
2946 2943 _('guess renamed files by similarity (0<=s<=100)')),
2947 2944 ] + walkopts + dryrunopts,
2948 2945 _('hg addremove [OPTION]... [FILE]...')),
2949 2946 "^annotate|blame":
2950 2947 (annotate,
2951 2948 [('r', 'rev', '', _('annotate the specified revision')),
2952 2949 ('f', 'follow', None, _('follow file copies and renames')),
2953 2950 ('a', 'text', None, _('treat all files as text')),
2954 2951 ('u', 'user', None, _('list the author (long with -v)')),
2955 2952 ('d', 'date', None, _('list the date (short with -q)')),
2956 2953 ('n', 'number', None, _('list the revision number (default)')),
2957 2954 ('c', 'changeset', None, _('list the changeset')),
2958 2955 ('l', 'line-number', None,
2959 2956 _('show line number at the first appearance'))
2960 2957 ] + walkopts,
2961 2958 _('hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
2962 2959 "archive":
2963 2960 (archive,
2964 2961 [('', 'no-decode', None, _('do not pass files through decoders')),
2965 2962 ('p', 'prefix', '', _('directory prefix for files in archive')),
2966 2963 ('r', 'rev', '', _('revision to distribute')),
2967 2964 ('t', 'type', '', _('type of distribution to create')),
2968 2965 ] + walkopts,
2969 2966 _('hg archive [OPTION]... DEST')),
2970 2967 "backout":
2971 2968 (backout,
2972 2969 [('', 'merge', None,
2973 2970 _('merge with old dirstate parent after backout')),
2974 2971 ('', 'parent', '', _('parent to choose when backing out merge')),
2975 2972 ('r', 'rev', '', _('revision to backout')),
2976 2973 ] + walkopts + commitopts + commitopts2,
2977 2974 _('hg backout [OPTION]... [-r] REV')),
2978 2975 "bisect":
2979 2976 (bisect,
2980 2977 [('r', 'reset', False, _('reset bisect state')),
2981 2978 ('g', 'good', False, _('mark changeset good')),
2982 2979 ('b', 'bad', False, _('mark changeset bad')),
2983 2980 ('s', 'skip', False, _('skip testing changeset')),
2984 2981 ('U', 'noupdate', False, _('do not update to target'))],
2985 2982 _("hg bisect [-gbsr] [REV]")),
2986 2983 "branch":
2987 2984 (branch,
2988 2985 [('f', 'force', None,
2989 2986 _('set branch name even if it shadows an existing branch'))],
2990 2987 _('hg branch [-f] [NAME]')),
2991 2988 "branches":
2992 2989 (branches,
2993 2990 [('a', 'active', False,
2994 2991 _('show only branches that have unmerged heads'))],
2995 2992 _('hg branches [-a]')),
2996 2993 "bundle":
2997 2994 (bundle,
2998 2995 [('f', 'force', None,
2999 2996 _('run even when remote repository is unrelated')),
3000 2997 ('r', 'rev', [],
3001 2998 _('a changeset up to which you would like to bundle')),
3002 2999 ('', 'base', [],
3003 3000 _('a base changeset to specify instead of a destination')),
3004 3001 ('a', 'all', None, _('bundle all changesets in the repository')),
3005 3002 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3006 3003 ] + remoteopts,
3007 3004 _('hg bundle [-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3008 3005 "cat":
3009 3006 (cat,
3010 3007 [('o', 'output', '', _('print output to file with formatted name')),
3011 3008 ('r', 'rev', '', _('print the given revision')),
3012 3009 ('', 'decode', None, _('apply any matching decode filter')),
3013 3010 ] + walkopts,
3014 3011 _('hg cat [OPTION]... FILE...')),
3015 3012 "^clone":
3016 3013 (clone,
3017 3014 [('U', 'noupdate', None, _('do not update the new working directory')),
3018 3015 ('r', 'rev', [],
3019 3016 _('a changeset you would like to have after cloning')),
3020 3017 ('', 'pull', None, _('use pull protocol to copy metadata')),
3021 3018 ('', 'uncompressed', None,
3022 3019 _('use uncompressed transfer (fast over LAN)')),
3023 3020 ] + remoteopts,
3024 3021 _('hg clone [OPTION]... SOURCE [DEST]')),
3025 3022 "^commit|ci":
3026 3023 (commit,
3027 3024 [('A', 'addremove', None,
3028 3025 _('mark new/missing files as added/removed before committing')),
3029 3026 ] + walkopts + commitopts + commitopts2,
3030 3027 _('hg commit [OPTION]... [FILE]...')),
3031 3028 "copy|cp":
3032 3029 (copy,
3033 3030 [('A', 'after', None, _('record a copy that has already occurred')),
3034 3031 ('f', 'force', None,
3035 3032 _('forcibly copy over an existing managed file')),
3036 3033 ] + walkopts + dryrunopts,
3037 3034 _('hg copy [OPTION]... [SOURCE]... DEST')),
3038 3035 "debugancestor": (debugancestor, [],
3039 3036 _('hg debugancestor [INDEX] REV1 REV2')),
3040 3037 "debugcheckstate": (debugcheckstate, [], _('hg debugcheckstate')),
3041 3038 "debugcomplete":
3042 3039 (debugcomplete,
3043 3040 [('o', 'options', None, _('show the command options'))],
3044 3041 _('hg debugcomplete [-o] CMD')),
3045 3042 "debugdate":
3046 3043 (debugdate,
3047 3044 [('e', 'extended', None, _('try extended date formats'))],
3048 3045 _('hg debugdate [-e] DATE [RANGE]')),
3049 3046 "debugdata": (debugdata, [], _('hg debugdata FILE REV')),
3050 3047 "debugfsinfo": (debugfsinfo, [], _('hg debugfsinfo [PATH]')),
3051 3048 "debugindex": (debugindex, [], _('hg debugindex FILE')),
3052 3049 "debugindexdot": (debugindexdot, [], _('hg debugindexdot FILE')),
3053 3050 "debuginstall": (debuginstall, [], _('hg debuginstall')),
3054 3051 "debugrawcommit|rawcommit":
3055 3052 (rawcommit,
3056 3053 [('p', 'parent', [], _('parent')),
3057 3054 ('F', 'files', '', _('file list'))
3058 3055 ] + commitopts + commitopts2,
3059 3056 _('hg debugrawcommit [OPTION]... [FILE]...')),
3060 3057 "debugrebuildstate":
3061 3058 (debugrebuildstate,
3062 3059 [('r', 'rev', '', _('revision to rebuild to'))],
3063 3060 _('hg debugrebuildstate [-r REV] [REV]')),
3064 3061 "debugrename":
3065 3062 (debugrename,
3066 3063 [('r', 'rev', '', _('revision to debug'))],
3067 3064 _('hg debugrename [-r REV] FILE')),
3068 3065 "debugsetparents":
3069 3066 (debugsetparents,
3070 3067 [],
3071 3068 _('hg debugsetparents REV1 [REV2]')),
3072 3069 "debugstate":
3073 3070 (debugstate,
3074 3071 [('', 'nodates', None, _('do not display the saved mtime'))],
3075 3072 _('hg debugstate [OPTS]')),
3076 3073 "debugwalk": (debugwalk, walkopts, _('hg debugwalk [OPTION]... [FILE]...')),
3077 3074 "^diff":
3078 3075 (diff,
3079 3076 [('r', 'rev', [], _('revision')),
3080 3077 ('a', 'text', None, _('treat all files as text')),
3081 3078 ('p', 'show-function', None,
3082 3079 _('show which function each change is in')),
3083 3080 ('g', 'git', None, _('use git extended diff format')),
3084 3081 ('', 'nodates', None, _("don't include dates in diff headers")),
3085 3082 ('w', 'ignore-all-space', None,
3086 3083 _('ignore white space when comparing lines')),
3087 3084 ('b', 'ignore-space-change', None,
3088 3085 _('ignore changes in the amount of white space')),
3089 3086 ('B', 'ignore-blank-lines', None,
3090 3087 _('ignore changes whose lines are all blank')),
3091 3088 ('U', 'unified', '',
3092 3089 _('number of lines of context to show'))
3093 3090 ] + walkopts,
3094 3091 _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3095 3092 "^export":
3096 3093 (export,
3097 3094 [('o', 'output', '', _('print output to file with formatted name')),
3098 3095 ('a', 'text', None, _('treat all files as text')),
3099 3096 ('g', 'git', None, _('use git extended diff format')),
3100 3097 ('', 'nodates', None, _("don't include dates in diff headers")),
3101 3098 ('', 'switch-parent', None, _('diff against the second parent'))],
3102 3099 _('hg export [OPTION]... [-o OUTFILESPEC] REV...')),
3103 3100 "grep":
3104 3101 (grep,
3105 3102 [('0', 'print0', None, _('end fields with NUL')),
3106 3103 ('', 'all', None, _('print all revisions that match')),
3107 3104 ('f', 'follow', None,
3108 3105 _('follow changeset history, or file history across copies and renames')),
3109 3106 ('i', 'ignore-case', None, _('ignore case when matching')),
3110 3107 ('l', 'files-with-matches', None,
3111 3108 _('print only filenames and revs that match')),
3112 3109 ('n', 'line-number', None, _('print matching line numbers')),
3113 3110 ('r', 'rev', [], _('search in given revision range')),
3114 3111 ('u', 'user', None, _('list the author (long with -v)')),
3115 3112 ('d', 'date', None, _('list the date (short with -q)')),
3116 3113 ] + walkopts,
3117 3114 _('hg grep [OPTION]... PATTERN [FILE]...')),
3118 3115 "heads":
3119 3116 (heads,
3120 3117 [('r', 'rev', '', _('show only heads which are descendants of rev')),
3121 3118 ] + templateopts,
3122 3119 _('hg heads [-r REV] [REV]...')),
3123 3120 "help": (help_, [], _('hg help [COMMAND]')),
3124 3121 "identify|id":
3125 3122 (identify,
3126 3123 [('r', 'rev', '', _('identify the specified rev')),
3127 3124 ('n', 'num', None, _('show local revision number')),
3128 3125 ('i', 'id', None, _('show global revision id')),
3129 3126 ('b', 'branch', None, _('show branch')),
3130 3127 ('t', 'tags', None, _('show tags'))],
3131 3128 _('hg identify [-nibt] [-r REV] [SOURCE]')),
3132 3129 "import|patch":
3133 3130 (import_,
3134 3131 [('p', 'strip', 1,
3135 3132 _('directory strip option for patch. This has the same\n'
3136 3133 'meaning as the corresponding patch option')),
3137 3134 ('b', 'base', '', _('base path')),
3138 3135 ('f', 'force', None,
3139 3136 _('skip check for outstanding uncommitted changes')),
3140 3137 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3141 3138 ('', 'exact', None,
3142 3139 _('apply patch to the nodes from which it was generated')),
3143 3140 ('', 'import-branch', None,
3144 3141 _('Use any branch information in patch (implied by --exact)'))] +
3145 3142 commitopts + commitopts2,
3146 3143 _('hg import [OPTION]... PATCH...')),
3147 3144 "incoming|in":
3148 3145 (incoming,
3149 3146 [('f', 'force', None,
3150 3147 _('run even when remote repository is unrelated')),
3151 3148 ('n', 'newest-first', None, _('show newest record first')),
3152 3149 ('', 'bundle', '', _('file to store the bundles into')),
3153 3150 ('r', 'rev', [],
3154 3151 _('a specific revision up to which you would like to pull')),
3155 3152 ] + logopts + remoteopts,
3156 3153 _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
3157 3154 ' [--bundle FILENAME] [SOURCE]')),
3158 3155 "^init":
3159 3156 (init,
3160 3157 remoteopts,
3161 3158 _('hg init [-e CMD] [--remotecmd CMD] [DEST]')),
3162 3159 "locate":
3163 3160 (locate,
3164 3161 [('r', 'rev', '', _('search the repository as it stood at rev')),
3165 3162 ('0', 'print0', None,
3166 3163 _('end filenames with NUL, for use with xargs')),
3167 3164 ('f', 'fullpath', None,
3168 3165 _('print complete paths from the filesystem root')),
3169 3166 ] + walkopts,
3170 3167 _('hg locate [OPTION]... [PATTERN]...')),
3171 3168 "^log|history":
3172 3169 (log,
3173 3170 [('f', 'follow', None,
3174 3171 _('follow changeset history, or file history across copies and renames')),
3175 3172 ('', 'follow-first', None,
3176 3173 _('only follow the first parent of merge changesets')),
3177 3174 ('d', 'date', '', _('show revs matching date spec')),
3178 3175 ('C', 'copies', None, _('show copied files')),
3179 3176 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3180 3177 ('r', 'rev', [], _('show the specified revision or range')),
3181 3178 ('', 'removed', None, _('include revs where files were removed')),
3182 3179 ('m', 'only-merges', None, _('show only merges')),
3183 3180 ('b', 'only-branch', [],
3184 3181 _('show only changesets within the given named branch')),
3185 3182 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3186 3183 ] + logopts + walkopts,
3187 3184 _('hg log [OPTION]... [FILE]')),
3188 3185 "manifest":
3189 3186 (manifest,
3190 3187 [('r', 'rev', '', _('revision to display'))],
3191 3188 _('hg manifest [-r REV]')),
3192 3189 "^merge":
3193 3190 (merge,
3194 3191 [('f', 'force', None, _('force a merge with outstanding changes')),
3195 3192 ('r', 'rev', '', _('revision to merge')),
3196 3193 ],
3197 3194 _('hg merge [-f] [[-r] REV]')),
3198 3195 "outgoing|out":
3199 3196 (outgoing,
3200 3197 [('f', 'force', None,
3201 3198 _('run even when remote repository is unrelated')),
3202 3199 ('r', 'rev', [],
3203 3200 _('a specific revision up to which you would like to push')),
3204 3201 ('n', 'newest-first', None, _('show newest record first')),
3205 3202 ] + logopts + remoteopts,
3206 3203 _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3207 3204 "^parents":
3208 3205 (parents,
3209 3206 [('r', 'rev', '', _('show parents from the specified rev')),
3210 3207 ] + templateopts,
3211 3208 _('hg parents [-r REV] [FILE]')),
3212 3209 "paths": (paths, [], _('hg paths [NAME]')),
3213 3210 "^pull":
3214 3211 (pull,
3215 3212 [('u', 'update', None,
3216 3213 _('update to new tip if changesets were pulled')),
3217 3214 ('f', 'force', None,
3218 3215 _('run even when remote repository is unrelated')),
3219 3216 ('r', 'rev', [],
3220 3217 _('a specific revision up to which you would like to pull')),
3221 3218 ] + remoteopts,
3222 3219 _('hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3223 3220 "^push":
3224 3221 (push,
3225 3222 [('f', 'force', None, _('force push')),
3226 3223 ('r', 'rev', [],
3227 3224 _('a specific revision up to which you would like to push')),
3228 3225 ] + remoteopts,
3229 3226 _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3230 3227 "recover": (recover, [], _('hg recover')),
3231 3228 "^remove|rm":
3232 3229 (remove,
3233 3230 [('A', 'after', None, _('record delete for missing files')),
3234 3231 ('f', 'force', None,
3235 3232 _('remove (and delete) file even if added or modified')),
3236 3233 ] + walkopts,
3237 3234 _('hg remove [OPTION]... FILE...')),
3238 3235 "rename|mv":
3239 3236 (rename,
3240 3237 [('A', 'after', None, _('record a rename that has already occurred')),
3241 3238 ('f', 'force', None,
3242 3239 _('forcibly copy over an existing managed file')),
3243 3240 ] + walkopts + dryrunopts,
3244 3241 _('hg rename [OPTION]... SOURCE... DEST')),
3245 3242 "resolve":
3246 3243 (resolve,
3247 3244 [('l', 'list', None, _('list state of files needing merge')),
3248 3245 ('m', 'mark', None, _('mark files as resolved')),
3249 3246 ('u', 'unmark', None, _('unmark files as resolved'))],
3250 3247 ('hg resolve [OPTION] [FILES...]')),
3251 3248 "revert":
3252 3249 (revert,
3253 3250 [('a', 'all', None, _('revert all changes when no arguments given')),
3254 3251 ('d', 'date', '', _('tipmost revision matching date')),
3255 3252 ('r', 'rev', '', _('revision to revert to')),
3256 3253 ('', 'no-backup', None, _('do not save backup copies of files')),
3257 3254 ] + walkopts + dryrunopts,
3258 3255 _('hg revert [OPTION]... [-r REV] [NAME]...')),
3259 3256 "rollback": (rollback, [], _('hg rollback')),
3260 3257 "root": (root, [], _('hg root')),
3261 3258 "^serve":
3262 3259 (serve,
3263 3260 [('A', 'accesslog', '', _('name of access log file to write to')),
3264 3261 ('d', 'daemon', None, _('run server in background')),
3265 3262 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3266 3263 ('E', 'errorlog', '', _('name of error log file to write to')),
3267 3264 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3268 3265 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3269 3266 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3270 3267 ('n', 'name', '',
3271 3268 _('name to show in web pages (default: working dir)')),
3272 3269 ('', 'webdir-conf', '', _('name of the webdir config file'
3273 3270 ' (serve more than one repo)')),
3274 3271 ('', 'pid-file', '', _('name of file to write process ID to')),
3275 3272 ('', 'stdio', None, _('for remote clients')),
3276 3273 ('t', 'templates', '', _('web templates to use')),
3277 3274 ('', 'style', '', _('template style to use')),
3278 3275 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3279 3276 ('', 'certificate', '', _('SSL certificate file'))],
3280 3277 _('hg serve [OPTION]...')),
3281 3278 "showconfig|debugconfig":
3282 3279 (showconfig,
3283 3280 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3284 3281 _('hg showconfig [-u] [NAME]...')),
3285 3282 "^status|st":
3286 3283 (status,
3287 3284 [('A', 'all', None, _('show status of all files')),
3288 3285 ('m', 'modified', None, _('show only modified files')),
3289 3286 ('a', 'added', None, _('show only added files')),
3290 3287 ('r', 'removed', None, _('show only removed files')),
3291 3288 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3292 3289 ('c', 'clean', None, _('show only files without changes')),
3293 3290 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3294 3291 ('i', 'ignored', None, _('show only ignored files')),
3295 3292 ('n', 'no-status', None, _('hide status prefix')),
3296 3293 ('C', 'copies', None, _('show source of copied files')),
3297 3294 ('0', 'print0', None,
3298 3295 _('end filenames with NUL, for use with xargs')),
3299 3296 ('', 'rev', [], _('show difference from revision')),
3300 3297 ] + walkopts,
3301 3298 _('hg status [OPTION]... [FILE]...')),
3302 3299 "tag":
3303 3300 (tag,
3304 3301 [('f', 'force', None, _('replace existing tag')),
3305 3302 ('l', 'local', None, _('make the tag local')),
3306 3303 ('r', 'rev', '', _('revision to tag')),
3307 3304 ('', 'remove', None, _('remove a tag')),
3308 3305 # -l/--local is already there, commitopts cannot be used
3309 3306 ('m', 'message', '', _('use <text> as commit message')),
3310 3307 ] + commitopts2,
3311 3308 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3312 3309 "tags": (tags, [], _('hg tags')),
3313 3310 "tip":
3314 3311 (tip,
3315 3312 [('p', 'patch', None, _('show patch')),
3316 3313 ] + templateopts,
3317 3314 _('hg tip [-p]')),
3318 3315 "unbundle":
3319 3316 (unbundle,
3320 3317 [('u', 'update', None,
3321 3318 _('update to new tip if changesets were unbundled'))],
3322 3319 _('hg unbundle [-u] FILE...')),
3323 3320 "^update|up|checkout|co":
3324 3321 (update,
3325 3322 [('C', 'clean', None, _('overwrite locally modified files')),
3326 3323 ('d', 'date', '', _('tipmost revision matching date')),
3327 3324 ('r', 'rev', '', _('revision'))],
3328 3325 _('hg update [-C] [-d DATE] [[-r] REV]')),
3329 3326 "verify": (verify, [], _('hg verify')),
3330 3327 "version": (version_, [], _('hg version')),
3331 3328 }
3332 3329
3333 3330 norepo = ("clone init version help debugcomplete debugdata"
3334 3331 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3335 3332 optionalrepo = ("identify paths serve showconfig debugancestor")
General Comments 0
You need to be logged in to leave comments. Login now