##// END OF EJS Templates
patch._applydiff: resolve prefix with respect to the cwd...
Siddharth Agarwal -
r24390:72d7d390 default
parent child Browse files
Show More
@@ -1,6370 +1,6370
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from node import hex, bin, nullid, nullrev, short
8 from node import hex, bin, nullid, nullrev, short
9 from lock import release
9 from lock import release
10 from i18n import _
10 from i18n import _
11 import os, re, difflib, time, tempfile, errno, shlex
11 import os, re, difflib, time, tempfile, errno, shlex
12 import sys, socket
12 import sys, socket
13 import hg, scmutil, util, revlog, copies, error, bookmarks
13 import hg, scmutil, util, revlog, copies, error, bookmarks
14 import patch, help, encoding, templatekw, discovery
14 import patch, help, encoding, templatekw, discovery
15 import archival, changegroup, cmdutil, hbisect
15 import archival, changegroup, cmdutil, hbisect
16 import sshserver, hgweb, commandserver
16 import sshserver, hgweb, commandserver
17 import extensions
17 import extensions
18 from hgweb import server as hgweb_server
18 from hgweb import server as hgweb_server
19 import merge as mergemod
19 import merge as mergemod
20 import minirst, revset, fileset
20 import minirst, revset, fileset
21 import dagparser, context, simplemerge, graphmod, copies
21 import dagparser, context, simplemerge, graphmod, copies
22 import random
22 import random
23 import setdiscovery, treediscovery, dagutil, pvec, localrepo
23 import setdiscovery, treediscovery, dagutil, pvec, localrepo
24 import phases, obsolete, exchange, bundle2
24 import phases, obsolete, exchange, bundle2
25 import ui as uimod
25 import ui as uimod
26
26
27 table = {}
27 table = {}
28
28
29 command = cmdutil.command(table)
29 command = cmdutil.command(table)
30
30
31 # Space delimited list of commands that don't require local repositories.
31 # Space delimited list of commands that don't require local repositories.
32 # This should be populated by passing norepo=True into the @command decorator.
32 # This should be populated by passing norepo=True into the @command decorator.
33 norepo = ''
33 norepo = ''
34 # Space delimited list of commands that optionally require local repositories.
34 # Space delimited list of commands that optionally require local repositories.
35 # This should be populated by passing optionalrepo=True into the @command
35 # This should be populated by passing optionalrepo=True into the @command
36 # decorator.
36 # decorator.
37 optionalrepo = ''
37 optionalrepo = ''
38 # Space delimited list of commands that will examine arguments looking for
38 # Space delimited list of commands that will examine arguments looking for
39 # a repository. This should be populated by passing inferrepo=True into the
39 # a repository. This should be populated by passing inferrepo=True into the
40 # @command decorator.
40 # @command decorator.
41 inferrepo = ''
41 inferrepo = ''
42
42
43 # common command options
43 # common command options
44
44
45 globalopts = [
45 globalopts = [
46 ('R', 'repository', '',
46 ('R', 'repository', '',
47 _('repository root directory or name of overlay bundle file'),
47 _('repository root directory or name of overlay bundle file'),
48 _('REPO')),
48 _('REPO')),
49 ('', 'cwd', '',
49 ('', 'cwd', '',
50 _('change working directory'), _('DIR')),
50 _('change working directory'), _('DIR')),
51 ('y', 'noninteractive', None,
51 ('y', 'noninteractive', None,
52 _('do not prompt, automatically pick the first choice for all prompts')),
52 _('do not prompt, automatically pick the first choice for all prompts')),
53 ('q', 'quiet', None, _('suppress output')),
53 ('q', 'quiet', None, _('suppress output')),
54 ('v', 'verbose', None, _('enable additional output')),
54 ('v', 'verbose', None, _('enable additional output')),
55 ('', 'config', [],
55 ('', 'config', [],
56 _('set/override config option (use \'section.name=value\')'),
56 _('set/override config option (use \'section.name=value\')'),
57 _('CONFIG')),
57 _('CONFIG')),
58 ('', 'debug', None, _('enable debugging output')),
58 ('', 'debug', None, _('enable debugging output')),
59 ('', 'debugger', None, _('start debugger')),
59 ('', 'debugger', None, _('start debugger')),
60 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
60 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
61 _('ENCODE')),
61 _('ENCODE')),
62 ('', 'encodingmode', encoding.encodingmode,
62 ('', 'encodingmode', encoding.encodingmode,
63 _('set the charset encoding mode'), _('MODE')),
63 _('set the charset encoding mode'), _('MODE')),
64 ('', 'traceback', None, _('always print a traceback on exception')),
64 ('', 'traceback', None, _('always print a traceback on exception')),
65 ('', 'time', None, _('time how long the command takes')),
65 ('', 'time', None, _('time how long the command takes')),
66 ('', 'profile', None, _('print command execution profile')),
66 ('', 'profile', None, _('print command execution profile')),
67 ('', 'version', None, _('output version information and exit')),
67 ('', 'version', None, _('output version information and exit')),
68 ('h', 'help', None, _('display help and exit')),
68 ('h', 'help', None, _('display help and exit')),
69 ('', 'hidden', False, _('consider hidden changesets')),
69 ('', 'hidden', False, _('consider hidden changesets')),
70 ]
70 ]
71
71
72 dryrunopts = [('n', 'dry-run', None,
72 dryrunopts = [('n', 'dry-run', None,
73 _('do not perform actions, just print output'))]
73 _('do not perform actions, just print output'))]
74
74
75 remoteopts = [
75 remoteopts = [
76 ('e', 'ssh', '',
76 ('e', 'ssh', '',
77 _('specify ssh command to use'), _('CMD')),
77 _('specify ssh command to use'), _('CMD')),
78 ('', 'remotecmd', '',
78 ('', 'remotecmd', '',
79 _('specify hg command to run on the remote side'), _('CMD')),
79 _('specify hg command to run on the remote side'), _('CMD')),
80 ('', 'insecure', None,
80 ('', 'insecure', None,
81 _('do not verify server certificate (ignoring web.cacerts config)')),
81 _('do not verify server certificate (ignoring web.cacerts config)')),
82 ]
82 ]
83
83
84 walkopts = [
84 walkopts = [
85 ('I', 'include', [],
85 ('I', 'include', [],
86 _('include names matching the given patterns'), _('PATTERN')),
86 _('include names matching the given patterns'), _('PATTERN')),
87 ('X', 'exclude', [],
87 ('X', 'exclude', [],
88 _('exclude names matching the given patterns'), _('PATTERN')),
88 _('exclude names matching the given patterns'), _('PATTERN')),
89 ]
89 ]
90
90
91 commitopts = [
91 commitopts = [
92 ('m', 'message', '',
92 ('m', 'message', '',
93 _('use text as commit message'), _('TEXT')),
93 _('use text as commit message'), _('TEXT')),
94 ('l', 'logfile', '',
94 ('l', 'logfile', '',
95 _('read commit message from file'), _('FILE')),
95 _('read commit message from file'), _('FILE')),
96 ]
96 ]
97
97
98 commitopts2 = [
98 commitopts2 = [
99 ('d', 'date', '',
99 ('d', 'date', '',
100 _('record the specified date as commit date'), _('DATE')),
100 _('record the specified date as commit date'), _('DATE')),
101 ('u', 'user', '',
101 ('u', 'user', '',
102 _('record the specified user as committer'), _('USER')),
102 _('record the specified user as committer'), _('USER')),
103 ]
103 ]
104
104
105 # hidden for now
105 # hidden for now
106 formatteropts = [
106 formatteropts = [
107 ('T', 'template', '',
107 ('T', 'template', '',
108 _('display with template (DEPRECATED)'), _('TEMPLATE')),
108 _('display with template (DEPRECATED)'), _('TEMPLATE')),
109 ]
109 ]
110
110
111 templateopts = [
111 templateopts = [
112 ('', 'style', '',
112 ('', 'style', '',
113 _('display using template map file (DEPRECATED)'), _('STYLE')),
113 _('display using template map file (DEPRECATED)'), _('STYLE')),
114 ('T', 'template', '',
114 ('T', 'template', '',
115 _('display with template'), _('TEMPLATE')),
115 _('display with template'), _('TEMPLATE')),
116 ]
116 ]
117
117
118 logopts = [
118 logopts = [
119 ('p', 'patch', None, _('show patch')),
119 ('p', 'patch', None, _('show patch')),
120 ('g', 'git', None, _('use git extended diff format')),
120 ('g', 'git', None, _('use git extended diff format')),
121 ('l', 'limit', '',
121 ('l', 'limit', '',
122 _('limit number of changes displayed'), _('NUM')),
122 _('limit number of changes displayed'), _('NUM')),
123 ('M', 'no-merges', None, _('do not show merges')),
123 ('M', 'no-merges', None, _('do not show merges')),
124 ('', 'stat', None, _('output diffstat-style summary of changes')),
124 ('', 'stat', None, _('output diffstat-style summary of changes')),
125 ('G', 'graph', None, _("show the revision DAG")),
125 ('G', 'graph', None, _("show the revision DAG")),
126 ] + templateopts
126 ] + templateopts
127
127
128 diffopts = [
128 diffopts = [
129 ('a', 'text', None, _('treat all files as text')),
129 ('a', 'text', None, _('treat all files as text')),
130 ('g', 'git', None, _('use git extended diff format')),
130 ('g', 'git', None, _('use git extended diff format')),
131 ('', 'nodates', None, _('omit dates from diff headers'))
131 ('', 'nodates', None, _('omit dates from diff headers'))
132 ]
132 ]
133
133
134 diffwsopts = [
134 diffwsopts = [
135 ('w', 'ignore-all-space', None,
135 ('w', 'ignore-all-space', None,
136 _('ignore white space when comparing lines')),
136 _('ignore white space when comparing lines')),
137 ('b', 'ignore-space-change', None,
137 ('b', 'ignore-space-change', None,
138 _('ignore changes in the amount of white space')),
138 _('ignore changes in the amount of white space')),
139 ('B', 'ignore-blank-lines', None,
139 ('B', 'ignore-blank-lines', None,
140 _('ignore changes whose lines are all blank')),
140 _('ignore changes whose lines are all blank')),
141 ]
141 ]
142
142
143 diffopts2 = [
143 diffopts2 = [
144 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
144 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
145 ('p', 'show-function', None, _('show which function each change is in')),
145 ('p', 'show-function', None, _('show which function each change is in')),
146 ('', 'reverse', None, _('produce a diff that undoes the changes')),
146 ('', 'reverse', None, _('produce a diff that undoes the changes')),
147 ] + diffwsopts + [
147 ] + diffwsopts + [
148 ('U', 'unified', '',
148 ('U', 'unified', '',
149 _('number of lines of context to show'), _('NUM')),
149 _('number of lines of context to show'), _('NUM')),
150 ('', 'stat', None, _('output diffstat-style summary of changes')),
150 ('', 'stat', None, _('output diffstat-style summary of changes')),
151 ]
151 ]
152
152
153 mergetoolopts = [
153 mergetoolopts = [
154 ('t', 'tool', '', _('specify merge tool')),
154 ('t', 'tool', '', _('specify merge tool')),
155 ]
155 ]
156
156
157 similarityopts = [
157 similarityopts = [
158 ('s', 'similarity', '',
158 ('s', 'similarity', '',
159 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
159 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
160 ]
160 ]
161
161
162 subrepoopts = [
162 subrepoopts = [
163 ('S', 'subrepos', None,
163 ('S', 'subrepos', None,
164 _('recurse into subrepositories'))
164 _('recurse into subrepositories'))
165 ]
165 ]
166
166
167 # Commands start here, listed alphabetically
167 # Commands start here, listed alphabetically
168
168
169 @command('^add',
169 @command('^add',
170 walkopts + subrepoopts + dryrunopts,
170 walkopts + subrepoopts + dryrunopts,
171 _('[OPTION]... [FILE]...'),
171 _('[OPTION]... [FILE]...'),
172 inferrepo=True)
172 inferrepo=True)
173 def add(ui, repo, *pats, **opts):
173 def add(ui, repo, *pats, **opts):
174 """add the specified files on the next commit
174 """add the specified files on the next commit
175
175
176 Schedule files to be version controlled and added to the
176 Schedule files to be version controlled and added to the
177 repository.
177 repository.
178
178
179 The files will be added to the repository at the next commit. To
179 The files will be added to the repository at the next commit. To
180 undo an add before that, see :hg:`forget`.
180 undo an add before that, see :hg:`forget`.
181
181
182 If no names are given, add all files to the repository.
182 If no names are given, add all files to the repository.
183
183
184 .. container:: verbose
184 .. container:: verbose
185
185
186 An example showing how new (unknown) files are added
186 An example showing how new (unknown) files are added
187 automatically by :hg:`add`::
187 automatically by :hg:`add`::
188
188
189 $ ls
189 $ ls
190 foo.c
190 foo.c
191 $ hg status
191 $ hg status
192 ? foo.c
192 ? foo.c
193 $ hg add
193 $ hg add
194 adding foo.c
194 adding foo.c
195 $ hg status
195 $ hg status
196 A foo.c
196 A foo.c
197
197
198 Returns 0 if all files are successfully added.
198 Returns 0 if all files are successfully added.
199 """
199 """
200
200
201 m = scmutil.match(repo[None], pats, opts)
201 m = scmutil.match(repo[None], pats, opts)
202 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
202 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
203 return rejected and 1 or 0
203 return rejected and 1 or 0
204
204
205 @command('addremove',
205 @command('addremove',
206 similarityopts + subrepoopts + walkopts + dryrunopts,
206 similarityopts + subrepoopts + walkopts + dryrunopts,
207 _('[OPTION]... [FILE]...'),
207 _('[OPTION]... [FILE]...'),
208 inferrepo=True)
208 inferrepo=True)
209 def addremove(ui, repo, *pats, **opts):
209 def addremove(ui, repo, *pats, **opts):
210 """add all new files, delete all missing files
210 """add all new files, delete all missing files
211
211
212 Add all new files and remove all missing files from the
212 Add all new files and remove all missing files from the
213 repository.
213 repository.
214
214
215 New files are ignored if they match any of the patterns in
215 New files are ignored if they match any of the patterns in
216 ``.hgignore``. As with add, these changes take effect at the next
216 ``.hgignore``. As with add, these changes take effect at the next
217 commit.
217 commit.
218
218
219 Use the -s/--similarity option to detect renamed files. This
219 Use the -s/--similarity option to detect renamed files. This
220 option takes a percentage between 0 (disabled) and 100 (files must
220 option takes a percentage between 0 (disabled) and 100 (files must
221 be identical) as its parameter. With a parameter greater than 0,
221 be identical) as its parameter. With a parameter greater than 0,
222 this compares every removed file with every added file and records
222 this compares every removed file with every added file and records
223 those similar enough as renames. Detecting renamed files this way
223 those similar enough as renames. Detecting renamed files this way
224 can be expensive. After using this option, :hg:`status -C` can be
224 can be expensive. After using this option, :hg:`status -C` can be
225 used to check which files were identified as moved or renamed. If
225 used to check which files were identified as moved or renamed. If
226 not specified, -s/--similarity defaults to 100 and only renames of
226 not specified, -s/--similarity defaults to 100 and only renames of
227 identical files are detected.
227 identical files are detected.
228
228
229 Returns 0 if all files are successfully added.
229 Returns 0 if all files are successfully added.
230 """
230 """
231 try:
231 try:
232 sim = float(opts.get('similarity') or 100)
232 sim = float(opts.get('similarity') or 100)
233 except ValueError:
233 except ValueError:
234 raise util.Abort(_('similarity must be a number'))
234 raise util.Abort(_('similarity must be a number'))
235 if sim < 0 or sim > 100:
235 if sim < 0 or sim > 100:
236 raise util.Abort(_('similarity must be between 0 and 100'))
236 raise util.Abort(_('similarity must be between 0 and 100'))
237 matcher = scmutil.match(repo[None], pats, opts)
237 matcher = scmutil.match(repo[None], pats, opts)
238 return scmutil.addremove(repo, matcher, "", opts, similarity=sim / 100.0)
238 return scmutil.addremove(repo, matcher, "", opts, similarity=sim / 100.0)
239
239
240 @command('^annotate|blame',
240 @command('^annotate|blame',
241 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
241 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
242 ('', 'follow', None,
242 ('', 'follow', None,
243 _('follow copies/renames and list the filename (DEPRECATED)')),
243 _('follow copies/renames and list the filename (DEPRECATED)')),
244 ('', 'no-follow', None, _("don't follow copies and renames")),
244 ('', 'no-follow', None, _("don't follow copies and renames")),
245 ('a', 'text', None, _('treat all files as text')),
245 ('a', 'text', None, _('treat all files as text')),
246 ('u', 'user', None, _('list the author (long with -v)')),
246 ('u', 'user', None, _('list the author (long with -v)')),
247 ('f', 'file', None, _('list the filename')),
247 ('f', 'file', None, _('list the filename')),
248 ('d', 'date', None, _('list the date (short with -q)')),
248 ('d', 'date', None, _('list the date (short with -q)')),
249 ('n', 'number', None, _('list the revision number (default)')),
249 ('n', 'number', None, _('list the revision number (default)')),
250 ('c', 'changeset', None, _('list the changeset')),
250 ('c', 'changeset', None, _('list the changeset')),
251 ('l', 'line-number', None, _('show line number at the first appearance'))
251 ('l', 'line-number', None, _('show line number at the first appearance'))
252 ] + diffwsopts + walkopts + formatteropts,
252 ] + diffwsopts + walkopts + formatteropts,
253 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
253 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
254 inferrepo=True)
254 inferrepo=True)
255 def annotate(ui, repo, *pats, **opts):
255 def annotate(ui, repo, *pats, **opts):
256 """show changeset information by line for each file
256 """show changeset information by line for each file
257
257
258 List changes in files, showing the revision id responsible for
258 List changes in files, showing the revision id responsible for
259 each line
259 each line
260
260
261 This command is useful for discovering when a change was made and
261 This command is useful for discovering when a change was made and
262 by whom.
262 by whom.
263
263
264 Without the -a/--text option, annotate will avoid processing files
264 Without the -a/--text option, annotate will avoid processing files
265 it detects as binary. With -a, annotate will annotate the file
265 it detects as binary. With -a, annotate will annotate the file
266 anyway, although the results will probably be neither useful
266 anyway, although the results will probably be neither useful
267 nor desirable.
267 nor desirable.
268
268
269 Returns 0 on success.
269 Returns 0 on success.
270 """
270 """
271 if not pats:
271 if not pats:
272 raise util.Abort(_('at least one filename or pattern is required'))
272 raise util.Abort(_('at least one filename or pattern is required'))
273
273
274 if opts.get('follow'):
274 if opts.get('follow'):
275 # --follow is deprecated and now just an alias for -f/--file
275 # --follow is deprecated and now just an alias for -f/--file
276 # to mimic the behavior of Mercurial before version 1.5
276 # to mimic the behavior of Mercurial before version 1.5
277 opts['file'] = True
277 opts['file'] = True
278
278
279 fm = ui.formatter('annotate', opts)
279 fm = ui.formatter('annotate', opts)
280 if ui.quiet:
280 if ui.quiet:
281 datefunc = util.shortdate
281 datefunc = util.shortdate
282 else:
282 else:
283 datefunc = util.datestr
283 datefunc = util.datestr
284 hexfn = fm.hexfunc
284 hexfn = fm.hexfunc
285
285
286 opmap = [('user', ' ', lambda x: x[0].user(), ui.shortuser),
286 opmap = [('user', ' ', lambda x: x[0].user(), ui.shortuser),
287 ('number', ' ', lambda x: x[0].rev(), str),
287 ('number', ' ', lambda x: x[0].rev(), str),
288 ('changeset', ' ', lambda x: hexfn(x[0].node()), str),
288 ('changeset', ' ', lambda x: hexfn(x[0].node()), str),
289 ('date', ' ', lambda x: x[0].date(), util.cachefunc(datefunc)),
289 ('date', ' ', lambda x: x[0].date(), util.cachefunc(datefunc)),
290 ('file', ' ', lambda x: x[0].path(), str),
290 ('file', ' ', lambda x: x[0].path(), str),
291 ('line_number', ':', lambda x: x[1], str),
291 ('line_number', ':', lambda x: x[1], str),
292 ]
292 ]
293 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
293 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
294
294
295 if (not opts.get('user') and not opts.get('changeset')
295 if (not opts.get('user') and not opts.get('changeset')
296 and not opts.get('date') and not opts.get('file')):
296 and not opts.get('date') and not opts.get('file')):
297 opts['number'] = True
297 opts['number'] = True
298
298
299 linenumber = opts.get('line_number') is not None
299 linenumber = opts.get('line_number') is not None
300 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
300 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
301 raise util.Abort(_('at least one of -n/-c is required for -l'))
301 raise util.Abort(_('at least one of -n/-c is required for -l'))
302
302
303 if fm:
303 if fm:
304 def makefunc(get, fmt):
304 def makefunc(get, fmt):
305 return get
305 return get
306 else:
306 else:
307 def makefunc(get, fmt):
307 def makefunc(get, fmt):
308 return lambda x: fmt(get(x))
308 return lambda x: fmt(get(x))
309 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
309 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
310 if opts.get(op)]
310 if opts.get(op)]
311 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
311 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
312 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
312 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
313 if opts.get(op))
313 if opts.get(op))
314
314
315 def bad(x, y):
315 def bad(x, y):
316 raise util.Abort("%s: %s" % (x, y))
316 raise util.Abort("%s: %s" % (x, y))
317
317
318 ctx = scmutil.revsingle(repo, opts.get('rev'))
318 ctx = scmutil.revsingle(repo, opts.get('rev'))
319 m = scmutil.match(ctx, pats, opts)
319 m = scmutil.match(ctx, pats, opts)
320 m.bad = bad
320 m.bad = bad
321 follow = not opts.get('no_follow')
321 follow = not opts.get('no_follow')
322 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
322 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
323 whitespace=True)
323 whitespace=True)
324 for abs in ctx.walk(m):
324 for abs in ctx.walk(m):
325 fctx = ctx[abs]
325 fctx = ctx[abs]
326 if not opts.get('text') and util.binary(fctx.data()):
326 if not opts.get('text') and util.binary(fctx.data()):
327 fm.plain(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
327 fm.plain(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
328 continue
328 continue
329
329
330 lines = fctx.annotate(follow=follow, linenumber=linenumber,
330 lines = fctx.annotate(follow=follow, linenumber=linenumber,
331 diffopts=diffopts)
331 diffopts=diffopts)
332 formats = []
332 formats = []
333 pieces = []
333 pieces = []
334
334
335 for f, sep in funcmap:
335 for f, sep in funcmap:
336 l = [f(n) for n, dummy in lines]
336 l = [f(n) for n, dummy in lines]
337 if l:
337 if l:
338 if fm:
338 if fm:
339 formats.append(['%s' for x in l])
339 formats.append(['%s' for x in l])
340 else:
340 else:
341 sizes = [encoding.colwidth(x) for x in l]
341 sizes = [encoding.colwidth(x) for x in l]
342 ml = max(sizes)
342 ml = max(sizes)
343 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
343 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
344 pieces.append(l)
344 pieces.append(l)
345
345
346 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
346 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
347 fm.startitem()
347 fm.startitem()
348 fm.write(fields, "".join(f), *p)
348 fm.write(fields, "".join(f), *p)
349 fm.write('line', ": %s", l[1])
349 fm.write('line', ": %s", l[1])
350
350
351 if lines and not lines[-1][1].endswith('\n'):
351 if lines and not lines[-1][1].endswith('\n'):
352 fm.plain('\n')
352 fm.plain('\n')
353
353
354 fm.end()
354 fm.end()
355
355
356 @command('archive',
356 @command('archive',
357 [('', 'no-decode', None, _('do not pass files through decoders')),
357 [('', 'no-decode', None, _('do not pass files through decoders')),
358 ('p', 'prefix', '', _('directory prefix for files in archive'),
358 ('p', 'prefix', '', _('directory prefix for files in archive'),
359 _('PREFIX')),
359 _('PREFIX')),
360 ('r', 'rev', '', _('revision to distribute'), _('REV')),
360 ('r', 'rev', '', _('revision to distribute'), _('REV')),
361 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
361 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
362 ] + subrepoopts + walkopts,
362 ] + subrepoopts + walkopts,
363 _('[OPTION]... DEST'))
363 _('[OPTION]... DEST'))
364 def archive(ui, repo, dest, **opts):
364 def archive(ui, repo, dest, **opts):
365 '''create an unversioned archive of a repository revision
365 '''create an unversioned archive of a repository revision
366
366
367 By default, the revision used is the parent of the working
367 By default, the revision used is the parent of the working
368 directory; use -r/--rev to specify a different revision.
368 directory; use -r/--rev to specify a different revision.
369
369
370 The archive type is automatically detected based on file
370 The archive type is automatically detected based on file
371 extension (or override using -t/--type).
371 extension (or override using -t/--type).
372
372
373 .. container:: verbose
373 .. container:: verbose
374
374
375 Examples:
375 Examples:
376
376
377 - create a zip file containing the 1.0 release::
377 - create a zip file containing the 1.0 release::
378
378
379 hg archive -r 1.0 project-1.0.zip
379 hg archive -r 1.0 project-1.0.zip
380
380
381 - create a tarball excluding .hg files::
381 - create a tarball excluding .hg files::
382
382
383 hg archive project.tar.gz -X ".hg*"
383 hg archive project.tar.gz -X ".hg*"
384
384
385 Valid types are:
385 Valid types are:
386
386
387 :``files``: a directory full of files (default)
387 :``files``: a directory full of files (default)
388 :``tar``: tar archive, uncompressed
388 :``tar``: tar archive, uncompressed
389 :``tbz2``: tar archive, compressed using bzip2
389 :``tbz2``: tar archive, compressed using bzip2
390 :``tgz``: tar archive, compressed using gzip
390 :``tgz``: tar archive, compressed using gzip
391 :``uzip``: zip archive, uncompressed
391 :``uzip``: zip archive, uncompressed
392 :``zip``: zip archive, compressed using deflate
392 :``zip``: zip archive, compressed using deflate
393
393
394 The exact name of the destination archive or directory is given
394 The exact name of the destination archive or directory is given
395 using a format string; see :hg:`help export` for details.
395 using a format string; see :hg:`help export` for details.
396
396
397 Each member added to an archive file has a directory prefix
397 Each member added to an archive file has a directory prefix
398 prepended. Use -p/--prefix to specify a format string for the
398 prepended. Use -p/--prefix to specify a format string for the
399 prefix. The default is the basename of the archive, with suffixes
399 prefix. The default is the basename of the archive, with suffixes
400 removed.
400 removed.
401
401
402 Returns 0 on success.
402 Returns 0 on success.
403 '''
403 '''
404
404
405 ctx = scmutil.revsingle(repo, opts.get('rev'))
405 ctx = scmutil.revsingle(repo, opts.get('rev'))
406 if not ctx:
406 if not ctx:
407 raise util.Abort(_('no working directory: please specify a revision'))
407 raise util.Abort(_('no working directory: please specify a revision'))
408 node = ctx.node()
408 node = ctx.node()
409 dest = cmdutil.makefilename(repo, dest, node)
409 dest = cmdutil.makefilename(repo, dest, node)
410 if os.path.realpath(dest) == repo.root:
410 if os.path.realpath(dest) == repo.root:
411 raise util.Abort(_('repository root cannot be destination'))
411 raise util.Abort(_('repository root cannot be destination'))
412
412
413 kind = opts.get('type') or archival.guesskind(dest) or 'files'
413 kind = opts.get('type') or archival.guesskind(dest) or 'files'
414 prefix = opts.get('prefix')
414 prefix = opts.get('prefix')
415
415
416 if dest == '-':
416 if dest == '-':
417 if kind == 'files':
417 if kind == 'files':
418 raise util.Abort(_('cannot archive plain files to stdout'))
418 raise util.Abort(_('cannot archive plain files to stdout'))
419 dest = cmdutil.makefileobj(repo, dest)
419 dest = cmdutil.makefileobj(repo, dest)
420 if not prefix:
420 if not prefix:
421 prefix = os.path.basename(repo.root) + '-%h'
421 prefix = os.path.basename(repo.root) + '-%h'
422
422
423 prefix = cmdutil.makefilename(repo, prefix, node)
423 prefix = cmdutil.makefilename(repo, prefix, node)
424 matchfn = scmutil.match(ctx, [], opts)
424 matchfn = scmutil.match(ctx, [], opts)
425 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
425 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
426 matchfn, prefix, subrepos=opts.get('subrepos'))
426 matchfn, prefix, subrepos=opts.get('subrepos'))
427
427
428 @command('backout',
428 @command('backout',
429 [('', 'merge', None, _('merge with old dirstate parent after backout')),
429 [('', 'merge', None, _('merge with old dirstate parent after backout')),
430 ('', 'commit', None, _('commit if no conflicts were encountered')),
430 ('', 'commit', None, _('commit if no conflicts were encountered')),
431 ('', 'parent', '',
431 ('', 'parent', '',
432 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
432 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
433 ('r', 'rev', '', _('revision to backout'), _('REV')),
433 ('r', 'rev', '', _('revision to backout'), _('REV')),
434 ('e', 'edit', False, _('invoke editor on commit messages')),
434 ('e', 'edit', False, _('invoke editor on commit messages')),
435 ] + mergetoolopts + walkopts + commitopts + commitopts2,
435 ] + mergetoolopts + walkopts + commitopts + commitopts2,
436 _('[OPTION]... [-r] REV'))
436 _('[OPTION]... [-r] REV'))
437 def backout(ui, repo, node=None, rev=None, commit=False, **opts):
437 def backout(ui, repo, node=None, rev=None, commit=False, **opts):
438 '''reverse effect of earlier changeset
438 '''reverse effect of earlier changeset
439
439
440 Prepare a new changeset with the effect of REV undone in the
440 Prepare a new changeset with the effect of REV undone in the
441 current working directory.
441 current working directory.
442
442
443 If REV is the parent of the working directory, then this new changeset
443 If REV is the parent of the working directory, then this new changeset
444 is committed automatically. Otherwise, hg needs to merge the
444 is committed automatically. Otherwise, hg needs to merge the
445 changes and the merged result is left uncommitted.
445 changes and the merged result is left uncommitted.
446
446
447 .. note::
447 .. note::
448
448
449 backout cannot be used to fix either an unwanted or
449 backout cannot be used to fix either an unwanted or
450 incorrect merge.
450 incorrect merge.
451
451
452 .. container:: verbose
452 .. container:: verbose
453
453
454 By default, the pending changeset will have one parent,
454 By default, the pending changeset will have one parent,
455 maintaining a linear history. With --merge, the pending
455 maintaining a linear history. With --merge, the pending
456 changeset will instead have two parents: the old parent of the
456 changeset will instead have two parents: the old parent of the
457 working directory and a new child of REV that simply undoes REV.
457 working directory and a new child of REV that simply undoes REV.
458
458
459 Before version 1.7, the behavior without --merge was equivalent
459 Before version 1.7, the behavior without --merge was equivalent
460 to specifying --merge followed by :hg:`update --clean .` to
460 to specifying --merge followed by :hg:`update --clean .` to
461 cancel the merge and leave the child of REV as a head to be
461 cancel the merge and leave the child of REV as a head to be
462 merged separately.
462 merged separately.
463
463
464 See :hg:`help dates` for a list of formats valid for -d/--date.
464 See :hg:`help dates` for a list of formats valid for -d/--date.
465
465
466 Returns 0 on success, 1 if nothing to backout or there are unresolved
466 Returns 0 on success, 1 if nothing to backout or there are unresolved
467 files.
467 files.
468 '''
468 '''
469 if rev and node:
469 if rev and node:
470 raise util.Abort(_("please specify just one revision"))
470 raise util.Abort(_("please specify just one revision"))
471
471
472 if not rev:
472 if not rev:
473 rev = node
473 rev = node
474
474
475 if not rev:
475 if not rev:
476 raise util.Abort(_("please specify a revision to backout"))
476 raise util.Abort(_("please specify a revision to backout"))
477
477
478 date = opts.get('date')
478 date = opts.get('date')
479 if date:
479 if date:
480 opts['date'] = util.parsedate(date)
480 opts['date'] = util.parsedate(date)
481
481
482 cmdutil.checkunfinished(repo)
482 cmdutil.checkunfinished(repo)
483 cmdutil.bailifchanged(repo)
483 cmdutil.bailifchanged(repo)
484 node = scmutil.revsingle(repo, rev).node()
484 node = scmutil.revsingle(repo, rev).node()
485
485
486 op1, op2 = repo.dirstate.parents()
486 op1, op2 = repo.dirstate.parents()
487 if not repo.changelog.isancestor(node, op1):
487 if not repo.changelog.isancestor(node, op1):
488 raise util.Abort(_('cannot backout change that is not an ancestor'))
488 raise util.Abort(_('cannot backout change that is not an ancestor'))
489
489
490 p1, p2 = repo.changelog.parents(node)
490 p1, p2 = repo.changelog.parents(node)
491 if p1 == nullid:
491 if p1 == nullid:
492 raise util.Abort(_('cannot backout a change with no parents'))
492 raise util.Abort(_('cannot backout a change with no parents'))
493 if p2 != nullid:
493 if p2 != nullid:
494 if not opts.get('parent'):
494 if not opts.get('parent'):
495 raise util.Abort(_('cannot backout a merge changeset'))
495 raise util.Abort(_('cannot backout a merge changeset'))
496 p = repo.lookup(opts['parent'])
496 p = repo.lookup(opts['parent'])
497 if p not in (p1, p2):
497 if p not in (p1, p2):
498 raise util.Abort(_('%s is not a parent of %s') %
498 raise util.Abort(_('%s is not a parent of %s') %
499 (short(p), short(node)))
499 (short(p), short(node)))
500 parent = p
500 parent = p
501 else:
501 else:
502 if opts.get('parent'):
502 if opts.get('parent'):
503 raise util.Abort(_('cannot use --parent on non-merge changeset'))
503 raise util.Abort(_('cannot use --parent on non-merge changeset'))
504 parent = p1
504 parent = p1
505
505
506 # the backout should appear on the same branch
506 # the backout should appear on the same branch
507 wlock = repo.wlock()
507 wlock = repo.wlock()
508 try:
508 try:
509 branch = repo.dirstate.branch()
509 branch = repo.dirstate.branch()
510 bheads = repo.branchheads(branch)
510 bheads = repo.branchheads(branch)
511 rctx = scmutil.revsingle(repo, hex(parent))
511 rctx = scmutil.revsingle(repo, hex(parent))
512 if not opts.get('merge') and op1 != node:
512 if not opts.get('merge') and op1 != node:
513 try:
513 try:
514 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
514 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
515 'backout')
515 'backout')
516 repo.dirstate.beginparentchange()
516 repo.dirstate.beginparentchange()
517 stats = mergemod.update(repo, parent, True, True, False,
517 stats = mergemod.update(repo, parent, True, True, False,
518 node, False)
518 node, False)
519 repo.setparents(op1, op2)
519 repo.setparents(op1, op2)
520 repo.dirstate.endparentchange()
520 repo.dirstate.endparentchange()
521 hg._showstats(repo, stats)
521 hg._showstats(repo, stats)
522 if stats[3]:
522 if stats[3]:
523 repo.ui.status(_("use 'hg resolve' to retry unresolved "
523 repo.ui.status(_("use 'hg resolve' to retry unresolved "
524 "file merges\n"))
524 "file merges\n"))
525 return 1
525 return 1
526 elif not commit:
526 elif not commit:
527 msg = _("changeset %s backed out, "
527 msg = _("changeset %s backed out, "
528 "don't forget to commit.\n")
528 "don't forget to commit.\n")
529 ui.status(msg % short(node))
529 ui.status(msg % short(node))
530 return 0
530 return 0
531 finally:
531 finally:
532 ui.setconfig('ui', 'forcemerge', '', '')
532 ui.setconfig('ui', 'forcemerge', '', '')
533 else:
533 else:
534 hg.clean(repo, node, show_stats=False)
534 hg.clean(repo, node, show_stats=False)
535 repo.dirstate.setbranch(branch)
535 repo.dirstate.setbranch(branch)
536 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
536 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
537
537
538
538
539 def commitfunc(ui, repo, message, match, opts):
539 def commitfunc(ui, repo, message, match, opts):
540 editform = 'backout'
540 editform = 'backout'
541 e = cmdutil.getcommiteditor(editform=editform, **opts)
541 e = cmdutil.getcommiteditor(editform=editform, **opts)
542 if not message:
542 if not message:
543 # we don't translate commit messages
543 # we don't translate commit messages
544 message = "Backed out changeset %s" % short(node)
544 message = "Backed out changeset %s" % short(node)
545 e = cmdutil.getcommiteditor(edit=True, editform=editform)
545 e = cmdutil.getcommiteditor(edit=True, editform=editform)
546 return repo.commit(message, opts.get('user'), opts.get('date'),
546 return repo.commit(message, opts.get('user'), opts.get('date'),
547 match, editor=e)
547 match, editor=e)
548 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
548 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
549 if not newnode:
549 if not newnode:
550 ui.status(_("nothing changed\n"))
550 ui.status(_("nothing changed\n"))
551 return 1
551 return 1
552 cmdutil.commitstatus(repo, newnode, branch, bheads)
552 cmdutil.commitstatus(repo, newnode, branch, bheads)
553
553
554 def nice(node):
554 def nice(node):
555 return '%d:%s' % (repo.changelog.rev(node), short(node))
555 return '%d:%s' % (repo.changelog.rev(node), short(node))
556 ui.status(_('changeset %s backs out changeset %s\n') %
556 ui.status(_('changeset %s backs out changeset %s\n') %
557 (nice(repo.changelog.tip()), nice(node)))
557 (nice(repo.changelog.tip()), nice(node)))
558 if opts.get('merge') and op1 != node:
558 if opts.get('merge') and op1 != node:
559 hg.clean(repo, op1, show_stats=False)
559 hg.clean(repo, op1, show_stats=False)
560 ui.status(_('merging with changeset %s\n')
560 ui.status(_('merging with changeset %s\n')
561 % nice(repo.changelog.tip()))
561 % nice(repo.changelog.tip()))
562 try:
562 try:
563 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
563 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
564 'backout')
564 'backout')
565 return hg.merge(repo, hex(repo.changelog.tip()))
565 return hg.merge(repo, hex(repo.changelog.tip()))
566 finally:
566 finally:
567 ui.setconfig('ui', 'forcemerge', '', '')
567 ui.setconfig('ui', 'forcemerge', '', '')
568 finally:
568 finally:
569 wlock.release()
569 wlock.release()
570 return 0
570 return 0
571
571
572 @command('bisect',
572 @command('bisect',
573 [('r', 'reset', False, _('reset bisect state')),
573 [('r', 'reset', False, _('reset bisect state')),
574 ('g', 'good', False, _('mark changeset good')),
574 ('g', 'good', False, _('mark changeset good')),
575 ('b', 'bad', False, _('mark changeset bad')),
575 ('b', 'bad', False, _('mark changeset bad')),
576 ('s', 'skip', False, _('skip testing changeset')),
576 ('s', 'skip', False, _('skip testing changeset')),
577 ('e', 'extend', False, _('extend the bisect range')),
577 ('e', 'extend', False, _('extend the bisect range')),
578 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
578 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
579 ('U', 'noupdate', False, _('do not update to target'))],
579 ('U', 'noupdate', False, _('do not update to target'))],
580 _("[-gbsr] [-U] [-c CMD] [REV]"))
580 _("[-gbsr] [-U] [-c CMD] [REV]"))
581 def bisect(ui, repo, rev=None, extra=None, command=None,
581 def bisect(ui, repo, rev=None, extra=None, command=None,
582 reset=None, good=None, bad=None, skip=None, extend=None,
582 reset=None, good=None, bad=None, skip=None, extend=None,
583 noupdate=None):
583 noupdate=None):
584 """subdivision search of changesets
584 """subdivision search of changesets
585
585
586 This command helps to find changesets which introduce problems. To
586 This command helps to find changesets which introduce problems. To
587 use, mark the earliest changeset you know exhibits the problem as
587 use, mark the earliest changeset you know exhibits the problem as
588 bad, then mark the latest changeset which is free from the problem
588 bad, then mark the latest changeset which is free from the problem
589 as good. Bisect will update your working directory to a revision
589 as good. Bisect will update your working directory to a revision
590 for testing (unless the -U/--noupdate option is specified). Once
590 for testing (unless the -U/--noupdate option is specified). Once
591 you have performed tests, mark the working directory as good or
591 you have performed tests, mark the working directory as good or
592 bad, and bisect will either update to another candidate changeset
592 bad, and bisect will either update to another candidate changeset
593 or announce that it has found the bad revision.
593 or announce that it has found the bad revision.
594
594
595 As a shortcut, you can also use the revision argument to mark a
595 As a shortcut, you can also use the revision argument to mark a
596 revision as good or bad without checking it out first.
596 revision as good or bad without checking it out first.
597
597
598 If you supply a command, it will be used for automatic bisection.
598 If you supply a command, it will be used for automatic bisection.
599 The environment variable HG_NODE will contain the ID of the
599 The environment variable HG_NODE will contain the ID of the
600 changeset being tested. The exit status of the command will be
600 changeset being tested. The exit status of the command will be
601 used to mark revisions as good or bad: status 0 means good, 125
601 used to mark revisions as good or bad: status 0 means good, 125
602 means to skip the revision, 127 (command not found) will abort the
602 means to skip the revision, 127 (command not found) will abort the
603 bisection, and any other non-zero exit status means the revision
603 bisection, and any other non-zero exit status means the revision
604 is bad.
604 is bad.
605
605
606 .. container:: verbose
606 .. container:: verbose
607
607
608 Some examples:
608 Some examples:
609
609
610 - start a bisection with known bad revision 34, and good revision 12::
610 - start a bisection with known bad revision 34, and good revision 12::
611
611
612 hg bisect --bad 34
612 hg bisect --bad 34
613 hg bisect --good 12
613 hg bisect --good 12
614
614
615 - advance the current bisection by marking current revision as good or
615 - advance the current bisection by marking current revision as good or
616 bad::
616 bad::
617
617
618 hg bisect --good
618 hg bisect --good
619 hg bisect --bad
619 hg bisect --bad
620
620
621 - mark the current revision, or a known revision, to be skipped (e.g. if
621 - mark the current revision, or a known revision, to be skipped (e.g. if
622 that revision is not usable because of another issue)::
622 that revision is not usable because of another issue)::
623
623
624 hg bisect --skip
624 hg bisect --skip
625 hg bisect --skip 23
625 hg bisect --skip 23
626
626
627 - skip all revisions that do not touch directories ``foo`` or ``bar``::
627 - skip all revisions that do not touch directories ``foo`` or ``bar``::
628
628
629 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
629 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
630
630
631 - forget the current bisection::
631 - forget the current bisection::
632
632
633 hg bisect --reset
633 hg bisect --reset
634
634
635 - use 'make && make tests' to automatically find the first broken
635 - use 'make && make tests' to automatically find the first broken
636 revision::
636 revision::
637
637
638 hg bisect --reset
638 hg bisect --reset
639 hg bisect --bad 34
639 hg bisect --bad 34
640 hg bisect --good 12
640 hg bisect --good 12
641 hg bisect --command "make && make tests"
641 hg bisect --command "make && make tests"
642
642
643 - see all changesets whose states are already known in the current
643 - see all changesets whose states are already known in the current
644 bisection::
644 bisection::
645
645
646 hg log -r "bisect(pruned)"
646 hg log -r "bisect(pruned)"
647
647
648 - see the changeset currently being bisected (especially useful
648 - see the changeset currently being bisected (especially useful
649 if running with -U/--noupdate)::
649 if running with -U/--noupdate)::
650
650
651 hg log -r "bisect(current)"
651 hg log -r "bisect(current)"
652
652
653 - see all changesets that took part in the current bisection::
653 - see all changesets that took part in the current bisection::
654
654
655 hg log -r "bisect(range)"
655 hg log -r "bisect(range)"
656
656
657 - you can even get a nice graph::
657 - you can even get a nice graph::
658
658
659 hg log --graph -r "bisect(range)"
659 hg log --graph -r "bisect(range)"
660
660
661 See :hg:`help revsets` for more about the `bisect()` keyword.
661 See :hg:`help revsets` for more about the `bisect()` keyword.
662
662
663 Returns 0 on success.
663 Returns 0 on success.
664 """
664 """
665 def extendbisectrange(nodes, good):
665 def extendbisectrange(nodes, good):
666 # bisect is incomplete when it ends on a merge node and
666 # bisect is incomplete when it ends on a merge node and
667 # one of the parent was not checked.
667 # one of the parent was not checked.
668 parents = repo[nodes[0]].parents()
668 parents = repo[nodes[0]].parents()
669 if len(parents) > 1:
669 if len(parents) > 1:
670 if good:
670 if good:
671 side = state['bad']
671 side = state['bad']
672 else:
672 else:
673 side = state['good']
673 side = state['good']
674 num = len(set(i.node() for i in parents) & set(side))
674 num = len(set(i.node() for i in parents) & set(side))
675 if num == 1:
675 if num == 1:
676 return parents[0].ancestor(parents[1])
676 return parents[0].ancestor(parents[1])
677 return None
677 return None
678
678
679 def print_result(nodes, good):
679 def print_result(nodes, good):
680 displayer = cmdutil.show_changeset(ui, repo, {})
680 displayer = cmdutil.show_changeset(ui, repo, {})
681 if len(nodes) == 1:
681 if len(nodes) == 1:
682 # narrowed it down to a single revision
682 # narrowed it down to a single revision
683 if good:
683 if good:
684 ui.write(_("The first good revision is:\n"))
684 ui.write(_("The first good revision is:\n"))
685 else:
685 else:
686 ui.write(_("The first bad revision is:\n"))
686 ui.write(_("The first bad revision is:\n"))
687 displayer.show(repo[nodes[0]])
687 displayer.show(repo[nodes[0]])
688 extendnode = extendbisectrange(nodes, good)
688 extendnode = extendbisectrange(nodes, good)
689 if extendnode is not None:
689 if extendnode is not None:
690 ui.write(_('Not all ancestors of this changeset have been'
690 ui.write(_('Not all ancestors of this changeset have been'
691 ' checked.\nUse bisect --extend to continue the '
691 ' checked.\nUse bisect --extend to continue the '
692 'bisection from\nthe common ancestor, %s.\n')
692 'bisection from\nthe common ancestor, %s.\n')
693 % extendnode)
693 % extendnode)
694 else:
694 else:
695 # multiple possible revisions
695 # multiple possible revisions
696 if good:
696 if good:
697 ui.write(_("Due to skipped revisions, the first "
697 ui.write(_("Due to skipped revisions, the first "
698 "good revision could be any of:\n"))
698 "good revision could be any of:\n"))
699 else:
699 else:
700 ui.write(_("Due to skipped revisions, the first "
700 ui.write(_("Due to skipped revisions, the first "
701 "bad revision could be any of:\n"))
701 "bad revision could be any of:\n"))
702 for n in nodes:
702 for n in nodes:
703 displayer.show(repo[n])
703 displayer.show(repo[n])
704 displayer.close()
704 displayer.close()
705
705
706 def check_state(state, interactive=True):
706 def check_state(state, interactive=True):
707 if not state['good'] or not state['bad']:
707 if not state['good'] or not state['bad']:
708 if (good or bad or skip or reset) and interactive:
708 if (good or bad or skip or reset) and interactive:
709 return
709 return
710 if not state['good']:
710 if not state['good']:
711 raise util.Abort(_('cannot bisect (no known good revisions)'))
711 raise util.Abort(_('cannot bisect (no known good revisions)'))
712 else:
712 else:
713 raise util.Abort(_('cannot bisect (no known bad revisions)'))
713 raise util.Abort(_('cannot bisect (no known bad revisions)'))
714 return True
714 return True
715
715
716 # backward compatibility
716 # backward compatibility
717 if rev in "good bad reset init".split():
717 if rev in "good bad reset init".split():
718 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
718 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
719 cmd, rev, extra = rev, extra, None
719 cmd, rev, extra = rev, extra, None
720 if cmd == "good":
720 if cmd == "good":
721 good = True
721 good = True
722 elif cmd == "bad":
722 elif cmd == "bad":
723 bad = True
723 bad = True
724 else:
724 else:
725 reset = True
725 reset = True
726 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
726 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
727 raise util.Abort(_('incompatible arguments'))
727 raise util.Abort(_('incompatible arguments'))
728
728
729 cmdutil.checkunfinished(repo)
729 cmdutil.checkunfinished(repo)
730
730
731 if reset:
731 if reset:
732 p = repo.join("bisect.state")
732 p = repo.join("bisect.state")
733 if os.path.exists(p):
733 if os.path.exists(p):
734 os.unlink(p)
734 os.unlink(p)
735 return
735 return
736
736
737 state = hbisect.load_state(repo)
737 state = hbisect.load_state(repo)
738
738
739 if command:
739 if command:
740 changesets = 1
740 changesets = 1
741 if noupdate:
741 if noupdate:
742 try:
742 try:
743 node = state['current'][0]
743 node = state['current'][0]
744 except LookupError:
744 except LookupError:
745 raise util.Abort(_('current bisect revision is unknown - '
745 raise util.Abort(_('current bisect revision is unknown - '
746 'start a new bisect to fix'))
746 'start a new bisect to fix'))
747 else:
747 else:
748 node, p2 = repo.dirstate.parents()
748 node, p2 = repo.dirstate.parents()
749 if p2 != nullid:
749 if p2 != nullid:
750 raise util.Abort(_('current bisect revision is a merge'))
750 raise util.Abort(_('current bisect revision is a merge'))
751 try:
751 try:
752 while changesets:
752 while changesets:
753 # update state
753 # update state
754 state['current'] = [node]
754 state['current'] = [node]
755 hbisect.save_state(repo, state)
755 hbisect.save_state(repo, state)
756 status = ui.system(command, environ={'HG_NODE': hex(node)})
756 status = ui.system(command, environ={'HG_NODE': hex(node)})
757 if status == 125:
757 if status == 125:
758 transition = "skip"
758 transition = "skip"
759 elif status == 0:
759 elif status == 0:
760 transition = "good"
760 transition = "good"
761 # status < 0 means process was killed
761 # status < 0 means process was killed
762 elif status == 127:
762 elif status == 127:
763 raise util.Abort(_("failed to execute %s") % command)
763 raise util.Abort(_("failed to execute %s") % command)
764 elif status < 0:
764 elif status < 0:
765 raise util.Abort(_("%s killed") % command)
765 raise util.Abort(_("%s killed") % command)
766 else:
766 else:
767 transition = "bad"
767 transition = "bad"
768 ctx = scmutil.revsingle(repo, rev, node)
768 ctx = scmutil.revsingle(repo, rev, node)
769 rev = None # clear for future iterations
769 rev = None # clear for future iterations
770 state[transition].append(ctx.node())
770 state[transition].append(ctx.node())
771 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
771 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
772 check_state(state, interactive=False)
772 check_state(state, interactive=False)
773 # bisect
773 # bisect
774 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
774 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
775 # update to next check
775 # update to next check
776 node = nodes[0]
776 node = nodes[0]
777 if not noupdate:
777 if not noupdate:
778 cmdutil.bailifchanged(repo)
778 cmdutil.bailifchanged(repo)
779 hg.clean(repo, node, show_stats=False)
779 hg.clean(repo, node, show_stats=False)
780 finally:
780 finally:
781 state['current'] = [node]
781 state['current'] = [node]
782 hbisect.save_state(repo, state)
782 hbisect.save_state(repo, state)
783 print_result(nodes, bgood)
783 print_result(nodes, bgood)
784 return
784 return
785
785
786 # update state
786 # update state
787
787
788 if rev:
788 if rev:
789 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
789 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
790 else:
790 else:
791 nodes = [repo.lookup('.')]
791 nodes = [repo.lookup('.')]
792
792
793 if good or bad or skip:
793 if good or bad or skip:
794 if good:
794 if good:
795 state['good'] += nodes
795 state['good'] += nodes
796 elif bad:
796 elif bad:
797 state['bad'] += nodes
797 state['bad'] += nodes
798 elif skip:
798 elif skip:
799 state['skip'] += nodes
799 state['skip'] += nodes
800 hbisect.save_state(repo, state)
800 hbisect.save_state(repo, state)
801
801
802 if not check_state(state):
802 if not check_state(state):
803 return
803 return
804
804
805 # actually bisect
805 # actually bisect
806 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
806 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
807 if extend:
807 if extend:
808 if not changesets:
808 if not changesets:
809 extendnode = extendbisectrange(nodes, good)
809 extendnode = extendbisectrange(nodes, good)
810 if extendnode is not None:
810 if extendnode is not None:
811 ui.write(_("Extending search to changeset %d:%s\n")
811 ui.write(_("Extending search to changeset %d:%s\n")
812 % (extendnode.rev(), extendnode))
812 % (extendnode.rev(), extendnode))
813 state['current'] = [extendnode.node()]
813 state['current'] = [extendnode.node()]
814 hbisect.save_state(repo, state)
814 hbisect.save_state(repo, state)
815 if noupdate:
815 if noupdate:
816 return
816 return
817 cmdutil.bailifchanged(repo)
817 cmdutil.bailifchanged(repo)
818 return hg.clean(repo, extendnode.node())
818 return hg.clean(repo, extendnode.node())
819 raise util.Abort(_("nothing to extend"))
819 raise util.Abort(_("nothing to extend"))
820
820
821 if changesets == 0:
821 if changesets == 0:
822 print_result(nodes, good)
822 print_result(nodes, good)
823 else:
823 else:
824 assert len(nodes) == 1 # only a single node can be tested next
824 assert len(nodes) == 1 # only a single node can be tested next
825 node = nodes[0]
825 node = nodes[0]
826 # compute the approximate number of remaining tests
826 # compute the approximate number of remaining tests
827 tests, size = 0, 2
827 tests, size = 0, 2
828 while size <= changesets:
828 while size <= changesets:
829 tests, size = tests + 1, size * 2
829 tests, size = tests + 1, size * 2
830 rev = repo.changelog.rev(node)
830 rev = repo.changelog.rev(node)
831 ui.write(_("Testing changeset %d:%s "
831 ui.write(_("Testing changeset %d:%s "
832 "(%d changesets remaining, ~%d tests)\n")
832 "(%d changesets remaining, ~%d tests)\n")
833 % (rev, short(node), changesets, tests))
833 % (rev, short(node), changesets, tests))
834 state['current'] = [node]
834 state['current'] = [node]
835 hbisect.save_state(repo, state)
835 hbisect.save_state(repo, state)
836 if not noupdate:
836 if not noupdate:
837 cmdutil.bailifchanged(repo)
837 cmdutil.bailifchanged(repo)
838 return hg.clean(repo, node)
838 return hg.clean(repo, node)
839
839
840 @command('bookmarks|bookmark',
840 @command('bookmarks|bookmark',
841 [('f', 'force', False, _('force')),
841 [('f', 'force', False, _('force')),
842 ('r', 'rev', '', _('revision'), _('REV')),
842 ('r', 'rev', '', _('revision'), _('REV')),
843 ('d', 'delete', False, _('delete a given bookmark')),
843 ('d', 'delete', False, _('delete a given bookmark')),
844 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
844 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
845 ('i', 'inactive', False, _('mark a bookmark inactive')),
845 ('i', 'inactive', False, _('mark a bookmark inactive')),
846 ] + formatteropts,
846 ] + formatteropts,
847 _('hg bookmarks [OPTIONS]... [NAME]...'))
847 _('hg bookmarks [OPTIONS]... [NAME]...'))
848 def bookmark(ui, repo, *names, **opts):
848 def bookmark(ui, repo, *names, **opts):
849 '''create a new bookmark or list existing bookmarks
849 '''create a new bookmark or list existing bookmarks
850
850
851 Bookmarks are labels on changesets to help track lines of development.
851 Bookmarks are labels on changesets to help track lines of development.
852 Bookmarks are unversioned and can be moved, renamed and deleted.
852 Bookmarks are unversioned and can be moved, renamed and deleted.
853 Deleting or moving a bookmark has no effect on the associated changesets.
853 Deleting or moving a bookmark has no effect on the associated changesets.
854
854
855 Creating or updating to a bookmark causes it to be marked as 'active'.
855 Creating or updating to a bookmark causes it to be marked as 'active'.
856 The active bookmark is indicated with a '*'.
856 The active bookmark is indicated with a '*'.
857 When a commit is made, the active bookmark will advance to the new commit.
857 When a commit is made, the active bookmark will advance to the new commit.
858 A plain :hg:`update` will also advance an active bookmark, if possible.
858 A plain :hg:`update` will also advance an active bookmark, if possible.
859 Updating away from a bookmark will cause it to be deactivated.
859 Updating away from a bookmark will cause it to be deactivated.
860
860
861 Bookmarks can be pushed and pulled between repositories (see
861 Bookmarks can be pushed and pulled between repositories (see
862 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
862 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
863 diverged, a new 'divergent bookmark' of the form 'name@path' will
863 diverged, a new 'divergent bookmark' of the form 'name@path' will
864 be created. Using :hg:`merge` will resolve the divergence.
864 be created. Using :hg:`merge` will resolve the divergence.
865
865
866 A bookmark named '@' has the special property that :hg:`clone` will
866 A bookmark named '@' has the special property that :hg:`clone` will
867 check it out by default if it exists.
867 check it out by default if it exists.
868
868
869 .. container:: verbose
869 .. container:: verbose
870
870
871 Examples:
871 Examples:
872
872
873 - create an active bookmark for a new line of development::
873 - create an active bookmark for a new line of development::
874
874
875 hg book new-feature
875 hg book new-feature
876
876
877 - create an inactive bookmark as a place marker::
877 - create an inactive bookmark as a place marker::
878
878
879 hg book -i reviewed
879 hg book -i reviewed
880
880
881 - create an inactive bookmark on another changeset::
881 - create an inactive bookmark on another changeset::
882
882
883 hg book -r .^ tested
883 hg book -r .^ tested
884
884
885 - move the '@' bookmark from another branch::
885 - move the '@' bookmark from another branch::
886
886
887 hg book -f @
887 hg book -f @
888 '''
888 '''
889 force = opts.get('force')
889 force = opts.get('force')
890 rev = opts.get('rev')
890 rev = opts.get('rev')
891 delete = opts.get('delete')
891 delete = opts.get('delete')
892 rename = opts.get('rename')
892 rename = opts.get('rename')
893 inactive = opts.get('inactive')
893 inactive = opts.get('inactive')
894
894
895 def checkformat(mark):
895 def checkformat(mark):
896 mark = mark.strip()
896 mark = mark.strip()
897 if not mark:
897 if not mark:
898 raise util.Abort(_("bookmark names cannot consist entirely of "
898 raise util.Abort(_("bookmark names cannot consist entirely of "
899 "whitespace"))
899 "whitespace"))
900 scmutil.checknewlabel(repo, mark, 'bookmark')
900 scmutil.checknewlabel(repo, mark, 'bookmark')
901 return mark
901 return mark
902
902
903 def checkconflict(repo, mark, cur, force=False, target=None):
903 def checkconflict(repo, mark, cur, force=False, target=None):
904 if mark in marks and not force:
904 if mark in marks and not force:
905 if target:
905 if target:
906 if marks[mark] == target and target == cur:
906 if marks[mark] == target and target == cur:
907 # re-activating a bookmark
907 # re-activating a bookmark
908 return
908 return
909 anc = repo.changelog.ancestors([repo[target].rev()])
909 anc = repo.changelog.ancestors([repo[target].rev()])
910 bmctx = repo[marks[mark]]
910 bmctx = repo[marks[mark]]
911 divs = [repo[b].node() for b in marks
911 divs = [repo[b].node() for b in marks
912 if b.split('@', 1)[0] == mark.split('@', 1)[0]]
912 if b.split('@', 1)[0] == mark.split('@', 1)[0]]
913
913
914 # allow resolving a single divergent bookmark even if moving
914 # allow resolving a single divergent bookmark even if moving
915 # the bookmark across branches when a revision is specified
915 # the bookmark across branches when a revision is specified
916 # that contains a divergent bookmark
916 # that contains a divergent bookmark
917 if bmctx.rev() not in anc and target in divs:
917 if bmctx.rev() not in anc and target in divs:
918 bookmarks.deletedivergent(repo, [target], mark)
918 bookmarks.deletedivergent(repo, [target], mark)
919 return
919 return
920
920
921 deletefrom = [b for b in divs
921 deletefrom = [b for b in divs
922 if repo[b].rev() in anc or b == target]
922 if repo[b].rev() in anc or b == target]
923 bookmarks.deletedivergent(repo, deletefrom, mark)
923 bookmarks.deletedivergent(repo, deletefrom, mark)
924 if bookmarks.validdest(repo, bmctx, repo[target]):
924 if bookmarks.validdest(repo, bmctx, repo[target]):
925 ui.status(_("moving bookmark '%s' forward from %s\n") %
925 ui.status(_("moving bookmark '%s' forward from %s\n") %
926 (mark, short(bmctx.node())))
926 (mark, short(bmctx.node())))
927 return
927 return
928 raise util.Abort(_("bookmark '%s' already exists "
928 raise util.Abort(_("bookmark '%s' already exists "
929 "(use -f to force)") % mark)
929 "(use -f to force)") % mark)
930 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
930 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
931 and not force):
931 and not force):
932 raise util.Abort(
932 raise util.Abort(
933 _("a bookmark cannot have the name of an existing branch"))
933 _("a bookmark cannot have the name of an existing branch"))
934
934
935 if delete and rename:
935 if delete and rename:
936 raise util.Abort(_("--delete and --rename are incompatible"))
936 raise util.Abort(_("--delete and --rename are incompatible"))
937 if delete and rev:
937 if delete and rev:
938 raise util.Abort(_("--rev is incompatible with --delete"))
938 raise util.Abort(_("--rev is incompatible with --delete"))
939 if rename and rev:
939 if rename and rev:
940 raise util.Abort(_("--rev is incompatible with --rename"))
940 raise util.Abort(_("--rev is incompatible with --rename"))
941 if not names and (delete or rev):
941 if not names and (delete or rev):
942 raise util.Abort(_("bookmark name required"))
942 raise util.Abort(_("bookmark name required"))
943
943
944 if delete or rename or names or inactive:
944 if delete or rename or names or inactive:
945 wlock = repo.wlock()
945 wlock = repo.wlock()
946 try:
946 try:
947 cur = repo.changectx('.').node()
947 cur = repo.changectx('.').node()
948 marks = repo._bookmarks
948 marks = repo._bookmarks
949 if delete:
949 if delete:
950 for mark in names:
950 for mark in names:
951 if mark not in marks:
951 if mark not in marks:
952 raise util.Abort(_("bookmark '%s' does not exist") %
952 raise util.Abort(_("bookmark '%s' does not exist") %
953 mark)
953 mark)
954 if mark == repo._bookmarkcurrent:
954 if mark == repo._bookmarkcurrent:
955 bookmarks.unsetcurrent(repo)
955 bookmarks.unsetcurrent(repo)
956 del marks[mark]
956 del marks[mark]
957 marks.write()
957 marks.write()
958
958
959 elif rename:
959 elif rename:
960 if not names:
960 if not names:
961 raise util.Abort(_("new bookmark name required"))
961 raise util.Abort(_("new bookmark name required"))
962 elif len(names) > 1:
962 elif len(names) > 1:
963 raise util.Abort(_("only one new bookmark name allowed"))
963 raise util.Abort(_("only one new bookmark name allowed"))
964 mark = checkformat(names[0])
964 mark = checkformat(names[0])
965 if rename not in marks:
965 if rename not in marks:
966 raise util.Abort(_("bookmark '%s' does not exist") % rename)
966 raise util.Abort(_("bookmark '%s' does not exist") % rename)
967 checkconflict(repo, mark, cur, force)
967 checkconflict(repo, mark, cur, force)
968 marks[mark] = marks[rename]
968 marks[mark] = marks[rename]
969 if repo._bookmarkcurrent == rename and not inactive:
969 if repo._bookmarkcurrent == rename and not inactive:
970 bookmarks.setcurrent(repo, mark)
970 bookmarks.setcurrent(repo, mark)
971 del marks[rename]
971 del marks[rename]
972 marks.write()
972 marks.write()
973
973
974 elif names:
974 elif names:
975 newact = None
975 newact = None
976 for mark in names:
976 for mark in names:
977 mark = checkformat(mark)
977 mark = checkformat(mark)
978 if newact is None:
978 if newact is None:
979 newact = mark
979 newact = mark
980 if inactive and mark == repo._bookmarkcurrent:
980 if inactive and mark == repo._bookmarkcurrent:
981 bookmarks.unsetcurrent(repo)
981 bookmarks.unsetcurrent(repo)
982 return
982 return
983 tgt = cur
983 tgt = cur
984 if rev:
984 if rev:
985 tgt = scmutil.revsingle(repo, rev).node()
985 tgt = scmutil.revsingle(repo, rev).node()
986 checkconflict(repo, mark, cur, force, tgt)
986 checkconflict(repo, mark, cur, force, tgt)
987 marks[mark] = tgt
987 marks[mark] = tgt
988 if not inactive and cur == marks[newact] and not rev:
988 if not inactive and cur == marks[newact] and not rev:
989 bookmarks.setcurrent(repo, newact)
989 bookmarks.setcurrent(repo, newact)
990 elif cur != tgt and newact == repo._bookmarkcurrent:
990 elif cur != tgt and newact == repo._bookmarkcurrent:
991 bookmarks.unsetcurrent(repo)
991 bookmarks.unsetcurrent(repo)
992 marks.write()
992 marks.write()
993
993
994 elif inactive:
994 elif inactive:
995 if len(marks) == 0:
995 if len(marks) == 0:
996 ui.status(_("no bookmarks set\n"))
996 ui.status(_("no bookmarks set\n"))
997 elif not repo._bookmarkcurrent:
997 elif not repo._bookmarkcurrent:
998 ui.status(_("no active bookmark\n"))
998 ui.status(_("no active bookmark\n"))
999 else:
999 else:
1000 bookmarks.unsetcurrent(repo)
1000 bookmarks.unsetcurrent(repo)
1001 finally:
1001 finally:
1002 wlock.release()
1002 wlock.release()
1003 else: # show bookmarks
1003 else: # show bookmarks
1004 fm = ui.formatter('bookmarks', opts)
1004 fm = ui.formatter('bookmarks', opts)
1005 hexfn = fm.hexfunc
1005 hexfn = fm.hexfunc
1006 marks = repo._bookmarks
1006 marks = repo._bookmarks
1007 if len(marks) == 0 and not fm:
1007 if len(marks) == 0 and not fm:
1008 ui.status(_("no bookmarks set\n"))
1008 ui.status(_("no bookmarks set\n"))
1009 for bmark, n in sorted(marks.iteritems()):
1009 for bmark, n in sorted(marks.iteritems()):
1010 current = repo._bookmarkcurrent
1010 current = repo._bookmarkcurrent
1011 if bmark == current:
1011 if bmark == current:
1012 prefix, label = '*', 'bookmarks.current'
1012 prefix, label = '*', 'bookmarks.current'
1013 else:
1013 else:
1014 prefix, label = ' ', ''
1014 prefix, label = ' ', ''
1015
1015
1016 fm.startitem()
1016 fm.startitem()
1017 if not ui.quiet:
1017 if not ui.quiet:
1018 fm.plain(' %s ' % prefix, label=label)
1018 fm.plain(' %s ' % prefix, label=label)
1019 fm.write('bookmark', '%s', bmark, label=label)
1019 fm.write('bookmark', '%s', bmark, label=label)
1020 pad = " " * (25 - encoding.colwidth(bmark))
1020 pad = " " * (25 - encoding.colwidth(bmark))
1021 fm.condwrite(not ui.quiet, 'rev node', pad + ' %d:%s',
1021 fm.condwrite(not ui.quiet, 'rev node', pad + ' %d:%s',
1022 repo.changelog.rev(n), hexfn(n), label=label)
1022 repo.changelog.rev(n), hexfn(n), label=label)
1023 fm.data(active=(bmark == current))
1023 fm.data(active=(bmark == current))
1024 fm.plain('\n')
1024 fm.plain('\n')
1025 fm.end()
1025 fm.end()
1026
1026
1027 @command('branch',
1027 @command('branch',
1028 [('f', 'force', None,
1028 [('f', 'force', None,
1029 _('set branch name even if it shadows an existing branch')),
1029 _('set branch name even if it shadows an existing branch')),
1030 ('C', 'clean', None, _('reset branch name to parent branch name'))],
1030 ('C', 'clean', None, _('reset branch name to parent branch name'))],
1031 _('[-fC] [NAME]'))
1031 _('[-fC] [NAME]'))
1032 def branch(ui, repo, label=None, **opts):
1032 def branch(ui, repo, label=None, **opts):
1033 """set or show the current branch name
1033 """set or show the current branch name
1034
1034
1035 .. note::
1035 .. note::
1036
1036
1037 Branch names are permanent and global. Use :hg:`bookmark` to create a
1037 Branch names are permanent and global. Use :hg:`bookmark` to create a
1038 light-weight bookmark instead. See :hg:`help glossary` for more
1038 light-weight bookmark instead. See :hg:`help glossary` for more
1039 information about named branches and bookmarks.
1039 information about named branches and bookmarks.
1040
1040
1041 With no argument, show the current branch name. With one argument,
1041 With no argument, show the current branch name. With one argument,
1042 set the working directory branch name (the branch will not exist
1042 set the working directory branch name (the branch will not exist
1043 in the repository until the next commit). Standard practice
1043 in the repository until the next commit). Standard practice
1044 recommends that primary development take place on the 'default'
1044 recommends that primary development take place on the 'default'
1045 branch.
1045 branch.
1046
1046
1047 Unless -f/--force is specified, branch will not let you set a
1047 Unless -f/--force is specified, branch will not let you set a
1048 branch name that already exists.
1048 branch name that already exists.
1049
1049
1050 Use -C/--clean to reset the working directory branch to that of
1050 Use -C/--clean to reset the working directory branch to that of
1051 the parent of the working directory, negating a previous branch
1051 the parent of the working directory, negating a previous branch
1052 change.
1052 change.
1053
1053
1054 Use the command :hg:`update` to switch to an existing branch. Use
1054 Use the command :hg:`update` to switch to an existing branch. Use
1055 :hg:`commit --close-branch` to mark this branch as closed.
1055 :hg:`commit --close-branch` to mark this branch as closed.
1056
1056
1057 Returns 0 on success.
1057 Returns 0 on success.
1058 """
1058 """
1059 if label:
1059 if label:
1060 label = label.strip()
1060 label = label.strip()
1061
1061
1062 if not opts.get('clean') and not label:
1062 if not opts.get('clean') and not label:
1063 ui.write("%s\n" % repo.dirstate.branch())
1063 ui.write("%s\n" % repo.dirstate.branch())
1064 return
1064 return
1065
1065
1066 wlock = repo.wlock()
1066 wlock = repo.wlock()
1067 try:
1067 try:
1068 if opts.get('clean'):
1068 if opts.get('clean'):
1069 label = repo[None].p1().branch()
1069 label = repo[None].p1().branch()
1070 repo.dirstate.setbranch(label)
1070 repo.dirstate.setbranch(label)
1071 ui.status(_('reset working directory to branch %s\n') % label)
1071 ui.status(_('reset working directory to branch %s\n') % label)
1072 elif label:
1072 elif label:
1073 if not opts.get('force') and label in repo.branchmap():
1073 if not opts.get('force') and label in repo.branchmap():
1074 if label not in [p.branch() for p in repo.parents()]:
1074 if label not in [p.branch() for p in repo.parents()]:
1075 raise util.Abort(_('a branch of the same name already'
1075 raise util.Abort(_('a branch of the same name already'
1076 ' exists'),
1076 ' exists'),
1077 # i18n: "it" refers to an existing branch
1077 # i18n: "it" refers to an existing branch
1078 hint=_("use 'hg update' to switch to it"))
1078 hint=_("use 'hg update' to switch to it"))
1079 scmutil.checknewlabel(repo, label, 'branch')
1079 scmutil.checknewlabel(repo, label, 'branch')
1080 repo.dirstate.setbranch(label)
1080 repo.dirstate.setbranch(label)
1081 ui.status(_('marked working directory as branch %s\n') % label)
1081 ui.status(_('marked working directory as branch %s\n') % label)
1082 ui.status(_('(branches are permanent and global, '
1082 ui.status(_('(branches are permanent and global, '
1083 'did you want a bookmark?)\n'))
1083 'did you want a bookmark?)\n'))
1084 finally:
1084 finally:
1085 wlock.release()
1085 wlock.release()
1086
1086
1087 @command('branches',
1087 @command('branches',
1088 [('a', 'active', False,
1088 [('a', 'active', False,
1089 _('show only branches that have unmerged heads (DEPRECATED)')),
1089 _('show only branches that have unmerged heads (DEPRECATED)')),
1090 ('c', 'closed', False, _('show normal and closed branches')),
1090 ('c', 'closed', False, _('show normal and closed branches')),
1091 ] + formatteropts,
1091 ] + formatteropts,
1092 _('[-ac]'))
1092 _('[-ac]'))
1093 def branches(ui, repo, active=False, closed=False, **opts):
1093 def branches(ui, repo, active=False, closed=False, **opts):
1094 """list repository named branches
1094 """list repository named branches
1095
1095
1096 List the repository's named branches, indicating which ones are
1096 List the repository's named branches, indicating which ones are
1097 inactive. If -c/--closed is specified, also list branches which have
1097 inactive. If -c/--closed is specified, also list branches which have
1098 been marked closed (see :hg:`commit --close-branch`).
1098 been marked closed (see :hg:`commit --close-branch`).
1099
1099
1100 Use the command :hg:`update` to switch to an existing branch.
1100 Use the command :hg:`update` to switch to an existing branch.
1101
1101
1102 Returns 0.
1102 Returns 0.
1103 """
1103 """
1104
1104
1105 fm = ui.formatter('branches', opts)
1105 fm = ui.formatter('branches', opts)
1106 hexfunc = fm.hexfunc
1106 hexfunc = fm.hexfunc
1107
1107
1108 allheads = set(repo.heads())
1108 allheads = set(repo.heads())
1109 branches = []
1109 branches = []
1110 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1110 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1111 isactive = not isclosed and bool(set(heads) & allheads)
1111 isactive = not isclosed and bool(set(heads) & allheads)
1112 branches.append((tag, repo[tip], isactive, not isclosed))
1112 branches.append((tag, repo[tip], isactive, not isclosed))
1113 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1113 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1114 reverse=True)
1114 reverse=True)
1115
1115
1116 for tag, ctx, isactive, isopen in branches:
1116 for tag, ctx, isactive, isopen in branches:
1117 if active and not isactive:
1117 if active and not isactive:
1118 continue
1118 continue
1119 if isactive:
1119 if isactive:
1120 label = 'branches.active'
1120 label = 'branches.active'
1121 notice = ''
1121 notice = ''
1122 elif not isopen:
1122 elif not isopen:
1123 if not closed:
1123 if not closed:
1124 continue
1124 continue
1125 label = 'branches.closed'
1125 label = 'branches.closed'
1126 notice = _(' (closed)')
1126 notice = _(' (closed)')
1127 else:
1127 else:
1128 label = 'branches.inactive'
1128 label = 'branches.inactive'
1129 notice = _(' (inactive)')
1129 notice = _(' (inactive)')
1130 current = (tag == repo.dirstate.branch())
1130 current = (tag == repo.dirstate.branch())
1131 if current:
1131 if current:
1132 label = 'branches.current'
1132 label = 'branches.current'
1133
1133
1134 fm.startitem()
1134 fm.startitem()
1135 fm.write('branch', '%s', tag, label=label)
1135 fm.write('branch', '%s', tag, label=label)
1136 rev = ctx.rev()
1136 rev = ctx.rev()
1137 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1137 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1138 fmt = ' ' * padsize + ' %d:%s'
1138 fmt = ' ' * padsize + ' %d:%s'
1139 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1139 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1140 label='log.changeset changeset.%s' % ctx.phasestr())
1140 label='log.changeset changeset.%s' % ctx.phasestr())
1141 fm.data(active=isactive, closed=not isopen, current=current)
1141 fm.data(active=isactive, closed=not isopen, current=current)
1142 if not ui.quiet:
1142 if not ui.quiet:
1143 fm.plain(notice)
1143 fm.plain(notice)
1144 fm.plain('\n')
1144 fm.plain('\n')
1145 fm.end()
1145 fm.end()
1146
1146
1147 @command('bundle',
1147 @command('bundle',
1148 [('f', 'force', None, _('run even when the destination is unrelated')),
1148 [('f', 'force', None, _('run even when the destination is unrelated')),
1149 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1149 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1150 _('REV')),
1150 _('REV')),
1151 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1151 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1152 _('BRANCH')),
1152 _('BRANCH')),
1153 ('', 'base', [],
1153 ('', 'base', [],
1154 _('a base changeset assumed to be available at the destination'),
1154 _('a base changeset assumed to be available at the destination'),
1155 _('REV')),
1155 _('REV')),
1156 ('a', 'all', None, _('bundle all changesets in the repository')),
1156 ('a', 'all', None, _('bundle all changesets in the repository')),
1157 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1157 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1158 ] + remoteopts,
1158 ] + remoteopts,
1159 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1159 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1160 def bundle(ui, repo, fname, dest=None, **opts):
1160 def bundle(ui, repo, fname, dest=None, **opts):
1161 """create a changegroup file
1161 """create a changegroup file
1162
1162
1163 Generate a compressed changegroup file collecting changesets not
1163 Generate a compressed changegroup file collecting changesets not
1164 known to be in another repository.
1164 known to be in another repository.
1165
1165
1166 If you omit the destination repository, then hg assumes the
1166 If you omit the destination repository, then hg assumes the
1167 destination will have all the nodes you specify with --base
1167 destination will have all the nodes you specify with --base
1168 parameters. To create a bundle containing all changesets, use
1168 parameters. To create a bundle containing all changesets, use
1169 -a/--all (or --base null).
1169 -a/--all (or --base null).
1170
1170
1171 You can change compression method with the -t/--type option.
1171 You can change compression method with the -t/--type option.
1172 The available compression methods are: none, bzip2, and
1172 The available compression methods are: none, bzip2, and
1173 gzip (by default, bundles are compressed using bzip2).
1173 gzip (by default, bundles are compressed using bzip2).
1174
1174
1175 The bundle file can then be transferred using conventional means
1175 The bundle file can then be transferred using conventional means
1176 and applied to another repository with the unbundle or pull
1176 and applied to another repository with the unbundle or pull
1177 command. This is useful when direct push and pull are not
1177 command. This is useful when direct push and pull are not
1178 available or when exporting an entire repository is undesirable.
1178 available or when exporting an entire repository is undesirable.
1179
1179
1180 Applying bundles preserves all changeset contents including
1180 Applying bundles preserves all changeset contents including
1181 permissions, copy/rename information, and revision history.
1181 permissions, copy/rename information, and revision history.
1182
1182
1183 Returns 0 on success, 1 if no changes found.
1183 Returns 0 on success, 1 if no changes found.
1184 """
1184 """
1185 revs = None
1185 revs = None
1186 if 'rev' in opts:
1186 if 'rev' in opts:
1187 revs = scmutil.revrange(repo, opts['rev'])
1187 revs = scmutil.revrange(repo, opts['rev'])
1188
1188
1189 bundletype = opts.get('type', 'bzip2').lower()
1189 bundletype = opts.get('type', 'bzip2').lower()
1190 btypes = {'none': 'HG10UN',
1190 btypes = {'none': 'HG10UN',
1191 'bzip2': 'HG10BZ',
1191 'bzip2': 'HG10BZ',
1192 'gzip': 'HG10GZ',
1192 'gzip': 'HG10GZ',
1193 'bundle2': 'HG2Y'}
1193 'bundle2': 'HG2Y'}
1194 bundletype = btypes.get(bundletype)
1194 bundletype = btypes.get(bundletype)
1195 if bundletype not in changegroup.bundletypes:
1195 if bundletype not in changegroup.bundletypes:
1196 raise util.Abort(_('unknown bundle type specified with --type'))
1196 raise util.Abort(_('unknown bundle type specified with --type'))
1197
1197
1198 if opts.get('all'):
1198 if opts.get('all'):
1199 base = ['null']
1199 base = ['null']
1200 else:
1200 else:
1201 base = scmutil.revrange(repo, opts.get('base'))
1201 base = scmutil.revrange(repo, opts.get('base'))
1202 # TODO: get desired bundlecaps from command line.
1202 # TODO: get desired bundlecaps from command line.
1203 bundlecaps = None
1203 bundlecaps = None
1204 if base:
1204 if base:
1205 if dest:
1205 if dest:
1206 raise util.Abort(_("--base is incompatible with specifying "
1206 raise util.Abort(_("--base is incompatible with specifying "
1207 "a destination"))
1207 "a destination"))
1208 common = [repo.lookup(rev) for rev in base]
1208 common = [repo.lookup(rev) for rev in base]
1209 heads = revs and map(repo.lookup, revs) or revs
1209 heads = revs and map(repo.lookup, revs) or revs
1210 cg = changegroup.getchangegroup(repo, 'bundle', heads=heads,
1210 cg = changegroup.getchangegroup(repo, 'bundle', heads=heads,
1211 common=common, bundlecaps=bundlecaps)
1211 common=common, bundlecaps=bundlecaps)
1212 outgoing = None
1212 outgoing = None
1213 else:
1213 else:
1214 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1214 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1215 dest, branches = hg.parseurl(dest, opts.get('branch'))
1215 dest, branches = hg.parseurl(dest, opts.get('branch'))
1216 other = hg.peer(repo, opts, dest)
1216 other = hg.peer(repo, opts, dest)
1217 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1217 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1218 heads = revs and map(repo.lookup, revs) or revs
1218 heads = revs and map(repo.lookup, revs) or revs
1219 outgoing = discovery.findcommonoutgoing(repo, other,
1219 outgoing = discovery.findcommonoutgoing(repo, other,
1220 onlyheads=heads,
1220 onlyheads=heads,
1221 force=opts.get('force'),
1221 force=opts.get('force'),
1222 portable=True)
1222 portable=True)
1223 cg = changegroup.getlocalchangegroup(repo, 'bundle', outgoing,
1223 cg = changegroup.getlocalchangegroup(repo, 'bundle', outgoing,
1224 bundlecaps)
1224 bundlecaps)
1225 if not cg:
1225 if not cg:
1226 scmutil.nochangesfound(ui, repo, outgoing and outgoing.excluded)
1226 scmutil.nochangesfound(ui, repo, outgoing and outgoing.excluded)
1227 return 1
1227 return 1
1228
1228
1229 changegroup.writebundle(ui, cg, fname, bundletype)
1229 changegroup.writebundle(ui, cg, fname, bundletype)
1230
1230
1231 @command('cat',
1231 @command('cat',
1232 [('o', 'output', '',
1232 [('o', 'output', '',
1233 _('print output to file with formatted name'), _('FORMAT')),
1233 _('print output to file with formatted name'), _('FORMAT')),
1234 ('r', 'rev', '', _('print the given revision'), _('REV')),
1234 ('r', 'rev', '', _('print the given revision'), _('REV')),
1235 ('', 'decode', None, _('apply any matching decode filter')),
1235 ('', 'decode', None, _('apply any matching decode filter')),
1236 ] + walkopts,
1236 ] + walkopts,
1237 _('[OPTION]... FILE...'),
1237 _('[OPTION]... FILE...'),
1238 inferrepo=True)
1238 inferrepo=True)
1239 def cat(ui, repo, file1, *pats, **opts):
1239 def cat(ui, repo, file1, *pats, **opts):
1240 """output the current or given revision of files
1240 """output the current or given revision of files
1241
1241
1242 Print the specified files as they were at the given revision. If
1242 Print the specified files as they were at the given revision. If
1243 no revision is given, the parent of the working directory is used.
1243 no revision is given, the parent of the working directory is used.
1244
1244
1245 Output may be to a file, in which case the name of the file is
1245 Output may be to a file, in which case the name of the file is
1246 given using a format string. The formatting rules as follows:
1246 given using a format string. The formatting rules as follows:
1247
1247
1248 :``%%``: literal "%" character
1248 :``%%``: literal "%" character
1249 :``%s``: basename of file being printed
1249 :``%s``: basename of file being printed
1250 :``%d``: dirname of file being printed, or '.' if in repository root
1250 :``%d``: dirname of file being printed, or '.' if in repository root
1251 :``%p``: root-relative path name of file being printed
1251 :``%p``: root-relative path name of file being printed
1252 :``%H``: changeset hash (40 hexadecimal digits)
1252 :``%H``: changeset hash (40 hexadecimal digits)
1253 :``%R``: changeset revision number
1253 :``%R``: changeset revision number
1254 :``%h``: short-form changeset hash (12 hexadecimal digits)
1254 :``%h``: short-form changeset hash (12 hexadecimal digits)
1255 :``%r``: zero-padded changeset revision number
1255 :``%r``: zero-padded changeset revision number
1256 :``%b``: basename of the exporting repository
1256 :``%b``: basename of the exporting repository
1257
1257
1258 Returns 0 on success.
1258 Returns 0 on success.
1259 """
1259 """
1260 ctx = scmutil.revsingle(repo, opts.get('rev'))
1260 ctx = scmutil.revsingle(repo, opts.get('rev'))
1261 m = scmutil.match(ctx, (file1,) + pats, opts)
1261 m = scmutil.match(ctx, (file1,) + pats, opts)
1262
1262
1263 return cmdutil.cat(ui, repo, ctx, m, '', **opts)
1263 return cmdutil.cat(ui, repo, ctx, m, '', **opts)
1264
1264
1265 @command('^clone',
1265 @command('^clone',
1266 [('U', 'noupdate', None, _('the clone will include an empty working '
1266 [('U', 'noupdate', None, _('the clone will include an empty working '
1267 'directory (only a repository)')),
1267 'directory (only a repository)')),
1268 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
1268 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
1269 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1269 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1270 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1270 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1271 ('', 'pull', None, _('use pull protocol to copy metadata')),
1271 ('', 'pull', None, _('use pull protocol to copy metadata')),
1272 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1272 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1273 ] + remoteopts,
1273 ] + remoteopts,
1274 _('[OPTION]... SOURCE [DEST]'),
1274 _('[OPTION]... SOURCE [DEST]'),
1275 norepo=True)
1275 norepo=True)
1276 def clone(ui, source, dest=None, **opts):
1276 def clone(ui, source, dest=None, **opts):
1277 """make a copy of an existing repository
1277 """make a copy of an existing repository
1278
1278
1279 Create a copy of an existing repository in a new directory.
1279 Create a copy of an existing repository in a new directory.
1280
1280
1281 If no destination directory name is specified, it defaults to the
1281 If no destination directory name is specified, it defaults to the
1282 basename of the source.
1282 basename of the source.
1283
1283
1284 The location of the source is added to the new repository's
1284 The location of the source is added to the new repository's
1285 ``.hg/hgrc`` file, as the default to be used for future pulls.
1285 ``.hg/hgrc`` file, as the default to be used for future pulls.
1286
1286
1287 Only local paths and ``ssh://`` URLs are supported as
1287 Only local paths and ``ssh://`` URLs are supported as
1288 destinations. For ``ssh://`` destinations, no working directory or
1288 destinations. For ``ssh://`` destinations, no working directory or
1289 ``.hg/hgrc`` will be created on the remote side.
1289 ``.hg/hgrc`` will be created on the remote side.
1290
1290
1291 To pull only a subset of changesets, specify one or more revisions
1291 To pull only a subset of changesets, specify one or more revisions
1292 identifiers with -r/--rev or branches with -b/--branch. The
1292 identifiers with -r/--rev or branches with -b/--branch. The
1293 resulting clone will contain only the specified changesets and
1293 resulting clone will contain only the specified changesets and
1294 their ancestors. These options (or 'clone src#rev dest') imply
1294 their ancestors. These options (or 'clone src#rev dest') imply
1295 --pull, even for local source repositories. Note that specifying a
1295 --pull, even for local source repositories. Note that specifying a
1296 tag will include the tagged changeset but not the changeset
1296 tag will include the tagged changeset but not the changeset
1297 containing the tag.
1297 containing the tag.
1298
1298
1299 If the source repository has a bookmark called '@' set, that
1299 If the source repository has a bookmark called '@' set, that
1300 revision will be checked out in the new repository by default.
1300 revision will be checked out in the new repository by default.
1301
1301
1302 To check out a particular version, use -u/--update, or
1302 To check out a particular version, use -u/--update, or
1303 -U/--noupdate to create a clone with no working directory.
1303 -U/--noupdate to create a clone with no working directory.
1304
1304
1305 .. container:: verbose
1305 .. container:: verbose
1306
1306
1307 For efficiency, hardlinks are used for cloning whenever the
1307 For efficiency, hardlinks are used for cloning whenever the
1308 source and destination are on the same filesystem (note this
1308 source and destination are on the same filesystem (note this
1309 applies only to the repository data, not to the working
1309 applies only to the repository data, not to the working
1310 directory). Some filesystems, such as AFS, implement hardlinking
1310 directory). Some filesystems, such as AFS, implement hardlinking
1311 incorrectly, but do not report errors. In these cases, use the
1311 incorrectly, but do not report errors. In these cases, use the
1312 --pull option to avoid hardlinking.
1312 --pull option to avoid hardlinking.
1313
1313
1314 In some cases, you can clone repositories and the working
1314 In some cases, you can clone repositories and the working
1315 directory using full hardlinks with ::
1315 directory using full hardlinks with ::
1316
1316
1317 $ cp -al REPO REPOCLONE
1317 $ cp -al REPO REPOCLONE
1318
1318
1319 This is the fastest way to clone, but it is not always safe. The
1319 This is the fastest way to clone, but it is not always safe. The
1320 operation is not atomic (making sure REPO is not modified during
1320 operation is not atomic (making sure REPO is not modified during
1321 the operation is up to you) and you have to make sure your
1321 the operation is up to you) and you have to make sure your
1322 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1322 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1323 so). Also, this is not compatible with certain extensions that
1323 so). Also, this is not compatible with certain extensions that
1324 place their metadata under the .hg directory, such as mq.
1324 place their metadata under the .hg directory, such as mq.
1325
1325
1326 Mercurial will update the working directory to the first applicable
1326 Mercurial will update the working directory to the first applicable
1327 revision from this list:
1327 revision from this list:
1328
1328
1329 a) null if -U or the source repository has no changesets
1329 a) null if -U or the source repository has no changesets
1330 b) if -u . and the source repository is local, the first parent of
1330 b) if -u . and the source repository is local, the first parent of
1331 the source repository's working directory
1331 the source repository's working directory
1332 c) the changeset specified with -u (if a branch name, this means the
1332 c) the changeset specified with -u (if a branch name, this means the
1333 latest head of that branch)
1333 latest head of that branch)
1334 d) the changeset specified with -r
1334 d) the changeset specified with -r
1335 e) the tipmost head specified with -b
1335 e) the tipmost head specified with -b
1336 f) the tipmost head specified with the url#branch source syntax
1336 f) the tipmost head specified with the url#branch source syntax
1337 g) the revision marked with the '@' bookmark, if present
1337 g) the revision marked with the '@' bookmark, if present
1338 h) the tipmost head of the default branch
1338 h) the tipmost head of the default branch
1339 i) tip
1339 i) tip
1340
1340
1341 Examples:
1341 Examples:
1342
1342
1343 - clone a remote repository to a new directory named hg/::
1343 - clone a remote repository to a new directory named hg/::
1344
1344
1345 hg clone http://selenic.com/hg
1345 hg clone http://selenic.com/hg
1346
1346
1347 - create a lightweight local clone::
1347 - create a lightweight local clone::
1348
1348
1349 hg clone project/ project-feature/
1349 hg clone project/ project-feature/
1350
1350
1351 - clone from an absolute path on an ssh server (note double-slash)::
1351 - clone from an absolute path on an ssh server (note double-slash)::
1352
1352
1353 hg clone ssh://user@server//home/projects/alpha/
1353 hg clone ssh://user@server//home/projects/alpha/
1354
1354
1355 - do a high-speed clone over a LAN while checking out a
1355 - do a high-speed clone over a LAN while checking out a
1356 specified version::
1356 specified version::
1357
1357
1358 hg clone --uncompressed http://server/repo -u 1.5
1358 hg clone --uncompressed http://server/repo -u 1.5
1359
1359
1360 - create a repository without changesets after a particular revision::
1360 - create a repository without changesets after a particular revision::
1361
1361
1362 hg clone -r 04e544 experimental/ good/
1362 hg clone -r 04e544 experimental/ good/
1363
1363
1364 - clone (and track) a particular named branch::
1364 - clone (and track) a particular named branch::
1365
1365
1366 hg clone http://selenic.com/hg#stable
1366 hg clone http://selenic.com/hg#stable
1367
1367
1368 See :hg:`help urls` for details on specifying URLs.
1368 See :hg:`help urls` for details on specifying URLs.
1369
1369
1370 Returns 0 on success.
1370 Returns 0 on success.
1371 """
1371 """
1372 if opts.get('noupdate') and opts.get('updaterev'):
1372 if opts.get('noupdate') and opts.get('updaterev'):
1373 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1373 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1374
1374
1375 r = hg.clone(ui, opts, source, dest,
1375 r = hg.clone(ui, opts, source, dest,
1376 pull=opts.get('pull'),
1376 pull=opts.get('pull'),
1377 stream=opts.get('uncompressed'),
1377 stream=opts.get('uncompressed'),
1378 rev=opts.get('rev'),
1378 rev=opts.get('rev'),
1379 update=opts.get('updaterev') or not opts.get('noupdate'),
1379 update=opts.get('updaterev') or not opts.get('noupdate'),
1380 branch=opts.get('branch'))
1380 branch=opts.get('branch'))
1381
1381
1382 return r is None
1382 return r is None
1383
1383
1384 @command('^commit|ci',
1384 @command('^commit|ci',
1385 [('A', 'addremove', None,
1385 [('A', 'addremove', None,
1386 _('mark new/missing files as added/removed before committing')),
1386 _('mark new/missing files as added/removed before committing')),
1387 ('', 'close-branch', None,
1387 ('', 'close-branch', None,
1388 _('mark a branch as closed, hiding it from the branch list')),
1388 _('mark a branch as closed, hiding it from the branch list')),
1389 ('', 'amend', None, _('amend the parent of the working directory')),
1389 ('', 'amend', None, _('amend the parent of the working directory')),
1390 ('s', 'secret', None, _('use the secret phase for committing')),
1390 ('s', 'secret', None, _('use the secret phase for committing')),
1391 ('e', 'edit', None, _('invoke editor on commit messages')),
1391 ('e', 'edit', None, _('invoke editor on commit messages')),
1392 ('i', 'interactive', None, _('use interactive mode')),
1392 ('i', 'interactive', None, _('use interactive mode')),
1393 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1393 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1394 _('[OPTION]... [FILE]...'),
1394 _('[OPTION]... [FILE]...'),
1395 inferrepo=True)
1395 inferrepo=True)
1396 def commit(ui, repo, *pats, **opts):
1396 def commit(ui, repo, *pats, **opts):
1397 """commit the specified files or all outstanding changes
1397 """commit the specified files or all outstanding changes
1398
1398
1399 Commit changes to the given files into the repository. Unlike a
1399 Commit changes to the given files into the repository. Unlike a
1400 centralized SCM, this operation is a local operation. See
1400 centralized SCM, this operation is a local operation. See
1401 :hg:`push` for a way to actively distribute your changes.
1401 :hg:`push` for a way to actively distribute your changes.
1402
1402
1403 If a list of files is omitted, all changes reported by :hg:`status`
1403 If a list of files is omitted, all changes reported by :hg:`status`
1404 will be committed.
1404 will be committed.
1405
1405
1406 If you are committing the result of a merge, do not provide any
1406 If you are committing the result of a merge, do not provide any
1407 filenames or -I/-X filters.
1407 filenames or -I/-X filters.
1408
1408
1409 If no commit message is specified, Mercurial starts your
1409 If no commit message is specified, Mercurial starts your
1410 configured editor where you can enter a message. In case your
1410 configured editor where you can enter a message. In case your
1411 commit fails, you will find a backup of your message in
1411 commit fails, you will find a backup of your message in
1412 ``.hg/last-message.txt``.
1412 ``.hg/last-message.txt``.
1413
1413
1414 The --amend flag can be used to amend the parent of the
1414 The --amend flag can be used to amend the parent of the
1415 working directory with a new commit that contains the changes
1415 working directory with a new commit that contains the changes
1416 in the parent in addition to those currently reported by :hg:`status`,
1416 in the parent in addition to those currently reported by :hg:`status`,
1417 if there are any. The old commit is stored in a backup bundle in
1417 if there are any. The old commit is stored in a backup bundle in
1418 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1418 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1419 on how to restore it).
1419 on how to restore it).
1420
1420
1421 Message, user and date are taken from the amended commit unless
1421 Message, user and date are taken from the amended commit unless
1422 specified. When a message isn't specified on the command line,
1422 specified. When a message isn't specified on the command line,
1423 the editor will open with the message of the amended commit.
1423 the editor will open with the message of the amended commit.
1424
1424
1425 It is not possible to amend public changesets (see :hg:`help phases`)
1425 It is not possible to amend public changesets (see :hg:`help phases`)
1426 or changesets that have children.
1426 or changesets that have children.
1427
1427
1428 See :hg:`help dates` for a list of formats valid for -d/--date.
1428 See :hg:`help dates` for a list of formats valid for -d/--date.
1429
1429
1430 Returns 0 on success, 1 if nothing changed.
1430 Returns 0 on success, 1 if nothing changed.
1431 """
1431 """
1432 if opts.get('interactive'):
1432 if opts.get('interactive'):
1433 opts.pop('interactive')
1433 opts.pop('interactive')
1434 cmdutil.dorecord(ui, repo, commit, 'commit', False,
1434 cmdutil.dorecord(ui, repo, commit, 'commit', False,
1435 cmdutil.recordfilter, *pats, **opts)
1435 cmdutil.recordfilter, *pats, **opts)
1436 return
1436 return
1437
1437
1438 if opts.get('subrepos'):
1438 if opts.get('subrepos'):
1439 if opts.get('amend'):
1439 if opts.get('amend'):
1440 raise util.Abort(_('cannot amend with --subrepos'))
1440 raise util.Abort(_('cannot amend with --subrepos'))
1441 # Let --subrepos on the command line override config setting.
1441 # Let --subrepos on the command line override config setting.
1442 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1442 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1443
1443
1444 cmdutil.checkunfinished(repo, commit=True)
1444 cmdutil.checkunfinished(repo, commit=True)
1445
1445
1446 branch = repo[None].branch()
1446 branch = repo[None].branch()
1447 bheads = repo.branchheads(branch)
1447 bheads = repo.branchheads(branch)
1448
1448
1449 extra = {}
1449 extra = {}
1450 if opts.get('close_branch'):
1450 if opts.get('close_branch'):
1451 extra['close'] = 1
1451 extra['close'] = 1
1452
1452
1453 if not bheads:
1453 if not bheads:
1454 raise util.Abort(_('can only close branch heads'))
1454 raise util.Abort(_('can only close branch heads'))
1455 elif opts.get('amend'):
1455 elif opts.get('amend'):
1456 if repo.parents()[0].p1().branch() != branch and \
1456 if repo.parents()[0].p1().branch() != branch and \
1457 repo.parents()[0].p2().branch() != branch:
1457 repo.parents()[0].p2().branch() != branch:
1458 raise util.Abort(_('can only close branch heads'))
1458 raise util.Abort(_('can only close branch heads'))
1459
1459
1460 if opts.get('amend'):
1460 if opts.get('amend'):
1461 if ui.configbool('ui', 'commitsubrepos'):
1461 if ui.configbool('ui', 'commitsubrepos'):
1462 raise util.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1462 raise util.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1463
1463
1464 old = repo['.']
1464 old = repo['.']
1465 if not old.mutable():
1465 if not old.mutable():
1466 raise util.Abort(_('cannot amend public changesets'))
1466 raise util.Abort(_('cannot amend public changesets'))
1467 if len(repo[None].parents()) > 1:
1467 if len(repo[None].parents()) > 1:
1468 raise util.Abort(_('cannot amend while merging'))
1468 raise util.Abort(_('cannot amend while merging'))
1469 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1469 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1470 if not allowunstable and old.children():
1470 if not allowunstable and old.children():
1471 raise util.Abort(_('cannot amend changeset with children'))
1471 raise util.Abort(_('cannot amend changeset with children'))
1472
1472
1473 # commitfunc is used only for temporary amend commit by cmdutil.amend
1473 # commitfunc is used only for temporary amend commit by cmdutil.amend
1474 def commitfunc(ui, repo, message, match, opts):
1474 def commitfunc(ui, repo, message, match, opts):
1475 return repo.commit(message,
1475 return repo.commit(message,
1476 opts.get('user') or old.user(),
1476 opts.get('user') or old.user(),
1477 opts.get('date') or old.date(),
1477 opts.get('date') or old.date(),
1478 match,
1478 match,
1479 extra=extra)
1479 extra=extra)
1480
1480
1481 current = repo._bookmarkcurrent
1481 current = repo._bookmarkcurrent
1482 marks = old.bookmarks()
1482 marks = old.bookmarks()
1483 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1483 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1484 if node == old.node():
1484 if node == old.node():
1485 ui.status(_("nothing changed\n"))
1485 ui.status(_("nothing changed\n"))
1486 return 1
1486 return 1
1487 elif marks:
1487 elif marks:
1488 ui.debug('moving bookmarks %r from %s to %s\n' %
1488 ui.debug('moving bookmarks %r from %s to %s\n' %
1489 (marks, old.hex(), hex(node)))
1489 (marks, old.hex(), hex(node)))
1490 newmarks = repo._bookmarks
1490 newmarks = repo._bookmarks
1491 for bm in marks:
1491 for bm in marks:
1492 newmarks[bm] = node
1492 newmarks[bm] = node
1493 if bm == current:
1493 if bm == current:
1494 bookmarks.setcurrent(repo, bm)
1494 bookmarks.setcurrent(repo, bm)
1495 newmarks.write()
1495 newmarks.write()
1496 else:
1496 else:
1497 def commitfunc(ui, repo, message, match, opts):
1497 def commitfunc(ui, repo, message, match, opts):
1498 backup = ui.backupconfig('phases', 'new-commit')
1498 backup = ui.backupconfig('phases', 'new-commit')
1499 baseui = repo.baseui
1499 baseui = repo.baseui
1500 basebackup = baseui.backupconfig('phases', 'new-commit')
1500 basebackup = baseui.backupconfig('phases', 'new-commit')
1501 try:
1501 try:
1502 if opts.get('secret'):
1502 if opts.get('secret'):
1503 ui.setconfig('phases', 'new-commit', 'secret', 'commit')
1503 ui.setconfig('phases', 'new-commit', 'secret', 'commit')
1504 # Propagate to subrepos
1504 # Propagate to subrepos
1505 baseui.setconfig('phases', 'new-commit', 'secret', 'commit')
1505 baseui.setconfig('phases', 'new-commit', 'secret', 'commit')
1506
1506
1507 editform = cmdutil.mergeeditform(repo[None], 'commit.normal')
1507 editform = cmdutil.mergeeditform(repo[None], 'commit.normal')
1508 editor = cmdutil.getcommiteditor(editform=editform, **opts)
1508 editor = cmdutil.getcommiteditor(editform=editform, **opts)
1509 return repo.commit(message, opts.get('user'), opts.get('date'),
1509 return repo.commit(message, opts.get('user'), opts.get('date'),
1510 match,
1510 match,
1511 editor=editor,
1511 editor=editor,
1512 extra=extra)
1512 extra=extra)
1513 finally:
1513 finally:
1514 ui.restoreconfig(backup)
1514 ui.restoreconfig(backup)
1515 repo.baseui.restoreconfig(basebackup)
1515 repo.baseui.restoreconfig(basebackup)
1516
1516
1517
1517
1518 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1518 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1519
1519
1520 if not node:
1520 if not node:
1521 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1521 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1522 if stat[3]:
1522 if stat[3]:
1523 ui.status(_("nothing changed (%d missing files, see "
1523 ui.status(_("nothing changed (%d missing files, see "
1524 "'hg status')\n") % len(stat[3]))
1524 "'hg status')\n") % len(stat[3]))
1525 else:
1525 else:
1526 ui.status(_("nothing changed\n"))
1526 ui.status(_("nothing changed\n"))
1527 return 1
1527 return 1
1528
1528
1529 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1529 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1530
1530
1531 @command('config|showconfig|debugconfig',
1531 @command('config|showconfig|debugconfig',
1532 [('u', 'untrusted', None, _('show untrusted configuration options')),
1532 [('u', 'untrusted', None, _('show untrusted configuration options')),
1533 ('e', 'edit', None, _('edit user config')),
1533 ('e', 'edit', None, _('edit user config')),
1534 ('l', 'local', None, _('edit repository config')),
1534 ('l', 'local', None, _('edit repository config')),
1535 ('g', 'global', None, _('edit global config'))],
1535 ('g', 'global', None, _('edit global config'))],
1536 _('[-u] [NAME]...'),
1536 _('[-u] [NAME]...'),
1537 optionalrepo=True)
1537 optionalrepo=True)
1538 def config(ui, repo, *values, **opts):
1538 def config(ui, repo, *values, **opts):
1539 """show combined config settings from all hgrc files
1539 """show combined config settings from all hgrc files
1540
1540
1541 With no arguments, print names and values of all config items.
1541 With no arguments, print names and values of all config items.
1542
1542
1543 With one argument of the form section.name, print just the value
1543 With one argument of the form section.name, print just the value
1544 of that config item.
1544 of that config item.
1545
1545
1546 With multiple arguments, print names and values of all config
1546 With multiple arguments, print names and values of all config
1547 items with matching section names.
1547 items with matching section names.
1548
1548
1549 With --edit, start an editor on the user-level config file. With
1549 With --edit, start an editor on the user-level config file. With
1550 --global, edit the system-wide config file. With --local, edit the
1550 --global, edit the system-wide config file. With --local, edit the
1551 repository-level config file.
1551 repository-level config file.
1552
1552
1553 With --debug, the source (filename and line number) is printed
1553 With --debug, the source (filename and line number) is printed
1554 for each config item.
1554 for each config item.
1555
1555
1556 See :hg:`help config` for more information about config files.
1556 See :hg:`help config` for more information about config files.
1557
1557
1558 Returns 0 on success, 1 if NAME does not exist.
1558 Returns 0 on success, 1 if NAME does not exist.
1559
1559
1560 """
1560 """
1561
1561
1562 if opts.get('edit') or opts.get('local') or opts.get('global'):
1562 if opts.get('edit') or opts.get('local') or opts.get('global'):
1563 if opts.get('local') and opts.get('global'):
1563 if opts.get('local') and opts.get('global'):
1564 raise util.Abort(_("can't use --local and --global together"))
1564 raise util.Abort(_("can't use --local and --global together"))
1565
1565
1566 if opts.get('local'):
1566 if opts.get('local'):
1567 if not repo:
1567 if not repo:
1568 raise util.Abort(_("can't use --local outside a repository"))
1568 raise util.Abort(_("can't use --local outside a repository"))
1569 paths = [repo.join('hgrc')]
1569 paths = [repo.join('hgrc')]
1570 elif opts.get('global'):
1570 elif opts.get('global'):
1571 paths = scmutil.systemrcpath()
1571 paths = scmutil.systemrcpath()
1572 else:
1572 else:
1573 paths = scmutil.userrcpath()
1573 paths = scmutil.userrcpath()
1574
1574
1575 for f in paths:
1575 for f in paths:
1576 if os.path.exists(f):
1576 if os.path.exists(f):
1577 break
1577 break
1578 else:
1578 else:
1579 if opts.get('global'):
1579 if opts.get('global'):
1580 samplehgrc = uimod.samplehgrcs['global']
1580 samplehgrc = uimod.samplehgrcs['global']
1581 elif opts.get('local'):
1581 elif opts.get('local'):
1582 samplehgrc = uimod.samplehgrcs['local']
1582 samplehgrc = uimod.samplehgrcs['local']
1583 else:
1583 else:
1584 samplehgrc = uimod.samplehgrcs['user']
1584 samplehgrc = uimod.samplehgrcs['user']
1585
1585
1586 f = paths[0]
1586 f = paths[0]
1587 fp = open(f, "w")
1587 fp = open(f, "w")
1588 fp.write(samplehgrc)
1588 fp.write(samplehgrc)
1589 fp.close()
1589 fp.close()
1590
1590
1591 editor = ui.geteditor()
1591 editor = ui.geteditor()
1592 ui.system("%s \"%s\"" % (editor, f),
1592 ui.system("%s \"%s\"" % (editor, f),
1593 onerr=util.Abort, errprefix=_("edit failed"))
1593 onerr=util.Abort, errprefix=_("edit failed"))
1594 return
1594 return
1595
1595
1596 for f in scmutil.rcpath():
1596 for f in scmutil.rcpath():
1597 ui.debug('read config from: %s\n' % f)
1597 ui.debug('read config from: %s\n' % f)
1598 untrusted = bool(opts.get('untrusted'))
1598 untrusted = bool(opts.get('untrusted'))
1599 if values:
1599 if values:
1600 sections = [v for v in values if '.' not in v]
1600 sections = [v for v in values if '.' not in v]
1601 items = [v for v in values if '.' in v]
1601 items = [v for v in values if '.' in v]
1602 if len(items) > 1 or items and sections:
1602 if len(items) > 1 or items and sections:
1603 raise util.Abort(_('only one config item permitted'))
1603 raise util.Abort(_('only one config item permitted'))
1604 matched = False
1604 matched = False
1605 for section, name, value in ui.walkconfig(untrusted=untrusted):
1605 for section, name, value in ui.walkconfig(untrusted=untrusted):
1606 value = str(value).replace('\n', '\\n')
1606 value = str(value).replace('\n', '\\n')
1607 sectname = section + '.' + name
1607 sectname = section + '.' + name
1608 if values:
1608 if values:
1609 for v in values:
1609 for v in values:
1610 if v == section:
1610 if v == section:
1611 ui.debug('%s: ' %
1611 ui.debug('%s: ' %
1612 ui.configsource(section, name, untrusted))
1612 ui.configsource(section, name, untrusted))
1613 ui.write('%s=%s\n' % (sectname, value))
1613 ui.write('%s=%s\n' % (sectname, value))
1614 matched = True
1614 matched = True
1615 elif v == sectname:
1615 elif v == sectname:
1616 ui.debug('%s: ' %
1616 ui.debug('%s: ' %
1617 ui.configsource(section, name, untrusted))
1617 ui.configsource(section, name, untrusted))
1618 ui.write(value, '\n')
1618 ui.write(value, '\n')
1619 matched = True
1619 matched = True
1620 else:
1620 else:
1621 ui.debug('%s: ' %
1621 ui.debug('%s: ' %
1622 ui.configsource(section, name, untrusted))
1622 ui.configsource(section, name, untrusted))
1623 ui.write('%s=%s\n' % (sectname, value))
1623 ui.write('%s=%s\n' % (sectname, value))
1624 matched = True
1624 matched = True
1625 if matched:
1625 if matched:
1626 return 0
1626 return 0
1627 return 1
1627 return 1
1628
1628
1629 @command('copy|cp',
1629 @command('copy|cp',
1630 [('A', 'after', None, _('record a copy that has already occurred')),
1630 [('A', 'after', None, _('record a copy that has already occurred')),
1631 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1631 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1632 ] + walkopts + dryrunopts,
1632 ] + walkopts + dryrunopts,
1633 _('[OPTION]... [SOURCE]... DEST'))
1633 _('[OPTION]... [SOURCE]... DEST'))
1634 def copy(ui, repo, *pats, **opts):
1634 def copy(ui, repo, *pats, **opts):
1635 """mark files as copied for the next commit
1635 """mark files as copied for the next commit
1636
1636
1637 Mark dest as having copies of source files. If dest is a
1637 Mark dest as having copies of source files. If dest is a
1638 directory, copies are put in that directory. If dest is a file,
1638 directory, copies are put in that directory. If dest is a file,
1639 the source must be a single file.
1639 the source must be a single file.
1640
1640
1641 By default, this command copies the contents of files as they
1641 By default, this command copies the contents of files as they
1642 exist in the working directory. If invoked with -A/--after, the
1642 exist in the working directory. If invoked with -A/--after, the
1643 operation is recorded, but no copying is performed.
1643 operation is recorded, but no copying is performed.
1644
1644
1645 This command takes effect with the next commit. To undo a copy
1645 This command takes effect with the next commit. To undo a copy
1646 before that, see :hg:`revert`.
1646 before that, see :hg:`revert`.
1647
1647
1648 Returns 0 on success, 1 if errors are encountered.
1648 Returns 0 on success, 1 if errors are encountered.
1649 """
1649 """
1650 wlock = repo.wlock(False)
1650 wlock = repo.wlock(False)
1651 try:
1651 try:
1652 return cmdutil.copy(ui, repo, pats, opts)
1652 return cmdutil.copy(ui, repo, pats, opts)
1653 finally:
1653 finally:
1654 wlock.release()
1654 wlock.release()
1655
1655
1656 @command('debugancestor', [], _('[INDEX] REV1 REV2'), optionalrepo=True)
1656 @command('debugancestor', [], _('[INDEX] REV1 REV2'), optionalrepo=True)
1657 def debugancestor(ui, repo, *args):
1657 def debugancestor(ui, repo, *args):
1658 """find the ancestor revision of two revisions in a given index"""
1658 """find the ancestor revision of two revisions in a given index"""
1659 if len(args) == 3:
1659 if len(args) == 3:
1660 index, rev1, rev2 = args
1660 index, rev1, rev2 = args
1661 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1661 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1662 lookup = r.lookup
1662 lookup = r.lookup
1663 elif len(args) == 2:
1663 elif len(args) == 2:
1664 if not repo:
1664 if not repo:
1665 raise util.Abort(_("there is no Mercurial repository here "
1665 raise util.Abort(_("there is no Mercurial repository here "
1666 "(.hg not found)"))
1666 "(.hg not found)"))
1667 rev1, rev2 = args
1667 rev1, rev2 = args
1668 r = repo.changelog
1668 r = repo.changelog
1669 lookup = repo.lookup
1669 lookup = repo.lookup
1670 else:
1670 else:
1671 raise util.Abort(_('either two or three arguments required'))
1671 raise util.Abort(_('either two or three arguments required'))
1672 a = r.ancestor(lookup(rev1), lookup(rev2))
1672 a = r.ancestor(lookup(rev1), lookup(rev2))
1673 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1673 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1674
1674
1675 @command('debugbuilddag',
1675 @command('debugbuilddag',
1676 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1676 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1677 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1677 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1678 ('n', 'new-file', None, _('add new file at each rev'))],
1678 ('n', 'new-file', None, _('add new file at each rev'))],
1679 _('[OPTION]... [TEXT]'))
1679 _('[OPTION]... [TEXT]'))
1680 def debugbuilddag(ui, repo, text=None,
1680 def debugbuilddag(ui, repo, text=None,
1681 mergeable_file=False,
1681 mergeable_file=False,
1682 overwritten_file=False,
1682 overwritten_file=False,
1683 new_file=False):
1683 new_file=False):
1684 """builds a repo with a given DAG from scratch in the current empty repo
1684 """builds a repo with a given DAG from scratch in the current empty repo
1685
1685
1686 The description of the DAG is read from stdin if not given on the
1686 The description of the DAG is read from stdin if not given on the
1687 command line.
1687 command line.
1688
1688
1689 Elements:
1689 Elements:
1690
1690
1691 - "+n" is a linear run of n nodes based on the current default parent
1691 - "+n" is a linear run of n nodes based on the current default parent
1692 - "." is a single node based on the current default parent
1692 - "." is a single node based on the current default parent
1693 - "$" resets the default parent to null (implied at the start);
1693 - "$" resets the default parent to null (implied at the start);
1694 otherwise the default parent is always the last node created
1694 otherwise the default parent is always the last node created
1695 - "<p" sets the default parent to the backref p
1695 - "<p" sets the default parent to the backref p
1696 - "*p" is a fork at parent p, which is a backref
1696 - "*p" is a fork at parent p, which is a backref
1697 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1697 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1698 - "/p2" is a merge of the preceding node and p2
1698 - "/p2" is a merge of the preceding node and p2
1699 - ":tag" defines a local tag for the preceding node
1699 - ":tag" defines a local tag for the preceding node
1700 - "@branch" sets the named branch for subsequent nodes
1700 - "@branch" sets the named branch for subsequent nodes
1701 - "#...\\n" is a comment up to the end of the line
1701 - "#...\\n" is a comment up to the end of the line
1702
1702
1703 Whitespace between the above elements is ignored.
1703 Whitespace between the above elements is ignored.
1704
1704
1705 A backref is either
1705 A backref is either
1706
1706
1707 - a number n, which references the node curr-n, where curr is the current
1707 - a number n, which references the node curr-n, where curr is the current
1708 node, or
1708 node, or
1709 - the name of a local tag you placed earlier using ":tag", or
1709 - the name of a local tag you placed earlier using ":tag", or
1710 - empty to denote the default parent.
1710 - empty to denote the default parent.
1711
1711
1712 All string valued-elements are either strictly alphanumeric, or must
1712 All string valued-elements are either strictly alphanumeric, or must
1713 be enclosed in double quotes ("..."), with "\\" as escape character.
1713 be enclosed in double quotes ("..."), with "\\" as escape character.
1714 """
1714 """
1715
1715
1716 if text is None:
1716 if text is None:
1717 ui.status(_("reading DAG from stdin\n"))
1717 ui.status(_("reading DAG from stdin\n"))
1718 text = ui.fin.read()
1718 text = ui.fin.read()
1719
1719
1720 cl = repo.changelog
1720 cl = repo.changelog
1721 if len(cl) > 0:
1721 if len(cl) > 0:
1722 raise util.Abort(_('repository is not empty'))
1722 raise util.Abort(_('repository is not empty'))
1723
1723
1724 # determine number of revs in DAG
1724 # determine number of revs in DAG
1725 total = 0
1725 total = 0
1726 for type, data in dagparser.parsedag(text):
1726 for type, data in dagparser.parsedag(text):
1727 if type == 'n':
1727 if type == 'n':
1728 total += 1
1728 total += 1
1729
1729
1730 if mergeable_file:
1730 if mergeable_file:
1731 linesperrev = 2
1731 linesperrev = 2
1732 # make a file with k lines per rev
1732 # make a file with k lines per rev
1733 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1733 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1734 initialmergedlines.append("")
1734 initialmergedlines.append("")
1735
1735
1736 tags = []
1736 tags = []
1737
1737
1738 lock = tr = None
1738 lock = tr = None
1739 try:
1739 try:
1740 lock = repo.lock()
1740 lock = repo.lock()
1741 tr = repo.transaction("builddag")
1741 tr = repo.transaction("builddag")
1742
1742
1743 at = -1
1743 at = -1
1744 atbranch = 'default'
1744 atbranch = 'default'
1745 nodeids = []
1745 nodeids = []
1746 id = 0
1746 id = 0
1747 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1747 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1748 for type, data in dagparser.parsedag(text):
1748 for type, data in dagparser.parsedag(text):
1749 if type == 'n':
1749 if type == 'n':
1750 ui.note(('node %s\n' % str(data)))
1750 ui.note(('node %s\n' % str(data)))
1751 id, ps = data
1751 id, ps = data
1752
1752
1753 files = []
1753 files = []
1754 fctxs = {}
1754 fctxs = {}
1755
1755
1756 p2 = None
1756 p2 = None
1757 if mergeable_file:
1757 if mergeable_file:
1758 fn = "mf"
1758 fn = "mf"
1759 p1 = repo[ps[0]]
1759 p1 = repo[ps[0]]
1760 if len(ps) > 1:
1760 if len(ps) > 1:
1761 p2 = repo[ps[1]]
1761 p2 = repo[ps[1]]
1762 pa = p1.ancestor(p2)
1762 pa = p1.ancestor(p2)
1763 base, local, other = [x[fn].data() for x in (pa, p1,
1763 base, local, other = [x[fn].data() for x in (pa, p1,
1764 p2)]
1764 p2)]
1765 m3 = simplemerge.Merge3Text(base, local, other)
1765 m3 = simplemerge.Merge3Text(base, local, other)
1766 ml = [l.strip() for l in m3.merge_lines()]
1766 ml = [l.strip() for l in m3.merge_lines()]
1767 ml.append("")
1767 ml.append("")
1768 elif at > 0:
1768 elif at > 0:
1769 ml = p1[fn].data().split("\n")
1769 ml = p1[fn].data().split("\n")
1770 else:
1770 else:
1771 ml = initialmergedlines
1771 ml = initialmergedlines
1772 ml[id * linesperrev] += " r%i" % id
1772 ml[id * linesperrev] += " r%i" % id
1773 mergedtext = "\n".join(ml)
1773 mergedtext = "\n".join(ml)
1774 files.append(fn)
1774 files.append(fn)
1775 fctxs[fn] = context.memfilectx(repo, fn, mergedtext)
1775 fctxs[fn] = context.memfilectx(repo, fn, mergedtext)
1776
1776
1777 if overwritten_file:
1777 if overwritten_file:
1778 fn = "of"
1778 fn = "of"
1779 files.append(fn)
1779 files.append(fn)
1780 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
1780 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
1781
1781
1782 if new_file:
1782 if new_file:
1783 fn = "nf%i" % id
1783 fn = "nf%i" % id
1784 files.append(fn)
1784 files.append(fn)
1785 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
1785 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
1786 if len(ps) > 1:
1786 if len(ps) > 1:
1787 if not p2:
1787 if not p2:
1788 p2 = repo[ps[1]]
1788 p2 = repo[ps[1]]
1789 for fn in p2:
1789 for fn in p2:
1790 if fn.startswith("nf"):
1790 if fn.startswith("nf"):
1791 files.append(fn)
1791 files.append(fn)
1792 fctxs[fn] = p2[fn]
1792 fctxs[fn] = p2[fn]
1793
1793
1794 def fctxfn(repo, cx, path):
1794 def fctxfn(repo, cx, path):
1795 return fctxs.get(path)
1795 return fctxs.get(path)
1796
1796
1797 if len(ps) == 0 or ps[0] < 0:
1797 if len(ps) == 0 or ps[0] < 0:
1798 pars = [None, None]
1798 pars = [None, None]
1799 elif len(ps) == 1:
1799 elif len(ps) == 1:
1800 pars = [nodeids[ps[0]], None]
1800 pars = [nodeids[ps[0]], None]
1801 else:
1801 else:
1802 pars = [nodeids[p] for p in ps]
1802 pars = [nodeids[p] for p in ps]
1803 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1803 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1804 date=(id, 0),
1804 date=(id, 0),
1805 user="debugbuilddag",
1805 user="debugbuilddag",
1806 extra={'branch': atbranch})
1806 extra={'branch': atbranch})
1807 nodeid = repo.commitctx(cx)
1807 nodeid = repo.commitctx(cx)
1808 nodeids.append(nodeid)
1808 nodeids.append(nodeid)
1809 at = id
1809 at = id
1810 elif type == 'l':
1810 elif type == 'l':
1811 id, name = data
1811 id, name = data
1812 ui.note(('tag %s\n' % name))
1812 ui.note(('tag %s\n' % name))
1813 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1813 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1814 elif type == 'a':
1814 elif type == 'a':
1815 ui.note(('branch %s\n' % data))
1815 ui.note(('branch %s\n' % data))
1816 atbranch = data
1816 atbranch = data
1817 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1817 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1818 tr.close()
1818 tr.close()
1819
1819
1820 if tags:
1820 if tags:
1821 repo.vfs.write("localtags", "".join(tags))
1821 repo.vfs.write("localtags", "".join(tags))
1822 finally:
1822 finally:
1823 ui.progress(_('building'), None)
1823 ui.progress(_('building'), None)
1824 release(tr, lock)
1824 release(tr, lock)
1825
1825
1826 @command('debugbundle',
1826 @command('debugbundle',
1827 [('a', 'all', None, _('show all details'))],
1827 [('a', 'all', None, _('show all details'))],
1828 _('FILE'),
1828 _('FILE'),
1829 norepo=True)
1829 norepo=True)
1830 def debugbundle(ui, bundlepath, all=None, **opts):
1830 def debugbundle(ui, bundlepath, all=None, **opts):
1831 """lists the contents of a bundle"""
1831 """lists the contents of a bundle"""
1832 f = hg.openpath(ui, bundlepath)
1832 f = hg.openpath(ui, bundlepath)
1833 try:
1833 try:
1834 gen = exchange.readbundle(ui, f, bundlepath)
1834 gen = exchange.readbundle(ui, f, bundlepath)
1835 if isinstance(gen, bundle2.unbundle20):
1835 if isinstance(gen, bundle2.unbundle20):
1836 return _debugbundle2(ui, gen, all=all, **opts)
1836 return _debugbundle2(ui, gen, all=all, **opts)
1837 if all:
1837 if all:
1838 ui.write(("format: id, p1, p2, cset, delta base, len(delta)\n"))
1838 ui.write(("format: id, p1, p2, cset, delta base, len(delta)\n"))
1839
1839
1840 def showchunks(named):
1840 def showchunks(named):
1841 ui.write("\n%s\n" % named)
1841 ui.write("\n%s\n" % named)
1842 chain = None
1842 chain = None
1843 while True:
1843 while True:
1844 chunkdata = gen.deltachunk(chain)
1844 chunkdata = gen.deltachunk(chain)
1845 if not chunkdata:
1845 if not chunkdata:
1846 break
1846 break
1847 node = chunkdata['node']
1847 node = chunkdata['node']
1848 p1 = chunkdata['p1']
1848 p1 = chunkdata['p1']
1849 p2 = chunkdata['p2']
1849 p2 = chunkdata['p2']
1850 cs = chunkdata['cs']
1850 cs = chunkdata['cs']
1851 deltabase = chunkdata['deltabase']
1851 deltabase = chunkdata['deltabase']
1852 delta = chunkdata['delta']
1852 delta = chunkdata['delta']
1853 ui.write("%s %s %s %s %s %s\n" %
1853 ui.write("%s %s %s %s %s %s\n" %
1854 (hex(node), hex(p1), hex(p2),
1854 (hex(node), hex(p1), hex(p2),
1855 hex(cs), hex(deltabase), len(delta)))
1855 hex(cs), hex(deltabase), len(delta)))
1856 chain = node
1856 chain = node
1857
1857
1858 chunkdata = gen.changelogheader()
1858 chunkdata = gen.changelogheader()
1859 showchunks("changelog")
1859 showchunks("changelog")
1860 chunkdata = gen.manifestheader()
1860 chunkdata = gen.manifestheader()
1861 showchunks("manifest")
1861 showchunks("manifest")
1862 while True:
1862 while True:
1863 chunkdata = gen.filelogheader()
1863 chunkdata = gen.filelogheader()
1864 if not chunkdata:
1864 if not chunkdata:
1865 break
1865 break
1866 fname = chunkdata['filename']
1866 fname = chunkdata['filename']
1867 showchunks(fname)
1867 showchunks(fname)
1868 else:
1868 else:
1869 if isinstance(gen, bundle2.unbundle20):
1869 if isinstance(gen, bundle2.unbundle20):
1870 raise util.Abort(_('use debugbundle2 for this file'))
1870 raise util.Abort(_('use debugbundle2 for this file'))
1871 chunkdata = gen.changelogheader()
1871 chunkdata = gen.changelogheader()
1872 chain = None
1872 chain = None
1873 while True:
1873 while True:
1874 chunkdata = gen.deltachunk(chain)
1874 chunkdata = gen.deltachunk(chain)
1875 if not chunkdata:
1875 if not chunkdata:
1876 break
1876 break
1877 node = chunkdata['node']
1877 node = chunkdata['node']
1878 ui.write("%s\n" % hex(node))
1878 ui.write("%s\n" % hex(node))
1879 chain = node
1879 chain = node
1880 finally:
1880 finally:
1881 f.close()
1881 f.close()
1882
1882
1883 def _debugbundle2(ui, gen, **opts):
1883 def _debugbundle2(ui, gen, **opts):
1884 """lists the contents of a bundle2"""
1884 """lists the contents of a bundle2"""
1885 if not isinstance(gen, bundle2.unbundle20):
1885 if not isinstance(gen, bundle2.unbundle20):
1886 raise util.Abort(_('not a bundle2 file'))
1886 raise util.Abort(_('not a bundle2 file'))
1887 ui.write(('Stream params: %s\n' % repr(gen.params)))
1887 ui.write(('Stream params: %s\n' % repr(gen.params)))
1888 for part in gen.iterparts():
1888 for part in gen.iterparts():
1889 ui.write('%s -- %r\n' % (part.type, repr(part.params)))
1889 ui.write('%s -- %r\n' % (part.type, repr(part.params)))
1890 if part.type == 'b2x:changegroup':
1890 if part.type == 'b2x:changegroup':
1891 version = part.params.get('version', '01')
1891 version = part.params.get('version', '01')
1892 cg = changegroup.packermap[version][1](part, 'UN')
1892 cg = changegroup.packermap[version][1](part, 'UN')
1893 chunkdata = cg.changelogheader()
1893 chunkdata = cg.changelogheader()
1894 chain = None
1894 chain = None
1895 while True:
1895 while True:
1896 chunkdata = cg.deltachunk(chain)
1896 chunkdata = cg.deltachunk(chain)
1897 if not chunkdata:
1897 if not chunkdata:
1898 break
1898 break
1899 node = chunkdata['node']
1899 node = chunkdata['node']
1900 ui.write(" %s\n" % hex(node))
1900 ui.write(" %s\n" % hex(node))
1901 chain = node
1901 chain = node
1902
1902
1903 @command('debugcheckstate', [], '')
1903 @command('debugcheckstate', [], '')
1904 def debugcheckstate(ui, repo):
1904 def debugcheckstate(ui, repo):
1905 """validate the correctness of the current dirstate"""
1905 """validate the correctness of the current dirstate"""
1906 parent1, parent2 = repo.dirstate.parents()
1906 parent1, parent2 = repo.dirstate.parents()
1907 m1 = repo[parent1].manifest()
1907 m1 = repo[parent1].manifest()
1908 m2 = repo[parent2].manifest()
1908 m2 = repo[parent2].manifest()
1909 errors = 0
1909 errors = 0
1910 for f in repo.dirstate:
1910 for f in repo.dirstate:
1911 state = repo.dirstate[f]
1911 state = repo.dirstate[f]
1912 if state in "nr" and f not in m1:
1912 if state in "nr" and f not in m1:
1913 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1913 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1914 errors += 1
1914 errors += 1
1915 if state in "a" and f in m1:
1915 if state in "a" and f in m1:
1916 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1916 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1917 errors += 1
1917 errors += 1
1918 if state in "m" and f not in m1 and f not in m2:
1918 if state in "m" and f not in m1 and f not in m2:
1919 ui.warn(_("%s in state %s, but not in either manifest\n") %
1919 ui.warn(_("%s in state %s, but not in either manifest\n") %
1920 (f, state))
1920 (f, state))
1921 errors += 1
1921 errors += 1
1922 for f in m1:
1922 for f in m1:
1923 state = repo.dirstate[f]
1923 state = repo.dirstate[f]
1924 if state not in "nrm":
1924 if state not in "nrm":
1925 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1925 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1926 errors += 1
1926 errors += 1
1927 if errors:
1927 if errors:
1928 error = _(".hg/dirstate inconsistent with current parent's manifest")
1928 error = _(".hg/dirstate inconsistent with current parent's manifest")
1929 raise util.Abort(error)
1929 raise util.Abort(error)
1930
1930
1931 @command('debugcommands', [], _('[COMMAND]'), norepo=True)
1931 @command('debugcommands', [], _('[COMMAND]'), norepo=True)
1932 def debugcommands(ui, cmd='', *args):
1932 def debugcommands(ui, cmd='', *args):
1933 """list all available commands and options"""
1933 """list all available commands and options"""
1934 for cmd, vals in sorted(table.iteritems()):
1934 for cmd, vals in sorted(table.iteritems()):
1935 cmd = cmd.split('|')[0].strip('^')
1935 cmd = cmd.split('|')[0].strip('^')
1936 opts = ', '.join([i[1] for i in vals[1]])
1936 opts = ', '.join([i[1] for i in vals[1]])
1937 ui.write('%s: %s\n' % (cmd, opts))
1937 ui.write('%s: %s\n' % (cmd, opts))
1938
1938
1939 @command('debugcomplete',
1939 @command('debugcomplete',
1940 [('o', 'options', None, _('show the command options'))],
1940 [('o', 'options', None, _('show the command options'))],
1941 _('[-o] CMD'),
1941 _('[-o] CMD'),
1942 norepo=True)
1942 norepo=True)
1943 def debugcomplete(ui, cmd='', **opts):
1943 def debugcomplete(ui, cmd='', **opts):
1944 """returns the completion list associated with the given command"""
1944 """returns the completion list associated with the given command"""
1945
1945
1946 if opts.get('options'):
1946 if opts.get('options'):
1947 options = []
1947 options = []
1948 otables = [globalopts]
1948 otables = [globalopts]
1949 if cmd:
1949 if cmd:
1950 aliases, entry = cmdutil.findcmd(cmd, table, False)
1950 aliases, entry = cmdutil.findcmd(cmd, table, False)
1951 otables.append(entry[1])
1951 otables.append(entry[1])
1952 for t in otables:
1952 for t in otables:
1953 for o in t:
1953 for o in t:
1954 if "(DEPRECATED)" in o[3]:
1954 if "(DEPRECATED)" in o[3]:
1955 continue
1955 continue
1956 if o[0]:
1956 if o[0]:
1957 options.append('-%s' % o[0])
1957 options.append('-%s' % o[0])
1958 options.append('--%s' % o[1])
1958 options.append('--%s' % o[1])
1959 ui.write("%s\n" % "\n".join(options))
1959 ui.write("%s\n" % "\n".join(options))
1960 return
1960 return
1961
1961
1962 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1962 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1963 if ui.verbose:
1963 if ui.verbose:
1964 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1964 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1965 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1965 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1966
1966
1967 @command('debugdag',
1967 @command('debugdag',
1968 [('t', 'tags', None, _('use tags as labels')),
1968 [('t', 'tags', None, _('use tags as labels')),
1969 ('b', 'branches', None, _('annotate with branch names')),
1969 ('b', 'branches', None, _('annotate with branch names')),
1970 ('', 'dots', None, _('use dots for runs')),
1970 ('', 'dots', None, _('use dots for runs')),
1971 ('s', 'spaces', None, _('separate elements by spaces'))],
1971 ('s', 'spaces', None, _('separate elements by spaces'))],
1972 _('[OPTION]... [FILE [REV]...]'),
1972 _('[OPTION]... [FILE [REV]...]'),
1973 optionalrepo=True)
1973 optionalrepo=True)
1974 def debugdag(ui, repo, file_=None, *revs, **opts):
1974 def debugdag(ui, repo, file_=None, *revs, **opts):
1975 """format the changelog or an index DAG as a concise textual description
1975 """format the changelog or an index DAG as a concise textual description
1976
1976
1977 If you pass a revlog index, the revlog's DAG is emitted. If you list
1977 If you pass a revlog index, the revlog's DAG is emitted. If you list
1978 revision numbers, they get labeled in the output as rN.
1978 revision numbers, they get labeled in the output as rN.
1979
1979
1980 Otherwise, the changelog DAG of the current repo is emitted.
1980 Otherwise, the changelog DAG of the current repo is emitted.
1981 """
1981 """
1982 spaces = opts.get('spaces')
1982 spaces = opts.get('spaces')
1983 dots = opts.get('dots')
1983 dots = opts.get('dots')
1984 if file_:
1984 if file_:
1985 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1985 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1986 revs = set((int(r) for r in revs))
1986 revs = set((int(r) for r in revs))
1987 def events():
1987 def events():
1988 for r in rlog:
1988 for r in rlog:
1989 yield 'n', (r, list(p for p in rlog.parentrevs(r)
1989 yield 'n', (r, list(p for p in rlog.parentrevs(r)
1990 if p != -1))
1990 if p != -1))
1991 if r in revs:
1991 if r in revs:
1992 yield 'l', (r, "r%i" % r)
1992 yield 'l', (r, "r%i" % r)
1993 elif repo:
1993 elif repo:
1994 cl = repo.changelog
1994 cl = repo.changelog
1995 tags = opts.get('tags')
1995 tags = opts.get('tags')
1996 branches = opts.get('branches')
1996 branches = opts.get('branches')
1997 if tags:
1997 if tags:
1998 labels = {}
1998 labels = {}
1999 for l, n in repo.tags().items():
1999 for l, n in repo.tags().items():
2000 labels.setdefault(cl.rev(n), []).append(l)
2000 labels.setdefault(cl.rev(n), []).append(l)
2001 def events():
2001 def events():
2002 b = "default"
2002 b = "default"
2003 for r in cl:
2003 for r in cl:
2004 if branches:
2004 if branches:
2005 newb = cl.read(cl.node(r))[5]['branch']
2005 newb = cl.read(cl.node(r))[5]['branch']
2006 if newb != b:
2006 if newb != b:
2007 yield 'a', newb
2007 yield 'a', newb
2008 b = newb
2008 b = newb
2009 yield 'n', (r, list(p for p in cl.parentrevs(r)
2009 yield 'n', (r, list(p for p in cl.parentrevs(r)
2010 if p != -1))
2010 if p != -1))
2011 if tags:
2011 if tags:
2012 ls = labels.get(r)
2012 ls = labels.get(r)
2013 if ls:
2013 if ls:
2014 for l in ls:
2014 for l in ls:
2015 yield 'l', (r, l)
2015 yield 'l', (r, l)
2016 else:
2016 else:
2017 raise util.Abort(_('need repo for changelog dag'))
2017 raise util.Abort(_('need repo for changelog dag'))
2018
2018
2019 for line in dagparser.dagtextlines(events(),
2019 for line in dagparser.dagtextlines(events(),
2020 addspaces=spaces,
2020 addspaces=spaces,
2021 wraplabels=True,
2021 wraplabels=True,
2022 wrapannotations=True,
2022 wrapannotations=True,
2023 wrapnonlinear=dots,
2023 wrapnonlinear=dots,
2024 usedots=dots,
2024 usedots=dots,
2025 maxlinewidth=70):
2025 maxlinewidth=70):
2026 ui.write(line)
2026 ui.write(line)
2027 ui.write("\n")
2027 ui.write("\n")
2028
2028
2029 @command('debugdata',
2029 @command('debugdata',
2030 [('c', 'changelog', False, _('open changelog')),
2030 [('c', 'changelog', False, _('open changelog')),
2031 ('m', 'manifest', False, _('open manifest'))],
2031 ('m', 'manifest', False, _('open manifest'))],
2032 _('-c|-m|FILE REV'))
2032 _('-c|-m|FILE REV'))
2033 def debugdata(ui, repo, file_, rev=None, **opts):
2033 def debugdata(ui, repo, file_, rev=None, **opts):
2034 """dump the contents of a data file revision"""
2034 """dump the contents of a data file revision"""
2035 if opts.get('changelog') or opts.get('manifest'):
2035 if opts.get('changelog') or opts.get('manifest'):
2036 file_, rev = None, file_
2036 file_, rev = None, file_
2037 elif rev is None:
2037 elif rev is None:
2038 raise error.CommandError('debugdata', _('invalid arguments'))
2038 raise error.CommandError('debugdata', _('invalid arguments'))
2039 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
2039 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
2040 try:
2040 try:
2041 ui.write(r.revision(r.lookup(rev)))
2041 ui.write(r.revision(r.lookup(rev)))
2042 except KeyError:
2042 except KeyError:
2043 raise util.Abort(_('invalid revision identifier %s') % rev)
2043 raise util.Abort(_('invalid revision identifier %s') % rev)
2044
2044
2045 @command('debugdate',
2045 @command('debugdate',
2046 [('e', 'extended', None, _('try extended date formats'))],
2046 [('e', 'extended', None, _('try extended date formats'))],
2047 _('[-e] DATE [RANGE]'),
2047 _('[-e] DATE [RANGE]'),
2048 norepo=True, optionalrepo=True)
2048 norepo=True, optionalrepo=True)
2049 def debugdate(ui, date, range=None, **opts):
2049 def debugdate(ui, date, range=None, **opts):
2050 """parse and display a date"""
2050 """parse and display a date"""
2051 if opts["extended"]:
2051 if opts["extended"]:
2052 d = util.parsedate(date, util.extendeddateformats)
2052 d = util.parsedate(date, util.extendeddateformats)
2053 else:
2053 else:
2054 d = util.parsedate(date)
2054 d = util.parsedate(date)
2055 ui.write(("internal: %s %s\n") % d)
2055 ui.write(("internal: %s %s\n") % d)
2056 ui.write(("standard: %s\n") % util.datestr(d))
2056 ui.write(("standard: %s\n") % util.datestr(d))
2057 if range:
2057 if range:
2058 m = util.matchdate(range)
2058 m = util.matchdate(range)
2059 ui.write(("match: %s\n") % m(d[0]))
2059 ui.write(("match: %s\n") % m(d[0]))
2060
2060
2061 @command('debugdiscovery',
2061 @command('debugdiscovery',
2062 [('', 'old', None, _('use old-style discovery')),
2062 [('', 'old', None, _('use old-style discovery')),
2063 ('', 'nonheads', None,
2063 ('', 'nonheads', None,
2064 _('use old-style discovery with non-heads included')),
2064 _('use old-style discovery with non-heads included')),
2065 ] + remoteopts,
2065 ] + remoteopts,
2066 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
2066 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
2067 def debugdiscovery(ui, repo, remoteurl="default", **opts):
2067 def debugdiscovery(ui, repo, remoteurl="default", **opts):
2068 """runs the changeset discovery protocol in isolation"""
2068 """runs the changeset discovery protocol in isolation"""
2069 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl),
2069 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl),
2070 opts.get('branch'))
2070 opts.get('branch'))
2071 remote = hg.peer(repo, opts, remoteurl)
2071 remote = hg.peer(repo, opts, remoteurl)
2072 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
2072 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
2073
2073
2074 # make sure tests are repeatable
2074 # make sure tests are repeatable
2075 random.seed(12323)
2075 random.seed(12323)
2076
2076
2077 def doit(localheads, remoteheads, remote=remote):
2077 def doit(localheads, remoteheads, remote=remote):
2078 if opts.get('old'):
2078 if opts.get('old'):
2079 if localheads:
2079 if localheads:
2080 raise util.Abort('cannot use localheads with old style '
2080 raise util.Abort('cannot use localheads with old style '
2081 'discovery')
2081 'discovery')
2082 if not util.safehasattr(remote, 'branches'):
2082 if not util.safehasattr(remote, 'branches'):
2083 # enable in-client legacy support
2083 # enable in-client legacy support
2084 remote = localrepo.locallegacypeer(remote.local())
2084 remote = localrepo.locallegacypeer(remote.local())
2085 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
2085 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
2086 force=True)
2086 force=True)
2087 common = set(common)
2087 common = set(common)
2088 if not opts.get('nonheads'):
2088 if not opts.get('nonheads'):
2089 ui.write(("unpruned common: %s\n") %
2089 ui.write(("unpruned common: %s\n") %
2090 " ".join(sorted(short(n) for n in common)))
2090 " ".join(sorted(short(n) for n in common)))
2091 dag = dagutil.revlogdag(repo.changelog)
2091 dag = dagutil.revlogdag(repo.changelog)
2092 all = dag.ancestorset(dag.internalizeall(common))
2092 all = dag.ancestorset(dag.internalizeall(common))
2093 common = dag.externalizeall(dag.headsetofconnecteds(all))
2093 common = dag.externalizeall(dag.headsetofconnecteds(all))
2094 else:
2094 else:
2095 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
2095 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
2096 common = set(common)
2096 common = set(common)
2097 rheads = set(hds)
2097 rheads = set(hds)
2098 lheads = set(repo.heads())
2098 lheads = set(repo.heads())
2099 ui.write(("common heads: %s\n") %
2099 ui.write(("common heads: %s\n") %
2100 " ".join(sorted(short(n) for n in common)))
2100 " ".join(sorted(short(n) for n in common)))
2101 if lheads <= common:
2101 if lheads <= common:
2102 ui.write(("local is subset\n"))
2102 ui.write(("local is subset\n"))
2103 elif rheads <= common:
2103 elif rheads <= common:
2104 ui.write(("remote is subset\n"))
2104 ui.write(("remote is subset\n"))
2105
2105
2106 serverlogs = opts.get('serverlog')
2106 serverlogs = opts.get('serverlog')
2107 if serverlogs:
2107 if serverlogs:
2108 for filename in serverlogs:
2108 for filename in serverlogs:
2109 logfile = open(filename, 'r')
2109 logfile = open(filename, 'r')
2110 try:
2110 try:
2111 line = logfile.readline()
2111 line = logfile.readline()
2112 while line:
2112 while line:
2113 parts = line.strip().split(';')
2113 parts = line.strip().split(';')
2114 op = parts[1]
2114 op = parts[1]
2115 if op == 'cg':
2115 if op == 'cg':
2116 pass
2116 pass
2117 elif op == 'cgss':
2117 elif op == 'cgss':
2118 doit(parts[2].split(' '), parts[3].split(' '))
2118 doit(parts[2].split(' '), parts[3].split(' '))
2119 elif op == 'unb':
2119 elif op == 'unb':
2120 doit(parts[3].split(' '), parts[2].split(' '))
2120 doit(parts[3].split(' '), parts[2].split(' '))
2121 line = logfile.readline()
2121 line = logfile.readline()
2122 finally:
2122 finally:
2123 logfile.close()
2123 logfile.close()
2124
2124
2125 else:
2125 else:
2126 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
2126 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
2127 opts.get('remote_head'))
2127 opts.get('remote_head'))
2128 localrevs = opts.get('local_head')
2128 localrevs = opts.get('local_head')
2129 doit(localrevs, remoterevs)
2129 doit(localrevs, remoterevs)
2130
2130
2131 @command('debugfileset',
2131 @command('debugfileset',
2132 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
2132 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
2133 _('[-r REV] FILESPEC'))
2133 _('[-r REV] FILESPEC'))
2134 def debugfileset(ui, repo, expr, **opts):
2134 def debugfileset(ui, repo, expr, **opts):
2135 '''parse and apply a fileset specification'''
2135 '''parse and apply a fileset specification'''
2136 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
2136 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
2137 if ui.verbose:
2137 if ui.verbose:
2138 tree = fileset.parse(expr)[0]
2138 tree = fileset.parse(expr)[0]
2139 ui.note(tree, "\n")
2139 ui.note(tree, "\n")
2140
2140
2141 for f in ctx.getfileset(expr):
2141 for f in ctx.getfileset(expr):
2142 ui.write("%s\n" % f)
2142 ui.write("%s\n" % f)
2143
2143
2144 @command('debugfsinfo', [], _('[PATH]'), norepo=True)
2144 @command('debugfsinfo', [], _('[PATH]'), norepo=True)
2145 def debugfsinfo(ui, path="."):
2145 def debugfsinfo(ui, path="."):
2146 """show information detected about current filesystem"""
2146 """show information detected about current filesystem"""
2147 util.writefile('.debugfsinfo', '')
2147 util.writefile('.debugfsinfo', '')
2148 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
2148 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
2149 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
2149 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
2150 ui.write(('hardlink: %s\n') % (util.checknlink(path) and 'yes' or 'no'))
2150 ui.write(('hardlink: %s\n') % (util.checknlink(path) and 'yes' or 'no'))
2151 ui.write(('case-sensitive: %s\n') % (util.checkcase('.debugfsinfo')
2151 ui.write(('case-sensitive: %s\n') % (util.checkcase('.debugfsinfo')
2152 and 'yes' or 'no'))
2152 and 'yes' or 'no'))
2153 os.unlink('.debugfsinfo')
2153 os.unlink('.debugfsinfo')
2154
2154
2155 @command('debuggetbundle',
2155 @command('debuggetbundle',
2156 [('H', 'head', [], _('id of head node'), _('ID')),
2156 [('H', 'head', [], _('id of head node'), _('ID')),
2157 ('C', 'common', [], _('id of common node'), _('ID')),
2157 ('C', 'common', [], _('id of common node'), _('ID')),
2158 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
2158 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
2159 _('REPO FILE [-H|-C ID]...'),
2159 _('REPO FILE [-H|-C ID]...'),
2160 norepo=True)
2160 norepo=True)
2161 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
2161 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
2162 """retrieves a bundle from a repo
2162 """retrieves a bundle from a repo
2163
2163
2164 Every ID must be a full-length hex node id string. Saves the bundle to the
2164 Every ID must be a full-length hex node id string. Saves the bundle to the
2165 given file.
2165 given file.
2166 """
2166 """
2167 repo = hg.peer(ui, opts, repopath)
2167 repo = hg.peer(ui, opts, repopath)
2168 if not repo.capable('getbundle'):
2168 if not repo.capable('getbundle'):
2169 raise util.Abort("getbundle() not supported by target repository")
2169 raise util.Abort("getbundle() not supported by target repository")
2170 args = {}
2170 args = {}
2171 if common:
2171 if common:
2172 args['common'] = [bin(s) for s in common]
2172 args['common'] = [bin(s) for s in common]
2173 if head:
2173 if head:
2174 args['heads'] = [bin(s) for s in head]
2174 args['heads'] = [bin(s) for s in head]
2175 # TODO: get desired bundlecaps from command line.
2175 # TODO: get desired bundlecaps from command line.
2176 args['bundlecaps'] = None
2176 args['bundlecaps'] = None
2177 bundle = repo.getbundle('debug', **args)
2177 bundle = repo.getbundle('debug', **args)
2178
2178
2179 bundletype = opts.get('type', 'bzip2').lower()
2179 bundletype = opts.get('type', 'bzip2').lower()
2180 btypes = {'none': 'HG10UN',
2180 btypes = {'none': 'HG10UN',
2181 'bzip2': 'HG10BZ',
2181 'bzip2': 'HG10BZ',
2182 'gzip': 'HG10GZ',
2182 'gzip': 'HG10GZ',
2183 'bundle2': 'HG2Y'}
2183 'bundle2': 'HG2Y'}
2184 bundletype = btypes.get(bundletype)
2184 bundletype = btypes.get(bundletype)
2185 if bundletype not in changegroup.bundletypes:
2185 if bundletype not in changegroup.bundletypes:
2186 raise util.Abort(_('unknown bundle type specified with --type'))
2186 raise util.Abort(_('unknown bundle type specified with --type'))
2187 changegroup.writebundle(ui, bundle, bundlepath, bundletype)
2187 changegroup.writebundle(ui, bundle, bundlepath, bundletype)
2188
2188
2189 @command('debugignore', [], '')
2189 @command('debugignore', [], '')
2190 def debugignore(ui, repo, *values, **opts):
2190 def debugignore(ui, repo, *values, **opts):
2191 """display the combined ignore pattern"""
2191 """display the combined ignore pattern"""
2192 ignore = repo.dirstate._ignore
2192 ignore = repo.dirstate._ignore
2193 includepat = getattr(ignore, 'includepat', None)
2193 includepat = getattr(ignore, 'includepat', None)
2194 if includepat is not None:
2194 if includepat is not None:
2195 ui.write("%s\n" % includepat)
2195 ui.write("%s\n" % includepat)
2196 else:
2196 else:
2197 raise util.Abort(_("no ignore patterns found"))
2197 raise util.Abort(_("no ignore patterns found"))
2198
2198
2199 @command('debugindex',
2199 @command('debugindex',
2200 [('c', 'changelog', False, _('open changelog')),
2200 [('c', 'changelog', False, _('open changelog')),
2201 ('m', 'manifest', False, _('open manifest')),
2201 ('m', 'manifest', False, _('open manifest')),
2202 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
2202 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
2203 _('[-f FORMAT] -c|-m|FILE'),
2203 _('[-f FORMAT] -c|-m|FILE'),
2204 optionalrepo=True)
2204 optionalrepo=True)
2205 def debugindex(ui, repo, file_=None, **opts):
2205 def debugindex(ui, repo, file_=None, **opts):
2206 """dump the contents of an index file"""
2206 """dump the contents of an index file"""
2207 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
2207 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
2208 format = opts.get('format', 0)
2208 format = opts.get('format', 0)
2209 if format not in (0, 1):
2209 if format not in (0, 1):
2210 raise util.Abort(_("unknown format %d") % format)
2210 raise util.Abort(_("unknown format %d") % format)
2211
2211
2212 generaldelta = r.version & revlog.REVLOGGENERALDELTA
2212 generaldelta = r.version & revlog.REVLOGGENERALDELTA
2213 if generaldelta:
2213 if generaldelta:
2214 basehdr = ' delta'
2214 basehdr = ' delta'
2215 else:
2215 else:
2216 basehdr = ' base'
2216 basehdr = ' base'
2217
2217
2218 if ui.debugflag:
2218 if ui.debugflag:
2219 shortfn = hex
2219 shortfn = hex
2220 else:
2220 else:
2221 shortfn = short
2221 shortfn = short
2222
2222
2223 # There might not be anything in r, so have a sane default
2223 # There might not be anything in r, so have a sane default
2224 idlen = 12
2224 idlen = 12
2225 for i in r:
2225 for i in r:
2226 idlen = len(shortfn(r.node(i)))
2226 idlen = len(shortfn(r.node(i)))
2227 break
2227 break
2228
2228
2229 if format == 0:
2229 if format == 0:
2230 ui.write(" rev offset length " + basehdr + " linkrev"
2230 ui.write(" rev offset length " + basehdr + " linkrev"
2231 " %s %s p2\n" % ("nodeid".ljust(idlen), "p1".ljust(idlen)))
2231 " %s %s p2\n" % ("nodeid".ljust(idlen), "p1".ljust(idlen)))
2232 elif format == 1:
2232 elif format == 1:
2233 ui.write(" rev flag offset length"
2233 ui.write(" rev flag offset length"
2234 " size " + basehdr + " link p1 p2"
2234 " size " + basehdr + " link p1 p2"
2235 " %s\n" % "nodeid".rjust(idlen))
2235 " %s\n" % "nodeid".rjust(idlen))
2236
2236
2237 for i in r:
2237 for i in r:
2238 node = r.node(i)
2238 node = r.node(i)
2239 if generaldelta:
2239 if generaldelta:
2240 base = r.deltaparent(i)
2240 base = r.deltaparent(i)
2241 else:
2241 else:
2242 base = r.chainbase(i)
2242 base = r.chainbase(i)
2243 if format == 0:
2243 if format == 0:
2244 try:
2244 try:
2245 pp = r.parents(node)
2245 pp = r.parents(node)
2246 except Exception:
2246 except Exception:
2247 pp = [nullid, nullid]
2247 pp = [nullid, nullid]
2248 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
2248 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
2249 i, r.start(i), r.length(i), base, r.linkrev(i),
2249 i, r.start(i), r.length(i), base, r.linkrev(i),
2250 shortfn(node), shortfn(pp[0]), shortfn(pp[1])))
2250 shortfn(node), shortfn(pp[0]), shortfn(pp[1])))
2251 elif format == 1:
2251 elif format == 1:
2252 pr = r.parentrevs(i)
2252 pr = r.parentrevs(i)
2253 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
2253 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
2254 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
2254 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
2255 base, r.linkrev(i), pr[0], pr[1], shortfn(node)))
2255 base, r.linkrev(i), pr[0], pr[1], shortfn(node)))
2256
2256
2257 @command('debugindexdot', [], _('FILE'), optionalrepo=True)
2257 @command('debugindexdot', [], _('FILE'), optionalrepo=True)
2258 def debugindexdot(ui, repo, file_):
2258 def debugindexdot(ui, repo, file_):
2259 """dump an index DAG as a graphviz dot file"""
2259 """dump an index DAG as a graphviz dot file"""
2260 r = None
2260 r = None
2261 if repo:
2261 if repo:
2262 filelog = repo.file(file_)
2262 filelog = repo.file(file_)
2263 if len(filelog):
2263 if len(filelog):
2264 r = filelog
2264 r = filelog
2265 if not r:
2265 if not r:
2266 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
2266 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
2267 ui.write(("digraph G {\n"))
2267 ui.write(("digraph G {\n"))
2268 for i in r:
2268 for i in r:
2269 node = r.node(i)
2269 node = r.node(i)
2270 pp = r.parents(node)
2270 pp = r.parents(node)
2271 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
2271 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
2272 if pp[1] != nullid:
2272 if pp[1] != nullid:
2273 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
2273 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
2274 ui.write("}\n")
2274 ui.write("}\n")
2275
2275
2276 @command('debuginstall', [], '', norepo=True)
2276 @command('debuginstall', [], '', norepo=True)
2277 def debuginstall(ui):
2277 def debuginstall(ui):
2278 '''test Mercurial installation
2278 '''test Mercurial installation
2279
2279
2280 Returns 0 on success.
2280 Returns 0 on success.
2281 '''
2281 '''
2282
2282
2283 def writetemp(contents):
2283 def writetemp(contents):
2284 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
2284 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
2285 f = os.fdopen(fd, "wb")
2285 f = os.fdopen(fd, "wb")
2286 f.write(contents)
2286 f.write(contents)
2287 f.close()
2287 f.close()
2288 return name
2288 return name
2289
2289
2290 problems = 0
2290 problems = 0
2291
2291
2292 # encoding
2292 # encoding
2293 ui.status(_("checking encoding (%s)...\n") % encoding.encoding)
2293 ui.status(_("checking encoding (%s)...\n") % encoding.encoding)
2294 try:
2294 try:
2295 encoding.fromlocal("test")
2295 encoding.fromlocal("test")
2296 except util.Abort, inst:
2296 except util.Abort, inst:
2297 ui.write(" %s\n" % inst)
2297 ui.write(" %s\n" % inst)
2298 ui.write(_(" (check that your locale is properly set)\n"))
2298 ui.write(_(" (check that your locale is properly set)\n"))
2299 problems += 1
2299 problems += 1
2300
2300
2301 # Python
2301 # Python
2302 ui.status(_("checking Python executable (%s)\n") % sys.executable)
2302 ui.status(_("checking Python executable (%s)\n") % sys.executable)
2303 ui.status(_("checking Python version (%s)\n")
2303 ui.status(_("checking Python version (%s)\n")
2304 % ("%s.%s.%s" % sys.version_info[:3]))
2304 % ("%s.%s.%s" % sys.version_info[:3]))
2305 ui.status(_("checking Python lib (%s)...\n")
2305 ui.status(_("checking Python lib (%s)...\n")
2306 % os.path.dirname(os.__file__))
2306 % os.path.dirname(os.__file__))
2307
2307
2308 # compiled modules
2308 # compiled modules
2309 ui.status(_("checking installed modules (%s)...\n")
2309 ui.status(_("checking installed modules (%s)...\n")
2310 % os.path.dirname(__file__))
2310 % os.path.dirname(__file__))
2311 try:
2311 try:
2312 import bdiff, mpatch, base85, osutil
2312 import bdiff, mpatch, base85, osutil
2313 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
2313 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
2314 except Exception, inst:
2314 except Exception, inst:
2315 ui.write(" %s\n" % inst)
2315 ui.write(" %s\n" % inst)
2316 ui.write(_(" One or more extensions could not be found"))
2316 ui.write(_(" One or more extensions could not be found"))
2317 ui.write(_(" (check that you compiled the extensions)\n"))
2317 ui.write(_(" (check that you compiled the extensions)\n"))
2318 problems += 1
2318 problems += 1
2319
2319
2320 # templates
2320 # templates
2321 import templater
2321 import templater
2322 p = templater.templatepaths()
2322 p = templater.templatepaths()
2323 ui.status(_("checking templates (%s)...\n") % ' '.join(p))
2323 ui.status(_("checking templates (%s)...\n") % ' '.join(p))
2324 if p:
2324 if p:
2325 m = templater.templatepath("map-cmdline.default")
2325 m = templater.templatepath("map-cmdline.default")
2326 if m:
2326 if m:
2327 # template found, check if it is working
2327 # template found, check if it is working
2328 try:
2328 try:
2329 templater.templater(m)
2329 templater.templater(m)
2330 except Exception, inst:
2330 except Exception, inst:
2331 ui.write(" %s\n" % inst)
2331 ui.write(" %s\n" % inst)
2332 p = None
2332 p = None
2333 else:
2333 else:
2334 ui.write(_(" template 'default' not found\n"))
2334 ui.write(_(" template 'default' not found\n"))
2335 p = None
2335 p = None
2336 else:
2336 else:
2337 ui.write(_(" no template directories found\n"))
2337 ui.write(_(" no template directories found\n"))
2338 if not p:
2338 if not p:
2339 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
2339 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
2340 problems += 1
2340 problems += 1
2341
2341
2342 # editor
2342 # editor
2343 ui.status(_("checking commit editor...\n"))
2343 ui.status(_("checking commit editor...\n"))
2344 editor = ui.geteditor()
2344 editor = ui.geteditor()
2345 cmdpath = util.findexe(shlex.split(editor)[0])
2345 cmdpath = util.findexe(shlex.split(editor)[0])
2346 if not cmdpath:
2346 if not cmdpath:
2347 if editor == 'vi':
2347 if editor == 'vi':
2348 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
2348 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
2349 ui.write(_(" (specify a commit editor in your configuration"
2349 ui.write(_(" (specify a commit editor in your configuration"
2350 " file)\n"))
2350 " file)\n"))
2351 else:
2351 else:
2352 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
2352 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
2353 ui.write(_(" (specify a commit editor in your configuration"
2353 ui.write(_(" (specify a commit editor in your configuration"
2354 " file)\n"))
2354 " file)\n"))
2355 problems += 1
2355 problems += 1
2356
2356
2357 # check username
2357 # check username
2358 ui.status(_("checking username...\n"))
2358 ui.status(_("checking username...\n"))
2359 try:
2359 try:
2360 ui.username()
2360 ui.username()
2361 except util.Abort, e:
2361 except util.Abort, e:
2362 ui.write(" %s\n" % e)
2362 ui.write(" %s\n" % e)
2363 ui.write(_(" (specify a username in your configuration file)\n"))
2363 ui.write(_(" (specify a username in your configuration file)\n"))
2364 problems += 1
2364 problems += 1
2365
2365
2366 if not problems:
2366 if not problems:
2367 ui.status(_("no problems detected\n"))
2367 ui.status(_("no problems detected\n"))
2368 else:
2368 else:
2369 ui.write(_("%s problems detected,"
2369 ui.write(_("%s problems detected,"
2370 " please check your install!\n") % problems)
2370 " please check your install!\n") % problems)
2371
2371
2372 return problems
2372 return problems
2373
2373
2374 @command('debugknown', [], _('REPO ID...'), norepo=True)
2374 @command('debugknown', [], _('REPO ID...'), norepo=True)
2375 def debugknown(ui, repopath, *ids, **opts):
2375 def debugknown(ui, repopath, *ids, **opts):
2376 """test whether node ids are known to a repo
2376 """test whether node ids are known to a repo
2377
2377
2378 Every ID must be a full-length hex node id string. Returns a list of 0s
2378 Every ID must be a full-length hex node id string. Returns a list of 0s
2379 and 1s indicating unknown/known.
2379 and 1s indicating unknown/known.
2380 """
2380 """
2381 repo = hg.peer(ui, opts, repopath)
2381 repo = hg.peer(ui, opts, repopath)
2382 if not repo.capable('known'):
2382 if not repo.capable('known'):
2383 raise util.Abort("known() not supported by target repository")
2383 raise util.Abort("known() not supported by target repository")
2384 flags = repo.known([bin(s) for s in ids])
2384 flags = repo.known([bin(s) for s in ids])
2385 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
2385 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
2386
2386
2387 @command('debuglabelcomplete', [], _('LABEL...'))
2387 @command('debuglabelcomplete', [], _('LABEL...'))
2388 def debuglabelcomplete(ui, repo, *args):
2388 def debuglabelcomplete(ui, repo, *args):
2389 '''backwards compatibility with old bash completion scripts (DEPRECATED)'''
2389 '''backwards compatibility with old bash completion scripts (DEPRECATED)'''
2390 debugnamecomplete(ui, repo, *args)
2390 debugnamecomplete(ui, repo, *args)
2391
2391
2392 @command('debugnamecomplete', [], _('NAME...'))
2392 @command('debugnamecomplete', [], _('NAME...'))
2393 def debugnamecomplete(ui, repo, *args):
2393 def debugnamecomplete(ui, repo, *args):
2394 '''complete "names" - tags, open branch names, bookmark names'''
2394 '''complete "names" - tags, open branch names, bookmark names'''
2395
2395
2396 names = set()
2396 names = set()
2397 # since we previously only listed open branches, we will handle that
2397 # since we previously only listed open branches, we will handle that
2398 # specially (after this for loop)
2398 # specially (after this for loop)
2399 for name, ns in repo.names.iteritems():
2399 for name, ns in repo.names.iteritems():
2400 if name != 'branches':
2400 if name != 'branches':
2401 names.update(ns.listnames(repo))
2401 names.update(ns.listnames(repo))
2402 names.update(tag for (tag, heads, tip, closed)
2402 names.update(tag for (tag, heads, tip, closed)
2403 in repo.branchmap().iterbranches() if not closed)
2403 in repo.branchmap().iterbranches() if not closed)
2404 completions = set()
2404 completions = set()
2405 if not args:
2405 if not args:
2406 args = ['']
2406 args = ['']
2407 for a in args:
2407 for a in args:
2408 completions.update(n for n in names if n.startswith(a))
2408 completions.update(n for n in names if n.startswith(a))
2409 ui.write('\n'.join(sorted(completions)))
2409 ui.write('\n'.join(sorted(completions)))
2410 ui.write('\n')
2410 ui.write('\n')
2411
2411
2412 @command('debuglocks',
2412 @command('debuglocks',
2413 [('L', 'force-lock', None, _('free the store lock (DANGEROUS)')),
2413 [('L', 'force-lock', None, _('free the store lock (DANGEROUS)')),
2414 ('W', 'force-wlock', None,
2414 ('W', 'force-wlock', None,
2415 _('free the working state lock (DANGEROUS)'))],
2415 _('free the working state lock (DANGEROUS)'))],
2416 _('[OPTION]...'))
2416 _('[OPTION]...'))
2417 def debuglocks(ui, repo, **opts):
2417 def debuglocks(ui, repo, **opts):
2418 """show or modify state of locks
2418 """show or modify state of locks
2419
2419
2420 By default, this command will show which locks are held. This
2420 By default, this command will show which locks are held. This
2421 includes the user and process holding the lock, the amount of time
2421 includes the user and process holding the lock, the amount of time
2422 the lock has been held, and the machine name where the process is
2422 the lock has been held, and the machine name where the process is
2423 running if it's not local.
2423 running if it's not local.
2424
2424
2425 Locks protect the integrity of Mercurial's data, so should be
2425 Locks protect the integrity of Mercurial's data, so should be
2426 treated with care. System crashes or other interruptions may cause
2426 treated with care. System crashes or other interruptions may cause
2427 locks to not be properly released, though Mercurial will usually
2427 locks to not be properly released, though Mercurial will usually
2428 detect and remove such stale locks automatically.
2428 detect and remove such stale locks automatically.
2429
2429
2430 However, detecting stale locks may not always be possible (for
2430 However, detecting stale locks may not always be possible (for
2431 instance, on a shared filesystem). Removing locks may also be
2431 instance, on a shared filesystem). Removing locks may also be
2432 blocked by filesystem permissions.
2432 blocked by filesystem permissions.
2433
2433
2434 Returns 0 if no locks are held.
2434 Returns 0 if no locks are held.
2435
2435
2436 """
2436 """
2437
2437
2438 if opts.get('force_lock'):
2438 if opts.get('force_lock'):
2439 repo.svfs.unlink('lock')
2439 repo.svfs.unlink('lock')
2440 if opts.get('force_wlock'):
2440 if opts.get('force_wlock'):
2441 repo.vfs.unlink('wlock')
2441 repo.vfs.unlink('wlock')
2442 if opts.get('force_lock') or opts.get('force_lock'):
2442 if opts.get('force_lock') or opts.get('force_lock'):
2443 return 0
2443 return 0
2444
2444
2445 now = time.time()
2445 now = time.time()
2446 held = 0
2446 held = 0
2447
2447
2448 def report(vfs, name, method):
2448 def report(vfs, name, method):
2449 # this causes stale locks to get reaped for more accurate reporting
2449 # this causes stale locks to get reaped for more accurate reporting
2450 try:
2450 try:
2451 l = method(False)
2451 l = method(False)
2452 except error.LockHeld:
2452 except error.LockHeld:
2453 l = None
2453 l = None
2454
2454
2455 if l:
2455 if l:
2456 l.release()
2456 l.release()
2457 else:
2457 else:
2458 try:
2458 try:
2459 stat = repo.svfs.lstat(name)
2459 stat = repo.svfs.lstat(name)
2460 age = now - stat.st_mtime
2460 age = now - stat.st_mtime
2461 user = util.username(stat.st_uid)
2461 user = util.username(stat.st_uid)
2462 locker = vfs.readlock(name)
2462 locker = vfs.readlock(name)
2463 if ":" in locker:
2463 if ":" in locker:
2464 host, pid = locker.split(':')
2464 host, pid = locker.split(':')
2465 if host == socket.gethostname():
2465 if host == socket.gethostname():
2466 locker = 'user %s, process %s' % (user, pid)
2466 locker = 'user %s, process %s' % (user, pid)
2467 else:
2467 else:
2468 locker = 'user %s, process %s, host %s' \
2468 locker = 'user %s, process %s, host %s' \
2469 % (user, pid, host)
2469 % (user, pid, host)
2470 ui.write("%-6s %s (%ds)\n" % (name + ":", locker, age))
2470 ui.write("%-6s %s (%ds)\n" % (name + ":", locker, age))
2471 return 1
2471 return 1
2472 except OSError, e:
2472 except OSError, e:
2473 if e.errno != errno.ENOENT:
2473 if e.errno != errno.ENOENT:
2474 raise
2474 raise
2475
2475
2476 ui.write("%-6s free\n" % (name + ":"))
2476 ui.write("%-6s free\n" % (name + ":"))
2477 return 0
2477 return 0
2478
2478
2479 held += report(repo.svfs, "lock", repo.lock)
2479 held += report(repo.svfs, "lock", repo.lock)
2480 held += report(repo.vfs, "wlock", repo.wlock)
2480 held += report(repo.vfs, "wlock", repo.wlock)
2481
2481
2482 return held
2482 return held
2483
2483
2484 @command('debugobsolete',
2484 @command('debugobsolete',
2485 [('', 'flags', 0, _('markers flag')),
2485 [('', 'flags', 0, _('markers flag')),
2486 ('', 'record-parents', False,
2486 ('', 'record-parents', False,
2487 _('record parent information for the precursor')),
2487 _('record parent information for the precursor')),
2488 ('r', 'rev', [], _('display markers relevant to REV')),
2488 ('r', 'rev', [], _('display markers relevant to REV')),
2489 ] + commitopts2,
2489 ] + commitopts2,
2490 _('[OBSOLETED [REPLACEMENT] [REPL... ]'))
2490 _('[OBSOLETED [REPLACEMENT] [REPL... ]'))
2491 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
2491 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
2492 """create arbitrary obsolete marker
2492 """create arbitrary obsolete marker
2493
2493
2494 With no arguments, displays the list of obsolescence markers."""
2494 With no arguments, displays the list of obsolescence markers."""
2495
2495
2496 def parsenodeid(s):
2496 def parsenodeid(s):
2497 try:
2497 try:
2498 # We do not use revsingle/revrange functions here to accept
2498 # We do not use revsingle/revrange functions here to accept
2499 # arbitrary node identifiers, possibly not present in the
2499 # arbitrary node identifiers, possibly not present in the
2500 # local repository.
2500 # local repository.
2501 n = bin(s)
2501 n = bin(s)
2502 if len(n) != len(nullid):
2502 if len(n) != len(nullid):
2503 raise TypeError()
2503 raise TypeError()
2504 return n
2504 return n
2505 except TypeError:
2505 except TypeError:
2506 raise util.Abort('changeset references must be full hexadecimal '
2506 raise util.Abort('changeset references must be full hexadecimal '
2507 'node identifiers')
2507 'node identifiers')
2508
2508
2509 if precursor is not None:
2509 if precursor is not None:
2510 if opts['rev']:
2510 if opts['rev']:
2511 raise util.Abort('cannot select revision when creating marker')
2511 raise util.Abort('cannot select revision when creating marker')
2512 metadata = {}
2512 metadata = {}
2513 metadata['user'] = opts['user'] or ui.username()
2513 metadata['user'] = opts['user'] or ui.username()
2514 succs = tuple(parsenodeid(succ) for succ in successors)
2514 succs = tuple(parsenodeid(succ) for succ in successors)
2515 l = repo.lock()
2515 l = repo.lock()
2516 try:
2516 try:
2517 tr = repo.transaction('debugobsolete')
2517 tr = repo.transaction('debugobsolete')
2518 try:
2518 try:
2519 try:
2519 try:
2520 date = opts.get('date')
2520 date = opts.get('date')
2521 if date:
2521 if date:
2522 date = util.parsedate(date)
2522 date = util.parsedate(date)
2523 else:
2523 else:
2524 date = None
2524 date = None
2525 prec = parsenodeid(precursor)
2525 prec = parsenodeid(precursor)
2526 parents = None
2526 parents = None
2527 if opts['record_parents']:
2527 if opts['record_parents']:
2528 if prec not in repo.unfiltered():
2528 if prec not in repo.unfiltered():
2529 raise util.Abort('cannot used --record-parents on '
2529 raise util.Abort('cannot used --record-parents on '
2530 'unknown changesets')
2530 'unknown changesets')
2531 parents = repo.unfiltered()[prec].parents()
2531 parents = repo.unfiltered()[prec].parents()
2532 parents = tuple(p.node() for p in parents)
2532 parents = tuple(p.node() for p in parents)
2533 repo.obsstore.create(tr, prec, succs, opts['flags'],
2533 repo.obsstore.create(tr, prec, succs, opts['flags'],
2534 parents=parents, date=date,
2534 parents=parents, date=date,
2535 metadata=metadata)
2535 metadata=metadata)
2536 tr.close()
2536 tr.close()
2537 except ValueError, exc:
2537 except ValueError, exc:
2538 raise util.Abort(_('bad obsmarker input: %s') % exc)
2538 raise util.Abort(_('bad obsmarker input: %s') % exc)
2539 finally:
2539 finally:
2540 tr.release()
2540 tr.release()
2541 finally:
2541 finally:
2542 l.release()
2542 l.release()
2543 else:
2543 else:
2544 if opts['rev']:
2544 if opts['rev']:
2545 revs = scmutil.revrange(repo, opts['rev'])
2545 revs = scmutil.revrange(repo, opts['rev'])
2546 nodes = [repo[r].node() for r in revs]
2546 nodes = [repo[r].node() for r in revs]
2547 markers = list(obsolete.getmarkers(repo, nodes=nodes))
2547 markers = list(obsolete.getmarkers(repo, nodes=nodes))
2548 markers.sort(key=lambda x: x._data)
2548 markers.sort(key=lambda x: x._data)
2549 else:
2549 else:
2550 markers = obsolete.getmarkers(repo)
2550 markers = obsolete.getmarkers(repo)
2551
2551
2552 for m in markers:
2552 for m in markers:
2553 cmdutil.showmarker(ui, m)
2553 cmdutil.showmarker(ui, m)
2554
2554
2555 @command('debugpathcomplete',
2555 @command('debugpathcomplete',
2556 [('f', 'full', None, _('complete an entire path')),
2556 [('f', 'full', None, _('complete an entire path')),
2557 ('n', 'normal', None, _('show only normal files')),
2557 ('n', 'normal', None, _('show only normal files')),
2558 ('a', 'added', None, _('show only added files')),
2558 ('a', 'added', None, _('show only added files')),
2559 ('r', 'removed', None, _('show only removed files'))],
2559 ('r', 'removed', None, _('show only removed files'))],
2560 _('FILESPEC...'))
2560 _('FILESPEC...'))
2561 def debugpathcomplete(ui, repo, *specs, **opts):
2561 def debugpathcomplete(ui, repo, *specs, **opts):
2562 '''complete part or all of a tracked path
2562 '''complete part or all of a tracked path
2563
2563
2564 This command supports shells that offer path name completion. It
2564 This command supports shells that offer path name completion. It
2565 currently completes only files already known to the dirstate.
2565 currently completes only files already known to the dirstate.
2566
2566
2567 Completion extends only to the next path segment unless
2567 Completion extends only to the next path segment unless
2568 --full is specified, in which case entire paths are used.'''
2568 --full is specified, in which case entire paths are used.'''
2569
2569
2570 def complete(path, acceptable):
2570 def complete(path, acceptable):
2571 dirstate = repo.dirstate
2571 dirstate = repo.dirstate
2572 spec = os.path.normpath(os.path.join(os.getcwd(), path))
2572 spec = os.path.normpath(os.path.join(os.getcwd(), path))
2573 rootdir = repo.root + os.sep
2573 rootdir = repo.root + os.sep
2574 if spec != repo.root and not spec.startswith(rootdir):
2574 if spec != repo.root and not spec.startswith(rootdir):
2575 return [], []
2575 return [], []
2576 if os.path.isdir(spec):
2576 if os.path.isdir(spec):
2577 spec += '/'
2577 spec += '/'
2578 spec = spec[len(rootdir):]
2578 spec = spec[len(rootdir):]
2579 fixpaths = os.sep != '/'
2579 fixpaths = os.sep != '/'
2580 if fixpaths:
2580 if fixpaths:
2581 spec = spec.replace(os.sep, '/')
2581 spec = spec.replace(os.sep, '/')
2582 speclen = len(spec)
2582 speclen = len(spec)
2583 fullpaths = opts['full']
2583 fullpaths = opts['full']
2584 files, dirs = set(), set()
2584 files, dirs = set(), set()
2585 adddir, addfile = dirs.add, files.add
2585 adddir, addfile = dirs.add, files.add
2586 for f, st in dirstate.iteritems():
2586 for f, st in dirstate.iteritems():
2587 if f.startswith(spec) and st[0] in acceptable:
2587 if f.startswith(spec) and st[0] in acceptable:
2588 if fixpaths:
2588 if fixpaths:
2589 f = f.replace('/', os.sep)
2589 f = f.replace('/', os.sep)
2590 if fullpaths:
2590 if fullpaths:
2591 addfile(f)
2591 addfile(f)
2592 continue
2592 continue
2593 s = f.find(os.sep, speclen)
2593 s = f.find(os.sep, speclen)
2594 if s >= 0:
2594 if s >= 0:
2595 adddir(f[:s])
2595 adddir(f[:s])
2596 else:
2596 else:
2597 addfile(f)
2597 addfile(f)
2598 return files, dirs
2598 return files, dirs
2599
2599
2600 acceptable = ''
2600 acceptable = ''
2601 if opts['normal']:
2601 if opts['normal']:
2602 acceptable += 'nm'
2602 acceptable += 'nm'
2603 if opts['added']:
2603 if opts['added']:
2604 acceptable += 'a'
2604 acceptable += 'a'
2605 if opts['removed']:
2605 if opts['removed']:
2606 acceptable += 'r'
2606 acceptable += 'r'
2607 cwd = repo.getcwd()
2607 cwd = repo.getcwd()
2608 if not specs:
2608 if not specs:
2609 specs = ['.']
2609 specs = ['.']
2610
2610
2611 files, dirs = set(), set()
2611 files, dirs = set(), set()
2612 for spec in specs:
2612 for spec in specs:
2613 f, d = complete(spec, acceptable or 'nmar')
2613 f, d = complete(spec, acceptable or 'nmar')
2614 files.update(f)
2614 files.update(f)
2615 dirs.update(d)
2615 dirs.update(d)
2616 files.update(dirs)
2616 files.update(dirs)
2617 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
2617 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
2618 ui.write('\n')
2618 ui.write('\n')
2619
2619
2620 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
2620 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
2621 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
2621 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
2622 '''access the pushkey key/value protocol
2622 '''access the pushkey key/value protocol
2623
2623
2624 With two args, list the keys in the given namespace.
2624 With two args, list the keys in the given namespace.
2625
2625
2626 With five args, set a key to new if it currently is set to old.
2626 With five args, set a key to new if it currently is set to old.
2627 Reports success or failure.
2627 Reports success or failure.
2628 '''
2628 '''
2629
2629
2630 target = hg.peer(ui, {}, repopath)
2630 target = hg.peer(ui, {}, repopath)
2631 if keyinfo:
2631 if keyinfo:
2632 key, old, new = keyinfo
2632 key, old, new = keyinfo
2633 r = target.pushkey(namespace, key, old, new)
2633 r = target.pushkey(namespace, key, old, new)
2634 ui.status(str(r) + '\n')
2634 ui.status(str(r) + '\n')
2635 return not r
2635 return not r
2636 else:
2636 else:
2637 for k, v in sorted(target.listkeys(namespace).iteritems()):
2637 for k, v in sorted(target.listkeys(namespace).iteritems()):
2638 ui.write("%s\t%s\n" % (k.encode('string-escape'),
2638 ui.write("%s\t%s\n" % (k.encode('string-escape'),
2639 v.encode('string-escape')))
2639 v.encode('string-escape')))
2640
2640
2641 @command('debugpvec', [], _('A B'))
2641 @command('debugpvec', [], _('A B'))
2642 def debugpvec(ui, repo, a, b=None):
2642 def debugpvec(ui, repo, a, b=None):
2643 ca = scmutil.revsingle(repo, a)
2643 ca = scmutil.revsingle(repo, a)
2644 cb = scmutil.revsingle(repo, b)
2644 cb = scmutil.revsingle(repo, b)
2645 pa = pvec.ctxpvec(ca)
2645 pa = pvec.ctxpvec(ca)
2646 pb = pvec.ctxpvec(cb)
2646 pb = pvec.ctxpvec(cb)
2647 if pa == pb:
2647 if pa == pb:
2648 rel = "="
2648 rel = "="
2649 elif pa > pb:
2649 elif pa > pb:
2650 rel = ">"
2650 rel = ">"
2651 elif pa < pb:
2651 elif pa < pb:
2652 rel = "<"
2652 rel = "<"
2653 elif pa | pb:
2653 elif pa | pb:
2654 rel = "|"
2654 rel = "|"
2655 ui.write(_("a: %s\n") % pa)
2655 ui.write(_("a: %s\n") % pa)
2656 ui.write(_("b: %s\n") % pb)
2656 ui.write(_("b: %s\n") % pb)
2657 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
2657 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
2658 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
2658 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
2659 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
2659 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
2660 pa.distance(pb), rel))
2660 pa.distance(pb), rel))
2661
2661
2662 @command('debugrebuilddirstate|debugrebuildstate',
2662 @command('debugrebuilddirstate|debugrebuildstate',
2663 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
2663 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
2664 _('[-r REV]'))
2664 _('[-r REV]'))
2665 def debugrebuilddirstate(ui, repo, rev):
2665 def debugrebuilddirstate(ui, repo, rev):
2666 """rebuild the dirstate as it would look like for the given revision
2666 """rebuild the dirstate as it would look like for the given revision
2667
2667
2668 If no revision is specified the first current parent will be used.
2668 If no revision is specified the first current parent will be used.
2669
2669
2670 The dirstate will be set to the files of the given revision.
2670 The dirstate will be set to the files of the given revision.
2671 The actual working directory content or existing dirstate
2671 The actual working directory content or existing dirstate
2672 information such as adds or removes is not considered.
2672 information such as adds or removes is not considered.
2673
2673
2674 One use of this command is to make the next :hg:`status` invocation
2674 One use of this command is to make the next :hg:`status` invocation
2675 check the actual file content.
2675 check the actual file content.
2676 """
2676 """
2677 ctx = scmutil.revsingle(repo, rev)
2677 ctx = scmutil.revsingle(repo, rev)
2678 wlock = repo.wlock()
2678 wlock = repo.wlock()
2679 try:
2679 try:
2680 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
2680 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
2681 finally:
2681 finally:
2682 wlock.release()
2682 wlock.release()
2683
2683
2684 @command('debugrename',
2684 @command('debugrename',
2685 [('r', 'rev', '', _('revision to debug'), _('REV'))],
2685 [('r', 'rev', '', _('revision to debug'), _('REV'))],
2686 _('[-r REV] FILE'))
2686 _('[-r REV] FILE'))
2687 def debugrename(ui, repo, file1, *pats, **opts):
2687 def debugrename(ui, repo, file1, *pats, **opts):
2688 """dump rename information"""
2688 """dump rename information"""
2689
2689
2690 ctx = scmutil.revsingle(repo, opts.get('rev'))
2690 ctx = scmutil.revsingle(repo, opts.get('rev'))
2691 m = scmutil.match(ctx, (file1,) + pats, opts)
2691 m = scmutil.match(ctx, (file1,) + pats, opts)
2692 for abs in ctx.walk(m):
2692 for abs in ctx.walk(m):
2693 fctx = ctx[abs]
2693 fctx = ctx[abs]
2694 o = fctx.filelog().renamed(fctx.filenode())
2694 o = fctx.filelog().renamed(fctx.filenode())
2695 rel = m.rel(abs)
2695 rel = m.rel(abs)
2696 if o:
2696 if o:
2697 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2697 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2698 else:
2698 else:
2699 ui.write(_("%s not renamed\n") % rel)
2699 ui.write(_("%s not renamed\n") % rel)
2700
2700
2701 @command('debugrevlog',
2701 @command('debugrevlog',
2702 [('c', 'changelog', False, _('open changelog')),
2702 [('c', 'changelog', False, _('open changelog')),
2703 ('m', 'manifest', False, _('open manifest')),
2703 ('m', 'manifest', False, _('open manifest')),
2704 ('d', 'dump', False, _('dump index data'))],
2704 ('d', 'dump', False, _('dump index data'))],
2705 _('-c|-m|FILE'),
2705 _('-c|-m|FILE'),
2706 optionalrepo=True)
2706 optionalrepo=True)
2707 def debugrevlog(ui, repo, file_=None, **opts):
2707 def debugrevlog(ui, repo, file_=None, **opts):
2708 """show data and statistics about a revlog"""
2708 """show data and statistics about a revlog"""
2709 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
2709 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
2710
2710
2711 if opts.get("dump"):
2711 if opts.get("dump"):
2712 numrevs = len(r)
2712 numrevs = len(r)
2713 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
2713 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
2714 " rawsize totalsize compression heads chainlen\n")
2714 " rawsize totalsize compression heads chainlen\n")
2715 ts = 0
2715 ts = 0
2716 heads = set()
2716 heads = set()
2717
2717
2718 for rev in xrange(numrevs):
2718 for rev in xrange(numrevs):
2719 dbase = r.deltaparent(rev)
2719 dbase = r.deltaparent(rev)
2720 if dbase == -1:
2720 if dbase == -1:
2721 dbase = rev
2721 dbase = rev
2722 cbase = r.chainbase(rev)
2722 cbase = r.chainbase(rev)
2723 clen = r.chainlen(rev)
2723 clen = r.chainlen(rev)
2724 p1, p2 = r.parentrevs(rev)
2724 p1, p2 = r.parentrevs(rev)
2725 rs = r.rawsize(rev)
2725 rs = r.rawsize(rev)
2726 ts = ts + rs
2726 ts = ts + rs
2727 heads -= set(r.parentrevs(rev))
2727 heads -= set(r.parentrevs(rev))
2728 heads.add(rev)
2728 heads.add(rev)
2729 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
2729 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
2730 "%11d %5d %8d\n" %
2730 "%11d %5d %8d\n" %
2731 (rev, p1, p2, r.start(rev), r.end(rev),
2731 (rev, p1, p2, r.start(rev), r.end(rev),
2732 r.start(dbase), r.start(cbase),
2732 r.start(dbase), r.start(cbase),
2733 r.start(p1), r.start(p2),
2733 r.start(p1), r.start(p2),
2734 rs, ts, ts / r.end(rev), len(heads), clen))
2734 rs, ts, ts / r.end(rev), len(heads), clen))
2735 return 0
2735 return 0
2736
2736
2737 v = r.version
2737 v = r.version
2738 format = v & 0xFFFF
2738 format = v & 0xFFFF
2739 flags = []
2739 flags = []
2740 gdelta = False
2740 gdelta = False
2741 if v & revlog.REVLOGNGINLINEDATA:
2741 if v & revlog.REVLOGNGINLINEDATA:
2742 flags.append('inline')
2742 flags.append('inline')
2743 if v & revlog.REVLOGGENERALDELTA:
2743 if v & revlog.REVLOGGENERALDELTA:
2744 gdelta = True
2744 gdelta = True
2745 flags.append('generaldelta')
2745 flags.append('generaldelta')
2746 if not flags:
2746 if not flags:
2747 flags = ['(none)']
2747 flags = ['(none)']
2748
2748
2749 nummerges = 0
2749 nummerges = 0
2750 numfull = 0
2750 numfull = 0
2751 numprev = 0
2751 numprev = 0
2752 nump1 = 0
2752 nump1 = 0
2753 nump2 = 0
2753 nump2 = 0
2754 numother = 0
2754 numother = 0
2755 nump1prev = 0
2755 nump1prev = 0
2756 nump2prev = 0
2756 nump2prev = 0
2757 chainlengths = []
2757 chainlengths = []
2758
2758
2759 datasize = [None, 0, 0L]
2759 datasize = [None, 0, 0L]
2760 fullsize = [None, 0, 0L]
2760 fullsize = [None, 0, 0L]
2761 deltasize = [None, 0, 0L]
2761 deltasize = [None, 0, 0L]
2762
2762
2763 def addsize(size, l):
2763 def addsize(size, l):
2764 if l[0] is None or size < l[0]:
2764 if l[0] is None or size < l[0]:
2765 l[0] = size
2765 l[0] = size
2766 if size > l[1]:
2766 if size > l[1]:
2767 l[1] = size
2767 l[1] = size
2768 l[2] += size
2768 l[2] += size
2769
2769
2770 numrevs = len(r)
2770 numrevs = len(r)
2771 for rev in xrange(numrevs):
2771 for rev in xrange(numrevs):
2772 p1, p2 = r.parentrevs(rev)
2772 p1, p2 = r.parentrevs(rev)
2773 delta = r.deltaparent(rev)
2773 delta = r.deltaparent(rev)
2774 if format > 0:
2774 if format > 0:
2775 addsize(r.rawsize(rev), datasize)
2775 addsize(r.rawsize(rev), datasize)
2776 if p2 != nullrev:
2776 if p2 != nullrev:
2777 nummerges += 1
2777 nummerges += 1
2778 size = r.length(rev)
2778 size = r.length(rev)
2779 if delta == nullrev:
2779 if delta == nullrev:
2780 chainlengths.append(0)
2780 chainlengths.append(0)
2781 numfull += 1
2781 numfull += 1
2782 addsize(size, fullsize)
2782 addsize(size, fullsize)
2783 else:
2783 else:
2784 chainlengths.append(chainlengths[delta] + 1)
2784 chainlengths.append(chainlengths[delta] + 1)
2785 addsize(size, deltasize)
2785 addsize(size, deltasize)
2786 if delta == rev - 1:
2786 if delta == rev - 1:
2787 numprev += 1
2787 numprev += 1
2788 if delta == p1:
2788 if delta == p1:
2789 nump1prev += 1
2789 nump1prev += 1
2790 elif delta == p2:
2790 elif delta == p2:
2791 nump2prev += 1
2791 nump2prev += 1
2792 elif delta == p1:
2792 elif delta == p1:
2793 nump1 += 1
2793 nump1 += 1
2794 elif delta == p2:
2794 elif delta == p2:
2795 nump2 += 1
2795 nump2 += 1
2796 elif delta != nullrev:
2796 elif delta != nullrev:
2797 numother += 1
2797 numother += 1
2798
2798
2799 # Adjust size min value for empty cases
2799 # Adjust size min value for empty cases
2800 for size in (datasize, fullsize, deltasize):
2800 for size in (datasize, fullsize, deltasize):
2801 if size[0] is None:
2801 if size[0] is None:
2802 size[0] = 0
2802 size[0] = 0
2803
2803
2804 numdeltas = numrevs - numfull
2804 numdeltas = numrevs - numfull
2805 numoprev = numprev - nump1prev - nump2prev
2805 numoprev = numprev - nump1prev - nump2prev
2806 totalrawsize = datasize[2]
2806 totalrawsize = datasize[2]
2807 datasize[2] /= numrevs
2807 datasize[2] /= numrevs
2808 fulltotal = fullsize[2]
2808 fulltotal = fullsize[2]
2809 fullsize[2] /= numfull
2809 fullsize[2] /= numfull
2810 deltatotal = deltasize[2]
2810 deltatotal = deltasize[2]
2811 if numrevs - numfull > 0:
2811 if numrevs - numfull > 0:
2812 deltasize[2] /= numrevs - numfull
2812 deltasize[2] /= numrevs - numfull
2813 totalsize = fulltotal + deltatotal
2813 totalsize = fulltotal + deltatotal
2814 avgchainlen = sum(chainlengths) / numrevs
2814 avgchainlen = sum(chainlengths) / numrevs
2815 compratio = totalrawsize / totalsize
2815 compratio = totalrawsize / totalsize
2816
2816
2817 basedfmtstr = '%%%dd\n'
2817 basedfmtstr = '%%%dd\n'
2818 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2818 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2819
2819
2820 def dfmtstr(max):
2820 def dfmtstr(max):
2821 return basedfmtstr % len(str(max))
2821 return basedfmtstr % len(str(max))
2822 def pcfmtstr(max, padding=0):
2822 def pcfmtstr(max, padding=0):
2823 return basepcfmtstr % (len(str(max)), ' ' * padding)
2823 return basepcfmtstr % (len(str(max)), ' ' * padding)
2824
2824
2825 def pcfmt(value, total):
2825 def pcfmt(value, total):
2826 return (value, 100 * float(value) / total)
2826 return (value, 100 * float(value) / total)
2827
2827
2828 ui.write(('format : %d\n') % format)
2828 ui.write(('format : %d\n') % format)
2829 ui.write(('flags : %s\n') % ', '.join(flags))
2829 ui.write(('flags : %s\n') % ', '.join(flags))
2830
2830
2831 ui.write('\n')
2831 ui.write('\n')
2832 fmt = pcfmtstr(totalsize)
2832 fmt = pcfmtstr(totalsize)
2833 fmt2 = dfmtstr(totalsize)
2833 fmt2 = dfmtstr(totalsize)
2834 ui.write(('revisions : ') + fmt2 % numrevs)
2834 ui.write(('revisions : ') + fmt2 % numrevs)
2835 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
2835 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
2836 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
2836 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
2837 ui.write(('revisions : ') + fmt2 % numrevs)
2837 ui.write(('revisions : ') + fmt2 % numrevs)
2838 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
2838 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
2839 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
2839 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
2840 ui.write(('revision size : ') + fmt2 % totalsize)
2840 ui.write(('revision size : ') + fmt2 % totalsize)
2841 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
2841 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
2842 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
2842 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
2843
2843
2844 ui.write('\n')
2844 ui.write('\n')
2845 fmt = dfmtstr(max(avgchainlen, compratio))
2845 fmt = dfmtstr(max(avgchainlen, compratio))
2846 ui.write(('avg chain length : ') + fmt % avgchainlen)
2846 ui.write(('avg chain length : ') + fmt % avgchainlen)
2847 ui.write(('compression ratio : ') + fmt % compratio)
2847 ui.write(('compression ratio : ') + fmt % compratio)
2848
2848
2849 if format > 0:
2849 if format > 0:
2850 ui.write('\n')
2850 ui.write('\n')
2851 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
2851 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
2852 % tuple(datasize))
2852 % tuple(datasize))
2853 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
2853 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
2854 % tuple(fullsize))
2854 % tuple(fullsize))
2855 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
2855 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
2856 % tuple(deltasize))
2856 % tuple(deltasize))
2857
2857
2858 if numdeltas > 0:
2858 if numdeltas > 0:
2859 ui.write('\n')
2859 ui.write('\n')
2860 fmt = pcfmtstr(numdeltas)
2860 fmt = pcfmtstr(numdeltas)
2861 fmt2 = pcfmtstr(numdeltas, 4)
2861 fmt2 = pcfmtstr(numdeltas, 4)
2862 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
2862 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
2863 if numprev > 0:
2863 if numprev > 0:
2864 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
2864 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
2865 numprev))
2865 numprev))
2866 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
2866 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
2867 numprev))
2867 numprev))
2868 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
2868 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
2869 numprev))
2869 numprev))
2870 if gdelta:
2870 if gdelta:
2871 ui.write(('deltas against p1 : ')
2871 ui.write(('deltas against p1 : ')
2872 + fmt % pcfmt(nump1, numdeltas))
2872 + fmt % pcfmt(nump1, numdeltas))
2873 ui.write(('deltas against p2 : ')
2873 ui.write(('deltas against p2 : ')
2874 + fmt % pcfmt(nump2, numdeltas))
2874 + fmt % pcfmt(nump2, numdeltas))
2875 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
2875 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
2876 numdeltas))
2876 numdeltas))
2877
2877
2878 @command('debugrevspec',
2878 @command('debugrevspec',
2879 [('', 'optimize', None, _('print parsed tree after optimizing'))],
2879 [('', 'optimize', None, _('print parsed tree after optimizing'))],
2880 ('REVSPEC'))
2880 ('REVSPEC'))
2881 def debugrevspec(ui, repo, expr, **opts):
2881 def debugrevspec(ui, repo, expr, **opts):
2882 """parse and apply a revision specification
2882 """parse and apply a revision specification
2883
2883
2884 Use --verbose to print the parsed tree before and after aliases
2884 Use --verbose to print the parsed tree before and after aliases
2885 expansion.
2885 expansion.
2886 """
2886 """
2887 if ui.verbose:
2887 if ui.verbose:
2888 tree = revset.parse(expr)[0]
2888 tree = revset.parse(expr)[0]
2889 ui.note(revset.prettyformat(tree), "\n")
2889 ui.note(revset.prettyformat(tree), "\n")
2890 newtree = revset.findaliases(ui, tree)
2890 newtree = revset.findaliases(ui, tree)
2891 if newtree != tree:
2891 if newtree != tree:
2892 ui.note(revset.prettyformat(newtree), "\n")
2892 ui.note(revset.prettyformat(newtree), "\n")
2893 tree = newtree
2893 tree = newtree
2894 newtree = revset.foldconcat(tree)
2894 newtree = revset.foldconcat(tree)
2895 if newtree != tree:
2895 if newtree != tree:
2896 ui.note(revset.prettyformat(newtree), "\n")
2896 ui.note(revset.prettyformat(newtree), "\n")
2897 if opts["optimize"]:
2897 if opts["optimize"]:
2898 weight, optimizedtree = revset.optimize(newtree, True)
2898 weight, optimizedtree = revset.optimize(newtree, True)
2899 ui.note("* optimized:\n", revset.prettyformat(optimizedtree), "\n")
2899 ui.note("* optimized:\n", revset.prettyformat(optimizedtree), "\n")
2900 func = revset.match(ui, expr)
2900 func = revset.match(ui, expr)
2901 for c in func(repo):
2901 for c in func(repo):
2902 ui.write("%s\n" % c)
2902 ui.write("%s\n" % c)
2903
2903
2904 @command('debugsetparents', [], _('REV1 [REV2]'))
2904 @command('debugsetparents', [], _('REV1 [REV2]'))
2905 def debugsetparents(ui, repo, rev1, rev2=None):
2905 def debugsetparents(ui, repo, rev1, rev2=None):
2906 """manually set the parents of the current working directory
2906 """manually set the parents of the current working directory
2907
2907
2908 This is useful for writing repository conversion tools, but should
2908 This is useful for writing repository conversion tools, but should
2909 be used with care. For example, neither the working directory nor the
2909 be used with care. For example, neither the working directory nor the
2910 dirstate is updated, so file status may be incorrect after running this
2910 dirstate is updated, so file status may be incorrect after running this
2911 command.
2911 command.
2912
2912
2913 Returns 0 on success.
2913 Returns 0 on success.
2914 """
2914 """
2915
2915
2916 r1 = scmutil.revsingle(repo, rev1).node()
2916 r1 = scmutil.revsingle(repo, rev1).node()
2917 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2917 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2918
2918
2919 wlock = repo.wlock()
2919 wlock = repo.wlock()
2920 try:
2920 try:
2921 repo.dirstate.beginparentchange()
2921 repo.dirstate.beginparentchange()
2922 repo.setparents(r1, r2)
2922 repo.setparents(r1, r2)
2923 repo.dirstate.endparentchange()
2923 repo.dirstate.endparentchange()
2924 finally:
2924 finally:
2925 wlock.release()
2925 wlock.release()
2926
2926
2927 @command('debugdirstate|debugstate',
2927 @command('debugdirstate|debugstate',
2928 [('', 'nodates', None, _('do not display the saved mtime')),
2928 [('', 'nodates', None, _('do not display the saved mtime')),
2929 ('', 'datesort', None, _('sort by saved mtime'))],
2929 ('', 'datesort', None, _('sort by saved mtime'))],
2930 _('[OPTION]...'))
2930 _('[OPTION]...'))
2931 def debugstate(ui, repo, nodates=None, datesort=None):
2931 def debugstate(ui, repo, nodates=None, datesort=None):
2932 """show the contents of the current dirstate"""
2932 """show the contents of the current dirstate"""
2933 timestr = ""
2933 timestr = ""
2934 if datesort:
2934 if datesort:
2935 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2935 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2936 else:
2936 else:
2937 keyfunc = None # sort by filename
2937 keyfunc = None # sort by filename
2938 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2938 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2939 if ent[3] == -1:
2939 if ent[3] == -1:
2940 timestr = 'unset '
2940 timestr = 'unset '
2941 elif nodates:
2941 elif nodates:
2942 timestr = 'set '
2942 timestr = 'set '
2943 else:
2943 else:
2944 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2944 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2945 time.localtime(ent[3]))
2945 time.localtime(ent[3]))
2946 if ent[1] & 020000:
2946 if ent[1] & 020000:
2947 mode = 'lnk'
2947 mode = 'lnk'
2948 else:
2948 else:
2949 mode = '%3o' % (ent[1] & 0777 & ~util.umask)
2949 mode = '%3o' % (ent[1] & 0777 & ~util.umask)
2950 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2950 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2951 for f in repo.dirstate.copies():
2951 for f in repo.dirstate.copies():
2952 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2952 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2953
2953
2954 @command('debugsub',
2954 @command('debugsub',
2955 [('r', 'rev', '',
2955 [('r', 'rev', '',
2956 _('revision to check'), _('REV'))],
2956 _('revision to check'), _('REV'))],
2957 _('[-r REV] [REV]'))
2957 _('[-r REV] [REV]'))
2958 def debugsub(ui, repo, rev=None):
2958 def debugsub(ui, repo, rev=None):
2959 ctx = scmutil.revsingle(repo, rev, None)
2959 ctx = scmutil.revsingle(repo, rev, None)
2960 for k, v in sorted(ctx.substate.items()):
2960 for k, v in sorted(ctx.substate.items()):
2961 ui.write(('path %s\n') % k)
2961 ui.write(('path %s\n') % k)
2962 ui.write((' source %s\n') % v[0])
2962 ui.write((' source %s\n') % v[0])
2963 ui.write((' revision %s\n') % v[1])
2963 ui.write((' revision %s\n') % v[1])
2964
2964
2965 @command('debugsuccessorssets',
2965 @command('debugsuccessorssets',
2966 [],
2966 [],
2967 _('[REV]'))
2967 _('[REV]'))
2968 def debugsuccessorssets(ui, repo, *revs):
2968 def debugsuccessorssets(ui, repo, *revs):
2969 """show set of successors for revision
2969 """show set of successors for revision
2970
2970
2971 A successors set of changeset A is a consistent group of revisions that
2971 A successors set of changeset A is a consistent group of revisions that
2972 succeed A. It contains non-obsolete changesets only.
2972 succeed A. It contains non-obsolete changesets only.
2973
2973
2974 In most cases a changeset A has a single successors set containing a single
2974 In most cases a changeset A has a single successors set containing a single
2975 successor (changeset A replaced by A').
2975 successor (changeset A replaced by A').
2976
2976
2977 A changeset that is made obsolete with no successors are called "pruned".
2977 A changeset that is made obsolete with no successors are called "pruned".
2978 Such changesets have no successors sets at all.
2978 Such changesets have no successors sets at all.
2979
2979
2980 A changeset that has been "split" will have a successors set containing
2980 A changeset that has been "split" will have a successors set containing
2981 more than one successor.
2981 more than one successor.
2982
2982
2983 A changeset that has been rewritten in multiple different ways is called
2983 A changeset that has been rewritten in multiple different ways is called
2984 "divergent". Such changesets have multiple successor sets (each of which
2984 "divergent". Such changesets have multiple successor sets (each of which
2985 may also be split, i.e. have multiple successors).
2985 may also be split, i.e. have multiple successors).
2986
2986
2987 Results are displayed as follows::
2987 Results are displayed as follows::
2988
2988
2989 <rev1>
2989 <rev1>
2990 <successors-1A>
2990 <successors-1A>
2991 <rev2>
2991 <rev2>
2992 <successors-2A>
2992 <successors-2A>
2993 <successors-2B1> <successors-2B2> <successors-2B3>
2993 <successors-2B1> <successors-2B2> <successors-2B3>
2994
2994
2995 Here rev2 has two possible (i.e. divergent) successors sets. The first
2995 Here rev2 has two possible (i.e. divergent) successors sets. The first
2996 holds one element, whereas the second holds three (i.e. the changeset has
2996 holds one element, whereas the second holds three (i.e. the changeset has
2997 been split).
2997 been split).
2998 """
2998 """
2999 # passed to successorssets caching computation from one call to another
2999 # passed to successorssets caching computation from one call to another
3000 cache = {}
3000 cache = {}
3001 ctx2str = str
3001 ctx2str = str
3002 node2str = short
3002 node2str = short
3003 if ui.debug():
3003 if ui.debug():
3004 def ctx2str(ctx):
3004 def ctx2str(ctx):
3005 return ctx.hex()
3005 return ctx.hex()
3006 node2str = hex
3006 node2str = hex
3007 for rev in scmutil.revrange(repo, revs):
3007 for rev in scmutil.revrange(repo, revs):
3008 ctx = repo[rev]
3008 ctx = repo[rev]
3009 ui.write('%s\n'% ctx2str(ctx))
3009 ui.write('%s\n'% ctx2str(ctx))
3010 for succsset in obsolete.successorssets(repo, ctx.node(), cache):
3010 for succsset in obsolete.successorssets(repo, ctx.node(), cache):
3011 if succsset:
3011 if succsset:
3012 ui.write(' ')
3012 ui.write(' ')
3013 ui.write(node2str(succsset[0]))
3013 ui.write(node2str(succsset[0]))
3014 for node in succsset[1:]:
3014 for node in succsset[1:]:
3015 ui.write(' ')
3015 ui.write(' ')
3016 ui.write(node2str(node))
3016 ui.write(node2str(node))
3017 ui.write('\n')
3017 ui.write('\n')
3018
3018
3019 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'), inferrepo=True)
3019 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'), inferrepo=True)
3020 def debugwalk(ui, repo, *pats, **opts):
3020 def debugwalk(ui, repo, *pats, **opts):
3021 """show how files match on given patterns"""
3021 """show how files match on given patterns"""
3022 m = scmutil.match(repo[None], pats, opts)
3022 m = scmutil.match(repo[None], pats, opts)
3023 items = list(repo.walk(m))
3023 items = list(repo.walk(m))
3024 if not items:
3024 if not items:
3025 return
3025 return
3026 f = lambda fn: fn
3026 f = lambda fn: fn
3027 if ui.configbool('ui', 'slash') and os.sep != '/':
3027 if ui.configbool('ui', 'slash') and os.sep != '/':
3028 f = lambda fn: util.normpath(fn)
3028 f = lambda fn: util.normpath(fn)
3029 fmt = 'f %%-%ds %%-%ds %%s' % (
3029 fmt = 'f %%-%ds %%-%ds %%s' % (
3030 max([len(abs) for abs in items]),
3030 max([len(abs) for abs in items]),
3031 max([len(m.rel(abs)) for abs in items]))
3031 max([len(m.rel(abs)) for abs in items]))
3032 for abs in items:
3032 for abs in items:
3033 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
3033 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
3034 ui.write("%s\n" % line.rstrip())
3034 ui.write("%s\n" % line.rstrip())
3035
3035
3036 @command('debugwireargs',
3036 @command('debugwireargs',
3037 [('', 'three', '', 'three'),
3037 [('', 'three', '', 'three'),
3038 ('', 'four', '', 'four'),
3038 ('', 'four', '', 'four'),
3039 ('', 'five', '', 'five'),
3039 ('', 'five', '', 'five'),
3040 ] + remoteopts,
3040 ] + remoteopts,
3041 _('REPO [OPTIONS]... [ONE [TWO]]'),
3041 _('REPO [OPTIONS]... [ONE [TWO]]'),
3042 norepo=True)
3042 norepo=True)
3043 def debugwireargs(ui, repopath, *vals, **opts):
3043 def debugwireargs(ui, repopath, *vals, **opts):
3044 repo = hg.peer(ui, opts, repopath)
3044 repo = hg.peer(ui, opts, repopath)
3045 for opt in remoteopts:
3045 for opt in remoteopts:
3046 del opts[opt[1]]
3046 del opts[opt[1]]
3047 args = {}
3047 args = {}
3048 for k, v in opts.iteritems():
3048 for k, v in opts.iteritems():
3049 if v:
3049 if v:
3050 args[k] = v
3050 args[k] = v
3051 # run twice to check that we don't mess up the stream for the next command
3051 # run twice to check that we don't mess up the stream for the next command
3052 res1 = repo.debugwireargs(*vals, **args)
3052 res1 = repo.debugwireargs(*vals, **args)
3053 res2 = repo.debugwireargs(*vals, **args)
3053 res2 = repo.debugwireargs(*vals, **args)
3054 ui.write("%s\n" % res1)
3054 ui.write("%s\n" % res1)
3055 if res1 != res2:
3055 if res1 != res2:
3056 ui.warn("%s\n" % res2)
3056 ui.warn("%s\n" % res2)
3057
3057
3058 @command('^diff',
3058 @command('^diff',
3059 [('r', 'rev', [], _('revision'), _('REV')),
3059 [('r', 'rev', [], _('revision'), _('REV')),
3060 ('c', 'change', '', _('change made by revision'), _('REV'))
3060 ('c', 'change', '', _('change made by revision'), _('REV'))
3061 ] + diffopts + diffopts2 + walkopts + subrepoopts,
3061 ] + diffopts + diffopts2 + walkopts + subrepoopts,
3062 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
3062 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
3063 inferrepo=True)
3063 inferrepo=True)
3064 def diff(ui, repo, *pats, **opts):
3064 def diff(ui, repo, *pats, **opts):
3065 """diff repository (or selected files)
3065 """diff repository (or selected files)
3066
3066
3067 Show differences between revisions for the specified files.
3067 Show differences between revisions for the specified files.
3068
3068
3069 Differences between files are shown using the unified diff format.
3069 Differences between files are shown using the unified diff format.
3070
3070
3071 .. note::
3071 .. note::
3072
3072
3073 diff may generate unexpected results for merges, as it will
3073 diff may generate unexpected results for merges, as it will
3074 default to comparing against the working directory's first
3074 default to comparing against the working directory's first
3075 parent changeset if no revisions are specified.
3075 parent changeset if no revisions are specified.
3076
3076
3077 When two revision arguments are given, then changes are shown
3077 When two revision arguments are given, then changes are shown
3078 between those revisions. If only one revision is specified then
3078 between those revisions. If only one revision is specified then
3079 that revision is compared to the working directory, and, when no
3079 that revision is compared to the working directory, and, when no
3080 revisions are specified, the working directory files are compared
3080 revisions are specified, the working directory files are compared
3081 to its parent.
3081 to its parent.
3082
3082
3083 Alternatively you can specify -c/--change with a revision to see
3083 Alternatively you can specify -c/--change with a revision to see
3084 the changes in that changeset relative to its first parent.
3084 the changes in that changeset relative to its first parent.
3085
3085
3086 Without the -a/--text option, diff will avoid generating diffs of
3086 Without the -a/--text option, diff will avoid generating diffs of
3087 files it detects as binary. With -a, diff will generate a diff
3087 files it detects as binary. With -a, diff will generate a diff
3088 anyway, probably with undesirable results.
3088 anyway, probably with undesirable results.
3089
3089
3090 Use the -g/--git option to generate diffs in the git extended diff
3090 Use the -g/--git option to generate diffs in the git extended diff
3091 format. For more information, read :hg:`help diffs`.
3091 format. For more information, read :hg:`help diffs`.
3092
3092
3093 .. container:: verbose
3093 .. container:: verbose
3094
3094
3095 Examples:
3095 Examples:
3096
3096
3097 - compare a file in the current working directory to its parent::
3097 - compare a file in the current working directory to its parent::
3098
3098
3099 hg diff foo.c
3099 hg diff foo.c
3100
3100
3101 - compare two historical versions of a directory, with rename info::
3101 - compare two historical versions of a directory, with rename info::
3102
3102
3103 hg diff --git -r 1.0:1.2 lib/
3103 hg diff --git -r 1.0:1.2 lib/
3104
3104
3105 - get change stats relative to the last change on some date::
3105 - get change stats relative to the last change on some date::
3106
3106
3107 hg diff --stat -r "date('may 2')"
3107 hg diff --stat -r "date('may 2')"
3108
3108
3109 - diff all newly-added files that contain a keyword::
3109 - diff all newly-added files that contain a keyword::
3110
3110
3111 hg diff "set:added() and grep(GNU)"
3111 hg diff "set:added() and grep(GNU)"
3112
3112
3113 - compare a revision and its parents::
3113 - compare a revision and its parents::
3114
3114
3115 hg diff -c 9353 # compare against first parent
3115 hg diff -c 9353 # compare against first parent
3116 hg diff -r 9353^:9353 # same using revset syntax
3116 hg diff -r 9353^:9353 # same using revset syntax
3117 hg diff -r 9353^2:9353 # compare against the second parent
3117 hg diff -r 9353^2:9353 # compare against the second parent
3118
3118
3119 Returns 0 on success.
3119 Returns 0 on success.
3120 """
3120 """
3121
3121
3122 revs = opts.get('rev')
3122 revs = opts.get('rev')
3123 change = opts.get('change')
3123 change = opts.get('change')
3124 stat = opts.get('stat')
3124 stat = opts.get('stat')
3125 reverse = opts.get('reverse')
3125 reverse = opts.get('reverse')
3126
3126
3127 if revs and change:
3127 if revs and change:
3128 msg = _('cannot specify --rev and --change at the same time')
3128 msg = _('cannot specify --rev and --change at the same time')
3129 raise util.Abort(msg)
3129 raise util.Abort(msg)
3130 elif change:
3130 elif change:
3131 node2 = scmutil.revsingle(repo, change, None).node()
3131 node2 = scmutil.revsingle(repo, change, None).node()
3132 node1 = repo[node2].p1().node()
3132 node1 = repo[node2].p1().node()
3133 else:
3133 else:
3134 node1, node2 = scmutil.revpair(repo, revs)
3134 node1, node2 = scmutil.revpair(repo, revs)
3135
3135
3136 if reverse:
3136 if reverse:
3137 node1, node2 = node2, node1
3137 node1, node2 = node2, node1
3138
3138
3139 diffopts = patch.diffallopts(ui, opts)
3139 diffopts = patch.diffallopts(ui, opts)
3140 m = scmutil.match(repo[node2], pats, opts)
3140 m = scmutil.match(repo[node2], pats, opts)
3141 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
3141 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
3142 listsubrepos=opts.get('subrepos'))
3142 listsubrepos=opts.get('subrepos'))
3143
3143
3144 @command('^export',
3144 @command('^export',
3145 [('o', 'output', '',
3145 [('o', 'output', '',
3146 _('print output to file with formatted name'), _('FORMAT')),
3146 _('print output to file with formatted name'), _('FORMAT')),
3147 ('', 'switch-parent', None, _('diff against the second parent')),
3147 ('', 'switch-parent', None, _('diff against the second parent')),
3148 ('r', 'rev', [], _('revisions to export'), _('REV')),
3148 ('r', 'rev', [], _('revisions to export'), _('REV')),
3149 ] + diffopts,
3149 ] + diffopts,
3150 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
3150 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
3151 def export(ui, repo, *changesets, **opts):
3151 def export(ui, repo, *changesets, **opts):
3152 """dump the header and diffs for one or more changesets
3152 """dump the header and diffs for one or more changesets
3153
3153
3154 Print the changeset header and diffs for one or more revisions.
3154 Print the changeset header and diffs for one or more revisions.
3155 If no revision is given, the parent of the working directory is used.
3155 If no revision is given, the parent of the working directory is used.
3156
3156
3157 The information shown in the changeset header is: author, date,
3157 The information shown in the changeset header is: author, date,
3158 branch name (if non-default), changeset hash, parent(s) and commit
3158 branch name (if non-default), changeset hash, parent(s) and commit
3159 comment.
3159 comment.
3160
3160
3161 .. note::
3161 .. note::
3162
3162
3163 export may generate unexpected diff output for merge
3163 export may generate unexpected diff output for merge
3164 changesets, as it will compare the merge changeset against its
3164 changesets, as it will compare the merge changeset against its
3165 first parent only.
3165 first parent only.
3166
3166
3167 Output may be to a file, in which case the name of the file is
3167 Output may be to a file, in which case the name of the file is
3168 given using a format string. The formatting rules are as follows:
3168 given using a format string. The formatting rules are as follows:
3169
3169
3170 :``%%``: literal "%" character
3170 :``%%``: literal "%" character
3171 :``%H``: changeset hash (40 hexadecimal digits)
3171 :``%H``: changeset hash (40 hexadecimal digits)
3172 :``%N``: number of patches being generated
3172 :``%N``: number of patches being generated
3173 :``%R``: changeset revision number
3173 :``%R``: changeset revision number
3174 :``%b``: basename of the exporting repository
3174 :``%b``: basename of the exporting repository
3175 :``%h``: short-form changeset hash (12 hexadecimal digits)
3175 :``%h``: short-form changeset hash (12 hexadecimal digits)
3176 :``%m``: first line of the commit message (only alphanumeric characters)
3176 :``%m``: first line of the commit message (only alphanumeric characters)
3177 :``%n``: zero-padded sequence number, starting at 1
3177 :``%n``: zero-padded sequence number, starting at 1
3178 :``%r``: zero-padded changeset revision number
3178 :``%r``: zero-padded changeset revision number
3179
3179
3180 Without the -a/--text option, export will avoid generating diffs
3180 Without the -a/--text option, export will avoid generating diffs
3181 of files it detects as binary. With -a, export will generate a
3181 of files it detects as binary. With -a, export will generate a
3182 diff anyway, probably with undesirable results.
3182 diff anyway, probably with undesirable results.
3183
3183
3184 Use the -g/--git option to generate diffs in the git extended diff
3184 Use the -g/--git option to generate diffs in the git extended diff
3185 format. See :hg:`help diffs` for more information.
3185 format. See :hg:`help diffs` for more information.
3186
3186
3187 With the --switch-parent option, the diff will be against the
3187 With the --switch-parent option, the diff will be against the
3188 second parent. It can be useful to review a merge.
3188 second parent. It can be useful to review a merge.
3189
3189
3190 .. container:: verbose
3190 .. container:: verbose
3191
3191
3192 Examples:
3192 Examples:
3193
3193
3194 - use export and import to transplant a bugfix to the current
3194 - use export and import to transplant a bugfix to the current
3195 branch::
3195 branch::
3196
3196
3197 hg export -r 9353 | hg import -
3197 hg export -r 9353 | hg import -
3198
3198
3199 - export all the changesets between two revisions to a file with
3199 - export all the changesets between two revisions to a file with
3200 rename information::
3200 rename information::
3201
3201
3202 hg export --git -r 123:150 > changes.txt
3202 hg export --git -r 123:150 > changes.txt
3203
3203
3204 - split outgoing changes into a series of patches with
3204 - split outgoing changes into a series of patches with
3205 descriptive names::
3205 descriptive names::
3206
3206
3207 hg export -r "outgoing()" -o "%n-%m.patch"
3207 hg export -r "outgoing()" -o "%n-%m.patch"
3208
3208
3209 Returns 0 on success.
3209 Returns 0 on success.
3210 """
3210 """
3211 changesets += tuple(opts.get('rev', []))
3211 changesets += tuple(opts.get('rev', []))
3212 if not changesets:
3212 if not changesets:
3213 changesets = ['.']
3213 changesets = ['.']
3214 revs = scmutil.revrange(repo, changesets)
3214 revs = scmutil.revrange(repo, changesets)
3215 if not revs:
3215 if not revs:
3216 raise util.Abort(_("export requires at least one changeset"))
3216 raise util.Abort(_("export requires at least one changeset"))
3217 if len(revs) > 1:
3217 if len(revs) > 1:
3218 ui.note(_('exporting patches:\n'))
3218 ui.note(_('exporting patches:\n'))
3219 else:
3219 else:
3220 ui.note(_('exporting patch:\n'))
3220 ui.note(_('exporting patch:\n'))
3221 cmdutil.export(repo, revs, template=opts.get('output'),
3221 cmdutil.export(repo, revs, template=opts.get('output'),
3222 switch_parent=opts.get('switch_parent'),
3222 switch_parent=opts.get('switch_parent'),
3223 opts=patch.diffallopts(ui, opts))
3223 opts=patch.diffallopts(ui, opts))
3224
3224
3225 @command('files',
3225 @command('files',
3226 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3226 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3227 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3227 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3228 ] + walkopts + formatteropts,
3228 ] + walkopts + formatteropts,
3229 _('[OPTION]... [PATTERN]...'))
3229 _('[OPTION]... [PATTERN]...'))
3230 def files(ui, repo, *pats, **opts):
3230 def files(ui, repo, *pats, **opts):
3231 """list tracked files
3231 """list tracked files
3232
3232
3233 Print files under Mercurial control in the working directory or
3233 Print files under Mercurial control in the working directory or
3234 specified revision whose names match the given patterns (excluding
3234 specified revision whose names match the given patterns (excluding
3235 removed files).
3235 removed files).
3236
3236
3237 If no patterns are given to match, this command prints the names
3237 If no patterns are given to match, this command prints the names
3238 of all files under Mercurial control in the working directory.
3238 of all files under Mercurial control in the working directory.
3239
3239
3240 .. container:: verbose
3240 .. container:: verbose
3241
3241
3242 Examples:
3242 Examples:
3243
3243
3244 - list all files under the current directory::
3244 - list all files under the current directory::
3245
3245
3246 hg files .
3246 hg files .
3247
3247
3248 - shows sizes and flags for current revision::
3248 - shows sizes and flags for current revision::
3249
3249
3250 hg files -vr .
3250 hg files -vr .
3251
3251
3252 - list all files named README::
3252 - list all files named README::
3253
3253
3254 hg files -I "**/README"
3254 hg files -I "**/README"
3255
3255
3256 - list all binary files::
3256 - list all binary files::
3257
3257
3258 hg files "set:binary()"
3258 hg files "set:binary()"
3259
3259
3260 - find files containing a regular expression::
3260 - find files containing a regular expression::
3261
3261
3262 hg files "set:grep('bob')"
3262 hg files "set:grep('bob')"
3263
3263
3264 - search tracked file contents with xargs and grep::
3264 - search tracked file contents with xargs and grep::
3265
3265
3266 hg files -0 | xargs -0 grep foo
3266 hg files -0 | xargs -0 grep foo
3267
3267
3268 See :hg:`help patterns` and :hg:`help filesets` for more information
3268 See :hg:`help patterns` and :hg:`help filesets` for more information
3269 on specifying file patterns.
3269 on specifying file patterns.
3270
3270
3271 Returns 0 if a match is found, 1 otherwise.
3271 Returns 0 if a match is found, 1 otherwise.
3272
3272
3273 """
3273 """
3274 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3274 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3275
3275
3276 end = '\n'
3276 end = '\n'
3277 if opts.get('print0'):
3277 if opts.get('print0'):
3278 end = '\0'
3278 end = '\0'
3279 fm = ui.formatter('files', opts)
3279 fm = ui.formatter('files', opts)
3280 fmt = '%s' + end
3280 fmt = '%s' + end
3281
3281
3282 m = scmutil.match(ctx, pats, opts)
3282 m = scmutil.match(ctx, pats, opts)
3283 ret = cmdutil.files(ui, ctx, m, fm, fmt)
3283 ret = cmdutil.files(ui, ctx, m, fm, fmt)
3284
3284
3285 fm.end()
3285 fm.end()
3286
3286
3287 return ret
3287 return ret
3288
3288
3289 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
3289 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
3290 def forget(ui, repo, *pats, **opts):
3290 def forget(ui, repo, *pats, **opts):
3291 """forget the specified files on the next commit
3291 """forget the specified files on the next commit
3292
3292
3293 Mark the specified files so they will no longer be tracked
3293 Mark the specified files so they will no longer be tracked
3294 after the next commit.
3294 after the next commit.
3295
3295
3296 This only removes files from the current branch, not from the
3296 This only removes files from the current branch, not from the
3297 entire project history, and it does not delete them from the
3297 entire project history, and it does not delete them from the
3298 working directory.
3298 working directory.
3299
3299
3300 To undo a forget before the next commit, see :hg:`add`.
3300 To undo a forget before the next commit, see :hg:`add`.
3301
3301
3302 .. container:: verbose
3302 .. container:: verbose
3303
3303
3304 Examples:
3304 Examples:
3305
3305
3306 - forget newly-added binary files::
3306 - forget newly-added binary files::
3307
3307
3308 hg forget "set:added() and binary()"
3308 hg forget "set:added() and binary()"
3309
3309
3310 - forget files that would be excluded by .hgignore::
3310 - forget files that would be excluded by .hgignore::
3311
3311
3312 hg forget "set:hgignore()"
3312 hg forget "set:hgignore()"
3313
3313
3314 Returns 0 on success.
3314 Returns 0 on success.
3315 """
3315 """
3316
3316
3317 if not pats:
3317 if not pats:
3318 raise util.Abort(_('no files specified'))
3318 raise util.Abort(_('no files specified'))
3319
3319
3320 m = scmutil.match(repo[None], pats, opts)
3320 m = scmutil.match(repo[None], pats, opts)
3321 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
3321 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
3322 return rejected and 1 or 0
3322 return rejected and 1 or 0
3323
3323
3324 @command(
3324 @command(
3325 'graft',
3325 'graft',
3326 [('r', 'rev', [], _('revisions to graft'), _('REV')),
3326 [('r', 'rev', [], _('revisions to graft'), _('REV')),
3327 ('c', 'continue', False, _('resume interrupted graft')),
3327 ('c', 'continue', False, _('resume interrupted graft')),
3328 ('e', 'edit', False, _('invoke editor on commit messages')),
3328 ('e', 'edit', False, _('invoke editor on commit messages')),
3329 ('', 'log', None, _('append graft info to log message')),
3329 ('', 'log', None, _('append graft info to log message')),
3330 ('f', 'force', False, _('force graft')),
3330 ('f', 'force', False, _('force graft')),
3331 ('D', 'currentdate', False,
3331 ('D', 'currentdate', False,
3332 _('record the current date as commit date')),
3332 _('record the current date as commit date')),
3333 ('U', 'currentuser', False,
3333 ('U', 'currentuser', False,
3334 _('record the current user as committer'), _('DATE'))]
3334 _('record the current user as committer'), _('DATE'))]
3335 + commitopts2 + mergetoolopts + dryrunopts,
3335 + commitopts2 + mergetoolopts + dryrunopts,
3336 _('[OPTION]... [-r] REV...'))
3336 _('[OPTION]... [-r] REV...'))
3337 def graft(ui, repo, *revs, **opts):
3337 def graft(ui, repo, *revs, **opts):
3338 '''copy changes from other branches onto the current branch
3338 '''copy changes from other branches onto the current branch
3339
3339
3340 This command uses Mercurial's merge logic to copy individual
3340 This command uses Mercurial's merge logic to copy individual
3341 changes from other branches without merging branches in the
3341 changes from other branches without merging branches in the
3342 history graph. This is sometimes known as 'backporting' or
3342 history graph. This is sometimes known as 'backporting' or
3343 'cherry-picking'. By default, graft will copy user, date, and
3343 'cherry-picking'. By default, graft will copy user, date, and
3344 description from the source changesets.
3344 description from the source changesets.
3345
3345
3346 Changesets that are ancestors of the current revision, that have
3346 Changesets that are ancestors of the current revision, that have
3347 already been grafted, or that are merges will be skipped.
3347 already been grafted, or that are merges will be skipped.
3348
3348
3349 If --log is specified, log messages will have a comment appended
3349 If --log is specified, log messages will have a comment appended
3350 of the form::
3350 of the form::
3351
3351
3352 (grafted from CHANGESETHASH)
3352 (grafted from CHANGESETHASH)
3353
3353
3354 If --force is specified, revisions will be grafted even if they
3354 If --force is specified, revisions will be grafted even if they
3355 are already ancestors of or have been grafted to the destination.
3355 are already ancestors of or have been grafted to the destination.
3356 This is useful when the revisions have since been backed out.
3356 This is useful when the revisions have since been backed out.
3357
3357
3358 If a graft merge results in conflicts, the graft process is
3358 If a graft merge results in conflicts, the graft process is
3359 interrupted so that the current merge can be manually resolved.
3359 interrupted so that the current merge can be manually resolved.
3360 Once all conflicts are addressed, the graft process can be
3360 Once all conflicts are addressed, the graft process can be
3361 continued with the -c/--continue option.
3361 continued with the -c/--continue option.
3362
3362
3363 .. note::
3363 .. note::
3364
3364
3365 The -c/--continue option does not reapply earlier options, except
3365 The -c/--continue option does not reapply earlier options, except
3366 for --force.
3366 for --force.
3367
3367
3368 .. container:: verbose
3368 .. container:: verbose
3369
3369
3370 Examples:
3370 Examples:
3371
3371
3372 - copy a single change to the stable branch and edit its description::
3372 - copy a single change to the stable branch and edit its description::
3373
3373
3374 hg update stable
3374 hg update stable
3375 hg graft --edit 9393
3375 hg graft --edit 9393
3376
3376
3377 - graft a range of changesets with one exception, updating dates::
3377 - graft a range of changesets with one exception, updating dates::
3378
3378
3379 hg graft -D "2085::2093 and not 2091"
3379 hg graft -D "2085::2093 and not 2091"
3380
3380
3381 - continue a graft after resolving conflicts::
3381 - continue a graft after resolving conflicts::
3382
3382
3383 hg graft -c
3383 hg graft -c
3384
3384
3385 - show the source of a grafted changeset::
3385 - show the source of a grafted changeset::
3386
3386
3387 hg log --debug -r .
3387 hg log --debug -r .
3388
3388
3389 See :hg:`help revisions` and :hg:`help revsets` for more about
3389 See :hg:`help revisions` and :hg:`help revsets` for more about
3390 specifying revisions.
3390 specifying revisions.
3391
3391
3392 Returns 0 on successful completion.
3392 Returns 0 on successful completion.
3393 '''
3393 '''
3394
3394
3395 revs = list(revs)
3395 revs = list(revs)
3396 revs.extend(opts['rev'])
3396 revs.extend(opts['rev'])
3397
3397
3398 if not opts.get('user') and opts.get('currentuser'):
3398 if not opts.get('user') and opts.get('currentuser'):
3399 opts['user'] = ui.username()
3399 opts['user'] = ui.username()
3400 if not opts.get('date') and opts.get('currentdate'):
3400 if not opts.get('date') and opts.get('currentdate'):
3401 opts['date'] = "%d %d" % util.makedate()
3401 opts['date'] = "%d %d" % util.makedate()
3402
3402
3403 editor = cmdutil.getcommiteditor(editform='graft', **opts)
3403 editor = cmdutil.getcommiteditor(editform='graft', **opts)
3404
3404
3405 cont = False
3405 cont = False
3406 if opts['continue']:
3406 if opts['continue']:
3407 cont = True
3407 cont = True
3408 if revs:
3408 if revs:
3409 raise util.Abort(_("can't specify --continue and revisions"))
3409 raise util.Abort(_("can't specify --continue and revisions"))
3410 # read in unfinished revisions
3410 # read in unfinished revisions
3411 try:
3411 try:
3412 nodes = repo.vfs.read('graftstate').splitlines()
3412 nodes = repo.vfs.read('graftstate').splitlines()
3413 revs = [repo[node].rev() for node in nodes]
3413 revs = [repo[node].rev() for node in nodes]
3414 except IOError, inst:
3414 except IOError, inst:
3415 if inst.errno != errno.ENOENT:
3415 if inst.errno != errno.ENOENT:
3416 raise
3416 raise
3417 raise util.Abort(_("no graft state found, can't continue"))
3417 raise util.Abort(_("no graft state found, can't continue"))
3418 else:
3418 else:
3419 cmdutil.checkunfinished(repo)
3419 cmdutil.checkunfinished(repo)
3420 cmdutil.bailifchanged(repo)
3420 cmdutil.bailifchanged(repo)
3421 if not revs:
3421 if not revs:
3422 raise util.Abort(_('no revisions specified'))
3422 raise util.Abort(_('no revisions specified'))
3423 revs = scmutil.revrange(repo, revs)
3423 revs = scmutil.revrange(repo, revs)
3424
3424
3425 skipped = set()
3425 skipped = set()
3426 # check for merges
3426 # check for merges
3427 for rev in repo.revs('%ld and merge()', revs):
3427 for rev in repo.revs('%ld and merge()', revs):
3428 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
3428 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
3429 skipped.add(rev)
3429 skipped.add(rev)
3430 revs = [r for r in revs if r not in skipped]
3430 revs = [r for r in revs if r not in skipped]
3431 if not revs:
3431 if not revs:
3432 return -1
3432 return -1
3433
3433
3434 # Don't check in the --continue case, in effect retaining --force across
3434 # Don't check in the --continue case, in effect retaining --force across
3435 # --continues. That's because without --force, any revisions we decided to
3435 # --continues. That's because without --force, any revisions we decided to
3436 # skip would have been filtered out here, so they wouldn't have made their
3436 # skip would have been filtered out here, so they wouldn't have made their
3437 # way to the graftstate. With --force, any revisions we would have otherwise
3437 # way to the graftstate. With --force, any revisions we would have otherwise
3438 # skipped would not have been filtered out, and if they hadn't been applied
3438 # skipped would not have been filtered out, and if they hadn't been applied
3439 # already, they'd have been in the graftstate.
3439 # already, they'd have been in the graftstate.
3440 if not (cont or opts.get('force')):
3440 if not (cont or opts.get('force')):
3441 # check for ancestors of dest branch
3441 # check for ancestors of dest branch
3442 crev = repo['.'].rev()
3442 crev = repo['.'].rev()
3443 ancestors = repo.changelog.ancestors([crev], inclusive=True)
3443 ancestors = repo.changelog.ancestors([crev], inclusive=True)
3444 # Cannot use x.remove(y) on smart set, this has to be a list.
3444 # Cannot use x.remove(y) on smart set, this has to be a list.
3445 # XXX make this lazy in the future
3445 # XXX make this lazy in the future
3446 revs = list(revs)
3446 revs = list(revs)
3447 # don't mutate while iterating, create a copy
3447 # don't mutate while iterating, create a copy
3448 for rev in list(revs):
3448 for rev in list(revs):
3449 if rev in ancestors:
3449 if rev in ancestors:
3450 ui.warn(_('skipping ancestor revision %d:%s\n') %
3450 ui.warn(_('skipping ancestor revision %d:%s\n') %
3451 (rev, repo[rev]))
3451 (rev, repo[rev]))
3452 # XXX remove on list is slow
3452 # XXX remove on list is slow
3453 revs.remove(rev)
3453 revs.remove(rev)
3454 if not revs:
3454 if not revs:
3455 return -1
3455 return -1
3456
3456
3457 # analyze revs for earlier grafts
3457 # analyze revs for earlier grafts
3458 ids = {}
3458 ids = {}
3459 for ctx in repo.set("%ld", revs):
3459 for ctx in repo.set("%ld", revs):
3460 ids[ctx.hex()] = ctx.rev()
3460 ids[ctx.hex()] = ctx.rev()
3461 n = ctx.extra().get('source')
3461 n = ctx.extra().get('source')
3462 if n:
3462 if n:
3463 ids[n] = ctx.rev()
3463 ids[n] = ctx.rev()
3464
3464
3465 # check ancestors for earlier grafts
3465 # check ancestors for earlier grafts
3466 ui.debug('scanning for duplicate grafts\n')
3466 ui.debug('scanning for duplicate grafts\n')
3467
3467
3468 for rev in repo.changelog.findmissingrevs(revs, [crev]):
3468 for rev in repo.changelog.findmissingrevs(revs, [crev]):
3469 ctx = repo[rev]
3469 ctx = repo[rev]
3470 n = ctx.extra().get('source')
3470 n = ctx.extra().get('source')
3471 if n in ids:
3471 if n in ids:
3472 try:
3472 try:
3473 r = repo[n].rev()
3473 r = repo[n].rev()
3474 except error.RepoLookupError:
3474 except error.RepoLookupError:
3475 r = None
3475 r = None
3476 if r in revs:
3476 if r in revs:
3477 ui.warn(_('skipping revision %d:%s '
3477 ui.warn(_('skipping revision %d:%s '
3478 '(already grafted to %d:%s)\n')
3478 '(already grafted to %d:%s)\n')
3479 % (r, repo[r], rev, ctx))
3479 % (r, repo[r], rev, ctx))
3480 revs.remove(r)
3480 revs.remove(r)
3481 elif ids[n] in revs:
3481 elif ids[n] in revs:
3482 if r is None:
3482 if r is None:
3483 ui.warn(_('skipping already grafted revision %d:%s '
3483 ui.warn(_('skipping already grafted revision %d:%s '
3484 '(%d:%s also has unknown origin %s)\n')
3484 '(%d:%s also has unknown origin %s)\n')
3485 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
3485 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
3486 else:
3486 else:
3487 ui.warn(_('skipping already grafted revision %d:%s '
3487 ui.warn(_('skipping already grafted revision %d:%s '
3488 '(%d:%s also has origin %d:%s)\n')
3488 '(%d:%s also has origin %d:%s)\n')
3489 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
3489 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
3490 revs.remove(ids[n])
3490 revs.remove(ids[n])
3491 elif ctx.hex() in ids:
3491 elif ctx.hex() in ids:
3492 r = ids[ctx.hex()]
3492 r = ids[ctx.hex()]
3493 ui.warn(_('skipping already grafted revision %d:%s '
3493 ui.warn(_('skipping already grafted revision %d:%s '
3494 '(was grafted from %d:%s)\n') %
3494 '(was grafted from %d:%s)\n') %
3495 (r, repo[r], rev, ctx))
3495 (r, repo[r], rev, ctx))
3496 revs.remove(r)
3496 revs.remove(r)
3497 if not revs:
3497 if not revs:
3498 return -1
3498 return -1
3499
3499
3500 wlock = repo.wlock()
3500 wlock = repo.wlock()
3501 try:
3501 try:
3502 for pos, ctx in enumerate(repo.set("%ld", revs)):
3502 for pos, ctx in enumerate(repo.set("%ld", revs)):
3503 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
3503 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
3504 ctx.description().split('\n', 1)[0])
3504 ctx.description().split('\n', 1)[0])
3505 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3505 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3506 if names:
3506 if names:
3507 desc += ' (%s)' % ' '.join(names)
3507 desc += ' (%s)' % ' '.join(names)
3508 ui.status(_('grafting %s\n') % desc)
3508 ui.status(_('grafting %s\n') % desc)
3509 if opts.get('dry_run'):
3509 if opts.get('dry_run'):
3510 continue
3510 continue
3511
3511
3512 source = ctx.extra().get('source')
3512 source = ctx.extra().get('source')
3513 if not source:
3513 if not source:
3514 source = ctx.hex()
3514 source = ctx.hex()
3515 extra = {'source': source}
3515 extra = {'source': source}
3516 user = ctx.user()
3516 user = ctx.user()
3517 if opts.get('user'):
3517 if opts.get('user'):
3518 user = opts['user']
3518 user = opts['user']
3519 date = ctx.date()
3519 date = ctx.date()
3520 if opts.get('date'):
3520 if opts.get('date'):
3521 date = opts['date']
3521 date = opts['date']
3522 message = ctx.description()
3522 message = ctx.description()
3523 if opts.get('log'):
3523 if opts.get('log'):
3524 message += '\n(grafted from %s)' % ctx.hex()
3524 message += '\n(grafted from %s)' % ctx.hex()
3525
3525
3526 # we don't merge the first commit when continuing
3526 # we don't merge the first commit when continuing
3527 if not cont:
3527 if not cont:
3528 # perform the graft merge with p1(rev) as 'ancestor'
3528 # perform the graft merge with p1(rev) as 'ancestor'
3529 try:
3529 try:
3530 # ui.forcemerge is an internal variable, do not document
3530 # ui.forcemerge is an internal variable, do not document
3531 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
3531 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
3532 'graft')
3532 'graft')
3533 stats = mergemod.graft(repo, ctx, ctx.p1(),
3533 stats = mergemod.graft(repo, ctx, ctx.p1(),
3534 ['local', 'graft'])
3534 ['local', 'graft'])
3535 finally:
3535 finally:
3536 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
3536 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
3537 # report any conflicts
3537 # report any conflicts
3538 if stats and stats[3] > 0:
3538 if stats and stats[3] > 0:
3539 # write out state for --continue
3539 # write out state for --continue
3540 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
3540 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
3541 repo.vfs.write('graftstate', ''.join(nodelines))
3541 repo.vfs.write('graftstate', ''.join(nodelines))
3542 raise util.Abort(
3542 raise util.Abort(
3543 _("unresolved conflicts, can't continue"),
3543 _("unresolved conflicts, can't continue"),
3544 hint=_('use hg resolve and hg graft --continue'))
3544 hint=_('use hg resolve and hg graft --continue'))
3545 else:
3545 else:
3546 cont = False
3546 cont = False
3547
3547
3548 # commit
3548 # commit
3549 node = repo.commit(text=message, user=user,
3549 node = repo.commit(text=message, user=user,
3550 date=date, extra=extra, editor=editor)
3550 date=date, extra=extra, editor=editor)
3551 if node is None:
3551 if node is None:
3552 ui.warn(
3552 ui.warn(
3553 _('note: graft of %d:%s created no changes to commit\n') %
3553 _('note: graft of %d:%s created no changes to commit\n') %
3554 (ctx.rev(), ctx))
3554 (ctx.rev(), ctx))
3555 finally:
3555 finally:
3556 wlock.release()
3556 wlock.release()
3557
3557
3558 # remove state when we complete successfully
3558 # remove state when we complete successfully
3559 if not opts.get('dry_run'):
3559 if not opts.get('dry_run'):
3560 util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
3560 util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
3561
3561
3562 return 0
3562 return 0
3563
3563
3564 @command('grep',
3564 @command('grep',
3565 [('0', 'print0', None, _('end fields with NUL')),
3565 [('0', 'print0', None, _('end fields with NUL')),
3566 ('', 'all', None, _('print all revisions that match')),
3566 ('', 'all', None, _('print all revisions that match')),
3567 ('a', 'text', None, _('treat all files as text')),
3567 ('a', 'text', None, _('treat all files as text')),
3568 ('f', 'follow', None,
3568 ('f', 'follow', None,
3569 _('follow changeset history,'
3569 _('follow changeset history,'
3570 ' or file history across copies and renames')),
3570 ' or file history across copies and renames')),
3571 ('i', 'ignore-case', None, _('ignore case when matching')),
3571 ('i', 'ignore-case', None, _('ignore case when matching')),
3572 ('l', 'files-with-matches', None,
3572 ('l', 'files-with-matches', None,
3573 _('print only filenames and revisions that match')),
3573 _('print only filenames and revisions that match')),
3574 ('n', 'line-number', None, _('print matching line numbers')),
3574 ('n', 'line-number', None, _('print matching line numbers')),
3575 ('r', 'rev', [],
3575 ('r', 'rev', [],
3576 _('only search files changed within revision range'), _('REV')),
3576 _('only search files changed within revision range'), _('REV')),
3577 ('u', 'user', None, _('list the author (long with -v)')),
3577 ('u', 'user', None, _('list the author (long with -v)')),
3578 ('d', 'date', None, _('list the date (short with -q)')),
3578 ('d', 'date', None, _('list the date (short with -q)')),
3579 ] + walkopts,
3579 ] + walkopts,
3580 _('[OPTION]... PATTERN [FILE]...'),
3580 _('[OPTION]... PATTERN [FILE]...'),
3581 inferrepo=True)
3581 inferrepo=True)
3582 def grep(ui, repo, pattern, *pats, **opts):
3582 def grep(ui, repo, pattern, *pats, **opts):
3583 """search for a pattern in specified files and revisions
3583 """search for a pattern in specified files and revisions
3584
3584
3585 Search revisions of files for a regular expression.
3585 Search revisions of files for a regular expression.
3586
3586
3587 This command behaves differently than Unix grep. It only accepts
3587 This command behaves differently than Unix grep. It only accepts
3588 Python/Perl regexps. It searches repository history, not the
3588 Python/Perl regexps. It searches repository history, not the
3589 working directory. It always prints the revision number in which a
3589 working directory. It always prints the revision number in which a
3590 match appears.
3590 match appears.
3591
3591
3592 By default, grep only prints output for the first revision of a
3592 By default, grep only prints output for the first revision of a
3593 file in which it finds a match. To get it to print every revision
3593 file in which it finds a match. To get it to print every revision
3594 that contains a change in match status ("-" for a match that
3594 that contains a change in match status ("-" for a match that
3595 becomes a non-match, or "+" for a non-match that becomes a match),
3595 becomes a non-match, or "+" for a non-match that becomes a match),
3596 use the --all flag.
3596 use the --all flag.
3597
3597
3598 Returns 0 if a match is found, 1 otherwise.
3598 Returns 0 if a match is found, 1 otherwise.
3599 """
3599 """
3600 reflags = re.M
3600 reflags = re.M
3601 if opts.get('ignore_case'):
3601 if opts.get('ignore_case'):
3602 reflags |= re.I
3602 reflags |= re.I
3603 try:
3603 try:
3604 regexp = util.re.compile(pattern, reflags)
3604 regexp = util.re.compile(pattern, reflags)
3605 except re.error, inst:
3605 except re.error, inst:
3606 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
3606 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
3607 return 1
3607 return 1
3608 sep, eol = ':', '\n'
3608 sep, eol = ':', '\n'
3609 if opts.get('print0'):
3609 if opts.get('print0'):
3610 sep = eol = '\0'
3610 sep = eol = '\0'
3611
3611
3612 getfile = util.lrucachefunc(repo.file)
3612 getfile = util.lrucachefunc(repo.file)
3613
3613
3614 def matchlines(body):
3614 def matchlines(body):
3615 begin = 0
3615 begin = 0
3616 linenum = 0
3616 linenum = 0
3617 while begin < len(body):
3617 while begin < len(body):
3618 match = regexp.search(body, begin)
3618 match = regexp.search(body, begin)
3619 if not match:
3619 if not match:
3620 break
3620 break
3621 mstart, mend = match.span()
3621 mstart, mend = match.span()
3622 linenum += body.count('\n', begin, mstart) + 1
3622 linenum += body.count('\n', begin, mstart) + 1
3623 lstart = body.rfind('\n', begin, mstart) + 1 or begin
3623 lstart = body.rfind('\n', begin, mstart) + 1 or begin
3624 begin = body.find('\n', mend) + 1 or len(body) + 1
3624 begin = body.find('\n', mend) + 1 or len(body) + 1
3625 lend = begin - 1
3625 lend = begin - 1
3626 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
3626 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
3627
3627
3628 class linestate(object):
3628 class linestate(object):
3629 def __init__(self, line, linenum, colstart, colend):
3629 def __init__(self, line, linenum, colstart, colend):
3630 self.line = line
3630 self.line = line
3631 self.linenum = linenum
3631 self.linenum = linenum
3632 self.colstart = colstart
3632 self.colstart = colstart
3633 self.colend = colend
3633 self.colend = colend
3634
3634
3635 def __hash__(self):
3635 def __hash__(self):
3636 return hash((self.linenum, self.line))
3636 return hash((self.linenum, self.line))
3637
3637
3638 def __eq__(self, other):
3638 def __eq__(self, other):
3639 return self.line == other.line
3639 return self.line == other.line
3640
3640
3641 def __iter__(self):
3641 def __iter__(self):
3642 yield (self.line[:self.colstart], '')
3642 yield (self.line[:self.colstart], '')
3643 yield (self.line[self.colstart:self.colend], 'grep.match')
3643 yield (self.line[self.colstart:self.colend], 'grep.match')
3644 rest = self.line[self.colend:]
3644 rest = self.line[self.colend:]
3645 while rest != '':
3645 while rest != '':
3646 match = regexp.search(rest)
3646 match = regexp.search(rest)
3647 if not match:
3647 if not match:
3648 yield (rest, '')
3648 yield (rest, '')
3649 break
3649 break
3650 mstart, mend = match.span()
3650 mstart, mend = match.span()
3651 yield (rest[:mstart], '')
3651 yield (rest[:mstart], '')
3652 yield (rest[mstart:mend], 'grep.match')
3652 yield (rest[mstart:mend], 'grep.match')
3653 rest = rest[mend:]
3653 rest = rest[mend:]
3654
3654
3655 matches = {}
3655 matches = {}
3656 copies = {}
3656 copies = {}
3657 def grepbody(fn, rev, body):
3657 def grepbody(fn, rev, body):
3658 matches[rev].setdefault(fn, [])
3658 matches[rev].setdefault(fn, [])
3659 m = matches[rev][fn]
3659 m = matches[rev][fn]
3660 for lnum, cstart, cend, line in matchlines(body):
3660 for lnum, cstart, cend, line in matchlines(body):
3661 s = linestate(line, lnum, cstart, cend)
3661 s = linestate(line, lnum, cstart, cend)
3662 m.append(s)
3662 m.append(s)
3663
3663
3664 def difflinestates(a, b):
3664 def difflinestates(a, b):
3665 sm = difflib.SequenceMatcher(None, a, b)
3665 sm = difflib.SequenceMatcher(None, a, b)
3666 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3666 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3667 if tag == 'insert':
3667 if tag == 'insert':
3668 for i in xrange(blo, bhi):
3668 for i in xrange(blo, bhi):
3669 yield ('+', b[i])
3669 yield ('+', b[i])
3670 elif tag == 'delete':
3670 elif tag == 'delete':
3671 for i in xrange(alo, ahi):
3671 for i in xrange(alo, ahi):
3672 yield ('-', a[i])
3672 yield ('-', a[i])
3673 elif tag == 'replace':
3673 elif tag == 'replace':
3674 for i in xrange(alo, ahi):
3674 for i in xrange(alo, ahi):
3675 yield ('-', a[i])
3675 yield ('-', a[i])
3676 for i in xrange(blo, bhi):
3676 for i in xrange(blo, bhi):
3677 yield ('+', b[i])
3677 yield ('+', b[i])
3678
3678
3679 def display(fn, ctx, pstates, states):
3679 def display(fn, ctx, pstates, states):
3680 rev = ctx.rev()
3680 rev = ctx.rev()
3681 if ui.quiet:
3681 if ui.quiet:
3682 datefunc = util.shortdate
3682 datefunc = util.shortdate
3683 else:
3683 else:
3684 datefunc = util.datestr
3684 datefunc = util.datestr
3685 found = False
3685 found = False
3686 @util.cachefunc
3686 @util.cachefunc
3687 def binary():
3687 def binary():
3688 flog = getfile(fn)
3688 flog = getfile(fn)
3689 return util.binary(flog.read(ctx.filenode(fn)))
3689 return util.binary(flog.read(ctx.filenode(fn)))
3690
3690
3691 if opts.get('all'):
3691 if opts.get('all'):
3692 iter = difflinestates(pstates, states)
3692 iter = difflinestates(pstates, states)
3693 else:
3693 else:
3694 iter = [('', l) for l in states]
3694 iter = [('', l) for l in states]
3695 for change, l in iter:
3695 for change, l in iter:
3696 cols = [(fn, 'grep.filename'), (str(rev), 'grep.rev')]
3696 cols = [(fn, 'grep.filename'), (str(rev), 'grep.rev')]
3697
3697
3698 if opts.get('line_number'):
3698 if opts.get('line_number'):
3699 cols.append((str(l.linenum), 'grep.linenumber'))
3699 cols.append((str(l.linenum), 'grep.linenumber'))
3700 if opts.get('all'):
3700 if opts.get('all'):
3701 cols.append((change, 'grep.change'))
3701 cols.append((change, 'grep.change'))
3702 if opts.get('user'):
3702 if opts.get('user'):
3703 cols.append((ui.shortuser(ctx.user()), 'grep.user'))
3703 cols.append((ui.shortuser(ctx.user()), 'grep.user'))
3704 if opts.get('date'):
3704 if opts.get('date'):
3705 cols.append((datefunc(ctx.date()), 'grep.date'))
3705 cols.append((datefunc(ctx.date()), 'grep.date'))
3706 for col, label in cols[:-1]:
3706 for col, label in cols[:-1]:
3707 ui.write(col, label=label)
3707 ui.write(col, label=label)
3708 ui.write(sep, label='grep.sep')
3708 ui.write(sep, label='grep.sep')
3709 ui.write(cols[-1][0], label=cols[-1][1])
3709 ui.write(cols[-1][0], label=cols[-1][1])
3710 if not opts.get('files_with_matches'):
3710 if not opts.get('files_with_matches'):
3711 ui.write(sep, label='grep.sep')
3711 ui.write(sep, label='grep.sep')
3712 if not opts.get('text') and binary():
3712 if not opts.get('text') and binary():
3713 ui.write(" Binary file matches")
3713 ui.write(" Binary file matches")
3714 else:
3714 else:
3715 for s, label in l:
3715 for s, label in l:
3716 ui.write(s, label=label)
3716 ui.write(s, label=label)
3717 ui.write(eol)
3717 ui.write(eol)
3718 found = True
3718 found = True
3719 if opts.get('files_with_matches'):
3719 if opts.get('files_with_matches'):
3720 break
3720 break
3721 return found
3721 return found
3722
3722
3723 skip = {}
3723 skip = {}
3724 revfiles = {}
3724 revfiles = {}
3725 matchfn = scmutil.match(repo[None], pats, opts)
3725 matchfn = scmutil.match(repo[None], pats, opts)
3726 found = False
3726 found = False
3727 follow = opts.get('follow')
3727 follow = opts.get('follow')
3728
3728
3729 def prep(ctx, fns):
3729 def prep(ctx, fns):
3730 rev = ctx.rev()
3730 rev = ctx.rev()
3731 pctx = ctx.p1()
3731 pctx = ctx.p1()
3732 parent = pctx.rev()
3732 parent = pctx.rev()
3733 matches.setdefault(rev, {})
3733 matches.setdefault(rev, {})
3734 matches.setdefault(parent, {})
3734 matches.setdefault(parent, {})
3735 files = revfiles.setdefault(rev, [])
3735 files = revfiles.setdefault(rev, [])
3736 for fn in fns:
3736 for fn in fns:
3737 flog = getfile(fn)
3737 flog = getfile(fn)
3738 try:
3738 try:
3739 fnode = ctx.filenode(fn)
3739 fnode = ctx.filenode(fn)
3740 except error.LookupError:
3740 except error.LookupError:
3741 continue
3741 continue
3742
3742
3743 copied = flog.renamed(fnode)
3743 copied = flog.renamed(fnode)
3744 copy = follow and copied and copied[0]
3744 copy = follow and copied and copied[0]
3745 if copy:
3745 if copy:
3746 copies.setdefault(rev, {})[fn] = copy
3746 copies.setdefault(rev, {})[fn] = copy
3747 if fn in skip:
3747 if fn in skip:
3748 if copy:
3748 if copy:
3749 skip[copy] = True
3749 skip[copy] = True
3750 continue
3750 continue
3751 files.append(fn)
3751 files.append(fn)
3752
3752
3753 if fn not in matches[rev]:
3753 if fn not in matches[rev]:
3754 grepbody(fn, rev, flog.read(fnode))
3754 grepbody(fn, rev, flog.read(fnode))
3755
3755
3756 pfn = copy or fn
3756 pfn = copy or fn
3757 if pfn not in matches[parent]:
3757 if pfn not in matches[parent]:
3758 try:
3758 try:
3759 fnode = pctx.filenode(pfn)
3759 fnode = pctx.filenode(pfn)
3760 grepbody(pfn, parent, flog.read(fnode))
3760 grepbody(pfn, parent, flog.read(fnode))
3761 except error.LookupError:
3761 except error.LookupError:
3762 pass
3762 pass
3763
3763
3764 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3764 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3765 rev = ctx.rev()
3765 rev = ctx.rev()
3766 parent = ctx.p1().rev()
3766 parent = ctx.p1().rev()
3767 for fn in sorted(revfiles.get(rev, [])):
3767 for fn in sorted(revfiles.get(rev, [])):
3768 states = matches[rev][fn]
3768 states = matches[rev][fn]
3769 copy = copies.get(rev, {}).get(fn)
3769 copy = copies.get(rev, {}).get(fn)
3770 if fn in skip:
3770 if fn in skip:
3771 if copy:
3771 if copy:
3772 skip[copy] = True
3772 skip[copy] = True
3773 continue
3773 continue
3774 pstates = matches.get(parent, {}).get(copy or fn, [])
3774 pstates = matches.get(parent, {}).get(copy or fn, [])
3775 if pstates or states:
3775 if pstates or states:
3776 r = display(fn, ctx, pstates, states)
3776 r = display(fn, ctx, pstates, states)
3777 found = found or r
3777 found = found or r
3778 if r and not opts.get('all'):
3778 if r and not opts.get('all'):
3779 skip[fn] = True
3779 skip[fn] = True
3780 if copy:
3780 if copy:
3781 skip[copy] = True
3781 skip[copy] = True
3782 del matches[rev]
3782 del matches[rev]
3783 del revfiles[rev]
3783 del revfiles[rev]
3784
3784
3785 return not found
3785 return not found
3786
3786
3787 @command('heads',
3787 @command('heads',
3788 [('r', 'rev', '',
3788 [('r', 'rev', '',
3789 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3789 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3790 ('t', 'topo', False, _('show topological heads only')),
3790 ('t', 'topo', False, _('show topological heads only')),
3791 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3791 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3792 ('c', 'closed', False, _('show normal and closed branch heads')),
3792 ('c', 'closed', False, _('show normal and closed branch heads')),
3793 ] + templateopts,
3793 ] + templateopts,
3794 _('[-ct] [-r STARTREV] [REV]...'))
3794 _('[-ct] [-r STARTREV] [REV]...'))
3795 def heads(ui, repo, *branchrevs, **opts):
3795 def heads(ui, repo, *branchrevs, **opts):
3796 """show branch heads
3796 """show branch heads
3797
3797
3798 With no arguments, show all open branch heads in the repository.
3798 With no arguments, show all open branch heads in the repository.
3799 Branch heads are changesets that have no descendants on the
3799 Branch heads are changesets that have no descendants on the
3800 same branch. They are where development generally takes place and
3800 same branch. They are where development generally takes place and
3801 are the usual targets for update and merge operations.
3801 are the usual targets for update and merge operations.
3802
3802
3803 If one or more REVs are given, only open branch heads on the
3803 If one or more REVs are given, only open branch heads on the
3804 branches associated with the specified changesets are shown. This
3804 branches associated with the specified changesets are shown. This
3805 means that you can use :hg:`heads .` to see the heads on the
3805 means that you can use :hg:`heads .` to see the heads on the
3806 currently checked-out branch.
3806 currently checked-out branch.
3807
3807
3808 If -c/--closed is specified, also show branch heads marked closed
3808 If -c/--closed is specified, also show branch heads marked closed
3809 (see :hg:`commit --close-branch`).
3809 (see :hg:`commit --close-branch`).
3810
3810
3811 If STARTREV is specified, only those heads that are descendants of
3811 If STARTREV is specified, only those heads that are descendants of
3812 STARTREV will be displayed.
3812 STARTREV will be displayed.
3813
3813
3814 If -t/--topo is specified, named branch mechanics will be ignored and only
3814 If -t/--topo is specified, named branch mechanics will be ignored and only
3815 topological heads (changesets with no children) will be shown.
3815 topological heads (changesets with no children) will be shown.
3816
3816
3817 Returns 0 if matching heads are found, 1 if not.
3817 Returns 0 if matching heads are found, 1 if not.
3818 """
3818 """
3819
3819
3820 start = None
3820 start = None
3821 if 'rev' in opts:
3821 if 'rev' in opts:
3822 start = scmutil.revsingle(repo, opts['rev'], None).node()
3822 start = scmutil.revsingle(repo, opts['rev'], None).node()
3823
3823
3824 if opts.get('topo'):
3824 if opts.get('topo'):
3825 heads = [repo[h] for h in repo.heads(start)]
3825 heads = [repo[h] for h in repo.heads(start)]
3826 else:
3826 else:
3827 heads = []
3827 heads = []
3828 for branch in repo.branchmap():
3828 for branch in repo.branchmap():
3829 heads += repo.branchheads(branch, start, opts.get('closed'))
3829 heads += repo.branchheads(branch, start, opts.get('closed'))
3830 heads = [repo[h] for h in heads]
3830 heads = [repo[h] for h in heads]
3831
3831
3832 if branchrevs:
3832 if branchrevs:
3833 branches = set(repo[br].branch() for br in branchrevs)
3833 branches = set(repo[br].branch() for br in branchrevs)
3834 heads = [h for h in heads if h.branch() in branches]
3834 heads = [h for h in heads if h.branch() in branches]
3835
3835
3836 if opts.get('active') and branchrevs:
3836 if opts.get('active') and branchrevs:
3837 dagheads = repo.heads(start)
3837 dagheads = repo.heads(start)
3838 heads = [h for h in heads if h.node() in dagheads]
3838 heads = [h for h in heads if h.node() in dagheads]
3839
3839
3840 if branchrevs:
3840 if branchrevs:
3841 haveheads = set(h.branch() for h in heads)
3841 haveheads = set(h.branch() for h in heads)
3842 if branches - haveheads:
3842 if branches - haveheads:
3843 headless = ', '.join(b for b in branches - haveheads)
3843 headless = ', '.join(b for b in branches - haveheads)
3844 msg = _('no open branch heads found on branches %s')
3844 msg = _('no open branch heads found on branches %s')
3845 if opts.get('rev'):
3845 if opts.get('rev'):
3846 msg += _(' (started at %s)') % opts['rev']
3846 msg += _(' (started at %s)') % opts['rev']
3847 ui.warn((msg + '\n') % headless)
3847 ui.warn((msg + '\n') % headless)
3848
3848
3849 if not heads:
3849 if not heads:
3850 return 1
3850 return 1
3851
3851
3852 heads = sorted(heads, key=lambda x: -x.rev())
3852 heads = sorted(heads, key=lambda x: -x.rev())
3853 displayer = cmdutil.show_changeset(ui, repo, opts)
3853 displayer = cmdutil.show_changeset(ui, repo, opts)
3854 for ctx in heads:
3854 for ctx in heads:
3855 displayer.show(ctx)
3855 displayer.show(ctx)
3856 displayer.close()
3856 displayer.close()
3857
3857
3858 @command('help',
3858 @command('help',
3859 [('e', 'extension', None, _('show only help for extensions')),
3859 [('e', 'extension', None, _('show only help for extensions')),
3860 ('c', 'command', None, _('show only help for commands')),
3860 ('c', 'command', None, _('show only help for commands')),
3861 ('k', 'keyword', '', _('show topics matching keyword')),
3861 ('k', 'keyword', '', _('show topics matching keyword')),
3862 ],
3862 ],
3863 _('[-ec] [TOPIC]'),
3863 _('[-ec] [TOPIC]'),
3864 norepo=True)
3864 norepo=True)
3865 def help_(ui, name=None, **opts):
3865 def help_(ui, name=None, **opts):
3866 """show help for a given topic or a help overview
3866 """show help for a given topic or a help overview
3867
3867
3868 With no arguments, print a list of commands with short help messages.
3868 With no arguments, print a list of commands with short help messages.
3869
3869
3870 Given a topic, extension, or command name, print help for that
3870 Given a topic, extension, or command name, print help for that
3871 topic.
3871 topic.
3872
3872
3873 Returns 0 if successful.
3873 Returns 0 if successful.
3874 """
3874 """
3875
3875
3876 textwidth = min(ui.termwidth(), 80) - 2
3876 textwidth = min(ui.termwidth(), 80) - 2
3877
3877
3878 keep = []
3878 keep = []
3879 if ui.verbose:
3879 if ui.verbose:
3880 keep.append('verbose')
3880 keep.append('verbose')
3881 if sys.platform.startswith('win'):
3881 if sys.platform.startswith('win'):
3882 keep.append('windows')
3882 keep.append('windows')
3883 elif sys.platform == 'OpenVMS':
3883 elif sys.platform == 'OpenVMS':
3884 keep.append('vms')
3884 keep.append('vms')
3885 elif sys.platform == 'plan9':
3885 elif sys.platform == 'plan9':
3886 keep.append('plan9')
3886 keep.append('plan9')
3887 else:
3887 else:
3888 keep.append('unix')
3888 keep.append('unix')
3889 keep.append(sys.platform.lower())
3889 keep.append(sys.platform.lower())
3890
3890
3891 section = None
3891 section = None
3892 if name and '.' in name:
3892 if name and '.' in name:
3893 name, section = name.split('.', 1)
3893 name, section = name.split('.', 1)
3894
3894
3895 text = help.help_(ui, name, **opts)
3895 text = help.help_(ui, name, **opts)
3896
3896
3897 formatted, pruned = minirst.format(text, textwidth, keep=keep,
3897 formatted, pruned = minirst.format(text, textwidth, keep=keep,
3898 section=section)
3898 section=section)
3899 if section and not formatted:
3899 if section and not formatted:
3900 raise util.Abort(_("help section not found"))
3900 raise util.Abort(_("help section not found"))
3901
3901
3902 if 'verbose' in pruned:
3902 if 'verbose' in pruned:
3903 keep.append('omitted')
3903 keep.append('omitted')
3904 else:
3904 else:
3905 keep.append('notomitted')
3905 keep.append('notomitted')
3906 formatted, pruned = minirst.format(text, textwidth, keep=keep,
3906 formatted, pruned = minirst.format(text, textwidth, keep=keep,
3907 section=section)
3907 section=section)
3908 ui.write(formatted)
3908 ui.write(formatted)
3909
3909
3910
3910
3911 @command('identify|id',
3911 @command('identify|id',
3912 [('r', 'rev', '',
3912 [('r', 'rev', '',
3913 _('identify the specified revision'), _('REV')),
3913 _('identify the specified revision'), _('REV')),
3914 ('n', 'num', None, _('show local revision number')),
3914 ('n', 'num', None, _('show local revision number')),
3915 ('i', 'id', None, _('show global revision id')),
3915 ('i', 'id', None, _('show global revision id')),
3916 ('b', 'branch', None, _('show branch')),
3916 ('b', 'branch', None, _('show branch')),
3917 ('t', 'tags', None, _('show tags')),
3917 ('t', 'tags', None, _('show tags')),
3918 ('B', 'bookmarks', None, _('show bookmarks')),
3918 ('B', 'bookmarks', None, _('show bookmarks')),
3919 ] + remoteopts,
3919 ] + remoteopts,
3920 _('[-nibtB] [-r REV] [SOURCE]'),
3920 _('[-nibtB] [-r REV] [SOURCE]'),
3921 optionalrepo=True)
3921 optionalrepo=True)
3922 def identify(ui, repo, source=None, rev=None,
3922 def identify(ui, repo, source=None, rev=None,
3923 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3923 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3924 """identify the working directory or specified revision
3924 """identify the working directory or specified revision
3925
3925
3926 Print a summary identifying the repository state at REV using one or
3926 Print a summary identifying the repository state at REV using one or
3927 two parent hash identifiers, followed by a "+" if the working
3927 two parent hash identifiers, followed by a "+" if the working
3928 directory has uncommitted changes, the branch name (if not default),
3928 directory has uncommitted changes, the branch name (if not default),
3929 a list of tags, and a list of bookmarks.
3929 a list of tags, and a list of bookmarks.
3930
3930
3931 When REV is not given, print a summary of the current state of the
3931 When REV is not given, print a summary of the current state of the
3932 repository.
3932 repository.
3933
3933
3934 Specifying a path to a repository root or Mercurial bundle will
3934 Specifying a path to a repository root or Mercurial bundle will
3935 cause lookup to operate on that repository/bundle.
3935 cause lookup to operate on that repository/bundle.
3936
3936
3937 .. container:: verbose
3937 .. container:: verbose
3938
3938
3939 Examples:
3939 Examples:
3940
3940
3941 - generate a build identifier for the working directory::
3941 - generate a build identifier for the working directory::
3942
3942
3943 hg id --id > build-id.dat
3943 hg id --id > build-id.dat
3944
3944
3945 - find the revision corresponding to a tag::
3945 - find the revision corresponding to a tag::
3946
3946
3947 hg id -n -r 1.3
3947 hg id -n -r 1.3
3948
3948
3949 - check the most recent revision of a remote repository::
3949 - check the most recent revision of a remote repository::
3950
3950
3951 hg id -r tip http://selenic.com/hg/
3951 hg id -r tip http://selenic.com/hg/
3952
3952
3953 Returns 0 if successful.
3953 Returns 0 if successful.
3954 """
3954 """
3955
3955
3956 if not repo and not source:
3956 if not repo and not source:
3957 raise util.Abort(_("there is no Mercurial repository here "
3957 raise util.Abort(_("there is no Mercurial repository here "
3958 "(.hg not found)"))
3958 "(.hg not found)"))
3959
3959
3960 if ui.debugflag:
3960 if ui.debugflag:
3961 hexfunc = hex
3961 hexfunc = hex
3962 else:
3962 else:
3963 hexfunc = short
3963 hexfunc = short
3964 default = not (num or id or branch or tags or bookmarks)
3964 default = not (num or id or branch or tags or bookmarks)
3965 output = []
3965 output = []
3966 revs = []
3966 revs = []
3967
3967
3968 if source:
3968 if source:
3969 source, branches = hg.parseurl(ui.expandpath(source))
3969 source, branches = hg.parseurl(ui.expandpath(source))
3970 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3970 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3971 repo = peer.local()
3971 repo = peer.local()
3972 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3972 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3973
3973
3974 if not repo:
3974 if not repo:
3975 if num or branch or tags:
3975 if num or branch or tags:
3976 raise util.Abort(
3976 raise util.Abort(
3977 _("can't query remote revision number, branch, or tags"))
3977 _("can't query remote revision number, branch, or tags"))
3978 if not rev and revs:
3978 if not rev and revs:
3979 rev = revs[0]
3979 rev = revs[0]
3980 if not rev:
3980 if not rev:
3981 rev = "tip"
3981 rev = "tip"
3982
3982
3983 remoterev = peer.lookup(rev)
3983 remoterev = peer.lookup(rev)
3984 if default or id:
3984 if default or id:
3985 output = [hexfunc(remoterev)]
3985 output = [hexfunc(remoterev)]
3986
3986
3987 def getbms():
3987 def getbms():
3988 bms = []
3988 bms = []
3989
3989
3990 if 'bookmarks' in peer.listkeys('namespaces'):
3990 if 'bookmarks' in peer.listkeys('namespaces'):
3991 hexremoterev = hex(remoterev)
3991 hexremoterev = hex(remoterev)
3992 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
3992 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
3993 if bmr == hexremoterev]
3993 if bmr == hexremoterev]
3994
3994
3995 return sorted(bms)
3995 return sorted(bms)
3996
3996
3997 if bookmarks:
3997 if bookmarks:
3998 output.extend(getbms())
3998 output.extend(getbms())
3999 elif default and not ui.quiet:
3999 elif default and not ui.quiet:
4000 # multiple bookmarks for a single parent separated by '/'
4000 # multiple bookmarks for a single parent separated by '/'
4001 bm = '/'.join(getbms())
4001 bm = '/'.join(getbms())
4002 if bm:
4002 if bm:
4003 output.append(bm)
4003 output.append(bm)
4004 else:
4004 else:
4005 if not rev:
4005 if not rev:
4006 ctx = repo[None]
4006 ctx = repo[None]
4007 parents = ctx.parents()
4007 parents = ctx.parents()
4008 changed = ""
4008 changed = ""
4009 if default or id or num:
4009 if default or id or num:
4010 if (util.any(repo.status())
4010 if (util.any(repo.status())
4011 or util.any(ctx.sub(s).dirty() for s in ctx.substate)):
4011 or util.any(ctx.sub(s).dirty() for s in ctx.substate)):
4012 changed = '+'
4012 changed = '+'
4013 if default or id:
4013 if default or id:
4014 output = ["%s%s" %
4014 output = ["%s%s" %
4015 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
4015 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
4016 if num:
4016 if num:
4017 output.append("%s%s" %
4017 output.append("%s%s" %
4018 ('+'.join([str(p.rev()) for p in parents]), changed))
4018 ('+'.join([str(p.rev()) for p in parents]), changed))
4019 else:
4019 else:
4020 ctx = scmutil.revsingle(repo, rev)
4020 ctx = scmutil.revsingle(repo, rev)
4021 if default or id:
4021 if default or id:
4022 output = [hexfunc(ctx.node())]
4022 output = [hexfunc(ctx.node())]
4023 if num:
4023 if num:
4024 output.append(str(ctx.rev()))
4024 output.append(str(ctx.rev()))
4025
4025
4026 if default and not ui.quiet:
4026 if default and not ui.quiet:
4027 b = ctx.branch()
4027 b = ctx.branch()
4028 if b != 'default':
4028 if b != 'default':
4029 output.append("(%s)" % b)
4029 output.append("(%s)" % b)
4030
4030
4031 # multiple tags for a single parent separated by '/'
4031 # multiple tags for a single parent separated by '/'
4032 t = '/'.join(ctx.tags())
4032 t = '/'.join(ctx.tags())
4033 if t:
4033 if t:
4034 output.append(t)
4034 output.append(t)
4035
4035
4036 # multiple bookmarks for a single parent separated by '/'
4036 # multiple bookmarks for a single parent separated by '/'
4037 bm = '/'.join(ctx.bookmarks())
4037 bm = '/'.join(ctx.bookmarks())
4038 if bm:
4038 if bm:
4039 output.append(bm)
4039 output.append(bm)
4040 else:
4040 else:
4041 if branch:
4041 if branch:
4042 output.append(ctx.branch())
4042 output.append(ctx.branch())
4043
4043
4044 if tags:
4044 if tags:
4045 output.extend(ctx.tags())
4045 output.extend(ctx.tags())
4046
4046
4047 if bookmarks:
4047 if bookmarks:
4048 output.extend(ctx.bookmarks())
4048 output.extend(ctx.bookmarks())
4049
4049
4050 ui.write("%s\n" % ' '.join(output))
4050 ui.write("%s\n" % ' '.join(output))
4051
4051
4052 @command('import|patch',
4052 @command('import|patch',
4053 [('p', 'strip', 1,
4053 [('p', 'strip', 1,
4054 _('directory strip option for patch. This has the same '
4054 _('directory strip option for patch. This has the same '
4055 'meaning as the corresponding patch option'), _('NUM')),
4055 'meaning as the corresponding patch option'), _('NUM')),
4056 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
4056 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
4057 ('e', 'edit', False, _('invoke editor on commit messages')),
4057 ('e', 'edit', False, _('invoke editor on commit messages')),
4058 ('f', 'force', None,
4058 ('f', 'force', None,
4059 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
4059 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
4060 ('', 'no-commit', None,
4060 ('', 'no-commit', None,
4061 _("don't commit, just update the working directory")),
4061 _("don't commit, just update the working directory")),
4062 ('', 'bypass', None,
4062 ('', 'bypass', None,
4063 _("apply patch without touching the working directory")),
4063 _("apply patch without touching the working directory")),
4064 ('', 'partial', None,
4064 ('', 'partial', None,
4065 _('commit even if some hunks fail')),
4065 _('commit even if some hunks fail')),
4066 ('', 'exact', None,
4066 ('', 'exact', None,
4067 _('apply patch to the nodes from which it was generated')),
4067 _('apply patch to the nodes from which it was generated')),
4068 ('', 'prefix', '',
4068 ('', 'prefix', '',
4069 _('apply patch to directory relative to the root'), _('DIR')),
4069 _('apply patch to subdirectory'), _('DIR')),
4070 ('', 'import-branch', None,
4070 ('', 'import-branch', None,
4071 _('use any branch information in patch (implied by --exact)'))] +
4071 _('use any branch information in patch (implied by --exact)'))] +
4072 commitopts + commitopts2 + similarityopts,
4072 commitopts + commitopts2 + similarityopts,
4073 _('[OPTION]... PATCH...'))
4073 _('[OPTION]... PATCH...'))
4074 def import_(ui, repo, patch1=None, *patches, **opts):
4074 def import_(ui, repo, patch1=None, *patches, **opts):
4075 """import an ordered set of patches
4075 """import an ordered set of patches
4076
4076
4077 Import a list of patches and commit them individually (unless
4077 Import a list of patches and commit them individually (unless
4078 --no-commit is specified).
4078 --no-commit is specified).
4079
4079
4080 Because import first applies changes to the working directory,
4080 Because import first applies changes to the working directory,
4081 import will abort if there are outstanding changes.
4081 import will abort if there are outstanding changes.
4082
4082
4083 You can import a patch straight from a mail message. Even patches
4083 You can import a patch straight from a mail message. Even patches
4084 as attachments work (to use the body part, it must have type
4084 as attachments work (to use the body part, it must have type
4085 text/plain or text/x-patch). From and Subject headers of email
4085 text/plain or text/x-patch). From and Subject headers of email
4086 message are used as default committer and commit message. All
4086 message are used as default committer and commit message. All
4087 text/plain body parts before first diff are added to commit
4087 text/plain body parts before first diff are added to commit
4088 message.
4088 message.
4089
4089
4090 If the imported patch was generated by :hg:`export`, user and
4090 If the imported patch was generated by :hg:`export`, user and
4091 description from patch override values from message headers and
4091 description from patch override values from message headers and
4092 body. Values given on command line with -m/--message and -u/--user
4092 body. Values given on command line with -m/--message and -u/--user
4093 override these.
4093 override these.
4094
4094
4095 If --exact is specified, import will set the working directory to
4095 If --exact is specified, import will set the working directory to
4096 the parent of each patch before applying it, and will abort if the
4096 the parent of each patch before applying it, and will abort if the
4097 resulting changeset has a different ID than the one recorded in
4097 resulting changeset has a different ID than the one recorded in
4098 the patch. This may happen due to character set problems or other
4098 the patch. This may happen due to character set problems or other
4099 deficiencies in the text patch format.
4099 deficiencies in the text patch format.
4100
4100
4101 Use --bypass to apply and commit patches directly to the
4101 Use --bypass to apply and commit patches directly to the
4102 repository, not touching the working directory. Without --exact,
4102 repository, not touching the working directory. Without --exact,
4103 patches will be applied on top of the working directory parent
4103 patches will be applied on top of the working directory parent
4104 revision.
4104 revision.
4105
4105
4106 With -s/--similarity, hg will attempt to discover renames and
4106 With -s/--similarity, hg will attempt to discover renames and
4107 copies in the patch in the same way as :hg:`addremove`.
4107 copies in the patch in the same way as :hg:`addremove`.
4108
4108
4109 Use --partial to ensure a changeset will be created from the patch
4109 Use --partial to ensure a changeset will be created from the patch
4110 even if some hunks fail to apply. Hunks that fail to apply will be
4110 even if some hunks fail to apply. Hunks that fail to apply will be
4111 written to a <target-file>.rej file. Conflicts can then be resolved
4111 written to a <target-file>.rej file. Conflicts can then be resolved
4112 by hand before :hg:`commit --amend` is run to update the created
4112 by hand before :hg:`commit --amend` is run to update the created
4113 changeset. This flag exists to let people import patches that
4113 changeset. This flag exists to let people import patches that
4114 partially apply without losing the associated metadata (author,
4114 partially apply without losing the associated metadata (author,
4115 date, description, ...). Note that when none of the hunk applies
4115 date, description, ...). Note that when none of the hunk applies
4116 cleanly, :hg:`import --partial` will create an empty changeset,
4116 cleanly, :hg:`import --partial` will create an empty changeset,
4117 importing only the patch metadata.
4117 importing only the patch metadata.
4118
4118
4119 To read a patch from standard input, use "-" as the patch name. If
4119 To read a patch from standard input, use "-" as the patch name. If
4120 a URL is specified, the patch will be downloaded from it.
4120 a URL is specified, the patch will be downloaded from it.
4121 See :hg:`help dates` for a list of formats valid for -d/--date.
4121 See :hg:`help dates` for a list of formats valid for -d/--date.
4122
4122
4123 .. container:: verbose
4123 .. container:: verbose
4124
4124
4125 Examples:
4125 Examples:
4126
4126
4127 - import a traditional patch from a website and detect renames::
4127 - import a traditional patch from a website and detect renames::
4128
4128
4129 hg import -s 80 http://example.com/bugfix.patch
4129 hg import -s 80 http://example.com/bugfix.patch
4130
4130
4131 - import a changeset from an hgweb server::
4131 - import a changeset from an hgweb server::
4132
4132
4133 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
4133 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
4134
4134
4135 - import all the patches in an Unix-style mbox::
4135 - import all the patches in an Unix-style mbox::
4136
4136
4137 hg import incoming-patches.mbox
4137 hg import incoming-patches.mbox
4138
4138
4139 - attempt to exactly restore an exported changeset (not always
4139 - attempt to exactly restore an exported changeset (not always
4140 possible)::
4140 possible)::
4141
4141
4142 hg import --exact proposed-fix.patch
4142 hg import --exact proposed-fix.patch
4143
4143
4144 Returns 0 on success, 1 on partial success (see --partial).
4144 Returns 0 on success, 1 on partial success (see --partial).
4145 """
4145 """
4146
4146
4147 if not patch1:
4147 if not patch1:
4148 raise util.Abort(_('need at least one patch to import'))
4148 raise util.Abort(_('need at least one patch to import'))
4149
4149
4150 patches = (patch1,) + patches
4150 patches = (patch1,) + patches
4151
4151
4152 date = opts.get('date')
4152 date = opts.get('date')
4153 if date:
4153 if date:
4154 opts['date'] = util.parsedate(date)
4154 opts['date'] = util.parsedate(date)
4155
4155
4156 update = not opts.get('bypass')
4156 update = not opts.get('bypass')
4157 if not update and opts.get('no_commit'):
4157 if not update and opts.get('no_commit'):
4158 raise util.Abort(_('cannot use --no-commit with --bypass'))
4158 raise util.Abort(_('cannot use --no-commit with --bypass'))
4159 try:
4159 try:
4160 sim = float(opts.get('similarity') or 0)
4160 sim = float(opts.get('similarity') or 0)
4161 except ValueError:
4161 except ValueError:
4162 raise util.Abort(_('similarity must be a number'))
4162 raise util.Abort(_('similarity must be a number'))
4163 if sim < 0 or sim > 100:
4163 if sim < 0 or sim > 100:
4164 raise util.Abort(_('similarity must be between 0 and 100'))
4164 raise util.Abort(_('similarity must be between 0 and 100'))
4165 if sim and not update:
4165 if sim and not update:
4166 raise util.Abort(_('cannot use --similarity with --bypass'))
4166 raise util.Abort(_('cannot use --similarity with --bypass'))
4167 if opts.get('exact') and opts.get('edit'):
4167 if opts.get('exact') and opts.get('edit'):
4168 raise util.Abort(_('cannot use --exact with --edit'))
4168 raise util.Abort(_('cannot use --exact with --edit'))
4169 if opts.get('exact') and opts.get('prefix'):
4169 if opts.get('exact') and opts.get('prefix'):
4170 raise util.Abort(_('cannot use --exact with --prefix'))
4170 raise util.Abort(_('cannot use --exact with --prefix'))
4171
4171
4172 if update:
4172 if update:
4173 cmdutil.checkunfinished(repo)
4173 cmdutil.checkunfinished(repo)
4174 if (opts.get('exact') or not opts.get('force')) and update:
4174 if (opts.get('exact') or not opts.get('force')) and update:
4175 cmdutil.bailifchanged(repo)
4175 cmdutil.bailifchanged(repo)
4176
4176
4177 base = opts["base"]
4177 base = opts["base"]
4178 wlock = lock = tr = None
4178 wlock = lock = tr = None
4179 msgs = []
4179 msgs = []
4180 ret = 0
4180 ret = 0
4181
4181
4182
4182
4183 try:
4183 try:
4184 try:
4184 try:
4185 wlock = repo.wlock()
4185 wlock = repo.wlock()
4186 repo.dirstate.beginparentchange()
4186 repo.dirstate.beginparentchange()
4187 if not opts.get('no_commit'):
4187 if not opts.get('no_commit'):
4188 lock = repo.lock()
4188 lock = repo.lock()
4189 tr = repo.transaction('import')
4189 tr = repo.transaction('import')
4190 parents = repo.parents()
4190 parents = repo.parents()
4191 for patchurl in patches:
4191 for patchurl in patches:
4192 if patchurl == '-':
4192 if patchurl == '-':
4193 ui.status(_('applying patch from stdin\n'))
4193 ui.status(_('applying patch from stdin\n'))
4194 patchfile = ui.fin
4194 patchfile = ui.fin
4195 patchurl = 'stdin' # for error message
4195 patchurl = 'stdin' # for error message
4196 else:
4196 else:
4197 patchurl = os.path.join(base, patchurl)
4197 patchurl = os.path.join(base, patchurl)
4198 ui.status(_('applying %s\n') % patchurl)
4198 ui.status(_('applying %s\n') % patchurl)
4199 patchfile = hg.openpath(ui, patchurl)
4199 patchfile = hg.openpath(ui, patchurl)
4200
4200
4201 haspatch = False
4201 haspatch = False
4202 for hunk in patch.split(patchfile):
4202 for hunk in patch.split(patchfile):
4203 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
4203 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
4204 parents, opts,
4204 parents, opts,
4205 msgs, hg.clean)
4205 msgs, hg.clean)
4206 if msg:
4206 if msg:
4207 haspatch = True
4207 haspatch = True
4208 ui.note(msg + '\n')
4208 ui.note(msg + '\n')
4209 if update or opts.get('exact'):
4209 if update or opts.get('exact'):
4210 parents = repo.parents()
4210 parents = repo.parents()
4211 else:
4211 else:
4212 parents = [repo[node]]
4212 parents = [repo[node]]
4213 if rej:
4213 if rej:
4214 ui.write_err(_("patch applied partially\n"))
4214 ui.write_err(_("patch applied partially\n"))
4215 ui.write_err(_("(fix the .rej files and run "
4215 ui.write_err(_("(fix the .rej files and run "
4216 "`hg commit --amend`)\n"))
4216 "`hg commit --amend`)\n"))
4217 ret = 1
4217 ret = 1
4218 break
4218 break
4219
4219
4220 if not haspatch:
4220 if not haspatch:
4221 raise util.Abort(_('%s: no diffs found') % patchurl)
4221 raise util.Abort(_('%s: no diffs found') % patchurl)
4222
4222
4223 if tr:
4223 if tr:
4224 tr.close()
4224 tr.close()
4225 if msgs:
4225 if msgs:
4226 repo.savecommitmessage('\n* * *\n'.join(msgs))
4226 repo.savecommitmessage('\n* * *\n'.join(msgs))
4227 repo.dirstate.endparentchange()
4227 repo.dirstate.endparentchange()
4228 return ret
4228 return ret
4229 except: # re-raises
4229 except: # re-raises
4230 # wlock.release() indirectly calls dirstate.write(): since
4230 # wlock.release() indirectly calls dirstate.write(): since
4231 # we're crashing, we do not want to change the working dir
4231 # we're crashing, we do not want to change the working dir
4232 # parent after all, so make sure it writes nothing
4232 # parent after all, so make sure it writes nothing
4233 repo.dirstate.invalidate()
4233 repo.dirstate.invalidate()
4234 raise
4234 raise
4235 finally:
4235 finally:
4236 if tr:
4236 if tr:
4237 tr.release()
4237 tr.release()
4238 release(lock, wlock)
4238 release(lock, wlock)
4239
4239
4240 @command('incoming|in',
4240 @command('incoming|in',
4241 [('f', 'force', None,
4241 [('f', 'force', None,
4242 _('run even if remote repository is unrelated')),
4242 _('run even if remote repository is unrelated')),
4243 ('n', 'newest-first', None, _('show newest record first')),
4243 ('n', 'newest-first', None, _('show newest record first')),
4244 ('', 'bundle', '',
4244 ('', 'bundle', '',
4245 _('file to store the bundles into'), _('FILE')),
4245 _('file to store the bundles into'), _('FILE')),
4246 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4246 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4247 ('B', 'bookmarks', False, _("compare bookmarks")),
4247 ('B', 'bookmarks', False, _("compare bookmarks")),
4248 ('b', 'branch', [],
4248 ('b', 'branch', [],
4249 _('a specific branch you would like to pull'), _('BRANCH')),
4249 _('a specific branch you would like to pull'), _('BRANCH')),
4250 ] + logopts + remoteopts + subrepoopts,
4250 ] + logopts + remoteopts + subrepoopts,
4251 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
4251 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
4252 def incoming(ui, repo, source="default", **opts):
4252 def incoming(ui, repo, source="default", **opts):
4253 """show new changesets found in source
4253 """show new changesets found in source
4254
4254
4255 Show new changesets found in the specified path/URL or the default
4255 Show new changesets found in the specified path/URL or the default
4256 pull location. These are the changesets that would have been pulled
4256 pull location. These are the changesets that would have been pulled
4257 if a pull at the time you issued this command.
4257 if a pull at the time you issued this command.
4258
4258
4259 See pull for valid source format details.
4259 See pull for valid source format details.
4260
4260
4261 .. container:: verbose
4261 .. container:: verbose
4262
4262
4263 For remote repository, using --bundle avoids downloading the
4263 For remote repository, using --bundle avoids downloading the
4264 changesets twice if the incoming is followed by a pull.
4264 changesets twice if the incoming is followed by a pull.
4265
4265
4266 Examples:
4266 Examples:
4267
4267
4268 - show incoming changes with patches and full description::
4268 - show incoming changes with patches and full description::
4269
4269
4270 hg incoming -vp
4270 hg incoming -vp
4271
4271
4272 - show incoming changes excluding merges, store a bundle::
4272 - show incoming changes excluding merges, store a bundle::
4273
4273
4274 hg in -vpM --bundle incoming.hg
4274 hg in -vpM --bundle incoming.hg
4275 hg pull incoming.hg
4275 hg pull incoming.hg
4276
4276
4277 - briefly list changes inside a bundle::
4277 - briefly list changes inside a bundle::
4278
4278
4279 hg in changes.hg -T "{desc|firstline}\\n"
4279 hg in changes.hg -T "{desc|firstline}\\n"
4280
4280
4281 Returns 0 if there are incoming changes, 1 otherwise.
4281 Returns 0 if there are incoming changes, 1 otherwise.
4282 """
4282 """
4283 if opts.get('graph'):
4283 if opts.get('graph'):
4284 cmdutil.checkunsupportedgraphflags([], opts)
4284 cmdutil.checkunsupportedgraphflags([], opts)
4285 def display(other, chlist, displayer):
4285 def display(other, chlist, displayer):
4286 revdag = cmdutil.graphrevs(other, chlist, opts)
4286 revdag = cmdutil.graphrevs(other, chlist, opts)
4287 showparents = [ctx.node() for ctx in repo[None].parents()]
4287 showparents = [ctx.node() for ctx in repo[None].parents()]
4288 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4288 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4289 graphmod.asciiedges)
4289 graphmod.asciiedges)
4290
4290
4291 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4291 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4292 return 0
4292 return 0
4293
4293
4294 if opts.get('bundle') and opts.get('subrepos'):
4294 if opts.get('bundle') and opts.get('subrepos'):
4295 raise util.Abort(_('cannot combine --bundle and --subrepos'))
4295 raise util.Abort(_('cannot combine --bundle and --subrepos'))
4296
4296
4297 if opts.get('bookmarks'):
4297 if opts.get('bookmarks'):
4298 source, branches = hg.parseurl(ui.expandpath(source),
4298 source, branches = hg.parseurl(ui.expandpath(source),
4299 opts.get('branch'))
4299 opts.get('branch'))
4300 other = hg.peer(repo, opts, source)
4300 other = hg.peer(repo, opts, source)
4301 if 'bookmarks' not in other.listkeys('namespaces'):
4301 if 'bookmarks' not in other.listkeys('namespaces'):
4302 ui.warn(_("remote doesn't support bookmarks\n"))
4302 ui.warn(_("remote doesn't support bookmarks\n"))
4303 return 0
4303 return 0
4304 ui.status(_('comparing with %s\n') % util.hidepassword(source))
4304 ui.status(_('comparing with %s\n') % util.hidepassword(source))
4305 return bookmarks.diff(ui, repo, other)
4305 return bookmarks.diff(ui, repo, other)
4306
4306
4307 repo._subtoppath = ui.expandpath(source)
4307 repo._subtoppath = ui.expandpath(source)
4308 try:
4308 try:
4309 return hg.incoming(ui, repo, source, opts)
4309 return hg.incoming(ui, repo, source, opts)
4310 finally:
4310 finally:
4311 del repo._subtoppath
4311 del repo._subtoppath
4312
4312
4313
4313
4314 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
4314 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
4315 norepo=True)
4315 norepo=True)
4316 def init(ui, dest=".", **opts):
4316 def init(ui, dest=".", **opts):
4317 """create a new repository in the given directory
4317 """create a new repository in the given directory
4318
4318
4319 Initialize a new repository in the given directory. If the given
4319 Initialize a new repository in the given directory. If the given
4320 directory does not exist, it will be created.
4320 directory does not exist, it will be created.
4321
4321
4322 If no directory is given, the current directory is used.
4322 If no directory is given, the current directory is used.
4323
4323
4324 It is possible to specify an ``ssh://`` URL as the destination.
4324 It is possible to specify an ``ssh://`` URL as the destination.
4325 See :hg:`help urls` for more information.
4325 See :hg:`help urls` for more information.
4326
4326
4327 Returns 0 on success.
4327 Returns 0 on success.
4328 """
4328 """
4329 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4329 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4330
4330
4331 @command('locate',
4331 @command('locate',
4332 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
4332 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
4333 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4333 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4334 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
4334 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
4335 ] + walkopts,
4335 ] + walkopts,
4336 _('[OPTION]... [PATTERN]...'))
4336 _('[OPTION]... [PATTERN]...'))
4337 def locate(ui, repo, *pats, **opts):
4337 def locate(ui, repo, *pats, **opts):
4338 """locate files matching specific patterns (DEPRECATED)
4338 """locate files matching specific patterns (DEPRECATED)
4339
4339
4340 Print files under Mercurial control in the working directory whose
4340 Print files under Mercurial control in the working directory whose
4341 names match the given patterns.
4341 names match the given patterns.
4342
4342
4343 By default, this command searches all directories in the working
4343 By default, this command searches all directories in the working
4344 directory. To search just the current directory and its
4344 directory. To search just the current directory and its
4345 subdirectories, use "--include .".
4345 subdirectories, use "--include .".
4346
4346
4347 If no patterns are given to match, this command prints the names
4347 If no patterns are given to match, this command prints the names
4348 of all files under Mercurial control in the working directory.
4348 of all files under Mercurial control in the working directory.
4349
4349
4350 If you want to feed the output of this command into the "xargs"
4350 If you want to feed the output of this command into the "xargs"
4351 command, use the -0 option to both this command and "xargs". This
4351 command, use the -0 option to both this command and "xargs". This
4352 will avoid the problem of "xargs" treating single filenames that
4352 will avoid the problem of "xargs" treating single filenames that
4353 contain whitespace as multiple filenames.
4353 contain whitespace as multiple filenames.
4354
4354
4355 See :hg:`help files` for a more versatile command.
4355 See :hg:`help files` for a more versatile command.
4356
4356
4357 Returns 0 if a match is found, 1 otherwise.
4357 Returns 0 if a match is found, 1 otherwise.
4358 """
4358 """
4359 if opts.get('print0'):
4359 if opts.get('print0'):
4360 end = '\0'
4360 end = '\0'
4361 else:
4361 else:
4362 end = '\n'
4362 end = '\n'
4363 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
4363 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
4364
4364
4365 ret = 1
4365 ret = 1
4366 ctx = repo[rev]
4366 ctx = repo[rev]
4367 m = scmutil.match(ctx, pats, opts, default='relglob')
4367 m = scmutil.match(ctx, pats, opts, default='relglob')
4368 m.bad = lambda x, y: False
4368 m.bad = lambda x, y: False
4369
4369
4370 for abs in ctx.matches(m):
4370 for abs in ctx.matches(m):
4371 if opts.get('fullpath'):
4371 if opts.get('fullpath'):
4372 ui.write(repo.wjoin(abs), end)
4372 ui.write(repo.wjoin(abs), end)
4373 else:
4373 else:
4374 ui.write(((pats and m.rel(abs)) or abs), end)
4374 ui.write(((pats and m.rel(abs)) or abs), end)
4375 ret = 0
4375 ret = 0
4376
4376
4377 return ret
4377 return ret
4378
4378
4379 @command('^log|history',
4379 @command('^log|history',
4380 [('f', 'follow', None,
4380 [('f', 'follow', None,
4381 _('follow changeset history, or file history across copies and renames')),
4381 _('follow changeset history, or file history across copies and renames')),
4382 ('', 'follow-first', None,
4382 ('', 'follow-first', None,
4383 _('only follow the first parent of merge changesets (DEPRECATED)')),
4383 _('only follow the first parent of merge changesets (DEPRECATED)')),
4384 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
4384 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
4385 ('C', 'copies', None, _('show copied files')),
4385 ('C', 'copies', None, _('show copied files')),
4386 ('k', 'keyword', [],
4386 ('k', 'keyword', [],
4387 _('do case-insensitive search for a given text'), _('TEXT')),
4387 _('do case-insensitive search for a given text'), _('TEXT')),
4388 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
4388 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
4389 ('', 'removed', None, _('include revisions where files were removed')),
4389 ('', 'removed', None, _('include revisions where files were removed')),
4390 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
4390 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
4391 ('u', 'user', [], _('revisions committed by user'), _('USER')),
4391 ('u', 'user', [], _('revisions committed by user'), _('USER')),
4392 ('', 'only-branch', [],
4392 ('', 'only-branch', [],
4393 _('show only changesets within the given named branch (DEPRECATED)'),
4393 _('show only changesets within the given named branch (DEPRECATED)'),
4394 _('BRANCH')),
4394 _('BRANCH')),
4395 ('b', 'branch', [],
4395 ('b', 'branch', [],
4396 _('show changesets within the given named branch'), _('BRANCH')),
4396 _('show changesets within the given named branch'), _('BRANCH')),
4397 ('P', 'prune', [],
4397 ('P', 'prune', [],
4398 _('do not display revision or any of its ancestors'), _('REV')),
4398 _('do not display revision or any of its ancestors'), _('REV')),
4399 ] + logopts + walkopts,
4399 ] + logopts + walkopts,
4400 _('[OPTION]... [FILE]'),
4400 _('[OPTION]... [FILE]'),
4401 inferrepo=True)
4401 inferrepo=True)
4402 def log(ui, repo, *pats, **opts):
4402 def log(ui, repo, *pats, **opts):
4403 """show revision history of entire repository or files
4403 """show revision history of entire repository or files
4404
4404
4405 Print the revision history of the specified files or the entire
4405 Print the revision history of the specified files or the entire
4406 project.
4406 project.
4407
4407
4408 If no revision range is specified, the default is ``tip:0`` unless
4408 If no revision range is specified, the default is ``tip:0`` unless
4409 --follow is set, in which case the working directory parent is
4409 --follow is set, in which case the working directory parent is
4410 used as the starting revision.
4410 used as the starting revision.
4411
4411
4412 File history is shown without following rename or copy history of
4412 File history is shown without following rename or copy history of
4413 files. Use -f/--follow with a filename to follow history across
4413 files. Use -f/--follow with a filename to follow history across
4414 renames and copies. --follow without a filename will only show
4414 renames and copies. --follow without a filename will only show
4415 ancestors or descendants of the starting revision.
4415 ancestors or descendants of the starting revision.
4416
4416
4417 By default this command prints revision number and changeset id,
4417 By default this command prints revision number and changeset id,
4418 tags, non-trivial parents, user, date and time, and a summary for
4418 tags, non-trivial parents, user, date and time, and a summary for
4419 each commit. When the -v/--verbose switch is used, the list of
4419 each commit. When the -v/--verbose switch is used, the list of
4420 changed files and full commit message are shown.
4420 changed files and full commit message are shown.
4421
4421
4422 With --graph the revisions are shown as an ASCII art DAG with the most
4422 With --graph the revisions are shown as an ASCII art DAG with the most
4423 recent changeset at the top.
4423 recent changeset at the top.
4424 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
4424 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
4425 and '+' represents a fork where the changeset from the lines below is a
4425 and '+' represents a fork where the changeset from the lines below is a
4426 parent of the 'o' merge on the same line.
4426 parent of the 'o' merge on the same line.
4427
4427
4428 .. note::
4428 .. note::
4429
4429
4430 log -p/--patch may generate unexpected diff output for merge
4430 log -p/--patch may generate unexpected diff output for merge
4431 changesets, as it will only compare the merge changeset against
4431 changesets, as it will only compare the merge changeset against
4432 its first parent. Also, only files different from BOTH parents
4432 its first parent. Also, only files different from BOTH parents
4433 will appear in files:.
4433 will appear in files:.
4434
4434
4435 .. note::
4435 .. note::
4436
4436
4437 for performance reasons, log FILE may omit duplicate changes
4437 for performance reasons, log FILE may omit duplicate changes
4438 made on branches and will not show removals or mode changes. To
4438 made on branches and will not show removals or mode changes. To
4439 see all such changes, use the --removed switch.
4439 see all such changes, use the --removed switch.
4440
4440
4441 .. container:: verbose
4441 .. container:: verbose
4442
4442
4443 Some examples:
4443 Some examples:
4444
4444
4445 - changesets with full descriptions and file lists::
4445 - changesets with full descriptions and file lists::
4446
4446
4447 hg log -v
4447 hg log -v
4448
4448
4449 - changesets ancestral to the working directory::
4449 - changesets ancestral to the working directory::
4450
4450
4451 hg log -f
4451 hg log -f
4452
4452
4453 - last 10 commits on the current branch::
4453 - last 10 commits on the current branch::
4454
4454
4455 hg log -l 10 -b .
4455 hg log -l 10 -b .
4456
4456
4457 - changesets showing all modifications of a file, including removals::
4457 - changesets showing all modifications of a file, including removals::
4458
4458
4459 hg log --removed file.c
4459 hg log --removed file.c
4460
4460
4461 - all changesets that touch a directory, with diffs, excluding merges::
4461 - all changesets that touch a directory, with diffs, excluding merges::
4462
4462
4463 hg log -Mp lib/
4463 hg log -Mp lib/
4464
4464
4465 - all revision numbers that match a keyword::
4465 - all revision numbers that match a keyword::
4466
4466
4467 hg log -k bug --template "{rev}\\n"
4467 hg log -k bug --template "{rev}\\n"
4468
4468
4469 - list available log templates::
4469 - list available log templates::
4470
4470
4471 hg log -T list
4471 hg log -T list
4472
4472
4473 - check if a given changeset is included in a tagged release::
4473 - check if a given changeset is included in a tagged release::
4474
4474
4475 hg log -r "a21ccf and ancestor(1.9)"
4475 hg log -r "a21ccf and ancestor(1.9)"
4476
4476
4477 - find all changesets by some user in a date range::
4477 - find all changesets by some user in a date range::
4478
4478
4479 hg log -k alice -d "may 2008 to jul 2008"
4479 hg log -k alice -d "may 2008 to jul 2008"
4480
4480
4481 - summary of all changesets after the last tag::
4481 - summary of all changesets after the last tag::
4482
4482
4483 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4483 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4484
4484
4485 See :hg:`help dates` for a list of formats valid for -d/--date.
4485 See :hg:`help dates` for a list of formats valid for -d/--date.
4486
4486
4487 See :hg:`help revisions` and :hg:`help revsets` for more about
4487 See :hg:`help revisions` and :hg:`help revsets` for more about
4488 specifying revisions.
4488 specifying revisions.
4489
4489
4490 See :hg:`help templates` for more about pre-packaged styles and
4490 See :hg:`help templates` for more about pre-packaged styles and
4491 specifying custom templates.
4491 specifying custom templates.
4492
4492
4493 Returns 0 on success.
4493 Returns 0 on success.
4494
4494
4495 """
4495 """
4496 if opts.get('follow') and opts.get('rev'):
4496 if opts.get('follow') and opts.get('rev'):
4497 opts['rev'] = [revset.formatspec('reverse(::%lr)', opts.get('rev'))]
4497 opts['rev'] = [revset.formatspec('reverse(::%lr)', opts.get('rev'))]
4498 del opts['follow']
4498 del opts['follow']
4499
4499
4500 if opts.get('graph'):
4500 if opts.get('graph'):
4501 return cmdutil.graphlog(ui, repo, *pats, **opts)
4501 return cmdutil.graphlog(ui, repo, *pats, **opts)
4502
4502
4503 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
4503 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
4504 limit = cmdutil.loglimit(opts)
4504 limit = cmdutil.loglimit(opts)
4505 count = 0
4505 count = 0
4506
4506
4507 getrenamed = None
4507 getrenamed = None
4508 if opts.get('copies'):
4508 if opts.get('copies'):
4509 endrev = None
4509 endrev = None
4510 if opts.get('rev'):
4510 if opts.get('rev'):
4511 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
4511 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
4512 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
4512 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
4513
4513
4514 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4514 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4515 for rev in revs:
4515 for rev in revs:
4516 if count == limit:
4516 if count == limit:
4517 break
4517 break
4518 ctx = repo[rev]
4518 ctx = repo[rev]
4519 copies = None
4519 copies = None
4520 if getrenamed is not None and rev:
4520 if getrenamed is not None and rev:
4521 copies = []
4521 copies = []
4522 for fn in ctx.files():
4522 for fn in ctx.files():
4523 rename = getrenamed(fn, rev)
4523 rename = getrenamed(fn, rev)
4524 if rename:
4524 if rename:
4525 copies.append((fn, rename[0]))
4525 copies.append((fn, rename[0]))
4526 if filematcher:
4526 if filematcher:
4527 revmatchfn = filematcher(ctx.rev())
4527 revmatchfn = filematcher(ctx.rev())
4528 else:
4528 else:
4529 revmatchfn = None
4529 revmatchfn = None
4530 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
4530 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
4531 if displayer.flush(rev):
4531 if displayer.flush(rev):
4532 count += 1
4532 count += 1
4533
4533
4534 displayer.close()
4534 displayer.close()
4535
4535
4536 @command('manifest',
4536 @command('manifest',
4537 [('r', 'rev', '', _('revision to display'), _('REV')),
4537 [('r', 'rev', '', _('revision to display'), _('REV')),
4538 ('', 'all', False, _("list files from all revisions"))]
4538 ('', 'all', False, _("list files from all revisions"))]
4539 + formatteropts,
4539 + formatteropts,
4540 _('[-r REV]'))
4540 _('[-r REV]'))
4541 def manifest(ui, repo, node=None, rev=None, **opts):
4541 def manifest(ui, repo, node=None, rev=None, **opts):
4542 """output the current or given revision of the project manifest
4542 """output the current or given revision of the project manifest
4543
4543
4544 Print a list of version controlled files for the given revision.
4544 Print a list of version controlled files for the given revision.
4545 If no revision is given, the first parent of the working directory
4545 If no revision is given, the first parent of the working directory
4546 is used, or the null revision if no revision is checked out.
4546 is used, or the null revision if no revision is checked out.
4547
4547
4548 With -v, print file permissions, symlink and executable bits.
4548 With -v, print file permissions, symlink and executable bits.
4549 With --debug, print file revision hashes.
4549 With --debug, print file revision hashes.
4550
4550
4551 If option --all is specified, the list of all files from all revisions
4551 If option --all is specified, the list of all files from all revisions
4552 is printed. This includes deleted and renamed files.
4552 is printed. This includes deleted and renamed files.
4553
4553
4554 Returns 0 on success.
4554 Returns 0 on success.
4555 """
4555 """
4556
4556
4557 fm = ui.formatter('manifest', opts)
4557 fm = ui.formatter('manifest', opts)
4558
4558
4559 if opts.get('all'):
4559 if opts.get('all'):
4560 if rev or node:
4560 if rev or node:
4561 raise util.Abort(_("can't specify a revision with --all"))
4561 raise util.Abort(_("can't specify a revision with --all"))
4562
4562
4563 res = []
4563 res = []
4564 prefix = "data/"
4564 prefix = "data/"
4565 suffix = ".i"
4565 suffix = ".i"
4566 plen = len(prefix)
4566 plen = len(prefix)
4567 slen = len(suffix)
4567 slen = len(suffix)
4568 lock = repo.lock()
4568 lock = repo.lock()
4569 try:
4569 try:
4570 for fn, b, size in repo.store.datafiles():
4570 for fn, b, size in repo.store.datafiles():
4571 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
4571 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
4572 res.append(fn[plen:-slen])
4572 res.append(fn[plen:-slen])
4573 finally:
4573 finally:
4574 lock.release()
4574 lock.release()
4575 for f in res:
4575 for f in res:
4576 fm.startitem()
4576 fm.startitem()
4577 fm.write("path", '%s\n', f)
4577 fm.write("path", '%s\n', f)
4578 fm.end()
4578 fm.end()
4579 return
4579 return
4580
4580
4581 if rev and node:
4581 if rev and node:
4582 raise util.Abort(_("please specify just one revision"))
4582 raise util.Abort(_("please specify just one revision"))
4583
4583
4584 if not node:
4584 if not node:
4585 node = rev
4585 node = rev
4586
4586
4587 char = {'l': '@', 'x': '*', '': ''}
4587 char = {'l': '@', 'x': '*', '': ''}
4588 mode = {'l': '644', 'x': '755', '': '644'}
4588 mode = {'l': '644', 'x': '755', '': '644'}
4589 ctx = scmutil.revsingle(repo, node)
4589 ctx = scmutil.revsingle(repo, node)
4590 mf = ctx.manifest()
4590 mf = ctx.manifest()
4591 for f in ctx:
4591 for f in ctx:
4592 fm.startitem()
4592 fm.startitem()
4593 fl = ctx[f].flags()
4593 fl = ctx[f].flags()
4594 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
4594 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
4595 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
4595 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
4596 fm.write('path', '%s\n', f)
4596 fm.write('path', '%s\n', f)
4597 fm.end()
4597 fm.end()
4598
4598
4599 @command('^merge',
4599 @command('^merge',
4600 [('f', 'force', None,
4600 [('f', 'force', None,
4601 _('force a merge including outstanding changes (DEPRECATED)')),
4601 _('force a merge including outstanding changes (DEPRECATED)')),
4602 ('r', 'rev', '', _('revision to merge'), _('REV')),
4602 ('r', 'rev', '', _('revision to merge'), _('REV')),
4603 ('P', 'preview', None,
4603 ('P', 'preview', None,
4604 _('review revisions to merge (no merge is performed)'))
4604 _('review revisions to merge (no merge is performed)'))
4605 ] + mergetoolopts,
4605 ] + mergetoolopts,
4606 _('[-P] [-f] [[-r] REV]'))
4606 _('[-P] [-f] [[-r] REV]'))
4607 def merge(ui, repo, node=None, **opts):
4607 def merge(ui, repo, node=None, **opts):
4608 """merge another revision into working directory
4608 """merge another revision into working directory
4609
4609
4610 The current working directory is updated with all changes made in
4610 The current working directory is updated with all changes made in
4611 the requested revision since the last common predecessor revision.
4611 the requested revision since the last common predecessor revision.
4612
4612
4613 Files that changed between either parent are marked as changed for
4613 Files that changed between either parent are marked as changed for
4614 the next commit and a commit must be performed before any further
4614 the next commit and a commit must be performed before any further
4615 updates to the repository are allowed. The next commit will have
4615 updates to the repository are allowed. The next commit will have
4616 two parents.
4616 two parents.
4617
4617
4618 ``--tool`` can be used to specify the merge tool used for file
4618 ``--tool`` can be used to specify the merge tool used for file
4619 merges. It overrides the HGMERGE environment variable and your
4619 merges. It overrides the HGMERGE environment variable and your
4620 configuration files. See :hg:`help merge-tools` for options.
4620 configuration files. See :hg:`help merge-tools` for options.
4621
4621
4622 If no revision is specified, the working directory's parent is a
4622 If no revision is specified, the working directory's parent is a
4623 head revision, and the current branch contains exactly one other
4623 head revision, and the current branch contains exactly one other
4624 head, the other head is merged with by default. Otherwise, an
4624 head, the other head is merged with by default. Otherwise, an
4625 explicit revision with which to merge with must be provided.
4625 explicit revision with which to merge with must be provided.
4626
4626
4627 :hg:`resolve` must be used to resolve unresolved files.
4627 :hg:`resolve` must be used to resolve unresolved files.
4628
4628
4629 To undo an uncommitted merge, use :hg:`update --clean .` which
4629 To undo an uncommitted merge, use :hg:`update --clean .` which
4630 will check out a clean copy of the original merge parent, losing
4630 will check out a clean copy of the original merge parent, losing
4631 all changes.
4631 all changes.
4632
4632
4633 Returns 0 on success, 1 if there are unresolved files.
4633 Returns 0 on success, 1 if there are unresolved files.
4634 """
4634 """
4635
4635
4636 if opts.get('rev') and node:
4636 if opts.get('rev') and node:
4637 raise util.Abort(_("please specify just one revision"))
4637 raise util.Abort(_("please specify just one revision"))
4638 if not node:
4638 if not node:
4639 node = opts.get('rev')
4639 node = opts.get('rev')
4640
4640
4641 if node:
4641 if node:
4642 node = scmutil.revsingle(repo, node).node()
4642 node = scmutil.revsingle(repo, node).node()
4643
4643
4644 if not node and repo._bookmarkcurrent:
4644 if not node and repo._bookmarkcurrent:
4645 bmheads = repo.bookmarkheads(repo._bookmarkcurrent)
4645 bmheads = repo.bookmarkheads(repo._bookmarkcurrent)
4646 curhead = repo[repo._bookmarkcurrent].node()
4646 curhead = repo[repo._bookmarkcurrent].node()
4647 if len(bmheads) == 2:
4647 if len(bmheads) == 2:
4648 if curhead == bmheads[0]:
4648 if curhead == bmheads[0]:
4649 node = bmheads[1]
4649 node = bmheads[1]
4650 else:
4650 else:
4651 node = bmheads[0]
4651 node = bmheads[0]
4652 elif len(bmheads) > 2:
4652 elif len(bmheads) > 2:
4653 raise util.Abort(_("multiple matching bookmarks to merge - "
4653 raise util.Abort(_("multiple matching bookmarks to merge - "
4654 "please merge with an explicit rev or bookmark"),
4654 "please merge with an explicit rev or bookmark"),
4655 hint=_("run 'hg heads' to see all heads"))
4655 hint=_("run 'hg heads' to see all heads"))
4656 elif len(bmheads) <= 1:
4656 elif len(bmheads) <= 1:
4657 raise util.Abort(_("no matching bookmark to merge - "
4657 raise util.Abort(_("no matching bookmark to merge - "
4658 "please merge with an explicit rev or bookmark"),
4658 "please merge with an explicit rev or bookmark"),
4659 hint=_("run 'hg heads' to see all heads"))
4659 hint=_("run 'hg heads' to see all heads"))
4660
4660
4661 if not node and not repo._bookmarkcurrent:
4661 if not node and not repo._bookmarkcurrent:
4662 branch = repo[None].branch()
4662 branch = repo[None].branch()
4663 bheads = repo.branchheads(branch)
4663 bheads = repo.branchheads(branch)
4664 nbhs = [bh for bh in bheads if not repo[bh].bookmarks()]
4664 nbhs = [bh for bh in bheads if not repo[bh].bookmarks()]
4665
4665
4666 if len(nbhs) > 2:
4666 if len(nbhs) > 2:
4667 raise util.Abort(_("branch '%s' has %d heads - "
4667 raise util.Abort(_("branch '%s' has %d heads - "
4668 "please merge with an explicit rev")
4668 "please merge with an explicit rev")
4669 % (branch, len(bheads)),
4669 % (branch, len(bheads)),
4670 hint=_("run 'hg heads .' to see heads"))
4670 hint=_("run 'hg heads .' to see heads"))
4671
4671
4672 parent = repo.dirstate.p1()
4672 parent = repo.dirstate.p1()
4673 if len(nbhs) <= 1:
4673 if len(nbhs) <= 1:
4674 if len(bheads) > 1:
4674 if len(bheads) > 1:
4675 raise util.Abort(_("heads are bookmarked - "
4675 raise util.Abort(_("heads are bookmarked - "
4676 "please merge with an explicit rev"),
4676 "please merge with an explicit rev"),
4677 hint=_("run 'hg heads' to see all heads"))
4677 hint=_("run 'hg heads' to see all heads"))
4678 if len(repo.heads()) > 1:
4678 if len(repo.heads()) > 1:
4679 raise util.Abort(_("branch '%s' has one head - "
4679 raise util.Abort(_("branch '%s' has one head - "
4680 "please merge with an explicit rev")
4680 "please merge with an explicit rev")
4681 % branch,
4681 % branch,
4682 hint=_("run 'hg heads' to see all heads"))
4682 hint=_("run 'hg heads' to see all heads"))
4683 msg, hint = _('nothing to merge'), None
4683 msg, hint = _('nothing to merge'), None
4684 if parent != repo.lookup(branch):
4684 if parent != repo.lookup(branch):
4685 hint = _("use 'hg update' instead")
4685 hint = _("use 'hg update' instead")
4686 raise util.Abort(msg, hint=hint)
4686 raise util.Abort(msg, hint=hint)
4687
4687
4688 if parent not in bheads:
4688 if parent not in bheads:
4689 raise util.Abort(_('working directory not at a head revision'),
4689 raise util.Abort(_('working directory not at a head revision'),
4690 hint=_("use 'hg update' or merge with an "
4690 hint=_("use 'hg update' or merge with an "
4691 "explicit revision"))
4691 "explicit revision"))
4692 if parent == nbhs[0]:
4692 if parent == nbhs[0]:
4693 node = nbhs[-1]
4693 node = nbhs[-1]
4694 else:
4694 else:
4695 node = nbhs[0]
4695 node = nbhs[0]
4696
4696
4697 if opts.get('preview'):
4697 if opts.get('preview'):
4698 # find nodes that are ancestors of p2 but not of p1
4698 # find nodes that are ancestors of p2 but not of p1
4699 p1 = repo.lookup('.')
4699 p1 = repo.lookup('.')
4700 p2 = repo.lookup(node)
4700 p2 = repo.lookup(node)
4701 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4701 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4702
4702
4703 displayer = cmdutil.show_changeset(ui, repo, opts)
4703 displayer = cmdutil.show_changeset(ui, repo, opts)
4704 for node in nodes:
4704 for node in nodes:
4705 displayer.show(repo[node])
4705 displayer.show(repo[node])
4706 displayer.close()
4706 displayer.close()
4707 return 0
4707 return 0
4708
4708
4709 try:
4709 try:
4710 # ui.forcemerge is an internal variable, do not document
4710 # ui.forcemerge is an internal variable, do not document
4711 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
4711 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
4712 return hg.merge(repo, node, force=opts.get('force'))
4712 return hg.merge(repo, node, force=opts.get('force'))
4713 finally:
4713 finally:
4714 ui.setconfig('ui', 'forcemerge', '', 'merge')
4714 ui.setconfig('ui', 'forcemerge', '', 'merge')
4715
4715
4716 @command('outgoing|out',
4716 @command('outgoing|out',
4717 [('f', 'force', None, _('run even when the destination is unrelated')),
4717 [('f', 'force', None, _('run even when the destination is unrelated')),
4718 ('r', 'rev', [],
4718 ('r', 'rev', [],
4719 _('a changeset intended to be included in the destination'), _('REV')),
4719 _('a changeset intended to be included in the destination'), _('REV')),
4720 ('n', 'newest-first', None, _('show newest record first')),
4720 ('n', 'newest-first', None, _('show newest record first')),
4721 ('B', 'bookmarks', False, _('compare bookmarks')),
4721 ('B', 'bookmarks', False, _('compare bookmarks')),
4722 ('b', 'branch', [], _('a specific branch you would like to push'),
4722 ('b', 'branch', [], _('a specific branch you would like to push'),
4723 _('BRANCH')),
4723 _('BRANCH')),
4724 ] + logopts + remoteopts + subrepoopts,
4724 ] + logopts + remoteopts + subrepoopts,
4725 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4725 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4726 def outgoing(ui, repo, dest=None, **opts):
4726 def outgoing(ui, repo, dest=None, **opts):
4727 """show changesets not found in the destination
4727 """show changesets not found in the destination
4728
4728
4729 Show changesets not found in the specified destination repository
4729 Show changesets not found in the specified destination repository
4730 or the default push location. These are the changesets that would
4730 or the default push location. These are the changesets that would
4731 be pushed if a push was requested.
4731 be pushed if a push was requested.
4732
4732
4733 See pull for details of valid destination formats.
4733 See pull for details of valid destination formats.
4734
4734
4735 Returns 0 if there are outgoing changes, 1 otherwise.
4735 Returns 0 if there are outgoing changes, 1 otherwise.
4736 """
4736 """
4737 if opts.get('graph'):
4737 if opts.get('graph'):
4738 cmdutil.checkunsupportedgraphflags([], opts)
4738 cmdutil.checkunsupportedgraphflags([], opts)
4739 o, other = hg._outgoing(ui, repo, dest, opts)
4739 o, other = hg._outgoing(ui, repo, dest, opts)
4740 if not o:
4740 if not o:
4741 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4741 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4742 return
4742 return
4743
4743
4744 revdag = cmdutil.graphrevs(repo, o, opts)
4744 revdag = cmdutil.graphrevs(repo, o, opts)
4745 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4745 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4746 showparents = [ctx.node() for ctx in repo[None].parents()]
4746 showparents = [ctx.node() for ctx in repo[None].parents()]
4747 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4747 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4748 graphmod.asciiedges)
4748 graphmod.asciiedges)
4749 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4749 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4750 return 0
4750 return 0
4751
4751
4752 if opts.get('bookmarks'):
4752 if opts.get('bookmarks'):
4753 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4753 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4754 dest, branches = hg.parseurl(dest, opts.get('branch'))
4754 dest, branches = hg.parseurl(dest, opts.get('branch'))
4755 other = hg.peer(repo, opts, dest)
4755 other = hg.peer(repo, opts, dest)
4756 if 'bookmarks' not in other.listkeys('namespaces'):
4756 if 'bookmarks' not in other.listkeys('namespaces'):
4757 ui.warn(_("remote doesn't support bookmarks\n"))
4757 ui.warn(_("remote doesn't support bookmarks\n"))
4758 return 0
4758 return 0
4759 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4759 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4760 return bookmarks.diff(ui, other, repo)
4760 return bookmarks.diff(ui, other, repo)
4761
4761
4762 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4762 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4763 try:
4763 try:
4764 return hg.outgoing(ui, repo, dest, opts)
4764 return hg.outgoing(ui, repo, dest, opts)
4765 finally:
4765 finally:
4766 del repo._subtoppath
4766 del repo._subtoppath
4767
4767
4768 @command('parents',
4768 @command('parents',
4769 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4769 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4770 ] + templateopts,
4770 ] + templateopts,
4771 _('[-r REV] [FILE]'),
4771 _('[-r REV] [FILE]'),
4772 inferrepo=True)
4772 inferrepo=True)
4773 def parents(ui, repo, file_=None, **opts):
4773 def parents(ui, repo, file_=None, **opts):
4774 """show the parents of the working directory or revision (DEPRECATED)
4774 """show the parents of the working directory or revision (DEPRECATED)
4775
4775
4776 Print the working directory's parent revisions. If a revision is
4776 Print the working directory's parent revisions. If a revision is
4777 given via -r/--rev, the parent of that revision will be printed.
4777 given via -r/--rev, the parent of that revision will be printed.
4778 If a file argument is given, the revision in which the file was
4778 If a file argument is given, the revision in which the file was
4779 last changed (before the working directory revision or the
4779 last changed (before the working directory revision or the
4780 argument to --rev if given) is printed.
4780 argument to --rev if given) is printed.
4781
4781
4782 See :hg:`summary` and :hg:`help revsets` for related information.
4782 See :hg:`summary` and :hg:`help revsets` for related information.
4783
4783
4784 Returns 0 on success.
4784 Returns 0 on success.
4785 """
4785 """
4786
4786
4787 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4787 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4788
4788
4789 if file_:
4789 if file_:
4790 m = scmutil.match(ctx, (file_,), opts)
4790 m = scmutil.match(ctx, (file_,), opts)
4791 if m.anypats() or len(m.files()) != 1:
4791 if m.anypats() or len(m.files()) != 1:
4792 raise util.Abort(_('can only specify an explicit filename'))
4792 raise util.Abort(_('can only specify an explicit filename'))
4793 file_ = m.files()[0]
4793 file_ = m.files()[0]
4794 filenodes = []
4794 filenodes = []
4795 for cp in ctx.parents():
4795 for cp in ctx.parents():
4796 if not cp:
4796 if not cp:
4797 continue
4797 continue
4798 try:
4798 try:
4799 filenodes.append(cp.filenode(file_))
4799 filenodes.append(cp.filenode(file_))
4800 except error.LookupError:
4800 except error.LookupError:
4801 pass
4801 pass
4802 if not filenodes:
4802 if not filenodes:
4803 raise util.Abort(_("'%s' not found in manifest!") % file_)
4803 raise util.Abort(_("'%s' not found in manifest!") % file_)
4804 p = []
4804 p = []
4805 for fn in filenodes:
4805 for fn in filenodes:
4806 fctx = repo.filectx(file_, fileid=fn)
4806 fctx = repo.filectx(file_, fileid=fn)
4807 p.append(fctx.node())
4807 p.append(fctx.node())
4808 else:
4808 else:
4809 p = [cp.node() for cp in ctx.parents()]
4809 p = [cp.node() for cp in ctx.parents()]
4810
4810
4811 displayer = cmdutil.show_changeset(ui, repo, opts)
4811 displayer = cmdutil.show_changeset(ui, repo, opts)
4812 for n in p:
4812 for n in p:
4813 if n != nullid:
4813 if n != nullid:
4814 displayer.show(repo[n])
4814 displayer.show(repo[n])
4815 displayer.close()
4815 displayer.close()
4816
4816
4817 @command('paths', [], _('[NAME]'), optionalrepo=True)
4817 @command('paths', [], _('[NAME]'), optionalrepo=True)
4818 def paths(ui, repo, search=None):
4818 def paths(ui, repo, search=None):
4819 """show aliases for remote repositories
4819 """show aliases for remote repositories
4820
4820
4821 Show definition of symbolic path name NAME. If no name is given,
4821 Show definition of symbolic path name NAME. If no name is given,
4822 show definition of all available names.
4822 show definition of all available names.
4823
4823
4824 Option -q/--quiet suppresses all output when searching for NAME
4824 Option -q/--quiet suppresses all output when searching for NAME
4825 and shows only the path names when listing all definitions.
4825 and shows only the path names when listing all definitions.
4826
4826
4827 Path names are defined in the [paths] section of your
4827 Path names are defined in the [paths] section of your
4828 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4828 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4829 repository, ``.hg/hgrc`` is used, too.
4829 repository, ``.hg/hgrc`` is used, too.
4830
4830
4831 The path names ``default`` and ``default-push`` have a special
4831 The path names ``default`` and ``default-push`` have a special
4832 meaning. When performing a push or pull operation, they are used
4832 meaning. When performing a push or pull operation, they are used
4833 as fallbacks if no location is specified on the command-line.
4833 as fallbacks if no location is specified on the command-line.
4834 When ``default-push`` is set, it will be used for push and
4834 When ``default-push`` is set, it will be used for push and
4835 ``default`` will be used for pull; otherwise ``default`` is used
4835 ``default`` will be used for pull; otherwise ``default`` is used
4836 as the fallback for both. When cloning a repository, the clone
4836 as the fallback for both. When cloning a repository, the clone
4837 source is written as ``default`` in ``.hg/hgrc``. Note that
4837 source is written as ``default`` in ``.hg/hgrc``. Note that
4838 ``default`` and ``default-push`` apply to all inbound (e.g.
4838 ``default`` and ``default-push`` apply to all inbound (e.g.
4839 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
4839 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
4840 :hg:`bundle`) operations.
4840 :hg:`bundle`) operations.
4841
4841
4842 See :hg:`help urls` for more information.
4842 See :hg:`help urls` for more information.
4843
4843
4844 Returns 0 on success.
4844 Returns 0 on success.
4845 """
4845 """
4846 if search:
4846 if search:
4847 for name, path in sorted(ui.paths.iteritems()):
4847 for name, path in sorted(ui.paths.iteritems()):
4848 if name == search:
4848 if name == search:
4849 ui.status("%s\n" % util.hidepassword(path.loc))
4849 ui.status("%s\n" % util.hidepassword(path.loc))
4850 return
4850 return
4851 if not ui.quiet:
4851 if not ui.quiet:
4852 ui.warn(_("not found!\n"))
4852 ui.warn(_("not found!\n"))
4853 return 1
4853 return 1
4854 else:
4854 else:
4855 for name, path in sorted(ui.paths.iteritems()):
4855 for name, path in sorted(ui.paths.iteritems()):
4856 if ui.quiet:
4856 if ui.quiet:
4857 ui.write("%s\n" % name)
4857 ui.write("%s\n" % name)
4858 else:
4858 else:
4859 ui.write("%s = %s\n" % (name,
4859 ui.write("%s = %s\n" % (name,
4860 util.hidepassword(path.loc)))
4860 util.hidepassword(path.loc)))
4861
4861
4862 @command('phase',
4862 @command('phase',
4863 [('p', 'public', False, _('set changeset phase to public')),
4863 [('p', 'public', False, _('set changeset phase to public')),
4864 ('d', 'draft', False, _('set changeset phase to draft')),
4864 ('d', 'draft', False, _('set changeset phase to draft')),
4865 ('s', 'secret', False, _('set changeset phase to secret')),
4865 ('s', 'secret', False, _('set changeset phase to secret')),
4866 ('f', 'force', False, _('allow to move boundary backward')),
4866 ('f', 'force', False, _('allow to move boundary backward')),
4867 ('r', 'rev', [], _('target revision'), _('REV')),
4867 ('r', 'rev', [], _('target revision'), _('REV')),
4868 ],
4868 ],
4869 _('[-p|-d|-s] [-f] [-r] REV...'))
4869 _('[-p|-d|-s] [-f] [-r] REV...'))
4870 def phase(ui, repo, *revs, **opts):
4870 def phase(ui, repo, *revs, **opts):
4871 """set or show the current phase name
4871 """set or show the current phase name
4872
4872
4873 With no argument, show the phase name of specified revisions.
4873 With no argument, show the phase name of specified revisions.
4874
4874
4875 With one of -p/--public, -d/--draft or -s/--secret, change the
4875 With one of -p/--public, -d/--draft or -s/--secret, change the
4876 phase value of the specified revisions.
4876 phase value of the specified revisions.
4877
4877
4878 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
4878 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
4879 lower phase to an higher phase. Phases are ordered as follows::
4879 lower phase to an higher phase. Phases are ordered as follows::
4880
4880
4881 public < draft < secret
4881 public < draft < secret
4882
4882
4883 Returns 0 on success, 1 if no phases were changed or some could not
4883 Returns 0 on success, 1 if no phases were changed or some could not
4884 be changed.
4884 be changed.
4885 """
4885 """
4886 # search for a unique phase argument
4886 # search for a unique phase argument
4887 targetphase = None
4887 targetphase = None
4888 for idx, name in enumerate(phases.phasenames):
4888 for idx, name in enumerate(phases.phasenames):
4889 if opts[name]:
4889 if opts[name]:
4890 if targetphase is not None:
4890 if targetphase is not None:
4891 raise util.Abort(_('only one phase can be specified'))
4891 raise util.Abort(_('only one phase can be specified'))
4892 targetphase = idx
4892 targetphase = idx
4893
4893
4894 # look for specified revision
4894 # look for specified revision
4895 revs = list(revs)
4895 revs = list(revs)
4896 revs.extend(opts['rev'])
4896 revs.extend(opts['rev'])
4897 if not revs:
4897 if not revs:
4898 raise util.Abort(_('no revisions specified'))
4898 raise util.Abort(_('no revisions specified'))
4899
4899
4900 revs = scmutil.revrange(repo, revs)
4900 revs = scmutil.revrange(repo, revs)
4901
4901
4902 lock = None
4902 lock = None
4903 ret = 0
4903 ret = 0
4904 if targetphase is None:
4904 if targetphase is None:
4905 # display
4905 # display
4906 for r in revs:
4906 for r in revs:
4907 ctx = repo[r]
4907 ctx = repo[r]
4908 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4908 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4909 else:
4909 else:
4910 tr = None
4910 tr = None
4911 lock = repo.lock()
4911 lock = repo.lock()
4912 try:
4912 try:
4913 tr = repo.transaction("phase")
4913 tr = repo.transaction("phase")
4914 # set phase
4914 # set phase
4915 if not revs:
4915 if not revs:
4916 raise util.Abort(_('empty revision set'))
4916 raise util.Abort(_('empty revision set'))
4917 nodes = [repo[r].node() for r in revs]
4917 nodes = [repo[r].node() for r in revs]
4918 # moving revision from public to draft may hide them
4918 # moving revision from public to draft may hide them
4919 # We have to check result on an unfiltered repository
4919 # We have to check result on an unfiltered repository
4920 unfi = repo.unfiltered()
4920 unfi = repo.unfiltered()
4921 getphase = unfi._phasecache.phase
4921 getphase = unfi._phasecache.phase
4922 olddata = [getphase(unfi, r) for r in unfi]
4922 olddata = [getphase(unfi, r) for r in unfi]
4923 phases.advanceboundary(repo, tr, targetphase, nodes)
4923 phases.advanceboundary(repo, tr, targetphase, nodes)
4924 if opts['force']:
4924 if opts['force']:
4925 phases.retractboundary(repo, tr, targetphase, nodes)
4925 phases.retractboundary(repo, tr, targetphase, nodes)
4926 tr.close()
4926 tr.close()
4927 finally:
4927 finally:
4928 if tr is not None:
4928 if tr is not None:
4929 tr.release()
4929 tr.release()
4930 lock.release()
4930 lock.release()
4931 getphase = unfi._phasecache.phase
4931 getphase = unfi._phasecache.phase
4932 newdata = [getphase(unfi, r) for r in unfi]
4932 newdata = [getphase(unfi, r) for r in unfi]
4933 changes = sum(newdata[r] != olddata[r] for r in unfi)
4933 changes = sum(newdata[r] != olddata[r] for r in unfi)
4934 cl = unfi.changelog
4934 cl = unfi.changelog
4935 rejected = [n for n in nodes
4935 rejected = [n for n in nodes
4936 if newdata[cl.rev(n)] < targetphase]
4936 if newdata[cl.rev(n)] < targetphase]
4937 if rejected:
4937 if rejected:
4938 ui.warn(_('cannot move %i changesets to a higher '
4938 ui.warn(_('cannot move %i changesets to a higher '
4939 'phase, use --force\n') % len(rejected))
4939 'phase, use --force\n') % len(rejected))
4940 ret = 1
4940 ret = 1
4941 if changes:
4941 if changes:
4942 msg = _('phase changed for %i changesets\n') % changes
4942 msg = _('phase changed for %i changesets\n') % changes
4943 if ret:
4943 if ret:
4944 ui.status(msg)
4944 ui.status(msg)
4945 else:
4945 else:
4946 ui.note(msg)
4946 ui.note(msg)
4947 else:
4947 else:
4948 ui.warn(_('no phases changed\n'))
4948 ui.warn(_('no phases changed\n'))
4949 ret = 1
4949 ret = 1
4950 return ret
4950 return ret
4951
4951
4952 def postincoming(ui, repo, modheads, optupdate, checkout):
4952 def postincoming(ui, repo, modheads, optupdate, checkout):
4953 if modheads == 0:
4953 if modheads == 0:
4954 return
4954 return
4955 if optupdate:
4955 if optupdate:
4956 checkout, movemarkfrom = bookmarks.calculateupdate(ui, repo, checkout)
4956 checkout, movemarkfrom = bookmarks.calculateupdate(ui, repo, checkout)
4957 try:
4957 try:
4958 ret = hg.update(repo, checkout)
4958 ret = hg.update(repo, checkout)
4959 except util.Abort, inst:
4959 except util.Abort, inst:
4960 ui.warn(_("not updating: %s\n") % str(inst))
4960 ui.warn(_("not updating: %s\n") % str(inst))
4961 if inst.hint:
4961 if inst.hint:
4962 ui.warn(_("(%s)\n") % inst.hint)
4962 ui.warn(_("(%s)\n") % inst.hint)
4963 return 0
4963 return 0
4964 if not ret and not checkout:
4964 if not ret and not checkout:
4965 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
4965 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
4966 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
4966 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
4967 return ret
4967 return ret
4968 if modheads > 1:
4968 if modheads > 1:
4969 currentbranchheads = len(repo.branchheads())
4969 currentbranchheads = len(repo.branchheads())
4970 if currentbranchheads == modheads:
4970 if currentbranchheads == modheads:
4971 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4971 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4972 elif currentbranchheads > 1:
4972 elif currentbranchheads > 1:
4973 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4973 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4974 "merge)\n"))
4974 "merge)\n"))
4975 else:
4975 else:
4976 ui.status(_("(run 'hg heads' to see heads)\n"))
4976 ui.status(_("(run 'hg heads' to see heads)\n"))
4977 else:
4977 else:
4978 ui.status(_("(run 'hg update' to get a working copy)\n"))
4978 ui.status(_("(run 'hg update' to get a working copy)\n"))
4979
4979
4980 @command('^pull',
4980 @command('^pull',
4981 [('u', 'update', None,
4981 [('u', 'update', None,
4982 _('update to new branch head if changesets were pulled')),
4982 _('update to new branch head if changesets were pulled')),
4983 ('f', 'force', None, _('run even when remote repository is unrelated')),
4983 ('f', 'force', None, _('run even when remote repository is unrelated')),
4984 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4984 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4985 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4985 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4986 ('b', 'branch', [], _('a specific branch you would like to pull'),
4986 ('b', 'branch', [], _('a specific branch you would like to pull'),
4987 _('BRANCH')),
4987 _('BRANCH')),
4988 ] + remoteopts,
4988 ] + remoteopts,
4989 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
4989 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
4990 def pull(ui, repo, source="default", **opts):
4990 def pull(ui, repo, source="default", **opts):
4991 """pull changes from the specified source
4991 """pull changes from the specified source
4992
4992
4993 Pull changes from a remote repository to a local one.
4993 Pull changes from a remote repository to a local one.
4994
4994
4995 This finds all changes from the repository at the specified path
4995 This finds all changes from the repository at the specified path
4996 or URL and adds them to a local repository (the current one unless
4996 or URL and adds them to a local repository (the current one unless
4997 -R is specified). By default, this does not update the copy of the
4997 -R is specified). By default, this does not update the copy of the
4998 project in the working directory.
4998 project in the working directory.
4999
4999
5000 Use :hg:`incoming` if you want to see what would have been added
5000 Use :hg:`incoming` if you want to see what would have been added
5001 by a pull at the time you issued this command. If you then decide
5001 by a pull at the time you issued this command. If you then decide
5002 to add those changes to the repository, you should use :hg:`pull
5002 to add those changes to the repository, you should use :hg:`pull
5003 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5003 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5004
5004
5005 If SOURCE is omitted, the 'default' path will be used.
5005 If SOURCE is omitted, the 'default' path will be used.
5006 See :hg:`help urls` for more information.
5006 See :hg:`help urls` for more information.
5007
5007
5008 Returns 0 on success, 1 if an update had unresolved files.
5008 Returns 0 on success, 1 if an update had unresolved files.
5009 """
5009 """
5010 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
5010 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
5011 ui.status(_('pulling from %s\n') % util.hidepassword(source))
5011 ui.status(_('pulling from %s\n') % util.hidepassword(source))
5012 other = hg.peer(repo, opts, source)
5012 other = hg.peer(repo, opts, source)
5013 try:
5013 try:
5014 revs, checkout = hg.addbranchrevs(repo, other, branches,
5014 revs, checkout = hg.addbranchrevs(repo, other, branches,
5015 opts.get('rev'))
5015 opts.get('rev'))
5016
5016
5017 remotebookmarks = other.listkeys('bookmarks')
5017 remotebookmarks = other.listkeys('bookmarks')
5018
5018
5019 if opts.get('bookmark'):
5019 if opts.get('bookmark'):
5020 if not revs:
5020 if not revs:
5021 revs = []
5021 revs = []
5022 for b in opts['bookmark']:
5022 for b in opts['bookmark']:
5023 if b not in remotebookmarks:
5023 if b not in remotebookmarks:
5024 raise util.Abort(_('remote bookmark %s not found!') % b)
5024 raise util.Abort(_('remote bookmark %s not found!') % b)
5025 revs.append(remotebookmarks[b])
5025 revs.append(remotebookmarks[b])
5026
5026
5027 if revs:
5027 if revs:
5028 try:
5028 try:
5029 revs = [other.lookup(rev) for rev in revs]
5029 revs = [other.lookup(rev) for rev in revs]
5030 except error.CapabilityError:
5030 except error.CapabilityError:
5031 err = _("other repository doesn't support revision lookup, "
5031 err = _("other repository doesn't support revision lookup, "
5032 "so a rev cannot be specified.")
5032 "so a rev cannot be specified.")
5033 raise util.Abort(err)
5033 raise util.Abort(err)
5034
5034
5035 modheads = exchange.pull(repo, other, heads=revs,
5035 modheads = exchange.pull(repo, other, heads=revs,
5036 force=opts.get('force'),
5036 force=opts.get('force'),
5037 bookmarks=opts.get('bookmark', ())).cgresult
5037 bookmarks=opts.get('bookmark', ())).cgresult
5038 if checkout:
5038 if checkout:
5039 checkout = str(repo.changelog.rev(other.lookup(checkout)))
5039 checkout = str(repo.changelog.rev(other.lookup(checkout)))
5040 repo._subtoppath = source
5040 repo._subtoppath = source
5041 try:
5041 try:
5042 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
5042 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
5043
5043
5044 finally:
5044 finally:
5045 del repo._subtoppath
5045 del repo._subtoppath
5046
5046
5047 finally:
5047 finally:
5048 other.close()
5048 other.close()
5049 return ret
5049 return ret
5050
5050
5051 @command('^push',
5051 @command('^push',
5052 [('f', 'force', None, _('force push')),
5052 [('f', 'force', None, _('force push')),
5053 ('r', 'rev', [],
5053 ('r', 'rev', [],
5054 _('a changeset intended to be included in the destination'),
5054 _('a changeset intended to be included in the destination'),
5055 _('REV')),
5055 _('REV')),
5056 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
5056 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
5057 ('b', 'branch', [],
5057 ('b', 'branch', [],
5058 _('a specific branch you would like to push'), _('BRANCH')),
5058 _('a specific branch you would like to push'), _('BRANCH')),
5059 ('', 'new-branch', False, _('allow pushing a new branch')),
5059 ('', 'new-branch', False, _('allow pushing a new branch')),
5060 ] + remoteopts,
5060 ] + remoteopts,
5061 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
5061 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
5062 def push(ui, repo, dest=None, **opts):
5062 def push(ui, repo, dest=None, **opts):
5063 """push changes to the specified destination
5063 """push changes to the specified destination
5064
5064
5065 Push changesets from the local repository to the specified
5065 Push changesets from the local repository to the specified
5066 destination.
5066 destination.
5067
5067
5068 This operation is symmetrical to pull: it is identical to a pull
5068 This operation is symmetrical to pull: it is identical to a pull
5069 in the destination repository from the current one.
5069 in the destination repository from the current one.
5070
5070
5071 By default, push will not allow creation of new heads at the
5071 By default, push will not allow creation of new heads at the
5072 destination, since multiple heads would make it unclear which head
5072 destination, since multiple heads would make it unclear which head
5073 to use. In this situation, it is recommended to pull and merge
5073 to use. In this situation, it is recommended to pull and merge
5074 before pushing.
5074 before pushing.
5075
5075
5076 Use --new-branch if you want to allow push to create a new named
5076 Use --new-branch if you want to allow push to create a new named
5077 branch that is not present at the destination. This allows you to
5077 branch that is not present at the destination. This allows you to
5078 only create a new branch without forcing other changes.
5078 only create a new branch without forcing other changes.
5079
5079
5080 .. note::
5080 .. note::
5081
5081
5082 Extra care should be taken with the -f/--force option,
5082 Extra care should be taken with the -f/--force option,
5083 which will push all new heads on all branches, an action which will
5083 which will push all new heads on all branches, an action which will
5084 almost always cause confusion for collaborators.
5084 almost always cause confusion for collaborators.
5085
5085
5086 If -r/--rev is used, the specified revision and all its ancestors
5086 If -r/--rev is used, the specified revision and all its ancestors
5087 will be pushed to the remote repository.
5087 will be pushed to the remote repository.
5088
5088
5089 If -B/--bookmark is used, the specified bookmarked revision, its
5089 If -B/--bookmark is used, the specified bookmarked revision, its
5090 ancestors, and the bookmark will be pushed to the remote
5090 ancestors, and the bookmark will be pushed to the remote
5091 repository.
5091 repository.
5092
5092
5093 Please see :hg:`help urls` for important details about ``ssh://``
5093 Please see :hg:`help urls` for important details about ``ssh://``
5094 URLs. If DESTINATION is omitted, a default path will be used.
5094 URLs. If DESTINATION is omitted, a default path will be used.
5095
5095
5096 Returns 0 if push was successful, 1 if nothing to push.
5096 Returns 0 if push was successful, 1 if nothing to push.
5097 """
5097 """
5098
5098
5099 if opts.get('bookmark'):
5099 if opts.get('bookmark'):
5100 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
5100 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
5101 for b in opts['bookmark']:
5101 for b in opts['bookmark']:
5102 # translate -B options to -r so changesets get pushed
5102 # translate -B options to -r so changesets get pushed
5103 if b in repo._bookmarks:
5103 if b in repo._bookmarks:
5104 opts.setdefault('rev', []).append(b)
5104 opts.setdefault('rev', []).append(b)
5105 else:
5105 else:
5106 # if we try to push a deleted bookmark, translate it to null
5106 # if we try to push a deleted bookmark, translate it to null
5107 # this lets simultaneous -r, -b options continue working
5107 # this lets simultaneous -r, -b options continue working
5108 opts.setdefault('rev', []).append("null")
5108 opts.setdefault('rev', []).append("null")
5109
5109
5110 dest = ui.expandpath(dest or 'default-push', dest or 'default')
5110 dest = ui.expandpath(dest or 'default-push', dest or 'default')
5111 dest, branches = hg.parseurl(dest, opts.get('branch'))
5111 dest, branches = hg.parseurl(dest, opts.get('branch'))
5112 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
5112 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
5113 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
5113 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
5114 try:
5114 try:
5115 other = hg.peer(repo, opts, dest)
5115 other = hg.peer(repo, opts, dest)
5116 except error.RepoError:
5116 except error.RepoError:
5117 if dest == "default-push":
5117 if dest == "default-push":
5118 raise util.Abort(_("default repository not configured!"),
5118 raise util.Abort(_("default repository not configured!"),
5119 hint=_('see the "path" section in "hg help config"'))
5119 hint=_('see the "path" section in "hg help config"'))
5120 else:
5120 else:
5121 raise
5121 raise
5122
5122
5123 if revs:
5123 if revs:
5124 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
5124 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
5125
5125
5126 repo._subtoppath = dest
5126 repo._subtoppath = dest
5127 try:
5127 try:
5128 # push subrepos depth-first for coherent ordering
5128 # push subrepos depth-first for coherent ordering
5129 c = repo['']
5129 c = repo['']
5130 subs = c.substate # only repos that are committed
5130 subs = c.substate # only repos that are committed
5131 for s in sorted(subs):
5131 for s in sorted(subs):
5132 result = c.sub(s).push(opts)
5132 result = c.sub(s).push(opts)
5133 if result == 0:
5133 if result == 0:
5134 return not result
5134 return not result
5135 finally:
5135 finally:
5136 del repo._subtoppath
5136 del repo._subtoppath
5137 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
5137 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
5138 newbranch=opts.get('new_branch'),
5138 newbranch=opts.get('new_branch'),
5139 bookmarks=opts.get('bookmark', ()))
5139 bookmarks=opts.get('bookmark', ()))
5140
5140
5141 result = not pushop.cgresult
5141 result = not pushop.cgresult
5142
5142
5143 if pushop.bkresult is not None:
5143 if pushop.bkresult is not None:
5144 if pushop.bkresult == 2:
5144 if pushop.bkresult == 2:
5145 result = 2
5145 result = 2
5146 elif not result and pushop.bkresult:
5146 elif not result and pushop.bkresult:
5147 result = 2
5147 result = 2
5148
5148
5149 return result
5149 return result
5150
5150
5151 @command('recover', [])
5151 @command('recover', [])
5152 def recover(ui, repo):
5152 def recover(ui, repo):
5153 """roll back an interrupted transaction
5153 """roll back an interrupted transaction
5154
5154
5155 Recover from an interrupted commit or pull.
5155 Recover from an interrupted commit or pull.
5156
5156
5157 This command tries to fix the repository status after an
5157 This command tries to fix the repository status after an
5158 interrupted operation. It should only be necessary when Mercurial
5158 interrupted operation. It should only be necessary when Mercurial
5159 suggests it.
5159 suggests it.
5160
5160
5161 Returns 0 if successful, 1 if nothing to recover or verify fails.
5161 Returns 0 if successful, 1 if nothing to recover or verify fails.
5162 """
5162 """
5163 if repo.recover():
5163 if repo.recover():
5164 return hg.verify(repo)
5164 return hg.verify(repo)
5165 return 1
5165 return 1
5166
5166
5167 @command('^remove|rm',
5167 @command('^remove|rm',
5168 [('A', 'after', None, _('record delete for missing files')),
5168 [('A', 'after', None, _('record delete for missing files')),
5169 ('f', 'force', None,
5169 ('f', 'force', None,
5170 _('remove (and delete) file even if added or modified')),
5170 _('remove (and delete) file even if added or modified')),
5171 ] + subrepoopts + walkopts,
5171 ] + subrepoopts + walkopts,
5172 _('[OPTION]... FILE...'),
5172 _('[OPTION]... FILE...'),
5173 inferrepo=True)
5173 inferrepo=True)
5174 def remove(ui, repo, *pats, **opts):
5174 def remove(ui, repo, *pats, **opts):
5175 """remove the specified files on the next commit
5175 """remove the specified files on the next commit
5176
5176
5177 Schedule the indicated files for removal from the current branch.
5177 Schedule the indicated files for removal from the current branch.
5178
5178
5179 This command schedules the files to be removed at the next commit.
5179 This command schedules the files to be removed at the next commit.
5180 To undo a remove before that, see :hg:`revert`. To undo added
5180 To undo a remove before that, see :hg:`revert`. To undo added
5181 files, see :hg:`forget`.
5181 files, see :hg:`forget`.
5182
5182
5183 .. container:: verbose
5183 .. container:: verbose
5184
5184
5185 -A/--after can be used to remove only files that have already
5185 -A/--after can be used to remove only files that have already
5186 been deleted, -f/--force can be used to force deletion, and -Af
5186 been deleted, -f/--force can be used to force deletion, and -Af
5187 can be used to remove files from the next revision without
5187 can be used to remove files from the next revision without
5188 deleting them from the working directory.
5188 deleting them from the working directory.
5189
5189
5190 The following table details the behavior of remove for different
5190 The following table details the behavior of remove for different
5191 file states (columns) and option combinations (rows). The file
5191 file states (columns) and option combinations (rows). The file
5192 states are Added [A], Clean [C], Modified [M] and Missing [!]
5192 states are Added [A], Clean [C], Modified [M] and Missing [!]
5193 (as reported by :hg:`status`). The actions are Warn, Remove
5193 (as reported by :hg:`status`). The actions are Warn, Remove
5194 (from branch) and Delete (from disk):
5194 (from branch) and Delete (from disk):
5195
5195
5196 ========= == == == ==
5196 ========= == == == ==
5197 opt/state A C M !
5197 opt/state A C M !
5198 ========= == == == ==
5198 ========= == == == ==
5199 none W RD W R
5199 none W RD W R
5200 -f R RD RD R
5200 -f R RD RD R
5201 -A W W W R
5201 -A W W W R
5202 -Af R R R R
5202 -Af R R R R
5203 ========= == == == ==
5203 ========= == == == ==
5204
5204
5205 Note that remove never deletes files in Added [A] state from the
5205 Note that remove never deletes files in Added [A] state from the
5206 working directory, not even if option --force is specified.
5206 working directory, not even if option --force is specified.
5207
5207
5208 Returns 0 on success, 1 if any warnings encountered.
5208 Returns 0 on success, 1 if any warnings encountered.
5209 """
5209 """
5210
5210
5211 after, force = opts.get('after'), opts.get('force')
5211 after, force = opts.get('after'), opts.get('force')
5212 if not pats and not after:
5212 if not pats and not after:
5213 raise util.Abort(_('no files specified'))
5213 raise util.Abort(_('no files specified'))
5214
5214
5215 m = scmutil.match(repo[None], pats, opts)
5215 m = scmutil.match(repo[None], pats, opts)
5216 subrepos = opts.get('subrepos')
5216 subrepos = opts.get('subrepos')
5217 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
5217 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
5218
5218
5219 @command('rename|move|mv',
5219 @command('rename|move|mv',
5220 [('A', 'after', None, _('record a rename that has already occurred')),
5220 [('A', 'after', None, _('record a rename that has already occurred')),
5221 ('f', 'force', None, _('forcibly copy over an existing managed file')),
5221 ('f', 'force', None, _('forcibly copy over an existing managed file')),
5222 ] + walkopts + dryrunopts,
5222 ] + walkopts + dryrunopts,
5223 _('[OPTION]... SOURCE... DEST'))
5223 _('[OPTION]... SOURCE... DEST'))
5224 def rename(ui, repo, *pats, **opts):
5224 def rename(ui, repo, *pats, **opts):
5225 """rename files; equivalent of copy + remove
5225 """rename files; equivalent of copy + remove
5226
5226
5227 Mark dest as copies of sources; mark sources for deletion. If dest
5227 Mark dest as copies of sources; mark sources for deletion. If dest
5228 is a directory, copies are put in that directory. If dest is a
5228 is a directory, copies are put in that directory. If dest is a
5229 file, there can only be one source.
5229 file, there can only be one source.
5230
5230
5231 By default, this command copies the contents of files as they
5231 By default, this command copies the contents of files as they
5232 exist in the working directory. If invoked with -A/--after, the
5232 exist in the working directory. If invoked with -A/--after, the
5233 operation is recorded, but no copying is performed.
5233 operation is recorded, but no copying is performed.
5234
5234
5235 This command takes effect at the next commit. To undo a rename
5235 This command takes effect at the next commit. To undo a rename
5236 before that, see :hg:`revert`.
5236 before that, see :hg:`revert`.
5237
5237
5238 Returns 0 on success, 1 if errors are encountered.
5238 Returns 0 on success, 1 if errors are encountered.
5239 """
5239 """
5240 wlock = repo.wlock(False)
5240 wlock = repo.wlock(False)
5241 try:
5241 try:
5242 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5242 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5243 finally:
5243 finally:
5244 wlock.release()
5244 wlock.release()
5245
5245
5246 @command('resolve',
5246 @command('resolve',
5247 [('a', 'all', None, _('select all unresolved files')),
5247 [('a', 'all', None, _('select all unresolved files')),
5248 ('l', 'list', None, _('list state of files needing merge')),
5248 ('l', 'list', None, _('list state of files needing merge')),
5249 ('m', 'mark', None, _('mark files as resolved')),
5249 ('m', 'mark', None, _('mark files as resolved')),
5250 ('u', 'unmark', None, _('mark files as unresolved')),
5250 ('u', 'unmark', None, _('mark files as unresolved')),
5251 ('n', 'no-status', None, _('hide status prefix'))]
5251 ('n', 'no-status', None, _('hide status prefix'))]
5252 + mergetoolopts + walkopts + formatteropts,
5252 + mergetoolopts + walkopts + formatteropts,
5253 _('[OPTION]... [FILE]...'),
5253 _('[OPTION]... [FILE]...'),
5254 inferrepo=True)
5254 inferrepo=True)
5255 def resolve(ui, repo, *pats, **opts):
5255 def resolve(ui, repo, *pats, **opts):
5256 """redo merges or set/view the merge status of files
5256 """redo merges or set/view the merge status of files
5257
5257
5258 Merges with unresolved conflicts are often the result of
5258 Merges with unresolved conflicts are often the result of
5259 non-interactive merging using the ``internal:merge`` configuration
5259 non-interactive merging using the ``internal:merge`` configuration
5260 setting, or a command-line merge tool like ``diff3``. The resolve
5260 setting, or a command-line merge tool like ``diff3``. The resolve
5261 command is used to manage the files involved in a merge, after
5261 command is used to manage the files involved in a merge, after
5262 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5262 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5263 working directory must have two parents). See :hg:`help
5263 working directory must have two parents). See :hg:`help
5264 merge-tools` for information on configuring merge tools.
5264 merge-tools` for information on configuring merge tools.
5265
5265
5266 The resolve command can be used in the following ways:
5266 The resolve command can be used in the following ways:
5267
5267
5268 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
5268 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
5269 files, discarding any previous merge attempts. Re-merging is not
5269 files, discarding any previous merge attempts. Re-merging is not
5270 performed for files already marked as resolved. Use ``--all/-a``
5270 performed for files already marked as resolved. Use ``--all/-a``
5271 to select all unresolved files. ``--tool`` can be used to specify
5271 to select all unresolved files. ``--tool`` can be used to specify
5272 the merge tool used for the given files. It overrides the HGMERGE
5272 the merge tool used for the given files. It overrides the HGMERGE
5273 environment variable and your configuration files. Previous file
5273 environment variable and your configuration files. Previous file
5274 contents are saved with a ``.orig`` suffix.
5274 contents are saved with a ``.orig`` suffix.
5275
5275
5276 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5276 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5277 (e.g. after having manually fixed-up the files). The default is
5277 (e.g. after having manually fixed-up the files). The default is
5278 to mark all unresolved files.
5278 to mark all unresolved files.
5279
5279
5280 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5280 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5281 default is to mark all resolved files.
5281 default is to mark all resolved files.
5282
5282
5283 - :hg:`resolve -l`: list files which had or still have conflicts.
5283 - :hg:`resolve -l`: list files which had or still have conflicts.
5284 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5284 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5285
5285
5286 Note that Mercurial will not let you commit files with unresolved
5286 Note that Mercurial will not let you commit files with unresolved
5287 merge conflicts. You must use :hg:`resolve -m ...` before you can
5287 merge conflicts. You must use :hg:`resolve -m ...` before you can
5288 commit after a conflicting merge.
5288 commit after a conflicting merge.
5289
5289
5290 Returns 0 on success, 1 if any files fail a resolve attempt.
5290 Returns 0 on success, 1 if any files fail a resolve attempt.
5291 """
5291 """
5292
5292
5293 all, mark, unmark, show, nostatus = \
5293 all, mark, unmark, show, nostatus = \
5294 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
5294 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
5295
5295
5296 if (show and (mark or unmark)) or (mark and unmark):
5296 if (show and (mark or unmark)) or (mark and unmark):
5297 raise util.Abort(_("too many options specified"))
5297 raise util.Abort(_("too many options specified"))
5298 if pats and all:
5298 if pats and all:
5299 raise util.Abort(_("can't specify --all and patterns"))
5299 raise util.Abort(_("can't specify --all and patterns"))
5300 if not (all or pats or show or mark or unmark):
5300 if not (all or pats or show or mark or unmark):
5301 raise util.Abort(_('no files or directories specified'),
5301 raise util.Abort(_('no files or directories specified'),
5302 hint=('use --all to remerge all files'))
5302 hint=('use --all to remerge all files'))
5303
5303
5304 if show:
5304 if show:
5305 fm = ui.formatter('resolve', opts)
5305 fm = ui.formatter('resolve', opts)
5306 ms = mergemod.mergestate(repo)
5306 ms = mergemod.mergestate(repo)
5307 m = scmutil.match(repo[None], pats, opts)
5307 m = scmutil.match(repo[None], pats, opts)
5308 for f in ms:
5308 for f in ms:
5309 if not m(f):
5309 if not m(f):
5310 continue
5310 continue
5311 l = 'resolve.' + {'u': 'unresolved', 'r': 'resolved'}[ms[f]]
5311 l = 'resolve.' + {'u': 'unresolved', 'r': 'resolved'}[ms[f]]
5312 fm.startitem()
5312 fm.startitem()
5313 fm.condwrite(not nostatus, 'status', '%s ', ms[f].upper(), label=l)
5313 fm.condwrite(not nostatus, 'status', '%s ', ms[f].upper(), label=l)
5314 fm.write('path', '%s\n', f, label=l)
5314 fm.write('path', '%s\n', f, label=l)
5315 fm.end()
5315 fm.end()
5316 return 0
5316 return 0
5317
5317
5318 wlock = repo.wlock()
5318 wlock = repo.wlock()
5319 try:
5319 try:
5320 ms = mergemod.mergestate(repo)
5320 ms = mergemod.mergestate(repo)
5321
5321
5322 if not (ms.active() or repo.dirstate.p2() != nullid):
5322 if not (ms.active() or repo.dirstate.p2() != nullid):
5323 raise util.Abort(
5323 raise util.Abort(
5324 _('resolve command not applicable when not merging'))
5324 _('resolve command not applicable when not merging'))
5325
5325
5326 m = scmutil.match(repo[None], pats, opts)
5326 m = scmutil.match(repo[None], pats, opts)
5327 ret = 0
5327 ret = 0
5328 didwork = False
5328 didwork = False
5329
5329
5330 for f in ms:
5330 for f in ms:
5331 if not m(f):
5331 if not m(f):
5332 continue
5332 continue
5333
5333
5334 didwork = True
5334 didwork = True
5335
5335
5336 if mark:
5336 if mark:
5337 ms.mark(f, "r")
5337 ms.mark(f, "r")
5338 elif unmark:
5338 elif unmark:
5339 ms.mark(f, "u")
5339 ms.mark(f, "u")
5340 else:
5340 else:
5341 wctx = repo[None]
5341 wctx = repo[None]
5342
5342
5343 # backup pre-resolve (merge uses .orig for its own purposes)
5343 # backup pre-resolve (merge uses .orig for its own purposes)
5344 a = repo.wjoin(f)
5344 a = repo.wjoin(f)
5345 util.copyfile(a, a + ".resolve")
5345 util.copyfile(a, a + ".resolve")
5346
5346
5347 try:
5347 try:
5348 # resolve file
5348 # resolve file
5349 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
5349 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
5350 'resolve')
5350 'resolve')
5351 if ms.resolve(f, wctx):
5351 if ms.resolve(f, wctx):
5352 ret = 1
5352 ret = 1
5353 finally:
5353 finally:
5354 ui.setconfig('ui', 'forcemerge', '', 'resolve')
5354 ui.setconfig('ui', 'forcemerge', '', 'resolve')
5355 ms.commit()
5355 ms.commit()
5356
5356
5357 # replace filemerge's .orig file with our resolve file
5357 # replace filemerge's .orig file with our resolve file
5358 util.rename(a + ".resolve", a + ".orig")
5358 util.rename(a + ".resolve", a + ".orig")
5359
5359
5360 ms.commit()
5360 ms.commit()
5361
5361
5362 if not didwork and pats:
5362 if not didwork and pats:
5363 ui.warn(_("arguments do not match paths that need resolving\n"))
5363 ui.warn(_("arguments do not match paths that need resolving\n"))
5364
5364
5365 finally:
5365 finally:
5366 wlock.release()
5366 wlock.release()
5367
5367
5368 # Nudge users into finishing an unfinished operation
5368 # Nudge users into finishing an unfinished operation
5369 if not list(ms.unresolved()):
5369 if not list(ms.unresolved()):
5370 ui.status(_('(no more unresolved files)\n'))
5370 ui.status(_('(no more unresolved files)\n'))
5371
5371
5372 return ret
5372 return ret
5373
5373
5374 @command('revert',
5374 @command('revert',
5375 [('a', 'all', None, _('revert all changes when no arguments given')),
5375 [('a', 'all', None, _('revert all changes when no arguments given')),
5376 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5376 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5377 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
5377 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
5378 ('C', 'no-backup', None, _('do not save backup copies of files')),
5378 ('C', 'no-backup', None, _('do not save backup copies of files')),
5379 ('i', 'interactive', None, _('interactively select the changes')),
5379 ('i', 'interactive', None, _('interactively select the changes')),
5380 ] + walkopts + dryrunopts,
5380 ] + walkopts + dryrunopts,
5381 _('[OPTION]... [-r REV] [NAME]...'))
5381 _('[OPTION]... [-r REV] [NAME]...'))
5382 def revert(ui, repo, *pats, **opts):
5382 def revert(ui, repo, *pats, **opts):
5383 """restore files to their checkout state
5383 """restore files to their checkout state
5384
5384
5385 .. note::
5385 .. note::
5386
5386
5387 To check out earlier revisions, you should use :hg:`update REV`.
5387 To check out earlier revisions, you should use :hg:`update REV`.
5388 To cancel an uncommitted merge (and lose your changes),
5388 To cancel an uncommitted merge (and lose your changes),
5389 use :hg:`update --clean .`.
5389 use :hg:`update --clean .`.
5390
5390
5391 With no revision specified, revert the specified files or directories
5391 With no revision specified, revert the specified files or directories
5392 to the contents they had in the parent of the working directory.
5392 to the contents they had in the parent of the working directory.
5393 This restores the contents of files to an unmodified
5393 This restores the contents of files to an unmodified
5394 state and unschedules adds, removes, copies, and renames. If the
5394 state and unschedules adds, removes, copies, and renames. If the
5395 working directory has two parents, you must explicitly specify a
5395 working directory has two parents, you must explicitly specify a
5396 revision.
5396 revision.
5397
5397
5398 Using the -r/--rev or -d/--date options, revert the given files or
5398 Using the -r/--rev or -d/--date options, revert the given files or
5399 directories to their states as of a specific revision. Because
5399 directories to their states as of a specific revision. Because
5400 revert does not change the working directory parents, this will
5400 revert does not change the working directory parents, this will
5401 cause these files to appear modified. This can be helpful to "back
5401 cause these files to appear modified. This can be helpful to "back
5402 out" some or all of an earlier change. See :hg:`backout` for a
5402 out" some or all of an earlier change. See :hg:`backout` for a
5403 related method.
5403 related method.
5404
5404
5405 Modified files are saved with a .orig suffix before reverting.
5405 Modified files are saved with a .orig suffix before reverting.
5406 To disable these backups, use --no-backup.
5406 To disable these backups, use --no-backup.
5407
5407
5408 See :hg:`help dates` for a list of formats valid for -d/--date.
5408 See :hg:`help dates` for a list of formats valid for -d/--date.
5409
5409
5410 Returns 0 on success.
5410 Returns 0 on success.
5411 """
5411 """
5412
5412
5413 if opts.get("date"):
5413 if opts.get("date"):
5414 if opts.get("rev"):
5414 if opts.get("rev"):
5415 raise util.Abort(_("you can't specify a revision and a date"))
5415 raise util.Abort(_("you can't specify a revision and a date"))
5416 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5416 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5417
5417
5418 parent, p2 = repo.dirstate.parents()
5418 parent, p2 = repo.dirstate.parents()
5419 if not opts.get('rev') and p2 != nullid:
5419 if not opts.get('rev') and p2 != nullid:
5420 # revert after merge is a trap for new users (issue2915)
5420 # revert after merge is a trap for new users (issue2915)
5421 raise util.Abort(_('uncommitted merge with no revision specified'),
5421 raise util.Abort(_('uncommitted merge with no revision specified'),
5422 hint=_('use "hg update" or see "hg help revert"'))
5422 hint=_('use "hg update" or see "hg help revert"'))
5423
5423
5424 ctx = scmutil.revsingle(repo, opts.get('rev'))
5424 ctx = scmutil.revsingle(repo, opts.get('rev'))
5425
5425
5426 if not pats and not opts.get('all'):
5426 if not pats and not opts.get('all'):
5427 msg = _("no files or directories specified")
5427 msg = _("no files or directories specified")
5428 if p2 != nullid:
5428 if p2 != nullid:
5429 hint = _("uncommitted merge, use --all to discard all changes,"
5429 hint = _("uncommitted merge, use --all to discard all changes,"
5430 " or 'hg update -C .' to abort the merge")
5430 " or 'hg update -C .' to abort the merge")
5431 raise util.Abort(msg, hint=hint)
5431 raise util.Abort(msg, hint=hint)
5432 dirty = util.any(repo.status())
5432 dirty = util.any(repo.status())
5433 node = ctx.node()
5433 node = ctx.node()
5434 if node != parent:
5434 if node != parent:
5435 if dirty:
5435 if dirty:
5436 hint = _("uncommitted changes, use --all to discard all"
5436 hint = _("uncommitted changes, use --all to discard all"
5437 " changes, or 'hg update %s' to update") % ctx.rev()
5437 " changes, or 'hg update %s' to update") % ctx.rev()
5438 else:
5438 else:
5439 hint = _("use --all to revert all files,"
5439 hint = _("use --all to revert all files,"
5440 " or 'hg update %s' to update") % ctx.rev()
5440 " or 'hg update %s' to update") % ctx.rev()
5441 elif dirty:
5441 elif dirty:
5442 hint = _("uncommitted changes, use --all to discard all changes")
5442 hint = _("uncommitted changes, use --all to discard all changes")
5443 else:
5443 else:
5444 hint = _("use --all to revert all files")
5444 hint = _("use --all to revert all files")
5445 raise util.Abort(msg, hint=hint)
5445 raise util.Abort(msg, hint=hint)
5446
5446
5447 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
5447 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
5448
5448
5449 @command('rollback', dryrunopts +
5449 @command('rollback', dryrunopts +
5450 [('f', 'force', False, _('ignore safety measures'))])
5450 [('f', 'force', False, _('ignore safety measures'))])
5451 def rollback(ui, repo, **opts):
5451 def rollback(ui, repo, **opts):
5452 """roll back the last transaction (DANGEROUS) (DEPRECATED)
5452 """roll back the last transaction (DANGEROUS) (DEPRECATED)
5453
5453
5454 Please use :hg:`commit --amend` instead of rollback to correct
5454 Please use :hg:`commit --amend` instead of rollback to correct
5455 mistakes in the last commit.
5455 mistakes in the last commit.
5456
5456
5457 This command should be used with care. There is only one level of
5457 This command should be used with care. There is only one level of
5458 rollback, and there is no way to undo a rollback. It will also
5458 rollback, and there is no way to undo a rollback. It will also
5459 restore the dirstate at the time of the last transaction, losing
5459 restore the dirstate at the time of the last transaction, losing
5460 any dirstate changes since that time. This command does not alter
5460 any dirstate changes since that time. This command does not alter
5461 the working directory.
5461 the working directory.
5462
5462
5463 Transactions are used to encapsulate the effects of all commands
5463 Transactions are used to encapsulate the effects of all commands
5464 that create new changesets or propagate existing changesets into a
5464 that create new changesets or propagate existing changesets into a
5465 repository.
5465 repository.
5466
5466
5467 .. container:: verbose
5467 .. container:: verbose
5468
5468
5469 For example, the following commands are transactional, and their
5469 For example, the following commands are transactional, and their
5470 effects can be rolled back:
5470 effects can be rolled back:
5471
5471
5472 - commit
5472 - commit
5473 - import
5473 - import
5474 - pull
5474 - pull
5475 - push (with this repository as the destination)
5475 - push (with this repository as the destination)
5476 - unbundle
5476 - unbundle
5477
5477
5478 To avoid permanent data loss, rollback will refuse to rollback a
5478 To avoid permanent data loss, rollback will refuse to rollback a
5479 commit transaction if it isn't checked out. Use --force to
5479 commit transaction if it isn't checked out. Use --force to
5480 override this protection.
5480 override this protection.
5481
5481
5482 This command is not intended for use on public repositories. Once
5482 This command is not intended for use on public repositories. Once
5483 changes are visible for pull by other users, rolling a transaction
5483 changes are visible for pull by other users, rolling a transaction
5484 back locally is ineffective (someone else may already have pulled
5484 back locally is ineffective (someone else may already have pulled
5485 the changes). Furthermore, a race is possible with readers of the
5485 the changes). Furthermore, a race is possible with readers of the
5486 repository; for example an in-progress pull from the repository
5486 repository; for example an in-progress pull from the repository
5487 may fail if a rollback is performed.
5487 may fail if a rollback is performed.
5488
5488
5489 Returns 0 on success, 1 if no rollback data is available.
5489 Returns 0 on success, 1 if no rollback data is available.
5490 """
5490 """
5491 return repo.rollback(dryrun=opts.get('dry_run'),
5491 return repo.rollback(dryrun=opts.get('dry_run'),
5492 force=opts.get('force'))
5492 force=opts.get('force'))
5493
5493
5494 @command('root', [])
5494 @command('root', [])
5495 def root(ui, repo):
5495 def root(ui, repo):
5496 """print the root (top) of the current working directory
5496 """print the root (top) of the current working directory
5497
5497
5498 Print the root directory of the current repository.
5498 Print the root directory of the current repository.
5499
5499
5500 Returns 0 on success.
5500 Returns 0 on success.
5501 """
5501 """
5502 ui.write(repo.root + "\n")
5502 ui.write(repo.root + "\n")
5503
5503
5504 @command('^serve',
5504 @command('^serve',
5505 [('A', 'accesslog', '', _('name of access log file to write to'),
5505 [('A', 'accesslog', '', _('name of access log file to write to'),
5506 _('FILE')),
5506 _('FILE')),
5507 ('d', 'daemon', None, _('run server in background')),
5507 ('d', 'daemon', None, _('run server in background')),
5508 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('FILE')),
5508 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('FILE')),
5509 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5509 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5510 # use string type, then we can check if something was passed
5510 # use string type, then we can check if something was passed
5511 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5511 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5512 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5512 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5513 _('ADDR')),
5513 _('ADDR')),
5514 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5514 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5515 _('PREFIX')),
5515 _('PREFIX')),
5516 ('n', 'name', '',
5516 ('n', 'name', '',
5517 _('name to show in web pages (default: working directory)'), _('NAME')),
5517 _('name to show in web pages (default: working directory)'), _('NAME')),
5518 ('', 'web-conf', '',
5518 ('', 'web-conf', '',
5519 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
5519 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
5520 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5520 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5521 _('FILE')),
5521 _('FILE')),
5522 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5522 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5523 ('', 'stdio', None, _('for remote clients')),
5523 ('', 'stdio', None, _('for remote clients')),
5524 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
5524 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
5525 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5525 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5526 ('', 'style', '', _('template style to use'), _('STYLE')),
5526 ('', 'style', '', _('template style to use'), _('STYLE')),
5527 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5527 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5528 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
5528 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
5529 _('[OPTION]...'),
5529 _('[OPTION]...'),
5530 optionalrepo=True)
5530 optionalrepo=True)
5531 def serve(ui, repo, **opts):
5531 def serve(ui, repo, **opts):
5532 """start stand-alone webserver
5532 """start stand-alone webserver
5533
5533
5534 Start a local HTTP repository browser and pull server. You can use
5534 Start a local HTTP repository browser and pull server. You can use
5535 this for ad-hoc sharing and browsing of repositories. It is
5535 this for ad-hoc sharing and browsing of repositories. It is
5536 recommended to use a real web server to serve a repository for
5536 recommended to use a real web server to serve a repository for
5537 longer periods of time.
5537 longer periods of time.
5538
5538
5539 Please note that the server does not implement access control.
5539 Please note that the server does not implement access control.
5540 This means that, by default, anybody can read from the server and
5540 This means that, by default, anybody can read from the server and
5541 nobody can write to it by default. Set the ``web.allow_push``
5541 nobody can write to it by default. Set the ``web.allow_push``
5542 option to ``*`` to allow everybody to push to the server. You
5542 option to ``*`` to allow everybody to push to the server. You
5543 should use a real web server if you need to authenticate users.
5543 should use a real web server if you need to authenticate users.
5544
5544
5545 By default, the server logs accesses to stdout and errors to
5545 By default, the server logs accesses to stdout and errors to
5546 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5546 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5547 files.
5547 files.
5548
5548
5549 To have the server choose a free port number to listen on, specify
5549 To have the server choose a free port number to listen on, specify
5550 a port number of 0; in this case, the server will print the port
5550 a port number of 0; in this case, the server will print the port
5551 number it uses.
5551 number it uses.
5552
5552
5553 Returns 0 on success.
5553 Returns 0 on success.
5554 """
5554 """
5555
5555
5556 if opts["stdio"] and opts["cmdserver"]:
5556 if opts["stdio"] and opts["cmdserver"]:
5557 raise util.Abort(_("cannot use --stdio with --cmdserver"))
5557 raise util.Abort(_("cannot use --stdio with --cmdserver"))
5558
5558
5559 if opts["stdio"]:
5559 if opts["stdio"]:
5560 if repo is None:
5560 if repo is None:
5561 raise error.RepoError(_("there is no Mercurial repository here"
5561 raise error.RepoError(_("there is no Mercurial repository here"
5562 " (.hg not found)"))
5562 " (.hg not found)"))
5563 s = sshserver.sshserver(ui, repo)
5563 s = sshserver.sshserver(ui, repo)
5564 s.serve_forever()
5564 s.serve_forever()
5565
5565
5566 if opts["cmdserver"]:
5566 if opts["cmdserver"]:
5567 service = commandserver.createservice(ui, repo, opts)
5567 service = commandserver.createservice(ui, repo, opts)
5568 return cmdutil.service(opts, initfn=service.init, runfn=service.run)
5568 return cmdutil.service(opts, initfn=service.init, runfn=service.run)
5569
5569
5570 # this way we can check if something was given in the command-line
5570 # this way we can check if something was given in the command-line
5571 if opts.get('port'):
5571 if opts.get('port'):
5572 opts['port'] = util.getport(opts.get('port'))
5572 opts['port'] = util.getport(opts.get('port'))
5573
5573
5574 if repo:
5574 if repo:
5575 baseui = repo.baseui
5575 baseui = repo.baseui
5576 else:
5576 else:
5577 baseui = ui
5577 baseui = ui
5578 optlist = ("name templates style address port prefix ipv6"
5578 optlist = ("name templates style address port prefix ipv6"
5579 " accesslog errorlog certificate encoding")
5579 " accesslog errorlog certificate encoding")
5580 for o in optlist.split():
5580 for o in optlist.split():
5581 val = opts.get(o, '')
5581 val = opts.get(o, '')
5582 if val in (None, ''): # should check against default options instead
5582 if val in (None, ''): # should check against default options instead
5583 continue
5583 continue
5584 baseui.setconfig("web", o, val, 'serve')
5584 baseui.setconfig("web", o, val, 'serve')
5585 if repo and repo.ui != baseui:
5585 if repo and repo.ui != baseui:
5586 repo.ui.setconfig("web", o, val, 'serve')
5586 repo.ui.setconfig("web", o, val, 'serve')
5587
5587
5588 o = opts.get('web_conf') or opts.get('webdir_conf')
5588 o = opts.get('web_conf') or opts.get('webdir_conf')
5589 if not o:
5589 if not o:
5590 if not repo:
5590 if not repo:
5591 raise error.RepoError(_("there is no Mercurial repository"
5591 raise error.RepoError(_("there is no Mercurial repository"
5592 " here (.hg not found)"))
5592 " here (.hg not found)"))
5593 o = repo
5593 o = repo
5594
5594
5595 app = hgweb.hgweb(o, baseui=baseui)
5595 app = hgweb.hgweb(o, baseui=baseui)
5596 service = httpservice(ui, app, opts)
5596 service = httpservice(ui, app, opts)
5597 cmdutil.service(opts, initfn=service.init, runfn=service.run)
5597 cmdutil.service(opts, initfn=service.init, runfn=service.run)
5598
5598
5599 class httpservice(object):
5599 class httpservice(object):
5600 def __init__(self, ui, app, opts):
5600 def __init__(self, ui, app, opts):
5601 self.ui = ui
5601 self.ui = ui
5602 self.app = app
5602 self.app = app
5603 self.opts = opts
5603 self.opts = opts
5604
5604
5605 def init(self):
5605 def init(self):
5606 util.setsignalhandler()
5606 util.setsignalhandler()
5607 self.httpd = hgweb_server.create_server(self.ui, self.app)
5607 self.httpd = hgweb_server.create_server(self.ui, self.app)
5608
5608
5609 if self.opts['port'] and not self.ui.verbose:
5609 if self.opts['port'] and not self.ui.verbose:
5610 return
5610 return
5611
5611
5612 if self.httpd.prefix:
5612 if self.httpd.prefix:
5613 prefix = self.httpd.prefix.strip('/') + '/'
5613 prefix = self.httpd.prefix.strip('/') + '/'
5614 else:
5614 else:
5615 prefix = ''
5615 prefix = ''
5616
5616
5617 port = ':%d' % self.httpd.port
5617 port = ':%d' % self.httpd.port
5618 if port == ':80':
5618 if port == ':80':
5619 port = ''
5619 port = ''
5620
5620
5621 bindaddr = self.httpd.addr
5621 bindaddr = self.httpd.addr
5622 if bindaddr == '0.0.0.0':
5622 if bindaddr == '0.0.0.0':
5623 bindaddr = '*'
5623 bindaddr = '*'
5624 elif ':' in bindaddr: # IPv6
5624 elif ':' in bindaddr: # IPv6
5625 bindaddr = '[%s]' % bindaddr
5625 bindaddr = '[%s]' % bindaddr
5626
5626
5627 fqaddr = self.httpd.fqaddr
5627 fqaddr = self.httpd.fqaddr
5628 if ':' in fqaddr:
5628 if ':' in fqaddr:
5629 fqaddr = '[%s]' % fqaddr
5629 fqaddr = '[%s]' % fqaddr
5630 if self.opts['port']:
5630 if self.opts['port']:
5631 write = self.ui.status
5631 write = self.ui.status
5632 else:
5632 else:
5633 write = self.ui.write
5633 write = self.ui.write
5634 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
5634 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
5635 (fqaddr, port, prefix, bindaddr, self.httpd.port))
5635 (fqaddr, port, prefix, bindaddr, self.httpd.port))
5636 self.ui.flush() # avoid buffering of status message
5636 self.ui.flush() # avoid buffering of status message
5637
5637
5638 def run(self):
5638 def run(self):
5639 self.httpd.serve_forever()
5639 self.httpd.serve_forever()
5640
5640
5641
5641
5642 @command('^status|st',
5642 @command('^status|st',
5643 [('A', 'all', None, _('show status of all files')),
5643 [('A', 'all', None, _('show status of all files')),
5644 ('m', 'modified', None, _('show only modified files')),
5644 ('m', 'modified', None, _('show only modified files')),
5645 ('a', 'added', None, _('show only added files')),
5645 ('a', 'added', None, _('show only added files')),
5646 ('r', 'removed', None, _('show only removed files')),
5646 ('r', 'removed', None, _('show only removed files')),
5647 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5647 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5648 ('c', 'clean', None, _('show only files without changes')),
5648 ('c', 'clean', None, _('show only files without changes')),
5649 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5649 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5650 ('i', 'ignored', None, _('show only ignored files')),
5650 ('i', 'ignored', None, _('show only ignored files')),
5651 ('n', 'no-status', None, _('hide status prefix')),
5651 ('n', 'no-status', None, _('hide status prefix')),
5652 ('C', 'copies', None, _('show source of copied files')),
5652 ('C', 'copies', None, _('show source of copied files')),
5653 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5653 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5654 ('', 'rev', [], _('show difference from revision'), _('REV')),
5654 ('', 'rev', [], _('show difference from revision'), _('REV')),
5655 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5655 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5656 ] + walkopts + subrepoopts + formatteropts,
5656 ] + walkopts + subrepoopts + formatteropts,
5657 _('[OPTION]... [FILE]...'),
5657 _('[OPTION]... [FILE]...'),
5658 inferrepo=True)
5658 inferrepo=True)
5659 def status(ui, repo, *pats, **opts):
5659 def status(ui, repo, *pats, **opts):
5660 """show changed files in the working directory
5660 """show changed files in the working directory
5661
5661
5662 Show status of files in the repository. If names are given, only
5662 Show status of files in the repository. If names are given, only
5663 files that match are shown. Files that are clean or ignored or
5663 files that match are shown. Files that are clean or ignored or
5664 the source of a copy/move operation, are not listed unless
5664 the source of a copy/move operation, are not listed unless
5665 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5665 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5666 Unless options described with "show only ..." are given, the
5666 Unless options described with "show only ..." are given, the
5667 options -mardu are used.
5667 options -mardu are used.
5668
5668
5669 Option -q/--quiet hides untracked (unknown and ignored) files
5669 Option -q/--quiet hides untracked (unknown and ignored) files
5670 unless explicitly requested with -u/--unknown or -i/--ignored.
5670 unless explicitly requested with -u/--unknown or -i/--ignored.
5671
5671
5672 .. note::
5672 .. note::
5673
5673
5674 status may appear to disagree with diff if permissions have
5674 status may appear to disagree with diff if permissions have
5675 changed or a merge has occurred. The standard diff format does
5675 changed or a merge has occurred. The standard diff format does
5676 not report permission changes and diff only reports changes
5676 not report permission changes and diff only reports changes
5677 relative to one merge parent.
5677 relative to one merge parent.
5678
5678
5679 If one revision is given, it is used as the base revision.
5679 If one revision is given, it is used as the base revision.
5680 If two revisions are given, the differences between them are
5680 If two revisions are given, the differences between them are
5681 shown. The --change option can also be used as a shortcut to list
5681 shown. The --change option can also be used as a shortcut to list
5682 the changed files of a revision from its first parent.
5682 the changed files of a revision from its first parent.
5683
5683
5684 The codes used to show the status of files are::
5684 The codes used to show the status of files are::
5685
5685
5686 M = modified
5686 M = modified
5687 A = added
5687 A = added
5688 R = removed
5688 R = removed
5689 C = clean
5689 C = clean
5690 ! = missing (deleted by non-hg command, but still tracked)
5690 ! = missing (deleted by non-hg command, but still tracked)
5691 ? = not tracked
5691 ? = not tracked
5692 I = ignored
5692 I = ignored
5693 = origin of the previous file (with --copies)
5693 = origin of the previous file (with --copies)
5694
5694
5695 .. container:: verbose
5695 .. container:: verbose
5696
5696
5697 Examples:
5697 Examples:
5698
5698
5699 - show changes in the working directory relative to a
5699 - show changes in the working directory relative to a
5700 changeset::
5700 changeset::
5701
5701
5702 hg status --rev 9353
5702 hg status --rev 9353
5703
5703
5704 - show all changes including copies in an existing changeset::
5704 - show all changes including copies in an existing changeset::
5705
5705
5706 hg status --copies --change 9353
5706 hg status --copies --change 9353
5707
5707
5708 - get a NUL separated list of added files, suitable for xargs::
5708 - get a NUL separated list of added files, suitable for xargs::
5709
5709
5710 hg status -an0
5710 hg status -an0
5711
5711
5712 Returns 0 on success.
5712 Returns 0 on success.
5713 """
5713 """
5714
5714
5715 revs = opts.get('rev')
5715 revs = opts.get('rev')
5716 change = opts.get('change')
5716 change = opts.get('change')
5717
5717
5718 if revs and change:
5718 if revs and change:
5719 msg = _('cannot specify --rev and --change at the same time')
5719 msg = _('cannot specify --rev and --change at the same time')
5720 raise util.Abort(msg)
5720 raise util.Abort(msg)
5721 elif change:
5721 elif change:
5722 node2 = scmutil.revsingle(repo, change, None).node()
5722 node2 = scmutil.revsingle(repo, change, None).node()
5723 node1 = repo[node2].p1().node()
5723 node1 = repo[node2].p1().node()
5724 else:
5724 else:
5725 node1, node2 = scmutil.revpair(repo, revs)
5725 node1, node2 = scmutil.revpair(repo, revs)
5726
5726
5727 if pats:
5727 if pats:
5728 cwd = repo.getcwd()
5728 cwd = repo.getcwd()
5729 else:
5729 else:
5730 cwd = ''
5730 cwd = ''
5731
5731
5732 if opts.get('print0'):
5732 if opts.get('print0'):
5733 end = '\0'
5733 end = '\0'
5734 else:
5734 else:
5735 end = '\n'
5735 end = '\n'
5736 copy = {}
5736 copy = {}
5737 states = 'modified added removed deleted unknown ignored clean'.split()
5737 states = 'modified added removed deleted unknown ignored clean'.split()
5738 show = [k for k in states if opts.get(k)]
5738 show = [k for k in states if opts.get(k)]
5739 if opts.get('all'):
5739 if opts.get('all'):
5740 show += ui.quiet and (states[:4] + ['clean']) or states
5740 show += ui.quiet and (states[:4] + ['clean']) or states
5741 if not show:
5741 if not show:
5742 if ui.quiet:
5742 if ui.quiet:
5743 show = states[:4]
5743 show = states[:4]
5744 else:
5744 else:
5745 show = states[:5]
5745 show = states[:5]
5746
5746
5747 stat = repo.status(node1, node2, scmutil.match(repo[node2], pats, opts),
5747 stat = repo.status(node1, node2, scmutil.match(repo[node2], pats, opts),
5748 'ignored' in show, 'clean' in show, 'unknown' in show,
5748 'ignored' in show, 'clean' in show, 'unknown' in show,
5749 opts.get('subrepos'))
5749 opts.get('subrepos'))
5750 changestates = zip(states, 'MAR!?IC', stat)
5750 changestates = zip(states, 'MAR!?IC', stat)
5751
5751
5752 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
5752 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
5753 copy = copies.pathcopies(repo[node1], repo[node2])
5753 copy = copies.pathcopies(repo[node1], repo[node2])
5754
5754
5755 fm = ui.formatter('status', opts)
5755 fm = ui.formatter('status', opts)
5756 fmt = '%s' + end
5756 fmt = '%s' + end
5757 showchar = not opts.get('no_status')
5757 showchar = not opts.get('no_status')
5758
5758
5759 for state, char, files in changestates:
5759 for state, char, files in changestates:
5760 if state in show:
5760 if state in show:
5761 label = 'status.' + state
5761 label = 'status.' + state
5762 for f in files:
5762 for f in files:
5763 fm.startitem()
5763 fm.startitem()
5764 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5764 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5765 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
5765 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
5766 if f in copy:
5766 if f in copy:
5767 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
5767 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
5768 label='status.copied')
5768 label='status.copied')
5769 fm.end()
5769 fm.end()
5770
5770
5771 @command('^summary|sum',
5771 @command('^summary|sum',
5772 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5772 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5773 def summary(ui, repo, **opts):
5773 def summary(ui, repo, **opts):
5774 """summarize working directory state
5774 """summarize working directory state
5775
5775
5776 This generates a brief summary of the working directory state,
5776 This generates a brief summary of the working directory state,
5777 including parents, branch, commit status, and available updates.
5777 including parents, branch, commit status, and available updates.
5778
5778
5779 With the --remote option, this will check the default paths for
5779 With the --remote option, this will check the default paths for
5780 incoming and outgoing changes. This can be time-consuming.
5780 incoming and outgoing changes. This can be time-consuming.
5781
5781
5782 Returns 0 on success.
5782 Returns 0 on success.
5783 """
5783 """
5784
5784
5785 ctx = repo[None]
5785 ctx = repo[None]
5786 parents = ctx.parents()
5786 parents = ctx.parents()
5787 pnode = parents[0].node()
5787 pnode = parents[0].node()
5788 marks = []
5788 marks = []
5789
5789
5790 for p in parents:
5790 for p in parents:
5791 # label with log.changeset (instead of log.parent) since this
5791 # label with log.changeset (instead of log.parent) since this
5792 # shows a working directory parent *changeset*:
5792 # shows a working directory parent *changeset*:
5793 # i18n: column positioning for "hg summary"
5793 # i18n: column positioning for "hg summary"
5794 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5794 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5795 label='log.changeset changeset.%s' % p.phasestr())
5795 label='log.changeset changeset.%s' % p.phasestr())
5796 ui.write(' '.join(p.tags()), label='log.tag')
5796 ui.write(' '.join(p.tags()), label='log.tag')
5797 if p.bookmarks():
5797 if p.bookmarks():
5798 marks.extend(p.bookmarks())
5798 marks.extend(p.bookmarks())
5799 if p.rev() == -1:
5799 if p.rev() == -1:
5800 if not len(repo):
5800 if not len(repo):
5801 ui.write(_(' (empty repository)'))
5801 ui.write(_(' (empty repository)'))
5802 else:
5802 else:
5803 ui.write(_(' (no revision checked out)'))
5803 ui.write(_(' (no revision checked out)'))
5804 ui.write('\n')
5804 ui.write('\n')
5805 if p.description():
5805 if p.description():
5806 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5806 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5807 label='log.summary')
5807 label='log.summary')
5808
5808
5809 branch = ctx.branch()
5809 branch = ctx.branch()
5810 bheads = repo.branchheads(branch)
5810 bheads = repo.branchheads(branch)
5811 # i18n: column positioning for "hg summary"
5811 # i18n: column positioning for "hg summary"
5812 m = _('branch: %s\n') % branch
5812 m = _('branch: %s\n') % branch
5813 if branch != 'default':
5813 if branch != 'default':
5814 ui.write(m, label='log.branch')
5814 ui.write(m, label='log.branch')
5815 else:
5815 else:
5816 ui.status(m, label='log.branch')
5816 ui.status(m, label='log.branch')
5817
5817
5818 if marks:
5818 if marks:
5819 current = repo._bookmarkcurrent
5819 current = repo._bookmarkcurrent
5820 # i18n: column positioning for "hg summary"
5820 # i18n: column positioning for "hg summary"
5821 ui.write(_('bookmarks:'), label='log.bookmark')
5821 ui.write(_('bookmarks:'), label='log.bookmark')
5822 if current is not None:
5822 if current is not None:
5823 if current in marks:
5823 if current in marks:
5824 ui.write(' *' + current, label='bookmarks.current')
5824 ui.write(' *' + current, label='bookmarks.current')
5825 marks.remove(current)
5825 marks.remove(current)
5826 else:
5826 else:
5827 ui.write(' [%s]' % current, label='bookmarks.current')
5827 ui.write(' [%s]' % current, label='bookmarks.current')
5828 for m in marks:
5828 for m in marks:
5829 ui.write(' ' + m, label='log.bookmark')
5829 ui.write(' ' + m, label='log.bookmark')
5830 ui.write('\n', label='log.bookmark')
5830 ui.write('\n', label='log.bookmark')
5831
5831
5832 status = repo.status(unknown=True)
5832 status = repo.status(unknown=True)
5833
5833
5834 c = repo.dirstate.copies()
5834 c = repo.dirstate.copies()
5835 copied, renamed = [], []
5835 copied, renamed = [], []
5836 for d, s in c.iteritems():
5836 for d, s in c.iteritems():
5837 if s in status.removed:
5837 if s in status.removed:
5838 status.removed.remove(s)
5838 status.removed.remove(s)
5839 renamed.append(d)
5839 renamed.append(d)
5840 else:
5840 else:
5841 copied.append(d)
5841 copied.append(d)
5842 if d in status.added:
5842 if d in status.added:
5843 status.added.remove(d)
5843 status.added.remove(d)
5844
5844
5845 ms = mergemod.mergestate(repo)
5845 ms = mergemod.mergestate(repo)
5846 unresolved = [f for f in ms if ms[f] == 'u']
5846 unresolved = [f for f in ms if ms[f] == 'u']
5847
5847
5848 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5848 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5849
5849
5850 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5850 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5851 (ui.label(_('%d added'), 'status.added'), status.added),
5851 (ui.label(_('%d added'), 'status.added'), status.added),
5852 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5852 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5853 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5853 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5854 (ui.label(_('%d copied'), 'status.copied'), copied),
5854 (ui.label(_('%d copied'), 'status.copied'), copied),
5855 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5855 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5856 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5856 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5857 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5857 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5858 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5858 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5859 t = []
5859 t = []
5860 for l, s in labels:
5860 for l, s in labels:
5861 if s:
5861 if s:
5862 t.append(l % len(s))
5862 t.append(l % len(s))
5863
5863
5864 t = ', '.join(t)
5864 t = ', '.join(t)
5865 cleanworkdir = False
5865 cleanworkdir = False
5866
5866
5867 if repo.vfs.exists('updatestate'):
5867 if repo.vfs.exists('updatestate'):
5868 t += _(' (interrupted update)')
5868 t += _(' (interrupted update)')
5869 elif len(parents) > 1:
5869 elif len(parents) > 1:
5870 t += _(' (merge)')
5870 t += _(' (merge)')
5871 elif branch != parents[0].branch():
5871 elif branch != parents[0].branch():
5872 t += _(' (new branch)')
5872 t += _(' (new branch)')
5873 elif (parents[0].closesbranch() and
5873 elif (parents[0].closesbranch() and
5874 pnode in repo.branchheads(branch, closed=True)):
5874 pnode in repo.branchheads(branch, closed=True)):
5875 t += _(' (head closed)')
5875 t += _(' (head closed)')
5876 elif not (status.modified or status.added or status.removed or renamed or
5876 elif not (status.modified or status.added or status.removed or renamed or
5877 copied or subs):
5877 copied or subs):
5878 t += _(' (clean)')
5878 t += _(' (clean)')
5879 cleanworkdir = True
5879 cleanworkdir = True
5880 elif pnode not in bheads:
5880 elif pnode not in bheads:
5881 t += _(' (new branch head)')
5881 t += _(' (new branch head)')
5882
5882
5883 if cleanworkdir:
5883 if cleanworkdir:
5884 # i18n: column positioning for "hg summary"
5884 # i18n: column positioning for "hg summary"
5885 ui.status(_('commit: %s\n') % t.strip())
5885 ui.status(_('commit: %s\n') % t.strip())
5886 else:
5886 else:
5887 # i18n: column positioning for "hg summary"
5887 # i18n: column positioning for "hg summary"
5888 ui.write(_('commit: %s\n') % t.strip())
5888 ui.write(_('commit: %s\n') % t.strip())
5889
5889
5890 # all ancestors of branch heads - all ancestors of parent = new csets
5890 # all ancestors of branch heads - all ancestors of parent = new csets
5891 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5891 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5892 bheads))
5892 bheads))
5893
5893
5894 if new == 0:
5894 if new == 0:
5895 # i18n: column positioning for "hg summary"
5895 # i18n: column positioning for "hg summary"
5896 ui.status(_('update: (current)\n'))
5896 ui.status(_('update: (current)\n'))
5897 elif pnode not in bheads:
5897 elif pnode not in bheads:
5898 # i18n: column positioning for "hg summary"
5898 # i18n: column positioning for "hg summary"
5899 ui.write(_('update: %d new changesets (update)\n') % new)
5899 ui.write(_('update: %d new changesets (update)\n') % new)
5900 else:
5900 else:
5901 # i18n: column positioning for "hg summary"
5901 # i18n: column positioning for "hg summary"
5902 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5902 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5903 (new, len(bheads)))
5903 (new, len(bheads)))
5904
5904
5905 cmdutil.summaryhooks(ui, repo)
5905 cmdutil.summaryhooks(ui, repo)
5906
5906
5907 if opts.get('remote'):
5907 if opts.get('remote'):
5908 needsincoming, needsoutgoing = True, True
5908 needsincoming, needsoutgoing = True, True
5909 else:
5909 else:
5910 needsincoming, needsoutgoing = False, False
5910 needsincoming, needsoutgoing = False, False
5911 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5911 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5912 if i:
5912 if i:
5913 needsincoming = True
5913 needsincoming = True
5914 if o:
5914 if o:
5915 needsoutgoing = True
5915 needsoutgoing = True
5916 if not needsincoming and not needsoutgoing:
5916 if not needsincoming and not needsoutgoing:
5917 return
5917 return
5918
5918
5919 def getincoming():
5919 def getincoming():
5920 source, branches = hg.parseurl(ui.expandpath('default'))
5920 source, branches = hg.parseurl(ui.expandpath('default'))
5921 sbranch = branches[0]
5921 sbranch = branches[0]
5922 try:
5922 try:
5923 other = hg.peer(repo, {}, source)
5923 other = hg.peer(repo, {}, source)
5924 except error.RepoError:
5924 except error.RepoError:
5925 if opts.get('remote'):
5925 if opts.get('remote'):
5926 raise
5926 raise
5927 return source, sbranch, None, None, None
5927 return source, sbranch, None, None, None
5928 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5928 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5929 if revs:
5929 if revs:
5930 revs = [other.lookup(rev) for rev in revs]
5930 revs = [other.lookup(rev) for rev in revs]
5931 ui.debug('comparing with %s\n' % util.hidepassword(source))
5931 ui.debug('comparing with %s\n' % util.hidepassword(source))
5932 repo.ui.pushbuffer()
5932 repo.ui.pushbuffer()
5933 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5933 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5934 repo.ui.popbuffer()
5934 repo.ui.popbuffer()
5935 return source, sbranch, other, commoninc, commoninc[1]
5935 return source, sbranch, other, commoninc, commoninc[1]
5936
5936
5937 if needsincoming:
5937 if needsincoming:
5938 source, sbranch, sother, commoninc, incoming = getincoming()
5938 source, sbranch, sother, commoninc, incoming = getincoming()
5939 else:
5939 else:
5940 source = sbranch = sother = commoninc = incoming = None
5940 source = sbranch = sother = commoninc = incoming = None
5941
5941
5942 def getoutgoing():
5942 def getoutgoing():
5943 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5943 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5944 dbranch = branches[0]
5944 dbranch = branches[0]
5945 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5945 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5946 if source != dest:
5946 if source != dest:
5947 try:
5947 try:
5948 dother = hg.peer(repo, {}, dest)
5948 dother = hg.peer(repo, {}, dest)
5949 except error.RepoError:
5949 except error.RepoError:
5950 if opts.get('remote'):
5950 if opts.get('remote'):
5951 raise
5951 raise
5952 return dest, dbranch, None, None
5952 return dest, dbranch, None, None
5953 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5953 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5954 elif sother is None:
5954 elif sother is None:
5955 # there is no explicit destination peer, but source one is invalid
5955 # there is no explicit destination peer, but source one is invalid
5956 return dest, dbranch, None, None
5956 return dest, dbranch, None, None
5957 else:
5957 else:
5958 dother = sother
5958 dother = sother
5959 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5959 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5960 common = None
5960 common = None
5961 else:
5961 else:
5962 common = commoninc
5962 common = commoninc
5963 if revs:
5963 if revs:
5964 revs = [repo.lookup(rev) for rev in revs]
5964 revs = [repo.lookup(rev) for rev in revs]
5965 repo.ui.pushbuffer()
5965 repo.ui.pushbuffer()
5966 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5966 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5967 commoninc=common)
5967 commoninc=common)
5968 repo.ui.popbuffer()
5968 repo.ui.popbuffer()
5969 return dest, dbranch, dother, outgoing
5969 return dest, dbranch, dother, outgoing
5970
5970
5971 if needsoutgoing:
5971 if needsoutgoing:
5972 dest, dbranch, dother, outgoing = getoutgoing()
5972 dest, dbranch, dother, outgoing = getoutgoing()
5973 else:
5973 else:
5974 dest = dbranch = dother = outgoing = None
5974 dest = dbranch = dother = outgoing = None
5975
5975
5976 if opts.get('remote'):
5976 if opts.get('remote'):
5977 t = []
5977 t = []
5978 if incoming:
5978 if incoming:
5979 t.append(_('1 or more incoming'))
5979 t.append(_('1 or more incoming'))
5980 o = outgoing.missing
5980 o = outgoing.missing
5981 if o:
5981 if o:
5982 t.append(_('%d outgoing') % len(o))
5982 t.append(_('%d outgoing') % len(o))
5983 other = dother or sother
5983 other = dother or sother
5984 if 'bookmarks' in other.listkeys('namespaces'):
5984 if 'bookmarks' in other.listkeys('namespaces'):
5985 lmarks = repo.listkeys('bookmarks')
5985 lmarks = repo.listkeys('bookmarks')
5986 rmarks = other.listkeys('bookmarks')
5986 rmarks = other.listkeys('bookmarks')
5987 diff = set(rmarks) - set(lmarks)
5987 diff = set(rmarks) - set(lmarks)
5988 if len(diff) > 0:
5988 if len(diff) > 0:
5989 t.append(_('%d incoming bookmarks') % len(diff))
5989 t.append(_('%d incoming bookmarks') % len(diff))
5990 diff = set(lmarks) - set(rmarks)
5990 diff = set(lmarks) - set(rmarks)
5991 if len(diff) > 0:
5991 if len(diff) > 0:
5992 t.append(_('%d outgoing bookmarks') % len(diff))
5992 t.append(_('%d outgoing bookmarks') % len(diff))
5993
5993
5994 if t:
5994 if t:
5995 # i18n: column positioning for "hg summary"
5995 # i18n: column positioning for "hg summary"
5996 ui.write(_('remote: %s\n') % (', '.join(t)))
5996 ui.write(_('remote: %s\n') % (', '.join(t)))
5997 else:
5997 else:
5998 # i18n: column positioning for "hg summary"
5998 # i18n: column positioning for "hg summary"
5999 ui.status(_('remote: (synced)\n'))
5999 ui.status(_('remote: (synced)\n'))
6000
6000
6001 cmdutil.summaryremotehooks(ui, repo, opts,
6001 cmdutil.summaryremotehooks(ui, repo, opts,
6002 ((source, sbranch, sother, commoninc),
6002 ((source, sbranch, sother, commoninc),
6003 (dest, dbranch, dother, outgoing)))
6003 (dest, dbranch, dother, outgoing)))
6004
6004
6005 @command('tag',
6005 @command('tag',
6006 [('f', 'force', None, _('force tag')),
6006 [('f', 'force', None, _('force tag')),
6007 ('l', 'local', None, _('make the tag local')),
6007 ('l', 'local', None, _('make the tag local')),
6008 ('r', 'rev', '', _('revision to tag'), _('REV')),
6008 ('r', 'rev', '', _('revision to tag'), _('REV')),
6009 ('', 'remove', None, _('remove a tag')),
6009 ('', 'remove', None, _('remove a tag')),
6010 # -l/--local is already there, commitopts cannot be used
6010 # -l/--local is already there, commitopts cannot be used
6011 ('e', 'edit', None, _('invoke editor on commit messages')),
6011 ('e', 'edit', None, _('invoke editor on commit messages')),
6012 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
6012 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
6013 ] + commitopts2,
6013 ] + commitopts2,
6014 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
6014 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
6015 def tag(ui, repo, name1, *names, **opts):
6015 def tag(ui, repo, name1, *names, **opts):
6016 """add one or more tags for the current or given revision
6016 """add one or more tags for the current or given revision
6017
6017
6018 Name a particular revision using <name>.
6018 Name a particular revision using <name>.
6019
6019
6020 Tags are used to name particular revisions of the repository and are
6020 Tags are used to name particular revisions of the repository and are
6021 very useful to compare different revisions, to go back to significant
6021 very useful to compare different revisions, to go back to significant
6022 earlier versions or to mark branch points as releases, etc. Changing
6022 earlier versions or to mark branch points as releases, etc. Changing
6023 an existing tag is normally disallowed; use -f/--force to override.
6023 an existing tag is normally disallowed; use -f/--force to override.
6024
6024
6025 If no revision is given, the parent of the working directory is
6025 If no revision is given, the parent of the working directory is
6026 used.
6026 used.
6027
6027
6028 To facilitate version control, distribution, and merging of tags,
6028 To facilitate version control, distribution, and merging of tags,
6029 they are stored as a file named ".hgtags" which is managed similarly
6029 they are stored as a file named ".hgtags" which is managed similarly
6030 to other project files and can be hand-edited if necessary. This
6030 to other project files and can be hand-edited if necessary. This
6031 also means that tagging creates a new commit. The file
6031 also means that tagging creates a new commit. The file
6032 ".hg/localtags" is used for local tags (not shared among
6032 ".hg/localtags" is used for local tags (not shared among
6033 repositories).
6033 repositories).
6034
6034
6035 Tag commits are usually made at the head of a branch. If the parent
6035 Tag commits are usually made at the head of a branch. If the parent
6036 of the working directory is not a branch head, :hg:`tag` aborts; use
6036 of the working directory is not a branch head, :hg:`tag` aborts; use
6037 -f/--force to force the tag commit to be based on a non-head
6037 -f/--force to force the tag commit to be based on a non-head
6038 changeset.
6038 changeset.
6039
6039
6040 See :hg:`help dates` for a list of formats valid for -d/--date.
6040 See :hg:`help dates` for a list of formats valid for -d/--date.
6041
6041
6042 Since tag names have priority over branch names during revision
6042 Since tag names have priority over branch names during revision
6043 lookup, using an existing branch name as a tag name is discouraged.
6043 lookup, using an existing branch name as a tag name is discouraged.
6044
6044
6045 Returns 0 on success.
6045 Returns 0 on success.
6046 """
6046 """
6047 wlock = lock = None
6047 wlock = lock = None
6048 try:
6048 try:
6049 wlock = repo.wlock()
6049 wlock = repo.wlock()
6050 lock = repo.lock()
6050 lock = repo.lock()
6051 rev_ = "."
6051 rev_ = "."
6052 names = [t.strip() for t in (name1,) + names]
6052 names = [t.strip() for t in (name1,) + names]
6053 if len(names) != len(set(names)):
6053 if len(names) != len(set(names)):
6054 raise util.Abort(_('tag names must be unique'))
6054 raise util.Abort(_('tag names must be unique'))
6055 for n in names:
6055 for n in names:
6056 scmutil.checknewlabel(repo, n, 'tag')
6056 scmutil.checknewlabel(repo, n, 'tag')
6057 if not n:
6057 if not n:
6058 raise util.Abort(_('tag names cannot consist entirely of '
6058 raise util.Abort(_('tag names cannot consist entirely of '
6059 'whitespace'))
6059 'whitespace'))
6060 if opts.get('rev') and opts.get('remove'):
6060 if opts.get('rev') and opts.get('remove'):
6061 raise util.Abort(_("--rev and --remove are incompatible"))
6061 raise util.Abort(_("--rev and --remove are incompatible"))
6062 if opts.get('rev'):
6062 if opts.get('rev'):
6063 rev_ = opts['rev']
6063 rev_ = opts['rev']
6064 message = opts.get('message')
6064 message = opts.get('message')
6065 if opts.get('remove'):
6065 if opts.get('remove'):
6066 if opts.get('local'):
6066 if opts.get('local'):
6067 expectedtype = 'local'
6067 expectedtype = 'local'
6068 else:
6068 else:
6069 expectedtype = 'global'
6069 expectedtype = 'global'
6070
6070
6071 for n in names:
6071 for n in names:
6072 if not repo.tagtype(n):
6072 if not repo.tagtype(n):
6073 raise util.Abort(_("tag '%s' does not exist") % n)
6073 raise util.Abort(_("tag '%s' does not exist") % n)
6074 if repo.tagtype(n) != expectedtype:
6074 if repo.tagtype(n) != expectedtype:
6075 if expectedtype == 'global':
6075 if expectedtype == 'global':
6076 raise util.Abort(_("tag '%s' is not a global tag") % n)
6076 raise util.Abort(_("tag '%s' is not a global tag") % n)
6077 else:
6077 else:
6078 raise util.Abort(_("tag '%s' is not a local tag") % n)
6078 raise util.Abort(_("tag '%s' is not a local tag") % n)
6079 rev_ = nullid
6079 rev_ = nullid
6080 if not message:
6080 if not message:
6081 # we don't translate commit messages
6081 # we don't translate commit messages
6082 message = 'Removed tag %s' % ', '.join(names)
6082 message = 'Removed tag %s' % ', '.join(names)
6083 elif not opts.get('force'):
6083 elif not opts.get('force'):
6084 for n in names:
6084 for n in names:
6085 if n in repo.tags():
6085 if n in repo.tags():
6086 raise util.Abort(_("tag '%s' already exists "
6086 raise util.Abort(_("tag '%s' already exists "
6087 "(use -f to force)") % n)
6087 "(use -f to force)") % n)
6088 if not opts.get('local'):
6088 if not opts.get('local'):
6089 p1, p2 = repo.dirstate.parents()
6089 p1, p2 = repo.dirstate.parents()
6090 if p2 != nullid:
6090 if p2 != nullid:
6091 raise util.Abort(_('uncommitted merge'))
6091 raise util.Abort(_('uncommitted merge'))
6092 bheads = repo.branchheads()
6092 bheads = repo.branchheads()
6093 if not opts.get('force') and bheads and p1 not in bheads:
6093 if not opts.get('force') and bheads and p1 not in bheads:
6094 raise util.Abort(_('not at a branch head (use -f to force)'))
6094 raise util.Abort(_('not at a branch head (use -f to force)'))
6095 r = scmutil.revsingle(repo, rev_).node()
6095 r = scmutil.revsingle(repo, rev_).node()
6096
6096
6097 if not message:
6097 if not message:
6098 # we don't translate commit messages
6098 # we don't translate commit messages
6099 message = ('Added tag %s for changeset %s' %
6099 message = ('Added tag %s for changeset %s' %
6100 (', '.join(names), short(r)))
6100 (', '.join(names), short(r)))
6101
6101
6102 date = opts.get('date')
6102 date = opts.get('date')
6103 if date:
6103 if date:
6104 date = util.parsedate(date)
6104 date = util.parsedate(date)
6105
6105
6106 if opts.get('remove'):
6106 if opts.get('remove'):
6107 editform = 'tag.remove'
6107 editform = 'tag.remove'
6108 else:
6108 else:
6109 editform = 'tag.add'
6109 editform = 'tag.add'
6110 editor = cmdutil.getcommiteditor(editform=editform, **opts)
6110 editor = cmdutil.getcommiteditor(editform=editform, **opts)
6111
6111
6112 # don't allow tagging the null rev
6112 # don't allow tagging the null rev
6113 if (not opts.get('remove') and
6113 if (not opts.get('remove') and
6114 scmutil.revsingle(repo, rev_).rev() == nullrev):
6114 scmutil.revsingle(repo, rev_).rev() == nullrev):
6115 raise util.Abort(_("cannot tag null revision"))
6115 raise util.Abort(_("cannot tag null revision"))
6116
6116
6117 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date,
6117 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date,
6118 editor=editor)
6118 editor=editor)
6119 finally:
6119 finally:
6120 release(lock, wlock)
6120 release(lock, wlock)
6121
6121
6122 @command('tags', formatteropts, '')
6122 @command('tags', formatteropts, '')
6123 def tags(ui, repo, **opts):
6123 def tags(ui, repo, **opts):
6124 """list repository tags
6124 """list repository tags
6125
6125
6126 This lists both regular and local tags. When the -v/--verbose
6126 This lists both regular and local tags. When the -v/--verbose
6127 switch is used, a third column "local" is printed for local tags.
6127 switch is used, a third column "local" is printed for local tags.
6128
6128
6129 Returns 0 on success.
6129 Returns 0 on success.
6130 """
6130 """
6131
6131
6132 fm = ui.formatter('tags', opts)
6132 fm = ui.formatter('tags', opts)
6133 hexfunc = fm.hexfunc
6133 hexfunc = fm.hexfunc
6134 tagtype = ""
6134 tagtype = ""
6135
6135
6136 for t, n in reversed(repo.tagslist()):
6136 for t, n in reversed(repo.tagslist()):
6137 hn = hexfunc(n)
6137 hn = hexfunc(n)
6138 label = 'tags.normal'
6138 label = 'tags.normal'
6139 tagtype = ''
6139 tagtype = ''
6140 if repo.tagtype(t) == 'local':
6140 if repo.tagtype(t) == 'local':
6141 label = 'tags.local'
6141 label = 'tags.local'
6142 tagtype = 'local'
6142 tagtype = 'local'
6143
6143
6144 fm.startitem()
6144 fm.startitem()
6145 fm.write('tag', '%s', t, label=label)
6145 fm.write('tag', '%s', t, label=label)
6146 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
6146 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
6147 fm.condwrite(not ui.quiet, 'rev node', fmt,
6147 fm.condwrite(not ui.quiet, 'rev node', fmt,
6148 repo.changelog.rev(n), hn, label=label)
6148 repo.changelog.rev(n), hn, label=label)
6149 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
6149 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
6150 tagtype, label=label)
6150 tagtype, label=label)
6151 fm.plain('\n')
6151 fm.plain('\n')
6152 fm.end()
6152 fm.end()
6153
6153
6154 @command('tip',
6154 @command('tip',
6155 [('p', 'patch', None, _('show patch')),
6155 [('p', 'patch', None, _('show patch')),
6156 ('g', 'git', None, _('use git extended diff format')),
6156 ('g', 'git', None, _('use git extended diff format')),
6157 ] + templateopts,
6157 ] + templateopts,
6158 _('[-p] [-g]'))
6158 _('[-p] [-g]'))
6159 def tip(ui, repo, **opts):
6159 def tip(ui, repo, **opts):
6160 """show the tip revision (DEPRECATED)
6160 """show the tip revision (DEPRECATED)
6161
6161
6162 The tip revision (usually just called the tip) is the changeset
6162 The tip revision (usually just called the tip) is the changeset
6163 most recently added to the repository (and therefore the most
6163 most recently added to the repository (and therefore the most
6164 recently changed head).
6164 recently changed head).
6165
6165
6166 If you have just made a commit, that commit will be the tip. If
6166 If you have just made a commit, that commit will be the tip. If
6167 you have just pulled changes from another repository, the tip of
6167 you have just pulled changes from another repository, the tip of
6168 that repository becomes the current tip. The "tip" tag is special
6168 that repository becomes the current tip. The "tip" tag is special
6169 and cannot be renamed or assigned to a different changeset.
6169 and cannot be renamed or assigned to a different changeset.
6170
6170
6171 This command is deprecated, please use :hg:`heads` instead.
6171 This command is deprecated, please use :hg:`heads` instead.
6172
6172
6173 Returns 0 on success.
6173 Returns 0 on success.
6174 """
6174 """
6175 displayer = cmdutil.show_changeset(ui, repo, opts)
6175 displayer = cmdutil.show_changeset(ui, repo, opts)
6176 displayer.show(repo['tip'])
6176 displayer.show(repo['tip'])
6177 displayer.close()
6177 displayer.close()
6178
6178
6179 @command('unbundle',
6179 @command('unbundle',
6180 [('u', 'update', None,
6180 [('u', 'update', None,
6181 _('update to new branch head if changesets were unbundled'))],
6181 _('update to new branch head if changesets were unbundled'))],
6182 _('[-u] FILE...'))
6182 _('[-u] FILE...'))
6183 def unbundle(ui, repo, fname1, *fnames, **opts):
6183 def unbundle(ui, repo, fname1, *fnames, **opts):
6184 """apply one or more changegroup files
6184 """apply one or more changegroup files
6185
6185
6186 Apply one or more compressed changegroup files generated by the
6186 Apply one or more compressed changegroup files generated by the
6187 bundle command.
6187 bundle command.
6188
6188
6189 Returns 0 on success, 1 if an update has unresolved files.
6189 Returns 0 on success, 1 if an update has unresolved files.
6190 """
6190 """
6191 fnames = (fname1,) + fnames
6191 fnames = (fname1,) + fnames
6192
6192
6193 lock = repo.lock()
6193 lock = repo.lock()
6194 try:
6194 try:
6195 for fname in fnames:
6195 for fname in fnames:
6196 f = hg.openpath(ui, fname)
6196 f = hg.openpath(ui, fname)
6197 gen = exchange.readbundle(ui, f, fname)
6197 gen = exchange.readbundle(ui, f, fname)
6198 if isinstance(gen, bundle2.unbundle20):
6198 if isinstance(gen, bundle2.unbundle20):
6199 tr = repo.transaction('unbundle')
6199 tr = repo.transaction('unbundle')
6200 try:
6200 try:
6201 op = bundle2.processbundle(repo, gen, lambda: tr)
6201 op = bundle2.processbundle(repo, gen, lambda: tr)
6202 tr.close()
6202 tr.close()
6203 finally:
6203 finally:
6204 if tr:
6204 if tr:
6205 tr.release()
6205 tr.release()
6206 changes = [r.get('result', 0)
6206 changes = [r.get('result', 0)
6207 for r in op.records['changegroup']]
6207 for r in op.records['changegroup']]
6208 modheads = changegroup.combineresults(changes)
6208 modheads = changegroup.combineresults(changes)
6209 else:
6209 else:
6210 modheads = changegroup.addchangegroup(repo, gen, 'unbundle',
6210 modheads = changegroup.addchangegroup(repo, gen, 'unbundle',
6211 'bundle:' + fname)
6211 'bundle:' + fname)
6212 finally:
6212 finally:
6213 lock.release()
6213 lock.release()
6214
6214
6215 return postincoming(ui, repo, modheads, opts.get('update'), None)
6215 return postincoming(ui, repo, modheads, opts.get('update'), None)
6216
6216
6217 @command('^update|up|checkout|co',
6217 @command('^update|up|checkout|co',
6218 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
6218 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
6219 ('c', 'check', None,
6219 ('c', 'check', None,
6220 _('update across branches if no uncommitted changes')),
6220 _('update across branches if no uncommitted changes')),
6221 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
6221 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
6222 ('r', 'rev', '', _('revision'), _('REV'))
6222 ('r', 'rev', '', _('revision'), _('REV'))
6223 ] + mergetoolopts,
6223 ] + mergetoolopts,
6224 _('[-c] [-C] [-d DATE] [[-r] REV]'))
6224 _('[-c] [-C] [-d DATE] [[-r] REV]'))
6225 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
6225 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
6226 tool=None):
6226 tool=None):
6227 """update working directory (or switch revisions)
6227 """update working directory (or switch revisions)
6228
6228
6229 Update the repository's working directory to the specified
6229 Update the repository's working directory to the specified
6230 changeset. If no changeset is specified, update to the tip of the
6230 changeset. If no changeset is specified, update to the tip of the
6231 current named branch and move the current bookmark (see :hg:`help
6231 current named branch and move the current bookmark (see :hg:`help
6232 bookmarks`).
6232 bookmarks`).
6233
6233
6234 Update sets the working directory's parent revision to the specified
6234 Update sets the working directory's parent revision to the specified
6235 changeset (see :hg:`help parents`).
6235 changeset (see :hg:`help parents`).
6236
6236
6237 If the changeset is not a descendant or ancestor of the working
6237 If the changeset is not a descendant or ancestor of the working
6238 directory's parent, the update is aborted. With the -c/--check
6238 directory's parent, the update is aborted. With the -c/--check
6239 option, the working directory is checked for uncommitted changes; if
6239 option, the working directory is checked for uncommitted changes; if
6240 none are found, the working directory is updated to the specified
6240 none are found, the working directory is updated to the specified
6241 changeset.
6241 changeset.
6242
6242
6243 .. container:: verbose
6243 .. container:: verbose
6244
6244
6245 The following rules apply when the working directory contains
6245 The following rules apply when the working directory contains
6246 uncommitted changes:
6246 uncommitted changes:
6247
6247
6248 1. If neither -c/--check nor -C/--clean is specified, and if
6248 1. If neither -c/--check nor -C/--clean is specified, and if
6249 the requested changeset is an ancestor or descendant of
6249 the requested changeset is an ancestor or descendant of
6250 the working directory's parent, the uncommitted changes
6250 the working directory's parent, the uncommitted changes
6251 are merged into the requested changeset and the merged
6251 are merged into the requested changeset and the merged
6252 result is left uncommitted. If the requested changeset is
6252 result is left uncommitted. If the requested changeset is
6253 not an ancestor or descendant (that is, it is on another
6253 not an ancestor or descendant (that is, it is on another
6254 branch), the update is aborted and the uncommitted changes
6254 branch), the update is aborted and the uncommitted changes
6255 are preserved.
6255 are preserved.
6256
6256
6257 2. With the -c/--check option, the update is aborted and the
6257 2. With the -c/--check option, the update is aborted and the
6258 uncommitted changes are preserved.
6258 uncommitted changes are preserved.
6259
6259
6260 3. With the -C/--clean option, uncommitted changes are discarded and
6260 3. With the -C/--clean option, uncommitted changes are discarded and
6261 the working directory is updated to the requested changeset.
6261 the working directory is updated to the requested changeset.
6262
6262
6263 To cancel an uncommitted merge (and lose your changes), use
6263 To cancel an uncommitted merge (and lose your changes), use
6264 :hg:`update --clean .`.
6264 :hg:`update --clean .`.
6265
6265
6266 Use null as the changeset to remove the working directory (like
6266 Use null as the changeset to remove the working directory (like
6267 :hg:`clone -U`).
6267 :hg:`clone -U`).
6268
6268
6269 If you want to revert just one file to an older revision, use
6269 If you want to revert just one file to an older revision, use
6270 :hg:`revert [-r REV] NAME`.
6270 :hg:`revert [-r REV] NAME`.
6271
6271
6272 See :hg:`help dates` for a list of formats valid for -d/--date.
6272 See :hg:`help dates` for a list of formats valid for -d/--date.
6273
6273
6274 Returns 0 on success, 1 if there are unresolved files.
6274 Returns 0 on success, 1 if there are unresolved files.
6275 """
6275 """
6276 if rev and node:
6276 if rev and node:
6277 raise util.Abort(_("please specify just one revision"))
6277 raise util.Abort(_("please specify just one revision"))
6278
6278
6279 if rev is None or rev == '':
6279 if rev is None or rev == '':
6280 rev = node
6280 rev = node
6281
6281
6282 cmdutil.clearunfinished(repo)
6282 cmdutil.clearunfinished(repo)
6283
6283
6284 # with no argument, we also move the current bookmark, if any
6284 # with no argument, we also move the current bookmark, if any
6285 rev, movemarkfrom = bookmarks.calculateupdate(ui, repo, rev)
6285 rev, movemarkfrom = bookmarks.calculateupdate(ui, repo, rev)
6286
6286
6287 # if we defined a bookmark, we have to remember the original bookmark name
6287 # if we defined a bookmark, we have to remember the original bookmark name
6288 brev = rev
6288 brev = rev
6289 rev = scmutil.revsingle(repo, rev, rev).rev()
6289 rev = scmutil.revsingle(repo, rev, rev).rev()
6290
6290
6291 if check and clean:
6291 if check and clean:
6292 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
6292 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
6293
6293
6294 if date:
6294 if date:
6295 if rev is not None:
6295 if rev is not None:
6296 raise util.Abort(_("you can't specify a revision and a date"))
6296 raise util.Abort(_("you can't specify a revision and a date"))
6297 rev = cmdutil.finddate(ui, repo, date)
6297 rev = cmdutil.finddate(ui, repo, date)
6298
6298
6299 if check:
6299 if check:
6300 c = repo[None]
6300 c = repo[None]
6301 if c.dirty(merge=False, branch=False, missing=True):
6301 if c.dirty(merge=False, branch=False, missing=True):
6302 raise util.Abort(_("uncommitted changes"))
6302 raise util.Abort(_("uncommitted changes"))
6303 if rev is None:
6303 if rev is None:
6304 rev = repo[repo[None].branch()].rev()
6304 rev = repo[repo[None].branch()].rev()
6305
6305
6306 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
6306 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
6307
6307
6308 if clean:
6308 if clean:
6309 ret = hg.clean(repo, rev)
6309 ret = hg.clean(repo, rev)
6310 else:
6310 else:
6311 ret = hg.update(repo, rev)
6311 ret = hg.update(repo, rev)
6312
6312
6313 if not ret and movemarkfrom:
6313 if not ret and movemarkfrom:
6314 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
6314 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
6315 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
6315 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
6316 elif brev in repo._bookmarks:
6316 elif brev in repo._bookmarks:
6317 bookmarks.setcurrent(repo, brev)
6317 bookmarks.setcurrent(repo, brev)
6318 ui.status(_("(activating bookmark %s)\n") % brev)
6318 ui.status(_("(activating bookmark %s)\n") % brev)
6319 elif brev:
6319 elif brev:
6320 if repo._bookmarkcurrent:
6320 if repo._bookmarkcurrent:
6321 ui.status(_("(leaving bookmark %s)\n") %
6321 ui.status(_("(leaving bookmark %s)\n") %
6322 repo._bookmarkcurrent)
6322 repo._bookmarkcurrent)
6323 bookmarks.unsetcurrent(repo)
6323 bookmarks.unsetcurrent(repo)
6324
6324
6325 return ret
6325 return ret
6326
6326
6327 @command('verify', [])
6327 @command('verify', [])
6328 def verify(ui, repo):
6328 def verify(ui, repo):
6329 """verify the integrity of the repository
6329 """verify the integrity of the repository
6330
6330
6331 Verify the integrity of the current repository.
6331 Verify the integrity of the current repository.
6332
6332
6333 This will perform an extensive check of the repository's
6333 This will perform an extensive check of the repository's
6334 integrity, validating the hashes and checksums of each entry in
6334 integrity, validating the hashes and checksums of each entry in
6335 the changelog, manifest, and tracked files, as well as the
6335 the changelog, manifest, and tracked files, as well as the
6336 integrity of their crosslinks and indices.
6336 integrity of their crosslinks and indices.
6337
6337
6338 Please see http://mercurial.selenic.com/wiki/RepositoryCorruption
6338 Please see http://mercurial.selenic.com/wiki/RepositoryCorruption
6339 for more information about recovery from corruption of the
6339 for more information about recovery from corruption of the
6340 repository.
6340 repository.
6341
6341
6342 Returns 0 on success, 1 if errors are encountered.
6342 Returns 0 on success, 1 if errors are encountered.
6343 """
6343 """
6344 return hg.verify(repo)
6344 return hg.verify(repo)
6345
6345
6346 @command('version', [], norepo=True)
6346 @command('version', [], norepo=True)
6347 def version_(ui):
6347 def version_(ui):
6348 """output version and copyright information"""
6348 """output version and copyright information"""
6349 ui.write(_("Mercurial Distributed SCM (version %s)\n")
6349 ui.write(_("Mercurial Distributed SCM (version %s)\n")
6350 % util.version())
6350 % util.version())
6351 ui.status(_(
6351 ui.status(_(
6352 "(see http://mercurial.selenic.com for more information)\n"
6352 "(see http://mercurial.selenic.com for more information)\n"
6353 "\nCopyright (C) 2005-2015 Matt Mackall and others\n"
6353 "\nCopyright (C) 2005-2015 Matt Mackall and others\n"
6354 "This is free software; see the source for copying conditions. "
6354 "This is free software; see the source for copying conditions. "
6355 "There is NO\nwarranty; "
6355 "There is NO\nwarranty; "
6356 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
6356 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
6357 ))
6357 ))
6358
6358
6359 ui.note(_("\nEnabled extensions:\n\n"))
6359 ui.note(_("\nEnabled extensions:\n\n"))
6360 if ui.verbose:
6360 if ui.verbose:
6361 # format names and versions into columns
6361 # format names and versions into columns
6362 names = []
6362 names = []
6363 vers = []
6363 vers = []
6364 for name, module in extensions.extensions():
6364 for name, module in extensions.extensions():
6365 names.append(name)
6365 names.append(name)
6366 vers.append(extensions.moduleversion(module))
6366 vers.append(extensions.moduleversion(module))
6367 if names:
6367 if names:
6368 maxnamelen = max(len(n) for n in names)
6368 maxnamelen = max(len(n) for n in names)
6369 for i, name in enumerate(names):
6369 for i, name in enumerate(names):
6370 ui.write(" %-*s %s\n" % (maxnamelen, name, vers[i]))
6370 ui.write(" %-*s %s\n" % (maxnamelen, name, vers[i]))
@@ -1,2424 +1,2427
1 # patch.py - patch file parsing routines
1 # patch.py - patch file parsing routines
2 #
2 #
3 # Copyright 2006 Brendan Cully <brendan@kublai.com>
3 # Copyright 2006 Brendan Cully <brendan@kublai.com>
4 # Copyright 2007 Chris Mason <chris.mason@oracle.com>
4 # Copyright 2007 Chris Mason <chris.mason@oracle.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 import cStringIO, email, os, errno, re, posixpath, copy
9 import cStringIO, email, os, errno, re, posixpath, copy
10 import tempfile, zlib, shutil
10 import tempfile, zlib, shutil
11 # On python2.4 you have to import these by name or they fail to
11 # On python2.4 you have to import these by name or they fail to
12 # load. This was not a problem on Python 2.7.
12 # load. This was not a problem on Python 2.7.
13 import email.Generator
13 import email.Generator
14 import email.Parser
14 import email.Parser
15
15
16 from i18n import _
16 from i18n import _
17 from node import hex, short
17 from node import hex, short
18 import cStringIO
18 import cStringIO
19 import base85, mdiff, scmutil, util, diffhelpers, copies, encoding, error
19 import base85, mdiff, scmutil, util, diffhelpers, copies, encoding, error
20 import pathutil
20
21
21 gitre = re.compile('diff --git a/(.*) b/(.*)')
22 gitre = re.compile('diff --git a/(.*) b/(.*)')
22 tabsplitter = re.compile(r'(\t+|[^\t]+)')
23 tabsplitter = re.compile(r'(\t+|[^\t]+)')
23
24
24 class PatchError(Exception):
25 class PatchError(Exception):
25 pass
26 pass
26
27
27
28
28 # public functions
29 # public functions
29
30
30 def split(stream):
31 def split(stream):
31 '''return an iterator of individual patches from a stream'''
32 '''return an iterator of individual patches from a stream'''
32 def isheader(line, inheader):
33 def isheader(line, inheader):
33 if inheader and line[0] in (' ', '\t'):
34 if inheader and line[0] in (' ', '\t'):
34 # continuation
35 # continuation
35 return True
36 return True
36 if line[0] in (' ', '-', '+'):
37 if line[0] in (' ', '-', '+'):
37 # diff line - don't check for header pattern in there
38 # diff line - don't check for header pattern in there
38 return False
39 return False
39 l = line.split(': ', 1)
40 l = line.split(': ', 1)
40 return len(l) == 2 and ' ' not in l[0]
41 return len(l) == 2 and ' ' not in l[0]
41
42
42 def chunk(lines):
43 def chunk(lines):
43 return cStringIO.StringIO(''.join(lines))
44 return cStringIO.StringIO(''.join(lines))
44
45
45 def hgsplit(stream, cur):
46 def hgsplit(stream, cur):
46 inheader = True
47 inheader = True
47
48
48 for line in stream:
49 for line in stream:
49 if not line.strip():
50 if not line.strip():
50 inheader = False
51 inheader = False
51 if not inheader and line.startswith('# HG changeset patch'):
52 if not inheader and line.startswith('# HG changeset patch'):
52 yield chunk(cur)
53 yield chunk(cur)
53 cur = []
54 cur = []
54 inheader = True
55 inheader = True
55
56
56 cur.append(line)
57 cur.append(line)
57
58
58 if cur:
59 if cur:
59 yield chunk(cur)
60 yield chunk(cur)
60
61
61 def mboxsplit(stream, cur):
62 def mboxsplit(stream, cur):
62 for line in stream:
63 for line in stream:
63 if line.startswith('From '):
64 if line.startswith('From '):
64 for c in split(chunk(cur[1:])):
65 for c in split(chunk(cur[1:])):
65 yield c
66 yield c
66 cur = []
67 cur = []
67
68
68 cur.append(line)
69 cur.append(line)
69
70
70 if cur:
71 if cur:
71 for c in split(chunk(cur[1:])):
72 for c in split(chunk(cur[1:])):
72 yield c
73 yield c
73
74
74 def mimesplit(stream, cur):
75 def mimesplit(stream, cur):
75 def msgfp(m):
76 def msgfp(m):
76 fp = cStringIO.StringIO()
77 fp = cStringIO.StringIO()
77 g = email.Generator.Generator(fp, mangle_from_=False)
78 g = email.Generator.Generator(fp, mangle_from_=False)
78 g.flatten(m)
79 g.flatten(m)
79 fp.seek(0)
80 fp.seek(0)
80 return fp
81 return fp
81
82
82 for line in stream:
83 for line in stream:
83 cur.append(line)
84 cur.append(line)
84 c = chunk(cur)
85 c = chunk(cur)
85
86
86 m = email.Parser.Parser().parse(c)
87 m = email.Parser.Parser().parse(c)
87 if not m.is_multipart():
88 if not m.is_multipart():
88 yield msgfp(m)
89 yield msgfp(m)
89 else:
90 else:
90 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
91 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
91 for part in m.walk():
92 for part in m.walk():
92 ct = part.get_content_type()
93 ct = part.get_content_type()
93 if ct not in ok_types:
94 if ct not in ok_types:
94 continue
95 continue
95 yield msgfp(part)
96 yield msgfp(part)
96
97
97 def headersplit(stream, cur):
98 def headersplit(stream, cur):
98 inheader = False
99 inheader = False
99
100
100 for line in stream:
101 for line in stream:
101 if not inheader and isheader(line, inheader):
102 if not inheader and isheader(line, inheader):
102 yield chunk(cur)
103 yield chunk(cur)
103 cur = []
104 cur = []
104 inheader = True
105 inheader = True
105 if inheader and not isheader(line, inheader):
106 if inheader and not isheader(line, inheader):
106 inheader = False
107 inheader = False
107
108
108 cur.append(line)
109 cur.append(line)
109
110
110 if cur:
111 if cur:
111 yield chunk(cur)
112 yield chunk(cur)
112
113
113 def remainder(cur):
114 def remainder(cur):
114 yield chunk(cur)
115 yield chunk(cur)
115
116
116 class fiter(object):
117 class fiter(object):
117 def __init__(self, fp):
118 def __init__(self, fp):
118 self.fp = fp
119 self.fp = fp
119
120
120 def __iter__(self):
121 def __iter__(self):
121 return self
122 return self
122
123
123 def next(self):
124 def next(self):
124 l = self.fp.readline()
125 l = self.fp.readline()
125 if not l:
126 if not l:
126 raise StopIteration
127 raise StopIteration
127 return l
128 return l
128
129
129 inheader = False
130 inheader = False
130 cur = []
131 cur = []
131
132
132 mimeheaders = ['content-type']
133 mimeheaders = ['content-type']
133
134
134 if not util.safehasattr(stream, 'next'):
135 if not util.safehasattr(stream, 'next'):
135 # http responses, for example, have readline but not next
136 # http responses, for example, have readline but not next
136 stream = fiter(stream)
137 stream = fiter(stream)
137
138
138 for line in stream:
139 for line in stream:
139 cur.append(line)
140 cur.append(line)
140 if line.startswith('# HG changeset patch'):
141 if line.startswith('# HG changeset patch'):
141 return hgsplit(stream, cur)
142 return hgsplit(stream, cur)
142 elif line.startswith('From '):
143 elif line.startswith('From '):
143 return mboxsplit(stream, cur)
144 return mboxsplit(stream, cur)
144 elif isheader(line, inheader):
145 elif isheader(line, inheader):
145 inheader = True
146 inheader = True
146 if line.split(':', 1)[0].lower() in mimeheaders:
147 if line.split(':', 1)[0].lower() in mimeheaders:
147 # let email parser handle this
148 # let email parser handle this
148 return mimesplit(stream, cur)
149 return mimesplit(stream, cur)
149 elif line.startswith('--- ') and inheader:
150 elif line.startswith('--- ') and inheader:
150 # No evil headers seen by diff start, split by hand
151 # No evil headers seen by diff start, split by hand
151 return headersplit(stream, cur)
152 return headersplit(stream, cur)
152 # Not enough info, keep reading
153 # Not enough info, keep reading
153
154
154 # if we are here, we have a very plain patch
155 # if we are here, we have a very plain patch
155 return remainder(cur)
156 return remainder(cur)
156
157
157 def extract(ui, fileobj):
158 def extract(ui, fileobj):
158 '''extract patch from data read from fileobj.
159 '''extract patch from data read from fileobj.
159
160
160 patch can be a normal patch or contained in an email message.
161 patch can be a normal patch or contained in an email message.
161
162
162 return tuple (filename, message, user, date, branch, node, p1, p2).
163 return tuple (filename, message, user, date, branch, node, p1, p2).
163 Any item in the returned tuple can be None. If filename is None,
164 Any item in the returned tuple can be None. If filename is None,
164 fileobj did not contain a patch. Caller must unlink filename when done.'''
165 fileobj did not contain a patch. Caller must unlink filename when done.'''
165
166
166 # attempt to detect the start of a patch
167 # attempt to detect the start of a patch
167 # (this heuristic is borrowed from quilt)
168 # (this heuristic is borrowed from quilt)
168 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |'
169 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |'
169 r'retrieving revision [0-9]+(\.[0-9]+)*$|'
170 r'retrieving revision [0-9]+(\.[0-9]+)*$|'
170 r'---[ \t].*?^\+\+\+[ \t]|'
171 r'---[ \t].*?^\+\+\+[ \t]|'
171 r'\*\*\*[ \t].*?^---[ \t])', re.MULTILINE|re.DOTALL)
172 r'\*\*\*[ \t].*?^---[ \t])', re.MULTILINE|re.DOTALL)
172
173
173 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
174 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
174 tmpfp = os.fdopen(fd, 'w')
175 tmpfp = os.fdopen(fd, 'w')
175 try:
176 try:
176 msg = email.Parser.Parser().parse(fileobj)
177 msg = email.Parser.Parser().parse(fileobj)
177
178
178 subject = msg['Subject']
179 subject = msg['Subject']
179 user = msg['From']
180 user = msg['From']
180 if not subject and not user:
181 if not subject and not user:
181 # Not an email, restore parsed headers if any
182 # Not an email, restore parsed headers if any
182 subject = '\n'.join(': '.join(h) for h in msg.items()) + '\n'
183 subject = '\n'.join(': '.join(h) for h in msg.items()) + '\n'
183
184
184 # should try to parse msg['Date']
185 # should try to parse msg['Date']
185 date = None
186 date = None
186 nodeid = None
187 nodeid = None
187 branch = None
188 branch = None
188 parents = []
189 parents = []
189
190
190 if subject:
191 if subject:
191 if subject.startswith('[PATCH'):
192 if subject.startswith('[PATCH'):
192 pend = subject.find(']')
193 pend = subject.find(']')
193 if pend >= 0:
194 if pend >= 0:
194 subject = subject[pend + 1:].lstrip()
195 subject = subject[pend + 1:].lstrip()
195 subject = re.sub(r'\n[ \t]+', ' ', subject)
196 subject = re.sub(r'\n[ \t]+', ' ', subject)
196 ui.debug('Subject: %s\n' % subject)
197 ui.debug('Subject: %s\n' % subject)
197 if user:
198 if user:
198 ui.debug('From: %s\n' % user)
199 ui.debug('From: %s\n' % user)
199 diffs_seen = 0
200 diffs_seen = 0
200 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
201 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
201 message = ''
202 message = ''
202 for part in msg.walk():
203 for part in msg.walk():
203 content_type = part.get_content_type()
204 content_type = part.get_content_type()
204 ui.debug('Content-Type: %s\n' % content_type)
205 ui.debug('Content-Type: %s\n' % content_type)
205 if content_type not in ok_types:
206 if content_type not in ok_types:
206 continue
207 continue
207 payload = part.get_payload(decode=True)
208 payload = part.get_payload(decode=True)
208 m = diffre.search(payload)
209 m = diffre.search(payload)
209 if m:
210 if m:
210 hgpatch = False
211 hgpatch = False
211 hgpatchheader = False
212 hgpatchheader = False
212 ignoretext = False
213 ignoretext = False
213
214
214 ui.debug('found patch at byte %d\n' % m.start(0))
215 ui.debug('found patch at byte %d\n' % m.start(0))
215 diffs_seen += 1
216 diffs_seen += 1
216 cfp = cStringIO.StringIO()
217 cfp = cStringIO.StringIO()
217 for line in payload[:m.start(0)].splitlines():
218 for line in payload[:m.start(0)].splitlines():
218 if line.startswith('# HG changeset patch') and not hgpatch:
219 if line.startswith('# HG changeset patch') and not hgpatch:
219 ui.debug('patch generated by hg export\n')
220 ui.debug('patch generated by hg export\n')
220 hgpatch = True
221 hgpatch = True
221 hgpatchheader = True
222 hgpatchheader = True
222 # drop earlier commit message content
223 # drop earlier commit message content
223 cfp.seek(0)
224 cfp.seek(0)
224 cfp.truncate()
225 cfp.truncate()
225 subject = None
226 subject = None
226 elif hgpatchheader:
227 elif hgpatchheader:
227 if line.startswith('# User '):
228 if line.startswith('# User '):
228 user = line[7:]
229 user = line[7:]
229 ui.debug('From: %s\n' % user)
230 ui.debug('From: %s\n' % user)
230 elif line.startswith("# Date "):
231 elif line.startswith("# Date "):
231 date = line[7:]
232 date = line[7:]
232 elif line.startswith("# Branch "):
233 elif line.startswith("# Branch "):
233 branch = line[9:]
234 branch = line[9:]
234 elif line.startswith("# Node ID "):
235 elif line.startswith("# Node ID "):
235 nodeid = line[10:]
236 nodeid = line[10:]
236 elif line.startswith("# Parent "):
237 elif line.startswith("# Parent "):
237 parents.append(line[9:].lstrip())
238 parents.append(line[9:].lstrip())
238 elif not line.startswith("# "):
239 elif not line.startswith("# "):
239 hgpatchheader = False
240 hgpatchheader = False
240 elif line == '---':
241 elif line == '---':
241 ignoretext = True
242 ignoretext = True
242 if not hgpatchheader and not ignoretext:
243 if not hgpatchheader and not ignoretext:
243 cfp.write(line)
244 cfp.write(line)
244 cfp.write('\n')
245 cfp.write('\n')
245 message = cfp.getvalue()
246 message = cfp.getvalue()
246 if tmpfp:
247 if tmpfp:
247 tmpfp.write(payload)
248 tmpfp.write(payload)
248 if not payload.endswith('\n'):
249 if not payload.endswith('\n'):
249 tmpfp.write('\n')
250 tmpfp.write('\n')
250 elif not diffs_seen and message and content_type == 'text/plain':
251 elif not diffs_seen and message and content_type == 'text/plain':
251 message += '\n' + payload
252 message += '\n' + payload
252 except: # re-raises
253 except: # re-raises
253 tmpfp.close()
254 tmpfp.close()
254 os.unlink(tmpname)
255 os.unlink(tmpname)
255 raise
256 raise
256
257
257 if subject and not message.startswith(subject):
258 if subject and not message.startswith(subject):
258 message = '%s\n%s' % (subject, message)
259 message = '%s\n%s' % (subject, message)
259 tmpfp.close()
260 tmpfp.close()
260 if not diffs_seen:
261 if not diffs_seen:
261 os.unlink(tmpname)
262 os.unlink(tmpname)
262 return None, message, user, date, branch, None, None, None
263 return None, message, user, date, branch, None, None, None
263
264
264 if parents:
265 if parents:
265 p1 = parents.pop(0)
266 p1 = parents.pop(0)
266 else:
267 else:
267 p1 = None
268 p1 = None
268
269
269 if parents:
270 if parents:
270 p2 = parents.pop(0)
271 p2 = parents.pop(0)
271 else:
272 else:
272 p2 = None
273 p2 = None
273
274
274 return tmpname, message, user, date, branch, nodeid, p1, p2
275 return tmpname, message, user, date, branch, nodeid, p1, p2
275
276
276 class patchmeta(object):
277 class patchmeta(object):
277 """Patched file metadata
278 """Patched file metadata
278
279
279 'op' is the performed operation within ADD, DELETE, RENAME, MODIFY
280 'op' is the performed operation within ADD, DELETE, RENAME, MODIFY
280 or COPY. 'path' is patched file path. 'oldpath' is set to the
281 or COPY. 'path' is patched file path. 'oldpath' is set to the
281 origin file when 'op' is either COPY or RENAME, None otherwise. If
282 origin file when 'op' is either COPY or RENAME, None otherwise. If
282 file mode is changed, 'mode' is a tuple (islink, isexec) where
283 file mode is changed, 'mode' is a tuple (islink, isexec) where
283 'islink' is True if the file is a symlink and 'isexec' is True if
284 'islink' is True if the file is a symlink and 'isexec' is True if
284 the file is executable. Otherwise, 'mode' is None.
285 the file is executable. Otherwise, 'mode' is None.
285 """
286 """
286 def __init__(self, path):
287 def __init__(self, path):
287 self.path = path
288 self.path = path
288 self.oldpath = None
289 self.oldpath = None
289 self.mode = None
290 self.mode = None
290 self.op = 'MODIFY'
291 self.op = 'MODIFY'
291 self.binary = False
292 self.binary = False
292
293
293 def setmode(self, mode):
294 def setmode(self, mode):
294 islink = mode & 020000
295 islink = mode & 020000
295 isexec = mode & 0100
296 isexec = mode & 0100
296 self.mode = (islink, isexec)
297 self.mode = (islink, isexec)
297
298
298 def copy(self):
299 def copy(self):
299 other = patchmeta(self.path)
300 other = patchmeta(self.path)
300 other.oldpath = self.oldpath
301 other.oldpath = self.oldpath
301 other.mode = self.mode
302 other.mode = self.mode
302 other.op = self.op
303 other.op = self.op
303 other.binary = self.binary
304 other.binary = self.binary
304 return other
305 return other
305
306
306 def _ispatchinga(self, afile):
307 def _ispatchinga(self, afile):
307 if afile == '/dev/null':
308 if afile == '/dev/null':
308 return self.op == 'ADD'
309 return self.op == 'ADD'
309 return afile == 'a/' + (self.oldpath or self.path)
310 return afile == 'a/' + (self.oldpath or self.path)
310
311
311 def _ispatchingb(self, bfile):
312 def _ispatchingb(self, bfile):
312 if bfile == '/dev/null':
313 if bfile == '/dev/null':
313 return self.op == 'DELETE'
314 return self.op == 'DELETE'
314 return bfile == 'b/' + self.path
315 return bfile == 'b/' + self.path
315
316
316 def ispatching(self, afile, bfile):
317 def ispatching(self, afile, bfile):
317 return self._ispatchinga(afile) and self._ispatchingb(bfile)
318 return self._ispatchinga(afile) and self._ispatchingb(bfile)
318
319
319 def __repr__(self):
320 def __repr__(self):
320 return "<patchmeta %s %r>" % (self.op, self.path)
321 return "<patchmeta %s %r>" % (self.op, self.path)
321
322
322 def readgitpatch(lr):
323 def readgitpatch(lr):
323 """extract git-style metadata about patches from <patchname>"""
324 """extract git-style metadata about patches from <patchname>"""
324
325
325 # Filter patch for git information
326 # Filter patch for git information
326 gp = None
327 gp = None
327 gitpatches = []
328 gitpatches = []
328 for line in lr:
329 for line in lr:
329 line = line.rstrip(' \r\n')
330 line = line.rstrip(' \r\n')
330 if line.startswith('diff --git a/'):
331 if line.startswith('diff --git a/'):
331 m = gitre.match(line)
332 m = gitre.match(line)
332 if m:
333 if m:
333 if gp:
334 if gp:
334 gitpatches.append(gp)
335 gitpatches.append(gp)
335 dst = m.group(2)
336 dst = m.group(2)
336 gp = patchmeta(dst)
337 gp = patchmeta(dst)
337 elif gp:
338 elif gp:
338 if line.startswith('--- '):
339 if line.startswith('--- '):
339 gitpatches.append(gp)
340 gitpatches.append(gp)
340 gp = None
341 gp = None
341 continue
342 continue
342 if line.startswith('rename from '):
343 if line.startswith('rename from '):
343 gp.op = 'RENAME'
344 gp.op = 'RENAME'
344 gp.oldpath = line[12:]
345 gp.oldpath = line[12:]
345 elif line.startswith('rename to '):
346 elif line.startswith('rename to '):
346 gp.path = line[10:]
347 gp.path = line[10:]
347 elif line.startswith('copy from '):
348 elif line.startswith('copy from '):
348 gp.op = 'COPY'
349 gp.op = 'COPY'
349 gp.oldpath = line[10:]
350 gp.oldpath = line[10:]
350 elif line.startswith('copy to '):
351 elif line.startswith('copy to '):
351 gp.path = line[8:]
352 gp.path = line[8:]
352 elif line.startswith('deleted file'):
353 elif line.startswith('deleted file'):
353 gp.op = 'DELETE'
354 gp.op = 'DELETE'
354 elif line.startswith('new file mode '):
355 elif line.startswith('new file mode '):
355 gp.op = 'ADD'
356 gp.op = 'ADD'
356 gp.setmode(int(line[-6:], 8))
357 gp.setmode(int(line[-6:], 8))
357 elif line.startswith('new mode '):
358 elif line.startswith('new mode '):
358 gp.setmode(int(line[-6:], 8))
359 gp.setmode(int(line[-6:], 8))
359 elif line.startswith('GIT binary patch'):
360 elif line.startswith('GIT binary patch'):
360 gp.binary = True
361 gp.binary = True
361 if gp:
362 if gp:
362 gitpatches.append(gp)
363 gitpatches.append(gp)
363
364
364 return gitpatches
365 return gitpatches
365
366
366 class linereader(object):
367 class linereader(object):
367 # simple class to allow pushing lines back into the input stream
368 # simple class to allow pushing lines back into the input stream
368 def __init__(self, fp):
369 def __init__(self, fp):
369 self.fp = fp
370 self.fp = fp
370 self.buf = []
371 self.buf = []
371
372
372 def push(self, line):
373 def push(self, line):
373 if line is not None:
374 if line is not None:
374 self.buf.append(line)
375 self.buf.append(line)
375
376
376 def readline(self):
377 def readline(self):
377 if self.buf:
378 if self.buf:
378 l = self.buf[0]
379 l = self.buf[0]
379 del self.buf[0]
380 del self.buf[0]
380 return l
381 return l
381 return self.fp.readline()
382 return self.fp.readline()
382
383
383 def __iter__(self):
384 def __iter__(self):
384 while True:
385 while True:
385 l = self.readline()
386 l = self.readline()
386 if not l:
387 if not l:
387 break
388 break
388 yield l
389 yield l
389
390
390 class abstractbackend(object):
391 class abstractbackend(object):
391 def __init__(self, ui):
392 def __init__(self, ui):
392 self.ui = ui
393 self.ui = ui
393
394
394 def getfile(self, fname):
395 def getfile(self, fname):
395 """Return target file data and flags as a (data, (islink,
396 """Return target file data and flags as a (data, (islink,
396 isexec)) tuple. Data is None if file is missing/deleted.
397 isexec)) tuple. Data is None if file is missing/deleted.
397 """
398 """
398 raise NotImplementedError
399 raise NotImplementedError
399
400
400 def setfile(self, fname, data, mode, copysource):
401 def setfile(self, fname, data, mode, copysource):
401 """Write data to target file fname and set its mode. mode is a
402 """Write data to target file fname and set its mode. mode is a
402 (islink, isexec) tuple. If data is None, the file content should
403 (islink, isexec) tuple. If data is None, the file content should
403 be left unchanged. If the file is modified after being copied,
404 be left unchanged. If the file is modified after being copied,
404 copysource is set to the original file name.
405 copysource is set to the original file name.
405 """
406 """
406 raise NotImplementedError
407 raise NotImplementedError
407
408
408 def unlink(self, fname):
409 def unlink(self, fname):
409 """Unlink target file."""
410 """Unlink target file."""
410 raise NotImplementedError
411 raise NotImplementedError
411
412
412 def writerej(self, fname, failed, total, lines):
413 def writerej(self, fname, failed, total, lines):
413 """Write rejected lines for fname. total is the number of hunks
414 """Write rejected lines for fname. total is the number of hunks
414 which failed to apply and total the total number of hunks for this
415 which failed to apply and total the total number of hunks for this
415 files.
416 files.
416 """
417 """
417 pass
418 pass
418
419
419 def exists(self, fname):
420 def exists(self, fname):
420 raise NotImplementedError
421 raise NotImplementedError
421
422
422 class fsbackend(abstractbackend):
423 class fsbackend(abstractbackend):
423 def __init__(self, ui, basedir):
424 def __init__(self, ui, basedir):
424 super(fsbackend, self).__init__(ui)
425 super(fsbackend, self).__init__(ui)
425 self.opener = scmutil.opener(basedir)
426 self.opener = scmutil.opener(basedir)
426
427
427 def _join(self, f):
428 def _join(self, f):
428 return os.path.join(self.opener.base, f)
429 return os.path.join(self.opener.base, f)
429
430
430 def getfile(self, fname):
431 def getfile(self, fname):
431 if self.opener.islink(fname):
432 if self.opener.islink(fname):
432 return (self.opener.readlink(fname), (True, False))
433 return (self.opener.readlink(fname), (True, False))
433
434
434 isexec = False
435 isexec = False
435 try:
436 try:
436 isexec = self.opener.lstat(fname).st_mode & 0100 != 0
437 isexec = self.opener.lstat(fname).st_mode & 0100 != 0
437 except OSError, e:
438 except OSError, e:
438 if e.errno != errno.ENOENT:
439 if e.errno != errno.ENOENT:
439 raise
440 raise
440 try:
441 try:
441 return (self.opener.read(fname), (False, isexec))
442 return (self.opener.read(fname), (False, isexec))
442 except IOError, e:
443 except IOError, e:
443 if e.errno != errno.ENOENT:
444 if e.errno != errno.ENOENT:
444 raise
445 raise
445 return None, None
446 return None, None
446
447
447 def setfile(self, fname, data, mode, copysource):
448 def setfile(self, fname, data, mode, copysource):
448 islink, isexec = mode
449 islink, isexec = mode
449 if data is None:
450 if data is None:
450 self.opener.setflags(fname, islink, isexec)
451 self.opener.setflags(fname, islink, isexec)
451 return
452 return
452 if islink:
453 if islink:
453 self.opener.symlink(data, fname)
454 self.opener.symlink(data, fname)
454 else:
455 else:
455 self.opener.write(fname, data)
456 self.opener.write(fname, data)
456 if isexec:
457 if isexec:
457 self.opener.setflags(fname, False, True)
458 self.opener.setflags(fname, False, True)
458
459
459 def unlink(self, fname):
460 def unlink(self, fname):
460 self.opener.unlinkpath(fname, ignoremissing=True)
461 self.opener.unlinkpath(fname, ignoremissing=True)
461
462
462 def writerej(self, fname, failed, total, lines):
463 def writerej(self, fname, failed, total, lines):
463 fname = fname + ".rej"
464 fname = fname + ".rej"
464 self.ui.warn(
465 self.ui.warn(
465 _("%d out of %d hunks FAILED -- saving rejects to file %s\n") %
466 _("%d out of %d hunks FAILED -- saving rejects to file %s\n") %
466 (failed, total, fname))
467 (failed, total, fname))
467 fp = self.opener(fname, 'w')
468 fp = self.opener(fname, 'w')
468 fp.writelines(lines)
469 fp.writelines(lines)
469 fp.close()
470 fp.close()
470
471
471 def exists(self, fname):
472 def exists(self, fname):
472 return self.opener.lexists(fname)
473 return self.opener.lexists(fname)
473
474
474 class workingbackend(fsbackend):
475 class workingbackend(fsbackend):
475 def __init__(self, ui, repo, similarity):
476 def __init__(self, ui, repo, similarity):
476 super(workingbackend, self).__init__(ui, repo.root)
477 super(workingbackend, self).__init__(ui, repo.root)
477 self.repo = repo
478 self.repo = repo
478 self.similarity = similarity
479 self.similarity = similarity
479 self.removed = set()
480 self.removed = set()
480 self.changed = set()
481 self.changed = set()
481 self.copied = []
482 self.copied = []
482
483
483 def _checkknown(self, fname):
484 def _checkknown(self, fname):
484 if self.repo.dirstate[fname] == '?' and self.exists(fname):
485 if self.repo.dirstate[fname] == '?' and self.exists(fname):
485 raise PatchError(_('cannot patch %s: file is not tracked') % fname)
486 raise PatchError(_('cannot patch %s: file is not tracked') % fname)
486
487
487 def setfile(self, fname, data, mode, copysource):
488 def setfile(self, fname, data, mode, copysource):
488 self._checkknown(fname)
489 self._checkknown(fname)
489 super(workingbackend, self).setfile(fname, data, mode, copysource)
490 super(workingbackend, self).setfile(fname, data, mode, copysource)
490 if copysource is not None:
491 if copysource is not None:
491 self.copied.append((copysource, fname))
492 self.copied.append((copysource, fname))
492 self.changed.add(fname)
493 self.changed.add(fname)
493
494
494 def unlink(self, fname):
495 def unlink(self, fname):
495 self._checkknown(fname)
496 self._checkknown(fname)
496 super(workingbackend, self).unlink(fname)
497 super(workingbackend, self).unlink(fname)
497 self.removed.add(fname)
498 self.removed.add(fname)
498 self.changed.add(fname)
499 self.changed.add(fname)
499
500
500 def close(self):
501 def close(self):
501 wctx = self.repo[None]
502 wctx = self.repo[None]
502 changed = set(self.changed)
503 changed = set(self.changed)
503 for src, dst in self.copied:
504 for src, dst in self.copied:
504 scmutil.dirstatecopy(self.ui, self.repo, wctx, src, dst)
505 scmutil.dirstatecopy(self.ui, self.repo, wctx, src, dst)
505 if self.removed:
506 if self.removed:
506 wctx.forget(sorted(self.removed))
507 wctx.forget(sorted(self.removed))
507 for f in self.removed:
508 for f in self.removed:
508 if f not in self.repo.dirstate:
509 if f not in self.repo.dirstate:
509 # File was deleted and no longer belongs to the
510 # File was deleted and no longer belongs to the
510 # dirstate, it was probably marked added then
511 # dirstate, it was probably marked added then
511 # deleted, and should not be considered by
512 # deleted, and should not be considered by
512 # marktouched().
513 # marktouched().
513 changed.discard(f)
514 changed.discard(f)
514 if changed:
515 if changed:
515 scmutil.marktouched(self.repo, changed, self.similarity)
516 scmutil.marktouched(self.repo, changed, self.similarity)
516 return sorted(self.changed)
517 return sorted(self.changed)
517
518
518 class filestore(object):
519 class filestore(object):
519 def __init__(self, maxsize=None):
520 def __init__(self, maxsize=None):
520 self.opener = None
521 self.opener = None
521 self.files = {}
522 self.files = {}
522 self.created = 0
523 self.created = 0
523 self.maxsize = maxsize
524 self.maxsize = maxsize
524 if self.maxsize is None:
525 if self.maxsize is None:
525 self.maxsize = 4*(2**20)
526 self.maxsize = 4*(2**20)
526 self.size = 0
527 self.size = 0
527 self.data = {}
528 self.data = {}
528
529
529 def setfile(self, fname, data, mode, copied=None):
530 def setfile(self, fname, data, mode, copied=None):
530 if self.maxsize < 0 or (len(data) + self.size) <= self.maxsize:
531 if self.maxsize < 0 or (len(data) + self.size) <= self.maxsize:
531 self.data[fname] = (data, mode, copied)
532 self.data[fname] = (data, mode, copied)
532 self.size += len(data)
533 self.size += len(data)
533 else:
534 else:
534 if self.opener is None:
535 if self.opener is None:
535 root = tempfile.mkdtemp(prefix='hg-patch-')
536 root = tempfile.mkdtemp(prefix='hg-patch-')
536 self.opener = scmutil.opener(root)
537 self.opener = scmutil.opener(root)
537 # Avoid filename issues with these simple names
538 # Avoid filename issues with these simple names
538 fn = str(self.created)
539 fn = str(self.created)
539 self.opener.write(fn, data)
540 self.opener.write(fn, data)
540 self.created += 1
541 self.created += 1
541 self.files[fname] = (fn, mode, copied)
542 self.files[fname] = (fn, mode, copied)
542
543
543 def getfile(self, fname):
544 def getfile(self, fname):
544 if fname in self.data:
545 if fname in self.data:
545 return self.data[fname]
546 return self.data[fname]
546 if not self.opener or fname not in self.files:
547 if not self.opener or fname not in self.files:
547 return None, None, None
548 return None, None, None
548 fn, mode, copied = self.files[fname]
549 fn, mode, copied = self.files[fname]
549 return self.opener.read(fn), mode, copied
550 return self.opener.read(fn), mode, copied
550
551
551 def close(self):
552 def close(self):
552 if self.opener:
553 if self.opener:
553 shutil.rmtree(self.opener.base)
554 shutil.rmtree(self.opener.base)
554
555
555 class repobackend(abstractbackend):
556 class repobackend(abstractbackend):
556 def __init__(self, ui, repo, ctx, store):
557 def __init__(self, ui, repo, ctx, store):
557 super(repobackend, self).__init__(ui)
558 super(repobackend, self).__init__(ui)
558 self.repo = repo
559 self.repo = repo
559 self.ctx = ctx
560 self.ctx = ctx
560 self.store = store
561 self.store = store
561 self.changed = set()
562 self.changed = set()
562 self.removed = set()
563 self.removed = set()
563 self.copied = {}
564 self.copied = {}
564
565
565 def _checkknown(self, fname):
566 def _checkknown(self, fname):
566 if fname not in self.ctx:
567 if fname not in self.ctx:
567 raise PatchError(_('cannot patch %s: file is not tracked') % fname)
568 raise PatchError(_('cannot patch %s: file is not tracked') % fname)
568
569
569 def getfile(self, fname):
570 def getfile(self, fname):
570 try:
571 try:
571 fctx = self.ctx[fname]
572 fctx = self.ctx[fname]
572 except error.LookupError:
573 except error.LookupError:
573 return None, None
574 return None, None
574 flags = fctx.flags()
575 flags = fctx.flags()
575 return fctx.data(), ('l' in flags, 'x' in flags)
576 return fctx.data(), ('l' in flags, 'x' in flags)
576
577
577 def setfile(self, fname, data, mode, copysource):
578 def setfile(self, fname, data, mode, copysource):
578 if copysource:
579 if copysource:
579 self._checkknown(copysource)
580 self._checkknown(copysource)
580 if data is None:
581 if data is None:
581 data = self.ctx[fname].data()
582 data = self.ctx[fname].data()
582 self.store.setfile(fname, data, mode, copysource)
583 self.store.setfile(fname, data, mode, copysource)
583 self.changed.add(fname)
584 self.changed.add(fname)
584 if copysource:
585 if copysource:
585 self.copied[fname] = copysource
586 self.copied[fname] = copysource
586
587
587 def unlink(self, fname):
588 def unlink(self, fname):
588 self._checkknown(fname)
589 self._checkknown(fname)
589 self.removed.add(fname)
590 self.removed.add(fname)
590
591
591 def exists(self, fname):
592 def exists(self, fname):
592 return fname in self.ctx
593 return fname in self.ctx
593
594
594 def close(self):
595 def close(self):
595 return self.changed | self.removed
596 return self.changed | self.removed
596
597
597 # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1
598 # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1
598 unidesc = re.compile('@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@')
599 unidesc = re.compile('@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@')
599 contextdesc = re.compile('(?:---|\*\*\*) (\d+)(?:,(\d+))? (?:---|\*\*\*)')
600 contextdesc = re.compile('(?:---|\*\*\*) (\d+)(?:,(\d+))? (?:---|\*\*\*)')
600 eolmodes = ['strict', 'crlf', 'lf', 'auto']
601 eolmodes = ['strict', 'crlf', 'lf', 'auto']
601
602
602 class patchfile(object):
603 class patchfile(object):
603 def __init__(self, ui, gp, backend, store, eolmode='strict'):
604 def __init__(self, ui, gp, backend, store, eolmode='strict'):
604 self.fname = gp.path
605 self.fname = gp.path
605 self.eolmode = eolmode
606 self.eolmode = eolmode
606 self.eol = None
607 self.eol = None
607 self.backend = backend
608 self.backend = backend
608 self.ui = ui
609 self.ui = ui
609 self.lines = []
610 self.lines = []
610 self.exists = False
611 self.exists = False
611 self.missing = True
612 self.missing = True
612 self.mode = gp.mode
613 self.mode = gp.mode
613 self.copysource = gp.oldpath
614 self.copysource = gp.oldpath
614 self.create = gp.op in ('ADD', 'COPY', 'RENAME')
615 self.create = gp.op in ('ADD', 'COPY', 'RENAME')
615 self.remove = gp.op == 'DELETE'
616 self.remove = gp.op == 'DELETE'
616 if self.copysource is None:
617 if self.copysource is None:
617 data, mode = backend.getfile(self.fname)
618 data, mode = backend.getfile(self.fname)
618 else:
619 else:
619 data, mode = store.getfile(self.copysource)[:2]
620 data, mode = store.getfile(self.copysource)[:2]
620 if data is not None:
621 if data is not None:
621 self.exists = self.copysource is None or backend.exists(self.fname)
622 self.exists = self.copysource is None or backend.exists(self.fname)
622 self.missing = False
623 self.missing = False
623 if data:
624 if data:
624 self.lines = mdiff.splitnewlines(data)
625 self.lines = mdiff.splitnewlines(data)
625 if self.mode is None:
626 if self.mode is None:
626 self.mode = mode
627 self.mode = mode
627 if self.lines:
628 if self.lines:
628 # Normalize line endings
629 # Normalize line endings
629 if self.lines[0].endswith('\r\n'):
630 if self.lines[0].endswith('\r\n'):
630 self.eol = '\r\n'
631 self.eol = '\r\n'
631 elif self.lines[0].endswith('\n'):
632 elif self.lines[0].endswith('\n'):
632 self.eol = '\n'
633 self.eol = '\n'
633 if eolmode != 'strict':
634 if eolmode != 'strict':
634 nlines = []
635 nlines = []
635 for l in self.lines:
636 for l in self.lines:
636 if l.endswith('\r\n'):
637 if l.endswith('\r\n'):
637 l = l[:-2] + '\n'
638 l = l[:-2] + '\n'
638 nlines.append(l)
639 nlines.append(l)
639 self.lines = nlines
640 self.lines = nlines
640 else:
641 else:
641 if self.create:
642 if self.create:
642 self.missing = False
643 self.missing = False
643 if self.mode is None:
644 if self.mode is None:
644 self.mode = (False, False)
645 self.mode = (False, False)
645 if self.missing:
646 if self.missing:
646 self.ui.warn(_("unable to find '%s' for patching\n") % self.fname)
647 self.ui.warn(_("unable to find '%s' for patching\n") % self.fname)
647
648
648 self.hash = {}
649 self.hash = {}
649 self.dirty = 0
650 self.dirty = 0
650 self.offset = 0
651 self.offset = 0
651 self.skew = 0
652 self.skew = 0
652 self.rej = []
653 self.rej = []
653 self.fileprinted = False
654 self.fileprinted = False
654 self.printfile(False)
655 self.printfile(False)
655 self.hunks = 0
656 self.hunks = 0
656
657
657 def writelines(self, fname, lines, mode):
658 def writelines(self, fname, lines, mode):
658 if self.eolmode == 'auto':
659 if self.eolmode == 'auto':
659 eol = self.eol
660 eol = self.eol
660 elif self.eolmode == 'crlf':
661 elif self.eolmode == 'crlf':
661 eol = '\r\n'
662 eol = '\r\n'
662 else:
663 else:
663 eol = '\n'
664 eol = '\n'
664
665
665 if self.eolmode != 'strict' and eol and eol != '\n':
666 if self.eolmode != 'strict' and eol and eol != '\n':
666 rawlines = []
667 rawlines = []
667 for l in lines:
668 for l in lines:
668 if l and l[-1] == '\n':
669 if l and l[-1] == '\n':
669 l = l[:-1] + eol
670 l = l[:-1] + eol
670 rawlines.append(l)
671 rawlines.append(l)
671 lines = rawlines
672 lines = rawlines
672
673
673 self.backend.setfile(fname, ''.join(lines), mode, self.copysource)
674 self.backend.setfile(fname, ''.join(lines), mode, self.copysource)
674
675
675 def printfile(self, warn):
676 def printfile(self, warn):
676 if self.fileprinted:
677 if self.fileprinted:
677 return
678 return
678 if warn or self.ui.verbose:
679 if warn or self.ui.verbose:
679 self.fileprinted = True
680 self.fileprinted = True
680 s = _("patching file %s\n") % self.fname
681 s = _("patching file %s\n") % self.fname
681 if warn:
682 if warn:
682 self.ui.warn(s)
683 self.ui.warn(s)
683 else:
684 else:
684 self.ui.note(s)
685 self.ui.note(s)
685
686
686
687
687 def findlines(self, l, linenum):
688 def findlines(self, l, linenum):
688 # looks through the hash and finds candidate lines. The
689 # looks through the hash and finds candidate lines. The
689 # result is a list of line numbers sorted based on distance
690 # result is a list of line numbers sorted based on distance
690 # from linenum
691 # from linenum
691
692
692 cand = self.hash.get(l, [])
693 cand = self.hash.get(l, [])
693 if len(cand) > 1:
694 if len(cand) > 1:
694 # resort our list of potentials forward then back.
695 # resort our list of potentials forward then back.
695 cand.sort(key=lambda x: abs(x - linenum))
696 cand.sort(key=lambda x: abs(x - linenum))
696 return cand
697 return cand
697
698
698 def write_rej(self):
699 def write_rej(self):
699 # our rejects are a little different from patch(1). This always
700 # our rejects are a little different from patch(1). This always
700 # creates rejects in the same form as the original patch. A file
701 # creates rejects in the same form as the original patch. A file
701 # header is inserted so that you can run the reject through patch again
702 # header is inserted so that you can run the reject through patch again
702 # without having to type the filename.
703 # without having to type the filename.
703 if not self.rej:
704 if not self.rej:
704 return
705 return
705 base = os.path.basename(self.fname)
706 base = os.path.basename(self.fname)
706 lines = ["--- %s\n+++ %s\n" % (base, base)]
707 lines = ["--- %s\n+++ %s\n" % (base, base)]
707 for x in self.rej:
708 for x in self.rej:
708 for l in x.hunk:
709 for l in x.hunk:
709 lines.append(l)
710 lines.append(l)
710 if l[-1] != '\n':
711 if l[-1] != '\n':
711 lines.append("\n\ No newline at end of file\n")
712 lines.append("\n\ No newline at end of file\n")
712 self.backend.writerej(self.fname, len(self.rej), self.hunks, lines)
713 self.backend.writerej(self.fname, len(self.rej), self.hunks, lines)
713
714
714 def apply(self, h):
715 def apply(self, h):
715 if not h.complete():
716 if not h.complete():
716 raise PatchError(_("bad hunk #%d %s (%d %d %d %d)") %
717 raise PatchError(_("bad hunk #%d %s (%d %d %d %d)") %
717 (h.number, h.desc, len(h.a), h.lena, len(h.b),
718 (h.number, h.desc, len(h.a), h.lena, len(h.b),
718 h.lenb))
719 h.lenb))
719
720
720 self.hunks += 1
721 self.hunks += 1
721
722
722 if self.missing:
723 if self.missing:
723 self.rej.append(h)
724 self.rej.append(h)
724 return -1
725 return -1
725
726
726 if self.exists and self.create:
727 if self.exists and self.create:
727 if self.copysource:
728 if self.copysource:
728 self.ui.warn(_("cannot create %s: destination already "
729 self.ui.warn(_("cannot create %s: destination already "
729 "exists\n") % self.fname)
730 "exists\n") % self.fname)
730 else:
731 else:
731 self.ui.warn(_("file %s already exists\n") % self.fname)
732 self.ui.warn(_("file %s already exists\n") % self.fname)
732 self.rej.append(h)
733 self.rej.append(h)
733 return -1
734 return -1
734
735
735 if isinstance(h, binhunk):
736 if isinstance(h, binhunk):
736 if self.remove:
737 if self.remove:
737 self.backend.unlink(self.fname)
738 self.backend.unlink(self.fname)
738 else:
739 else:
739 l = h.new(self.lines)
740 l = h.new(self.lines)
740 self.lines[:] = l
741 self.lines[:] = l
741 self.offset += len(l)
742 self.offset += len(l)
742 self.dirty = True
743 self.dirty = True
743 return 0
744 return 0
744
745
745 horig = h
746 horig = h
746 if (self.eolmode in ('crlf', 'lf')
747 if (self.eolmode in ('crlf', 'lf')
747 or self.eolmode == 'auto' and self.eol):
748 or self.eolmode == 'auto' and self.eol):
748 # If new eols are going to be normalized, then normalize
749 # If new eols are going to be normalized, then normalize
749 # hunk data before patching. Otherwise, preserve input
750 # hunk data before patching. Otherwise, preserve input
750 # line-endings.
751 # line-endings.
751 h = h.getnormalized()
752 h = h.getnormalized()
752
753
753 # fast case first, no offsets, no fuzz
754 # fast case first, no offsets, no fuzz
754 old, oldstart, new, newstart = h.fuzzit(0, False)
755 old, oldstart, new, newstart = h.fuzzit(0, False)
755 oldstart += self.offset
756 oldstart += self.offset
756 orig_start = oldstart
757 orig_start = oldstart
757 # if there's skew we want to emit the "(offset %d lines)" even
758 # if there's skew we want to emit the "(offset %d lines)" even
758 # when the hunk cleanly applies at start + skew, so skip the
759 # when the hunk cleanly applies at start + skew, so skip the
759 # fast case code
760 # fast case code
760 if (self.skew == 0 and
761 if (self.skew == 0 and
761 diffhelpers.testhunk(old, self.lines, oldstart) == 0):
762 diffhelpers.testhunk(old, self.lines, oldstart) == 0):
762 if self.remove:
763 if self.remove:
763 self.backend.unlink(self.fname)
764 self.backend.unlink(self.fname)
764 else:
765 else:
765 self.lines[oldstart:oldstart + len(old)] = new
766 self.lines[oldstart:oldstart + len(old)] = new
766 self.offset += len(new) - len(old)
767 self.offset += len(new) - len(old)
767 self.dirty = True
768 self.dirty = True
768 return 0
769 return 0
769
770
770 # ok, we couldn't match the hunk. Lets look for offsets and fuzz it
771 # ok, we couldn't match the hunk. Lets look for offsets and fuzz it
771 self.hash = {}
772 self.hash = {}
772 for x, s in enumerate(self.lines):
773 for x, s in enumerate(self.lines):
773 self.hash.setdefault(s, []).append(x)
774 self.hash.setdefault(s, []).append(x)
774
775
775 for fuzzlen in xrange(3):
776 for fuzzlen in xrange(3):
776 for toponly in [True, False]:
777 for toponly in [True, False]:
777 old, oldstart, new, newstart = h.fuzzit(fuzzlen, toponly)
778 old, oldstart, new, newstart = h.fuzzit(fuzzlen, toponly)
778 oldstart = oldstart + self.offset + self.skew
779 oldstart = oldstart + self.offset + self.skew
779 oldstart = min(oldstart, len(self.lines))
780 oldstart = min(oldstart, len(self.lines))
780 if old:
781 if old:
781 cand = self.findlines(old[0][1:], oldstart)
782 cand = self.findlines(old[0][1:], oldstart)
782 else:
783 else:
783 # Only adding lines with no or fuzzed context, just
784 # Only adding lines with no or fuzzed context, just
784 # take the skew in account
785 # take the skew in account
785 cand = [oldstart]
786 cand = [oldstart]
786
787
787 for l in cand:
788 for l in cand:
788 if not old or diffhelpers.testhunk(old, self.lines, l) == 0:
789 if not old or diffhelpers.testhunk(old, self.lines, l) == 0:
789 self.lines[l : l + len(old)] = new
790 self.lines[l : l + len(old)] = new
790 self.offset += len(new) - len(old)
791 self.offset += len(new) - len(old)
791 self.skew = l - orig_start
792 self.skew = l - orig_start
792 self.dirty = True
793 self.dirty = True
793 offset = l - orig_start - fuzzlen
794 offset = l - orig_start - fuzzlen
794 if fuzzlen:
795 if fuzzlen:
795 msg = _("Hunk #%d succeeded at %d "
796 msg = _("Hunk #%d succeeded at %d "
796 "with fuzz %d "
797 "with fuzz %d "
797 "(offset %d lines).\n")
798 "(offset %d lines).\n")
798 self.printfile(True)
799 self.printfile(True)
799 self.ui.warn(msg %
800 self.ui.warn(msg %
800 (h.number, l + 1, fuzzlen, offset))
801 (h.number, l + 1, fuzzlen, offset))
801 else:
802 else:
802 msg = _("Hunk #%d succeeded at %d "
803 msg = _("Hunk #%d succeeded at %d "
803 "(offset %d lines).\n")
804 "(offset %d lines).\n")
804 self.ui.note(msg % (h.number, l + 1, offset))
805 self.ui.note(msg % (h.number, l + 1, offset))
805 return fuzzlen
806 return fuzzlen
806 self.printfile(True)
807 self.printfile(True)
807 self.ui.warn(_("Hunk #%d FAILED at %d\n") % (h.number, orig_start))
808 self.ui.warn(_("Hunk #%d FAILED at %d\n") % (h.number, orig_start))
808 self.rej.append(horig)
809 self.rej.append(horig)
809 return -1
810 return -1
810
811
811 def close(self):
812 def close(self):
812 if self.dirty:
813 if self.dirty:
813 self.writelines(self.fname, self.lines, self.mode)
814 self.writelines(self.fname, self.lines, self.mode)
814 self.write_rej()
815 self.write_rej()
815 return len(self.rej)
816 return len(self.rej)
816
817
817 class header(object):
818 class header(object):
818 """patch header
819 """patch header
819 """
820 """
820 diffgit_re = re.compile('diff --git a/(.*) b/(.*)$')
821 diffgit_re = re.compile('diff --git a/(.*) b/(.*)$')
821 diff_re = re.compile('diff -r .* (.*)$')
822 diff_re = re.compile('diff -r .* (.*)$')
822 allhunks_re = re.compile('(?:index|deleted file) ')
823 allhunks_re = re.compile('(?:index|deleted file) ')
823 pretty_re = re.compile('(?:new file|deleted file) ')
824 pretty_re = re.compile('(?:new file|deleted file) ')
824 special_re = re.compile('(?:index|new|deleted|copy|rename) ')
825 special_re = re.compile('(?:index|new|deleted|copy|rename) ')
825
826
826 def __init__(self, header):
827 def __init__(self, header):
827 self.header = header
828 self.header = header
828 self.hunks = []
829 self.hunks = []
829
830
830 def binary(self):
831 def binary(self):
831 return util.any(h.startswith('index ') for h in self.header)
832 return util.any(h.startswith('index ') for h in self.header)
832
833
833 def pretty(self, fp):
834 def pretty(self, fp):
834 for h in self.header:
835 for h in self.header:
835 if h.startswith('index '):
836 if h.startswith('index '):
836 fp.write(_('this modifies a binary file (all or nothing)\n'))
837 fp.write(_('this modifies a binary file (all or nothing)\n'))
837 break
838 break
838 if self.pretty_re.match(h):
839 if self.pretty_re.match(h):
839 fp.write(h)
840 fp.write(h)
840 if self.binary():
841 if self.binary():
841 fp.write(_('this is a binary file\n'))
842 fp.write(_('this is a binary file\n'))
842 break
843 break
843 if h.startswith('---'):
844 if h.startswith('---'):
844 fp.write(_('%d hunks, %d lines changed\n') %
845 fp.write(_('%d hunks, %d lines changed\n') %
845 (len(self.hunks),
846 (len(self.hunks),
846 sum([max(h.added, h.removed) for h in self.hunks])))
847 sum([max(h.added, h.removed) for h in self.hunks])))
847 break
848 break
848 fp.write(h)
849 fp.write(h)
849
850
850 def write(self, fp):
851 def write(self, fp):
851 fp.write(''.join(self.header))
852 fp.write(''.join(self.header))
852
853
853 def allhunks(self):
854 def allhunks(self):
854 return util.any(self.allhunks_re.match(h) for h in self.header)
855 return util.any(self.allhunks_re.match(h) for h in self.header)
855
856
856 def files(self):
857 def files(self):
857 match = self.diffgit_re.match(self.header[0])
858 match = self.diffgit_re.match(self.header[0])
858 if match:
859 if match:
859 fromfile, tofile = match.groups()
860 fromfile, tofile = match.groups()
860 if fromfile == tofile:
861 if fromfile == tofile:
861 return [fromfile]
862 return [fromfile]
862 return [fromfile, tofile]
863 return [fromfile, tofile]
863 else:
864 else:
864 return self.diff_re.match(self.header[0]).groups()
865 return self.diff_re.match(self.header[0]).groups()
865
866
866 def filename(self):
867 def filename(self):
867 return self.files()[-1]
868 return self.files()[-1]
868
869
869 def __repr__(self):
870 def __repr__(self):
870 return '<header %s>' % (' '.join(map(repr, self.files())))
871 return '<header %s>' % (' '.join(map(repr, self.files())))
871
872
872 def special(self):
873 def special(self):
873 return util.any(self.special_re.match(h) for h in self.header)
874 return util.any(self.special_re.match(h) for h in self.header)
874
875
875 class recordhunk(object):
876 class recordhunk(object):
876 """patch hunk
877 """patch hunk
877
878
878 XXX shouldn't we merge this with the other hunk class?
879 XXX shouldn't we merge this with the other hunk class?
879 """
880 """
880 maxcontext = 3
881 maxcontext = 3
881
882
882 def __init__(self, header, fromline, toline, proc, before, hunk, after):
883 def __init__(self, header, fromline, toline, proc, before, hunk, after):
883 def trimcontext(number, lines):
884 def trimcontext(number, lines):
884 delta = len(lines) - self.maxcontext
885 delta = len(lines) - self.maxcontext
885 if False and delta > 0:
886 if False and delta > 0:
886 return number + delta, lines[:self.maxcontext]
887 return number + delta, lines[:self.maxcontext]
887 return number, lines
888 return number, lines
888
889
889 self.header = header
890 self.header = header
890 self.fromline, self.before = trimcontext(fromline, before)
891 self.fromline, self.before = trimcontext(fromline, before)
891 self.toline, self.after = trimcontext(toline, after)
892 self.toline, self.after = trimcontext(toline, after)
892 self.proc = proc
893 self.proc = proc
893 self.hunk = hunk
894 self.hunk = hunk
894 self.added, self.removed = self.countchanges(self.hunk)
895 self.added, self.removed = self.countchanges(self.hunk)
895
896
896 def __eq__(self, v):
897 def __eq__(self, v):
897 if not isinstance(v, recordhunk):
898 if not isinstance(v, recordhunk):
898 return False
899 return False
899
900
900 return ((v.hunk == self.hunk) and
901 return ((v.hunk == self.hunk) and
901 (v.proc == self.proc) and
902 (v.proc == self.proc) and
902 (self.fromline == v.fromline) and
903 (self.fromline == v.fromline) and
903 (self.header.files() == v.header.files()))
904 (self.header.files() == v.header.files()))
904
905
905 def __hash__(self):
906 def __hash__(self):
906 return hash((tuple(self.hunk),
907 return hash((tuple(self.hunk),
907 tuple(self.header.files()),
908 tuple(self.header.files()),
908 self.fromline,
909 self.fromline,
909 self.proc))
910 self.proc))
910
911
911 def countchanges(self, hunk):
912 def countchanges(self, hunk):
912 """hunk -> (n+,n-)"""
913 """hunk -> (n+,n-)"""
913 add = len([h for h in hunk if h[0] == '+'])
914 add = len([h for h in hunk if h[0] == '+'])
914 rem = len([h for h in hunk if h[0] == '-'])
915 rem = len([h for h in hunk if h[0] == '-'])
915 return add, rem
916 return add, rem
916
917
917 def write(self, fp):
918 def write(self, fp):
918 delta = len(self.before) + len(self.after)
919 delta = len(self.before) + len(self.after)
919 if self.after and self.after[-1] == '\\ No newline at end of file\n':
920 if self.after and self.after[-1] == '\\ No newline at end of file\n':
920 delta -= 1
921 delta -= 1
921 fromlen = delta + self.removed
922 fromlen = delta + self.removed
922 tolen = delta + self.added
923 tolen = delta + self.added
923 fp.write('@@ -%d,%d +%d,%d @@%s\n' %
924 fp.write('@@ -%d,%d +%d,%d @@%s\n' %
924 (self.fromline, fromlen, self.toline, tolen,
925 (self.fromline, fromlen, self.toline, tolen,
925 self.proc and (' ' + self.proc)))
926 self.proc and (' ' + self.proc)))
926 fp.write(''.join(self.before + self.hunk + self.after))
927 fp.write(''.join(self.before + self.hunk + self.after))
927
928
928 pretty = write
929 pretty = write
929
930
930 def filename(self):
931 def filename(self):
931 return self.header.filename()
932 return self.header.filename()
932
933
933 def __repr__(self):
934 def __repr__(self):
934 return '<hunk %r@%d>' % (self.filename(), self.fromline)
935 return '<hunk %r@%d>' % (self.filename(), self.fromline)
935
936
936 def filterpatch(ui, headers):
937 def filterpatch(ui, headers):
937 """Interactively filter patch chunks into applied-only chunks"""
938 """Interactively filter patch chunks into applied-only chunks"""
938
939
939 def prompt(skipfile, skipall, query, chunk):
940 def prompt(skipfile, skipall, query, chunk):
940 """prompt query, and process base inputs
941 """prompt query, and process base inputs
941
942
942 - y/n for the rest of file
943 - y/n for the rest of file
943 - y/n for the rest
944 - y/n for the rest
944 - ? (help)
945 - ? (help)
945 - q (quit)
946 - q (quit)
946
947
947 Return True/False and possibly updated skipfile and skipall.
948 Return True/False and possibly updated skipfile and skipall.
948 """
949 """
949 newpatches = None
950 newpatches = None
950 if skipall is not None:
951 if skipall is not None:
951 return skipall, skipfile, skipall, newpatches
952 return skipall, skipfile, skipall, newpatches
952 if skipfile is not None:
953 if skipfile is not None:
953 return skipfile, skipfile, skipall, newpatches
954 return skipfile, skipfile, skipall, newpatches
954 while True:
955 while True:
955 resps = _('[Ynesfdaq?]'
956 resps = _('[Ynesfdaq?]'
956 '$$ &Yes, record this change'
957 '$$ &Yes, record this change'
957 '$$ &No, skip this change'
958 '$$ &No, skip this change'
958 '$$ &Edit this change manually'
959 '$$ &Edit this change manually'
959 '$$ &Skip remaining changes to this file'
960 '$$ &Skip remaining changes to this file'
960 '$$ Record remaining changes to this &file'
961 '$$ Record remaining changes to this &file'
961 '$$ &Done, skip remaining changes and files'
962 '$$ &Done, skip remaining changes and files'
962 '$$ Record &all changes to all remaining files'
963 '$$ Record &all changes to all remaining files'
963 '$$ &Quit, recording no changes'
964 '$$ &Quit, recording no changes'
964 '$$ &? (display help)')
965 '$$ &? (display help)')
965 r = ui.promptchoice("%s %s" % (query, resps))
966 r = ui.promptchoice("%s %s" % (query, resps))
966 ui.write("\n")
967 ui.write("\n")
967 if r == 8: # ?
968 if r == 8: # ?
968 for c, t in ui.extractchoices(resps)[1]:
969 for c, t in ui.extractchoices(resps)[1]:
969 ui.write('%s - %s\n' % (c, t.lower()))
970 ui.write('%s - %s\n' % (c, t.lower()))
970 continue
971 continue
971 elif r == 0: # yes
972 elif r == 0: # yes
972 ret = True
973 ret = True
973 elif r == 1: # no
974 elif r == 1: # no
974 ret = False
975 ret = False
975 elif r == 2: # Edit patch
976 elif r == 2: # Edit patch
976 if chunk is None:
977 if chunk is None:
977 ui.write(_('cannot edit patch for whole file'))
978 ui.write(_('cannot edit patch for whole file'))
978 ui.write("\n")
979 ui.write("\n")
979 continue
980 continue
980 if chunk.header.binary():
981 if chunk.header.binary():
981 ui.write(_('cannot edit patch for binary file'))
982 ui.write(_('cannot edit patch for binary file'))
982 ui.write("\n")
983 ui.write("\n")
983 continue
984 continue
984 # Patch comment based on the Git one (based on comment at end of
985 # Patch comment based on the Git one (based on comment at end of
985 # http://mercurial.selenic.com/wiki/RecordExtension)
986 # http://mercurial.selenic.com/wiki/RecordExtension)
986 phelp = '---' + _("""
987 phelp = '---' + _("""
987 To remove '-' lines, make them ' ' lines (context).
988 To remove '-' lines, make them ' ' lines (context).
988 To remove '+' lines, delete them.
989 To remove '+' lines, delete them.
989 Lines starting with # will be removed from the patch.
990 Lines starting with # will be removed from the patch.
990
991
991 If the patch applies cleanly, the edited hunk will immediately be
992 If the patch applies cleanly, the edited hunk will immediately be
992 added to the record list. If it does not apply cleanly, a rejects
993 added to the record list. If it does not apply cleanly, a rejects
993 file will be generated: you can use that when you try again. If
994 file will be generated: you can use that when you try again. If
994 all lines of the hunk are removed, then the edit is aborted and
995 all lines of the hunk are removed, then the edit is aborted and
995 the hunk is left unchanged.
996 the hunk is left unchanged.
996 """)
997 """)
997 (patchfd, patchfn) = tempfile.mkstemp(prefix="hg-editor-",
998 (patchfd, patchfn) = tempfile.mkstemp(prefix="hg-editor-",
998 suffix=".diff", text=True)
999 suffix=".diff", text=True)
999 ncpatchfp = None
1000 ncpatchfp = None
1000 try:
1001 try:
1001 # Write the initial patch
1002 # Write the initial patch
1002 f = os.fdopen(patchfd, "w")
1003 f = os.fdopen(patchfd, "w")
1003 chunk.header.write(f)
1004 chunk.header.write(f)
1004 chunk.write(f)
1005 chunk.write(f)
1005 f.write('\n'.join(['# ' + i for i in phelp.splitlines()]))
1006 f.write('\n'.join(['# ' + i for i in phelp.splitlines()]))
1006 f.close()
1007 f.close()
1007 # Start the editor and wait for it to complete
1008 # Start the editor and wait for it to complete
1008 editor = ui.geteditor()
1009 editor = ui.geteditor()
1009 ui.system("%s \"%s\"" % (editor, patchfn),
1010 ui.system("%s \"%s\"" % (editor, patchfn),
1010 environ={'HGUSER': ui.username()},
1011 environ={'HGUSER': ui.username()},
1011 onerr=util.Abort, errprefix=_("edit failed"))
1012 onerr=util.Abort, errprefix=_("edit failed"))
1012 # Remove comment lines
1013 # Remove comment lines
1013 patchfp = open(patchfn)
1014 patchfp = open(patchfn)
1014 ncpatchfp = cStringIO.StringIO()
1015 ncpatchfp = cStringIO.StringIO()
1015 for line in patchfp:
1016 for line in patchfp:
1016 if not line.startswith('#'):
1017 if not line.startswith('#'):
1017 ncpatchfp.write(line)
1018 ncpatchfp.write(line)
1018 patchfp.close()
1019 patchfp.close()
1019 ncpatchfp.seek(0)
1020 ncpatchfp.seek(0)
1020 newpatches = parsepatch(ncpatchfp)
1021 newpatches = parsepatch(ncpatchfp)
1021 finally:
1022 finally:
1022 os.unlink(patchfn)
1023 os.unlink(patchfn)
1023 del ncpatchfp
1024 del ncpatchfp
1024 # Signal that the chunk shouldn't be applied as-is, but
1025 # Signal that the chunk shouldn't be applied as-is, but
1025 # provide the new patch to be used instead.
1026 # provide the new patch to be used instead.
1026 ret = False
1027 ret = False
1027 elif r == 3: # Skip
1028 elif r == 3: # Skip
1028 ret = skipfile = False
1029 ret = skipfile = False
1029 elif r == 4: # file (Record remaining)
1030 elif r == 4: # file (Record remaining)
1030 ret = skipfile = True
1031 ret = skipfile = True
1031 elif r == 5: # done, skip remaining
1032 elif r == 5: # done, skip remaining
1032 ret = skipall = False
1033 ret = skipall = False
1033 elif r == 6: # all
1034 elif r == 6: # all
1034 ret = skipall = True
1035 ret = skipall = True
1035 elif r == 7: # quit
1036 elif r == 7: # quit
1036 raise util.Abort(_('user quit'))
1037 raise util.Abort(_('user quit'))
1037 return ret, skipfile, skipall, newpatches
1038 return ret, skipfile, skipall, newpatches
1038
1039
1039 seen = set()
1040 seen = set()
1040 applied = {} # 'filename' -> [] of chunks
1041 applied = {} # 'filename' -> [] of chunks
1041 skipfile, skipall = None, None
1042 skipfile, skipall = None, None
1042 pos, total = 1, sum(len(h.hunks) for h in headers)
1043 pos, total = 1, sum(len(h.hunks) for h in headers)
1043 for h in headers:
1044 for h in headers:
1044 pos += len(h.hunks)
1045 pos += len(h.hunks)
1045 skipfile = None
1046 skipfile = None
1046 fixoffset = 0
1047 fixoffset = 0
1047 hdr = ''.join(h.header)
1048 hdr = ''.join(h.header)
1048 if hdr in seen:
1049 if hdr in seen:
1049 continue
1050 continue
1050 seen.add(hdr)
1051 seen.add(hdr)
1051 if skipall is None:
1052 if skipall is None:
1052 h.pretty(ui)
1053 h.pretty(ui)
1053 msg = (_('examine changes to %s?') %
1054 msg = (_('examine changes to %s?') %
1054 _(' and ').join("'%s'" % f for f in h.files()))
1055 _(' and ').join("'%s'" % f for f in h.files()))
1055 r, skipfile, skipall, np = prompt(skipfile, skipall, msg, None)
1056 r, skipfile, skipall, np = prompt(skipfile, skipall, msg, None)
1056 if not r:
1057 if not r:
1057 continue
1058 continue
1058 applied[h.filename()] = [h]
1059 applied[h.filename()] = [h]
1059 if h.allhunks():
1060 if h.allhunks():
1060 applied[h.filename()] += h.hunks
1061 applied[h.filename()] += h.hunks
1061 continue
1062 continue
1062 for i, chunk in enumerate(h.hunks):
1063 for i, chunk in enumerate(h.hunks):
1063 if skipfile is None and skipall is None:
1064 if skipfile is None and skipall is None:
1064 chunk.pretty(ui)
1065 chunk.pretty(ui)
1065 if total == 1:
1066 if total == 1:
1066 msg = _("record this change to '%s'?") % chunk.filename()
1067 msg = _("record this change to '%s'?") % chunk.filename()
1067 else:
1068 else:
1068 idx = pos - len(h.hunks) + i
1069 idx = pos - len(h.hunks) + i
1069 msg = _("record change %d/%d to '%s'?") % (idx, total,
1070 msg = _("record change %d/%d to '%s'?") % (idx, total,
1070 chunk.filename())
1071 chunk.filename())
1071 r, skipfile, skipall, newpatches = prompt(skipfile,
1072 r, skipfile, skipall, newpatches = prompt(skipfile,
1072 skipall, msg, chunk)
1073 skipall, msg, chunk)
1073 if r:
1074 if r:
1074 if fixoffset:
1075 if fixoffset:
1075 chunk = copy.copy(chunk)
1076 chunk = copy.copy(chunk)
1076 chunk.toline += fixoffset
1077 chunk.toline += fixoffset
1077 applied[chunk.filename()].append(chunk)
1078 applied[chunk.filename()].append(chunk)
1078 elif newpatches is not None:
1079 elif newpatches is not None:
1079 for newpatch in newpatches:
1080 for newpatch in newpatches:
1080 for newhunk in newpatch.hunks:
1081 for newhunk in newpatch.hunks:
1081 if fixoffset:
1082 if fixoffset:
1082 newhunk.toline += fixoffset
1083 newhunk.toline += fixoffset
1083 applied[newhunk.filename()].append(newhunk)
1084 applied[newhunk.filename()].append(newhunk)
1084 else:
1085 else:
1085 fixoffset += chunk.removed - chunk.added
1086 fixoffset += chunk.removed - chunk.added
1086 return sum([h for h in applied.itervalues()
1087 return sum([h for h in applied.itervalues()
1087 if h[0].special() or len(h) > 1], [])
1088 if h[0].special() or len(h) > 1], [])
1088 class hunk(object):
1089 class hunk(object):
1089 def __init__(self, desc, num, lr, context):
1090 def __init__(self, desc, num, lr, context):
1090 self.number = num
1091 self.number = num
1091 self.desc = desc
1092 self.desc = desc
1092 self.hunk = [desc]
1093 self.hunk = [desc]
1093 self.a = []
1094 self.a = []
1094 self.b = []
1095 self.b = []
1095 self.starta = self.lena = None
1096 self.starta = self.lena = None
1096 self.startb = self.lenb = None
1097 self.startb = self.lenb = None
1097 if lr is not None:
1098 if lr is not None:
1098 if context:
1099 if context:
1099 self.read_context_hunk(lr)
1100 self.read_context_hunk(lr)
1100 else:
1101 else:
1101 self.read_unified_hunk(lr)
1102 self.read_unified_hunk(lr)
1102
1103
1103 def getnormalized(self):
1104 def getnormalized(self):
1104 """Return a copy with line endings normalized to LF."""
1105 """Return a copy with line endings normalized to LF."""
1105
1106
1106 def normalize(lines):
1107 def normalize(lines):
1107 nlines = []
1108 nlines = []
1108 for line in lines:
1109 for line in lines:
1109 if line.endswith('\r\n'):
1110 if line.endswith('\r\n'):
1110 line = line[:-2] + '\n'
1111 line = line[:-2] + '\n'
1111 nlines.append(line)
1112 nlines.append(line)
1112 return nlines
1113 return nlines
1113
1114
1114 # Dummy object, it is rebuilt manually
1115 # Dummy object, it is rebuilt manually
1115 nh = hunk(self.desc, self.number, None, None)
1116 nh = hunk(self.desc, self.number, None, None)
1116 nh.number = self.number
1117 nh.number = self.number
1117 nh.desc = self.desc
1118 nh.desc = self.desc
1118 nh.hunk = self.hunk
1119 nh.hunk = self.hunk
1119 nh.a = normalize(self.a)
1120 nh.a = normalize(self.a)
1120 nh.b = normalize(self.b)
1121 nh.b = normalize(self.b)
1121 nh.starta = self.starta
1122 nh.starta = self.starta
1122 nh.startb = self.startb
1123 nh.startb = self.startb
1123 nh.lena = self.lena
1124 nh.lena = self.lena
1124 nh.lenb = self.lenb
1125 nh.lenb = self.lenb
1125 return nh
1126 return nh
1126
1127
1127 def read_unified_hunk(self, lr):
1128 def read_unified_hunk(self, lr):
1128 m = unidesc.match(self.desc)
1129 m = unidesc.match(self.desc)
1129 if not m:
1130 if not m:
1130 raise PatchError(_("bad hunk #%d") % self.number)
1131 raise PatchError(_("bad hunk #%d") % self.number)
1131 self.starta, self.lena, self.startb, self.lenb = m.groups()
1132 self.starta, self.lena, self.startb, self.lenb = m.groups()
1132 if self.lena is None:
1133 if self.lena is None:
1133 self.lena = 1
1134 self.lena = 1
1134 else:
1135 else:
1135 self.lena = int(self.lena)
1136 self.lena = int(self.lena)
1136 if self.lenb is None:
1137 if self.lenb is None:
1137 self.lenb = 1
1138 self.lenb = 1
1138 else:
1139 else:
1139 self.lenb = int(self.lenb)
1140 self.lenb = int(self.lenb)
1140 self.starta = int(self.starta)
1141 self.starta = int(self.starta)
1141 self.startb = int(self.startb)
1142 self.startb = int(self.startb)
1142 diffhelpers.addlines(lr, self.hunk, self.lena, self.lenb, self.a,
1143 diffhelpers.addlines(lr, self.hunk, self.lena, self.lenb, self.a,
1143 self.b)
1144 self.b)
1144 # if we hit eof before finishing out the hunk, the last line will
1145 # if we hit eof before finishing out the hunk, the last line will
1145 # be zero length. Lets try to fix it up.
1146 # be zero length. Lets try to fix it up.
1146 while len(self.hunk[-1]) == 0:
1147 while len(self.hunk[-1]) == 0:
1147 del self.hunk[-1]
1148 del self.hunk[-1]
1148 del self.a[-1]
1149 del self.a[-1]
1149 del self.b[-1]
1150 del self.b[-1]
1150 self.lena -= 1
1151 self.lena -= 1
1151 self.lenb -= 1
1152 self.lenb -= 1
1152 self._fixnewline(lr)
1153 self._fixnewline(lr)
1153
1154
1154 def read_context_hunk(self, lr):
1155 def read_context_hunk(self, lr):
1155 self.desc = lr.readline()
1156 self.desc = lr.readline()
1156 m = contextdesc.match(self.desc)
1157 m = contextdesc.match(self.desc)
1157 if not m:
1158 if not m:
1158 raise PatchError(_("bad hunk #%d") % self.number)
1159 raise PatchError(_("bad hunk #%d") % self.number)
1159 self.starta, aend = m.groups()
1160 self.starta, aend = m.groups()
1160 self.starta = int(self.starta)
1161 self.starta = int(self.starta)
1161 if aend is None:
1162 if aend is None:
1162 aend = self.starta
1163 aend = self.starta
1163 self.lena = int(aend) - self.starta
1164 self.lena = int(aend) - self.starta
1164 if self.starta:
1165 if self.starta:
1165 self.lena += 1
1166 self.lena += 1
1166 for x in xrange(self.lena):
1167 for x in xrange(self.lena):
1167 l = lr.readline()
1168 l = lr.readline()
1168 if l.startswith('---'):
1169 if l.startswith('---'):
1169 # lines addition, old block is empty
1170 # lines addition, old block is empty
1170 lr.push(l)
1171 lr.push(l)
1171 break
1172 break
1172 s = l[2:]
1173 s = l[2:]
1173 if l.startswith('- ') or l.startswith('! '):
1174 if l.startswith('- ') or l.startswith('! '):
1174 u = '-' + s
1175 u = '-' + s
1175 elif l.startswith(' '):
1176 elif l.startswith(' '):
1176 u = ' ' + s
1177 u = ' ' + s
1177 else:
1178 else:
1178 raise PatchError(_("bad hunk #%d old text line %d") %
1179 raise PatchError(_("bad hunk #%d old text line %d") %
1179 (self.number, x))
1180 (self.number, x))
1180 self.a.append(u)
1181 self.a.append(u)
1181 self.hunk.append(u)
1182 self.hunk.append(u)
1182
1183
1183 l = lr.readline()
1184 l = lr.readline()
1184 if l.startswith('\ '):
1185 if l.startswith('\ '):
1185 s = self.a[-1][:-1]
1186 s = self.a[-1][:-1]
1186 self.a[-1] = s
1187 self.a[-1] = s
1187 self.hunk[-1] = s
1188 self.hunk[-1] = s
1188 l = lr.readline()
1189 l = lr.readline()
1189 m = contextdesc.match(l)
1190 m = contextdesc.match(l)
1190 if not m:
1191 if not m:
1191 raise PatchError(_("bad hunk #%d") % self.number)
1192 raise PatchError(_("bad hunk #%d") % self.number)
1192 self.startb, bend = m.groups()
1193 self.startb, bend = m.groups()
1193 self.startb = int(self.startb)
1194 self.startb = int(self.startb)
1194 if bend is None:
1195 if bend is None:
1195 bend = self.startb
1196 bend = self.startb
1196 self.lenb = int(bend) - self.startb
1197 self.lenb = int(bend) - self.startb
1197 if self.startb:
1198 if self.startb:
1198 self.lenb += 1
1199 self.lenb += 1
1199 hunki = 1
1200 hunki = 1
1200 for x in xrange(self.lenb):
1201 for x in xrange(self.lenb):
1201 l = lr.readline()
1202 l = lr.readline()
1202 if l.startswith('\ '):
1203 if l.startswith('\ '):
1203 # XXX: the only way to hit this is with an invalid line range.
1204 # XXX: the only way to hit this is with an invalid line range.
1204 # The no-eol marker is not counted in the line range, but I
1205 # The no-eol marker is not counted in the line range, but I
1205 # guess there are diff(1) out there which behave differently.
1206 # guess there are diff(1) out there which behave differently.
1206 s = self.b[-1][:-1]
1207 s = self.b[-1][:-1]
1207 self.b[-1] = s
1208 self.b[-1] = s
1208 self.hunk[hunki - 1] = s
1209 self.hunk[hunki - 1] = s
1209 continue
1210 continue
1210 if not l:
1211 if not l:
1211 # line deletions, new block is empty and we hit EOF
1212 # line deletions, new block is empty and we hit EOF
1212 lr.push(l)
1213 lr.push(l)
1213 break
1214 break
1214 s = l[2:]
1215 s = l[2:]
1215 if l.startswith('+ ') or l.startswith('! '):
1216 if l.startswith('+ ') or l.startswith('! '):
1216 u = '+' + s
1217 u = '+' + s
1217 elif l.startswith(' '):
1218 elif l.startswith(' '):
1218 u = ' ' + s
1219 u = ' ' + s
1219 elif len(self.b) == 0:
1220 elif len(self.b) == 0:
1220 # line deletions, new block is empty
1221 # line deletions, new block is empty
1221 lr.push(l)
1222 lr.push(l)
1222 break
1223 break
1223 else:
1224 else:
1224 raise PatchError(_("bad hunk #%d old text line %d") %
1225 raise PatchError(_("bad hunk #%d old text line %d") %
1225 (self.number, x))
1226 (self.number, x))
1226 self.b.append(s)
1227 self.b.append(s)
1227 while True:
1228 while True:
1228 if hunki >= len(self.hunk):
1229 if hunki >= len(self.hunk):
1229 h = ""
1230 h = ""
1230 else:
1231 else:
1231 h = self.hunk[hunki]
1232 h = self.hunk[hunki]
1232 hunki += 1
1233 hunki += 1
1233 if h == u:
1234 if h == u:
1234 break
1235 break
1235 elif h.startswith('-'):
1236 elif h.startswith('-'):
1236 continue
1237 continue
1237 else:
1238 else:
1238 self.hunk.insert(hunki - 1, u)
1239 self.hunk.insert(hunki - 1, u)
1239 break
1240 break
1240
1241
1241 if not self.a:
1242 if not self.a:
1242 # this happens when lines were only added to the hunk
1243 # this happens when lines were only added to the hunk
1243 for x in self.hunk:
1244 for x in self.hunk:
1244 if x.startswith('-') or x.startswith(' '):
1245 if x.startswith('-') or x.startswith(' '):
1245 self.a.append(x)
1246 self.a.append(x)
1246 if not self.b:
1247 if not self.b:
1247 # this happens when lines were only deleted from the hunk
1248 # this happens when lines were only deleted from the hunk
1248 for x in self.hunk:
1249 for x in self.hunk:
1249 if x.startswith('+') or x.startswith(' '):
1250 if x.startswith('+') or x.startswith(' '):
1250 self.b.append(x[1:])
1251 self.b.append(x[1:])
1251 # @@ -start,len +start,len @@
1252 # @@ -start,len +start,len @@
1252 self.desc = "@@ -%d,%d +%d,%d @@\n" % (self.starta, self.lena,
1253 self.desc = "@@ -%d,%d +%d,%d @@\n" % (self.starta, self.lena,
1253 self.startb, self.lenb)
1254 self.startb, self.lenb)
1254 self.hunk[0] = self.desc
1255 self.hunk[0] = self.desc
1255 self._fixnewline(lr)
1256 self._fixnewline(lr)
1256
1257
1257 def _fixnewline(self, lr):
1258 def _fixnewline(self, lr):
1258 l = lr.readline()
1259 l = lr.readline()
1259 if l.startswith('\ '):
1260 if l.startswith('\ '):
1260 diffhelpers.fix_newline(self.hunk, self.a, self.b)
1261 diffhelpers.fix_newline(self.hunk, self.a, self.b)
1261 else:
1262 else:
1262 lr.push(l)
1263 lr.push(l)
1263
1264
1264 def complete(self):
1265 def complete(self):
1265 return len(self.a) == self.lena and len(self.b) == self.lenb
1266 return len(self.a) == self.lena and len(self.b) == self.lenb
1266
1267
1267 def _fuzzit(self, old, new, fuzz, toponly):
1268 def _fuzzit(self, old, new, fuzz, toponly):
1268 # this removes context lines from the top and bottom of list 'l'. It
1269 # this removes context lines from the top and bottom of list 'l'. It
1269 # checks the hunk to make sure only context lines are removed, and then
1270 # checks the hunk to make sure only context lines are removed, and then
1270 # returns a new shortened list of lines.
1271 # returns a new shortened list of lines.
1271 fuzz = min(fuzz, len(old))
1272 fuzz = min(fuzz, len(old))
1272 if fuzz:
1273 if fuzz:
1273 top = 0
1274 top = 0
1274 bot = 0
1275 bot = 0
1275 hlen = len(self.hunk)
1276 hlen = len(self.hunk)
1276 for x in xrange(hlen - 1):
1277 for x in xrange(hlen - 1):
1277 # the hunk starts with the @@ line, so use x+1
1278 # the hunk starts with the @@ line, so use x+1
1278 if self.hunk[x + 1][0] == ' ':
1279 if self.hunk[x + 1][0] == ' ':
1279 top += 1
1280 top += 1
1280 else:
1281 else:
1281 break
1282 break
1282 if not toponly:
1283 if not toponly:
1283 for x in xrange(hlen - 1):
1284 for x in xrange(hlen - 1):
1284 if self.hunk[hlen - bot - 1][0] == ' ':
1285 if self.hunk[hlen - bot - 1][0] == ' ':
1285 bot += 1
1286 bot += 1
1286 else:
1287 else:
1287 break
1288 break
1288
1289
1289 bot = min(fuzz, bot)
1290 bot = min(fuzz, bot)
1290 top = min(fuzz, top)
1291 top = min(fuzz, top)
1291 return old[top:len(old) - bot], new[top:len(new) - bot], top
1292 return old[top:len(old) - bot], new[top:len(new) - bot], top
1292 return old, new, 0
1293 return old, new, 0
1293
1294
1294 def fuzzit(self, fuzz, toponly):
1295 def fuzzit(self, fuzz, toponly):
1295 old, new, top = self._fuzzit(self.a, self.b, fuzz, toponly)
1296 old, new, top = self._fuzzit(self.a, self.b, fuzz, toponly)
1296 oldstart = self.starta + top
1297 oldstart = self.starta + top
1297 newstart = self.startb + top
1298 newstart = self.startb + top
1298 # zero length hunk ranges already have their start decremented
1299 # zero length hunk ranges already have their start decremented
1299 if self.lena and oldstart > 0:
1300 if self.lena and oldstart > 0:
1300 oldstart -= 1
1301 oldstart -= 1
1301 if self.lenb and newstart > 0:
1302 if self.lenb and newstart > 0:
1302 newstart -= 1
1303 newstart -= 1
1303 return old, oldstart, new, newstart
1304 return old, oldstart, new, newstart
1304
1305
1305 class binhunk(object):
1306 class binhunk(object):
1306 'A binary patch file.'
1307 'A binary patch file.'
1307 def __init__(self, lr, fname):
1308 def __init__(self, lr, fname):
1308 self.text = None
1309 self.text = None
1309 self.delta = False
1310 self.delta = False
1310 self.hunk = ['GIT binary patch\n']
1311 self.hunk = ['GIT binary patch\n']
1311 self._fname = fname
1312 self._fname = fname
1312 self._read(lr)
1313 self._read(lr)
1313
1314
1314 def complete(self):
1315 def complete(self):
1315 return self.text is not None
1316 return self.text is not None
1316
1317
1317 def new(self, lines):
1318 def new(self, lines):
1318 if self.delta:
1319 if self.delta:
1319 return [applybindelta(self.text, ''.join(lines))]
1320 return [applybindelta(self.text, ''.join(lines))]
1320 return [self.text]
1321 return [self.text]
1321
1322
1322 def _read(self, lr):
1323 def _read(self, lr):
1323 def getline(lr, hunk):
1324 def getline(lr, hunk):
1324 l = lr.readline()
1325 l = lr.readline()
1325 hunk.append(l)
1326 hunk.append(l)
1326 return l.rstrip('\r\n')
1327 return l.rstrip('\r\n')
1327
1328
1328 size = 0
1329 size = 0
1329 while True:
1330 while True:
1330 line = getline(lr, self.hunk)
1331 line = getline(lr, self.hunk)
1331 if not line:
1332 if not line:
1332 raise PatchError(_('could not extract "%s" binary data')
1333 raise PatchError(_('could not extract "%s" binary data')
1333 % self._fname)
1334 % self._fname)
1334 if line.startswith('literal '):
1335 if line.startswith('literal '):
1335 size = int(line[8:].rstrip())
1336 size = int(line[8:].rstrip())
1336 break
1337 break
1337 if line.startswith('delta '):
1338 if line.startswith('delta '):
1338 size = int(line[6:].rstrip())
1339 size = int(line[6:].rstrip())
1339 self.delta = True
1340 self.delta = True
1340 break
1341 break
1341 dec = []
1342 dec = []
1342 line = getline(lr, self.hunk)
1343 line = getline(lr, self.hunk)
1343 while len(line) > 1:
1344 while len(line) > 1:
1344 l = line[0]
1345 l = line[0]
1345 if l <= 'Z' and l >= 'A':
1346 if l <= 'Z' and l >= 'A':
1346 l = ord(l) - ord('A') + 1
1347 l = ord(l) - ord('A') + 1
1347 else:
1348 else:
1348 l = ord(l) - ord('a') + 27
1349 l = ord(l) - ord('a') + 27
1349 try:
1350 try:
1350 dec.append(base85.b85decode(line[1:])[:l])
1351 dec.append(base85.b85decode(line[1:])[:l])
1351 except ValueError, e:
1352 except ValueError, e:
1352 raise PatchError(_('could not decode "%s" binary patch: %s')
1353 raise PatchError(_('could not decode "%s" binary patch: %s')
1353 % (self._fname, str(e)))
1354 % (self._fname, str(e)))
1354 line = getline(lr, self.hunk)
1355 line = getline(lr, self.hunk)
1355 text = zlib.decompress(''.join(dec))
1356 text = zlib.decompress(''.join(dec))
1356 if len(text) != size:
1357 if len(text) != size:
1357 raise PatchError(_('"%s" length is %d bytes, should be %d')
1358 raise PatchError(_('"%s" length is %d bytes, should be %d')
1358 % (self._fname, len(text), size))
1359 % (self._fname, len(text), size))
1359 self.text = text
1360 self.text = text
1360
1361
1361 def parsefilename(str):
1362 def parsefilename(str):
1362 # --- filename \t|space stuff
1363 # --- filename \t|space stuff
1363 s = str[4:].rstrip('\r\n')
1364 s = str[4:].rstrip('\r\n')
1364 i = s.find('\t')
1365 i = s.find('\t')
1365 if i < 0:
1366 if i < 0:
1366 i = s.find(' ')
1367 i = s.find(' ')
1367 if i < 0:
1368 if i < 0:
1368 return s
1369 return s
1369 return s[:i]
1370 return s[:i]
1370
1371
1371 def parsepatch(originalchunks):
1372 def parsepatch(originalchunks):
1372 """patch -> [] of headers -> [] of hunks """
1373 """patch -> [] of headers -> [] of hunks """
1373 class parser(object):
1374 class parser(object):
1374 """patch parsing state machine"""
1375 """patch parsing state machine"""
1375 def __init__(self):
1376 def __init__(self):
1376 self.fromline = 0
1377 self.fromline = 0
1377 self.toline = 0
1378 self.toline = 0
1378 self.proc = ''
1379 self.proc = ''
1379 self.header = None
1380 self.header = None
1380 self.context = []
1381 self.context = []
1381 self.before = []
1382 self.before = []
1382 self.hunk = []
1383 self.hunk = []
1383 self.headers = []
1384 self.headers = []
1384
1385
1385 def addrange(self, limits):
1386 def addrange(self, limits):
1386 fromstart, fromend, tostart, toend, proc = limits
1387 fromstart, fromend, tostart, toend, proc = limits
1387 self.fromline = int(fromstart)
1388 self.fromline = int(fromstart)
1388 self.toline = int(tostart)
1389 self.toline = int(tostart)
1389 self.proc = proc
1390 self.proc = proc
1390
1391
1391 def addcontext(self, context):
1392 def addcontext(self, context):
1392 if self.hunk:
1393 if self.hunk:
1393 h = recordhunk(self.header, self.fromline, self.toline,
1394 h = recordhunk(self.header, self.fromline, self.toline,
1394 self.proc, self.before, self.hunk, context)
1395 self.proc, self.before, self.hunk, context)
1395 self.header.hunks.append(h)
1396 self.header.hunks.append(h)
1396 self.fromline += len(self.before) + h.removed
1397 self.fromline += len(self.before) + h.removed
1397 self.toline += len(self.before) + h.added
1398 self.toline += len(self.before) + h.added
1398 self.before = []
1399 self.before = []
1399 self.hunk = []
1400 self.hunk = []
1400 self.proc = ''
1401 self.proc = ''
1401 self.context = context
1402 self.context = context
1402
1403
1403 def addhunk(self, hunk):
1404 def addhunk(self, hunk):
1404 if self.context:
1405 if self.context:
1405 self.before = self.context
1406 self.before = self.context
1406 self.context = []
1407 self.context = []
1407 self.hunk = hunk
1408 self.hunk = hunk
1408
1409
1409 def newfile(self, hdr):
1410 def newfile(self, hdr):
1410 self.addcontext([])
1411 self.addcontext([])
1411 h = header(hdr)
1412 h = header(hdr)
1412 self.headers.append(h)
1413 self.headers.append(h)
1413 self.header = h
1414 self.header = h
1414
1415
1415 def addother(self, line):
1416 def addother(self, line):
1416 pass # 'other' lines are ignored
1417 pass # 'other' lines are ignored
1417
1418
1418 def finished(self):
1419 def finished(self):
1419 self.addcontext([])
1420 self.addcontext([])
1420 return self.headers
1421 return self.headers
1421
1422
1422 transitions = {
1423 transitions = {
1423 'file': {'context': addcontext,
1424 'file': {'context': addcontext,
1424 'file': newfile,
1425 'file': newfile,
1425 'hunk': addhunk,
1426 'hunk': addhunk,
1426 'range': addrange},
1427 'range': addrange},
1427 'context': {'file': newfile,
1428 'context': {'file': newfile,
1428 'hunk': addhunk,
1429 'hunk': addhunk,
1429 'range': addrange,
1430 'range': addrange,
1430 'other': addother},
1431 'other': addother},
1431 'hunk': {'context': addcontext,
1432 'hunk': {'context': addcontext,
1432 'file': newfile,
1433 'file': newfile,
1433 'range': addrange},
1434 'range': addrange},
1434 'range': {'context': addcontext,
1435 'range': {'context': addcontext,
1435 'hunk': addhunk},
1436 'hunk': addhunk},
1436 'other': {'other': addother},
1437 'other': {'other': addother},
1437 }
1438 }
1438
1439
1439 p = parser()
1440 p = parser()
1440 fp = cStringIO.StringIO()
1441 fp = cStringIO.StringIO()
1441 fp.write(''.join(originalchunks))
1442 fp.write(''.join(originalchunks))
1442 fp.seek(0)
1443 fp.seek(0)
1443
1444
1444 state = 'context'
1445 state = 'context'
1445 for newstate, data in scanpatch(fp):
1446 for newstate, data in scanpatch(fp):
1446 try:
1447 try:
1447 p.transitions[state][newstate](p, data)
1448 p.transitions[state][newstate](p, data)
1448 except KeyError:
1449 except KeyError:
1449 raise PatchError('unhandled transition: %s -> %s' %
1450 raise PatchError('unhandled transition: %s -> %s' %
1450 (state, newstate))
1451 (state, newstate))
1451 state = newstate
1452 state = newstate
1452 del fp
1453 del fp
1453 return p.finished()
1454 return p.finished()
1454
1455
1455 def pathtransform(path, strip, prefix):
1456 def pathtransform(path, strip, prefix):
1456 '''turn a path from a patch into a path suitable for the repository
1457 '''turn a path from a patch into a path suitable for the repository
1457
1458
1458 prefix, if not empty, is expected to be normalized with a / at the end.
1459 prefix, if not empty, is expected to be normalized with a / at the end.
1459
1460
1460 Returns (stripped components, path in repository).
1461 Returns (stripped components, path in repository).
1461
1462
1462 >>> pathtransform('a/b/c', 0, '')
1463 >>> pathtransform('a/b/c', 0, '')
1463 ('', 'a/b/c')
1464 ('', 'a/b/c')
1464 >>> pathtransform(' a/b/c ', 0, '')
1465 >>> pathtransform(' a/b/c ', 0, '')
1465 ('', ' a/b/c')
1466 ('', ' a/b/c')
1466 >>> pathtransform(' a/b/c ', 2, '')
1467 >>> pathtransform(' a/b/c ', 2, '')
1467 ('a/b/', 'c')
1468 ('a/b/', 'c')
1468 >>> pathtransform('a/b/c', 0, 'd/e/')
1469 >>> pathtransform('a/b/c', 0, 'd/e/')
1469 ('', 'd/e/a/b/c')
1470 ('', 'd/e/a/b/c')
1470 >>> pathtransform(' a//b/c ', 2, 'd/e/')
1471 >>> pathtransform(' a//b/c ', 2, 'd/e/')
1471 ('a//b/', 'd/e/c')
1472 ('a//b/', 'd/e/c')
1472 >>> pathtransform('a/b/c', 3, '')
1473 >>> pathtransform('a/b/c', 3, '')
1473 Traceback (most recent call last):
1474 Traceback (most recent call last):
1474 PatchError: unable to strip away 1 of 3 dirs from a/b/c
1475 PatchError: unable to strip away 1 of 3 dirs from a/b/c
1475 '''
1476 '''
1476 pathlen = len(path)
1477 pathlen = len(path)
1477 i = 0
1478 i = 0
1478 if strip == 0:
1479 if strip == 0:
1479 return '', prefix + path.rstrip()
1480 return '', prefix + path.rstrip()
1480 count = strip
1481 count = strip
1481 while count > 0:
1482 while count > 0:
1482 i = path.find('/', i)
1483 i = path.find('/', i)
1483 if i == -1:
1484 if i == -1:
1484 raise PatchError(_("unable to strip away %d of %d dirs from %s") %
1485 raise PatchError(_("unable to strip away %d of %d dirs from %s") %
1485 (count, strip, path))
1486 (count, strip, path))
1486 i += 1
1487 i += 1
1487 # consume '//' in the path
1488 # consume '//' in the path
1488 while i < pathlen - 1 and path[i] == '/':
1489 while i < pathlen - 1 and path[i] == '/':
1489 i += 1
1490 i += 1
1490 count -= 1
1491 count -= 1
1491 return path[:i].lstrip(), prefix + path[i:].rstrip()
1492 return path[:i].lstrip(), prefix + path[i:].rstrip()
1492
1493
1493 def makepatchmeta(backend, afile_orig, bfile_orig, hunk, strip, prefix):
1494 def makepatchmeta(backend, afile_orig, bfile_orig, hunk, strip, prefix):
1494 nulla = afile_orig == "/dev/null"
1495 nulla = afile_orig == "/dev/null"
1495 nullb = bfile_orig == "/dev/null"
1496 nullb = bfile_orig == "/dev/null"
1496 create = nulla and hunk.starta == 0 and hunk.lena == 0
1497 create = nulla and hunk.starta == 0 and hunk.lena == 0
1497 remove = nullb and hunk.startb == 0 and hunk.lenb == 0
1498 remove = nullb and hunk.startb == 0 and hunk.lenb == 0
1498 abase, afile = pathtransform(afile_orig, strip, prefix)
1499 abase, afile = pathtransform(afile_orig, strip, prefix)
1499 gooda = not nulla and backend.exists(afile)
1500 gooda = not nulla and backend.exists(afile)
1500 bbase, bfile = pathtransform(bfile_orig, strip, prefix)
1501 bbase, bfile = pathtransform(bfile_orig, strip, prefix)
1501 if afile == bfile:
1502 if afile == bfile:
1502 goodb = gooda
1503 goodb = gooda
1503 else:
1504 else:
1504 goodb = not nullb and backend.exists(bfile)
1505 goodb = not nullb and backend.exists(bfile)
1505 missing = not goodb and not gooda and not create
1506 missing = not goodb and not gooda and not create
1506
1507
1507 # some diff programs apparently produce patches where the afile is
1508 # some diff programs apparently produce patches where the afile is
1508 # not /dev/null, but afile starts with bfile
1509 # not /dev/null, but afile starts with bfile
1509 abasedir = afile[:afile.rfind('/') + 1]
1510 abasedir = afile[:afile.rfind('/') + 1]
1510 bbasedir = bfile[:bfile.rfind('/') + 1]
1511 bbasedir = bfile[:bfile.rfind('/') + 1]
1511 if (missing and abasedir == bbasedir and afile.startswith(bfile)
1512 if (missing and abasedir == bbasedir and afile.startswith(bfile)
1512 and hunk.starta == 0 and hunk.lena == 0):
1513 and hunk.starta == 0 and hunk.lena == 0):
1513 create = True
1514 create = True
1514 missing = False
1515 missing = False
1515
1516
1516 # If afile is "a/b/foo" and bfile is "a/b/foo.orig" we assume the
1517 # If afile is "a/b/foo" and bfile is "a/b/foo.orig" we assume the
1517 # diff is between a file and its backup. In this case, the original
1518 # diff is between a file and its backup. In this case, the original
1518 # file should be patched (see original mpatch code).
1519 # file should be patched (see original mpatch code).
1519 isbackup = (abase == bbase and bfile.startswith(afile))
1520 isbackup = (abase == bbase and bfile.startswith(afile))
1520 fname = None
1521 fname = None
1521 if not missing:
1522 if not missing:
1522 if gooda and goodb:
1523 if gooda and goodb:
1523 if isbackup:
1524 if isbackup:
1524 fname = afile
1525 fname = afile
1525 else:
1526 else:
1526 fname = bfile
1527 fname = bfile
1527 elif gooda:
1528 elif gooda:
1528 fname = afile
1529 fname = afile
1529
1530
1530 if not fname:
1531 if not fname:
1531 if not nullb:
1532 if not nullb:
1532 if isbackup:
1533 if isbackup:
1533 fname = afile
1534 fname = afile
1534 else:
1535 else:
1535 fname = bfile
1536 fname = bfile
1536 elif not nulla:
1537 elif not nulla:
1537 fname = afile
1538 fname = afile
1538 else:
1539 else:
1539 raise PatchError(_("undefined source and destination files"))
1540 raise PatchError(_("undefined source and destination files"))
1540
1541
1541 gp = patchmeta(fname)
1542 gp = patchmeta(fname)
1542 if create:
1543 if create:
1543 gp.op = 'ADD'
1544 gp.op = 'ADD'
1544 elif remove:
1545 elif remove:
1545 gp.op = 'DELETE'
1546 gp.op = 'DELETE'
1546 return gp
1547 return gp
1547
1548
1548 def scanpatch(fp):
1549 def scanpatch(fp):
1549 """like patch.iterhunks, but yield different events
1550 """like patch.iterhunks, but yield different events
1550
1551
1551 - ('file', [header_lines + fromfile + tofile])
1552 - ('file', [header_lines + fromfile + tofile])
1552 - ('context', [context_lines])
1553 - ('context', [context_lines])
1553 - ('hunk', [hunk_lines])
1554 - ('hunk', [hunk_lines])
1554 - ('range', (-start,len, +start,len, proc))
1555 - ('range', (-start,len, +start,len, proc))
1555 """
1556 """
1556 lines_re = re.compile(r'@@ -(\d+),(\d+) \+(\d+),(\d+) @@\s*(.*)')
1557 lines_re = re.compile(r'@@ -(\d+),(\d+) \+(\d+),(\d+) @@\s*(.*)')
1557 lr = linereader(fp)
1558 lr = linereader(fp)
1558
1559
1559 def scanwhile(first, p):
1560 def scanwhile(first, p):
1560 """scan lr while predicate holds"""
1561 """scan lr while predicate holds"""
1561 lines = [first]
1562 lines = [first]
1562 while True:
1563 while True:
1563 line = lr.readline()
1564 line = lr.readline()
1564 if not line:
1565 if not line:
1565 break
1566 break
1566 if p(line):
1567 if p(line):
1567 lines.append(line)
1568 lines.append(line)
1568 else:
1569 else:
1569 lr.push(line)
1570 lr.push(line)
1570 break
1571 break
1571 return lines
1572 return lines
1572
1573
1573 while True:
1574 while True:
1574 line = lr.readline()
1575 line = lr.readline()
1575 if not line:
1576 if not line:
1576 break
1577 break
1577 if line.startswith('diff --git a/') or line.startswith('diff -r '):
1578 if line.startswith('diff --git a/') or line.startswith('diff -r '):
1578 def notheader(line):
1579 def notheader(line):
1579 s = line.split(None, 1)
1580 s = line.split(None, 1)
1580 return not s or s[0] not in ('---', 'diff')
1581 return not s or s[0] not in ('---', 'diff')
1581 header = scanwhile(line, notheader)
1582 header = scanwhile(line, notheader)
1582 fromfile = lr.readline()
1583 fromfile = lr.readline()
1583 if fromfile.startswith('---'):
1584 if fromfile.startswith('---'):
1584 tofile = lr.readline()
1585 tofile = lr.readline()
1585 header += [fromfile, tofile]
1586 header += [fromfile, tofile]
1586 else:
1587 else:
1587 lr.push(fromfile)
1588 lr.push(fromfile)
1588 yield 'file', header
1589 yield 'file', header
1589 elif line[0] == ' ':
1590 elif line[0] == ' ':
1590 yield 'context', scanwhile(line, lambda l: l[0] in ' \\')
1591 yield 'context', scanwhile(line, lambda l: l[0] in ' \\')
1591 elif line[0] in '-+':
1592 elif line[0] in '-+':
1592 yield 'hunk', scanwhile(line, lambda l: l[0] in '-+\\')
1593 yield 'hunk', scanwhile(line, lambda l: l[0] in '-+\\')
1593 else:
1594 else:
1594 m = lines_re.match(line)
1595 m = lines_re.match(line)
1595 if m:
1596 if m:
1596 yield 'range', m.groups()
1597 yield 'range', m.groups()
1597 else:
1598 else:
1598 yield 'other', line
1599 yield 'other', line
1599
1600
1600 def scangitpatch(lr, firstline):
1601 def scangitpatch(lr, firstline):
1601 """
1602 """
1602 Git patches can emit:
1603 Git patches can emit:
1603 - rename a to b
1604 - rename a to b
1604 - change b
1605 - change b
1605 - copy a to c
1606 - copy a to c
1606 - change c
1607 - change c
1607
1608
1608 We cannot apply this sequence as-is, the renamed 'a' could not be
1609 We cannot apply this sequence as-is, the renamed 'a' could not be
1609 found for it would have been renamed already. And we cannot copy
1610 found for it would have been renamed already. And we cannot copy
1610 from 'b' instead because 'b' would have been changed already. So
1611 from 'b' instead because 'b' would have been changed already. So
1611 we scan the git patch for copy and rename commands so we can
1612 we scan the git patch for copy and rename commands so we can
1612 perform the copies ahead of time.
1613 perform the copies ahead of time.
1613 """
1614 """
1614 pos = 0
1615 pos = 0
1615 try:
1616 try:
1616 pos = lr.fp.tell()
1617 pos = lr.fp.tell()
1617 fp = lr.fp
1618 fp = lr.fp
1618 except IOError:
1619 except IOError:
1619 fp = cStringIO.StringIO(lr.fp.read())
1620 fp = cStringIO.StringIO(lr.fp.read())
1620 gitlr = linereader(fp)
1621 gitlr = linereader(fp)
1621 gitlr.push(firstline)
1622 gitlr.push(firstline)
1622 gitpatches = readgitpatch(gitlr)
1623 gitpatches = readgitpatch(gitlr)
1623 fp.seek(pos)
1624 fp.seek(pos)
1624 return gitpatches
1625 return gitpatches
1625
1626
1626 def iterhunks(fp):
1627 def iterhunks(fp):
1627 """Read a patch and yield the following events:
1628 """Read a patch and yield the following events:
1628 - ("file", afile, bfile, firsthunk): select a new target file.
1629 - ("file", afile, bfile, firsthunk): select a new target file.
1629 - ("hunk", hunk): a new hunk is ready to be applied, follows a
1630 - ("hunk", hunk): a new hunk is ready to be applied, follows a
1630 "file" event.
1631 "file" event.
1631 - ("git", gitchanges): current diff is in git format, gitchanges
1632 - ("git", gitchanges): current diff is in git format, gitchanges
1632 maps filenames to gitpatch records. Unique event.
1633 maps filenames to gitpatch records. Unique event.
1633 """
1634 """
1634 afile = ""
1635 afile = ""
1635 bfile = ""
1636 bfile = ""
1636 state = None
1637 state = None
1637 hunknum = 0
1638 hunknum = 0
1638 emitfile = newfile = False
1639 emitfile = newfile = False
1639 gitpatches = None
1640 gitpatches = None
1640
1641
1641 # our states
1642 # our states
1642 BFILE = 1
1643 BFILE = 1
1643 context = None
1644 context = None
1644 lr = linereader(fp)
1645 lr = linereader(fp)
1645
1646
1646 while True:
1647 while True:
1647 x = lr.readline()
1648 x = lr.readline()
1648 if not x:
1649 if not x:
1649 break
1650 break
1650 if state == BFILE and (
1651 if state == BFILE and (
1651 (not context and x[0] == '@')
1652 (not context and x[0] == '@')
1652 or (context is not False and x.startswith('***************'))
1653 or (context is not False and x.startswith('***************'))
1653 or x.startswith('GIT binary patch')):
1654 or x.startswith('GIT binary patch')):
1654 gp = None
1655 gp = None
1655 if (gitpatches and
1656 if (gitpatches and
1656 gitpatches[-1].ispatching(afile, bfile)):
1657 gitpatches[-1].ispatching(afile, bfile)):
1657 gp = gitpatches.pop()
1658 gp = gitpatches.pop()
1658 if x.startswith('GIT binary patch'):
1659 if x.startswith('GIT binary patch'):
1659 h = binhunk(lr, gp.path)
1660 h = binhunk(lr, gp.path)
1660 else:
1661 else:
1661 if context is None and x.startswith('***************'):
1662 if context is None and x.startswith('***************'):
1662 context = True
1663 context = True
1663 h = hunk(x, hunknum + 1, lr, context)
1664 h = hunk(x, hunknum + 1, lr, context)
1664 hunknum += 1
1665 hunknum += 1
1665 if emitfile:
1666 if emitfile:
1666 emitfile = False
1667 emitfile = False
1667 yield 'file', (afile, bfile, h, gp and gp.copy() or None)
1668 yield 'file', (afile, bfile, h, gp and gp.copy() or None)
1668 yield 'hunk', h
1669 yield 'hunk', h
1669 elif x.startswith('diff --git a/'):
1670 elif x.startswith('diff --git a/'):
1670 m = gitre.match(x.rstrip(' \r\n'))
1671 m = gitre.match(x.rstrip(' \r\n'))
1671 if not m:
1672 if not m:
1672 continue
1673 continue
1673 if gitpatches is None:
1674 if gitpatches is None:
1674 # scan whole input for git metadata
1675 # scan whole input for git metadata
1675 gitpatches = scangitpatch(lr, x)
1676 gitpatches = scangitpatch(lr, x)
1676 yield 'git', [g.copy() for g in gitpatches
1677 yield 'git', [g.copy() for g in gitpatches
1677 if g.op in ('COPY', 'RENAME')]
1678 if g.op in ('COPY', 'RENAME')]
1678 gitpatches.reverse()
1679 gitpatches.reverse()
1679 afile = 'a/' + m.group(1)
1680 afile = 'a/' + m.group(1)
1680 bfile = 'b/' + m.group(2)
1681 bfile = 'b/' + m.group(2)
1681 while gitpatches and not gitpatches[-1].ispatching(afile, bfile):
1682 while gitpatches and not gitpatches[-1].ispatching(afile, bfile):
1682 gp = gitpatches.pop()
1683 gp = gitpatches.pop()
1683 yield 'file', ('a/' + gp.path, 'b/' + gp.path, None, gp.copy())
1684 yield 'file', ('a/' + gp.path, 'b/' + gp.path, None, gp.copy())
1684 if not gitpatches:
1685 if not gitpatches:
1685 raise PatchError(_('failed to synchronize metadata for "%s"')
1686 raise PatchError(_('failed to synchronize metadata for "%s"')
1686 % afile[2:])
1687 % afile[2:])
1687 gp = gitpatches[-1]
1688 gp = gitpatches[-1]
1688 newfile = True
1689 newfile = True
1689 elif x.startswith('---'):
1690 elif x.startswith('---'):
1690 # check for a unified diff
1691 # check for a unified diff
1691 l2 = lr.readline()
1692 l2 = lr.readline()
1692 if not l2.startswith('+++'):
1693 if not l2.startswith('+++'):
1693 lr.push(l2)
1694 lr.push(l2)
1694 continue
1695 continue
1695 newfile = True
1696 newfile = True
1696 context = False
1697 context = False
1697 afile = parsefilename(x)
1698 afile = parsefilename(x)
1698 bfile = parsefilename(l2)
1699 bfile = parsefilename(l2)
1699 elif x.startswith('***'):
1700 elif x.startswith('***'):
1700 # check for a context diff
1701 # check for a context diff
1701 l2 = lr.readline()
1702 l2 = lr.readline()
1702 if not l2.startswith('---'):
1703 if not l2.startswith('---'):
1703 lr.push(l2)
1704 lr.push(l2)
1704 continue
1705 continue
1705 l3 = lr.readline()
1706 l3 = lr.readline()
1706 lr.push(l3)
1707 lr.push(l3)
1707 if not l3.startswith("***************"):
1708 if not l3.startswith("***************"):
1708 lr.push(l2)
1709 lr.push(l2)
1709 continue
1710 continue
1710 newfile = True
1711 newfile = True
1711 context = True
1712 context = True
1712 afile = parsefilename(x)
1713 afile = parsefilename(x)
1713 bfile = parsefilename(l2)
1714 bfile = parsefilename(l2)
1714
1715
1715 if newfile:
1716 if newfile:
1716 newfile = False
1717 newfile = False
1717 emitfile = True
1718 emitfile = True
1718 state = BFILE
1719 state = BFILE
1719 hunknum = 0
1720 hunknum = 0
1720
1721
1721 while gitpatches:
1722 while gitpatches:
1722 gp = gitpatches.pop()
1723 gp = gitpatches.pop()
1723 yield 'file', ('a/' + gp.path, 'b/' + gp.path, None, gp.copy())
1724 yield 'file', ('a/' + gp.path, 'b/' + gp.path, None, gp.copy())
1724
1725
1725 def applybindelta(binchunk, data):
1726 def applybindelta(binchunk, data):
1726 """Apply a binary delta hunk
1727 """Apply a binary delta hunk
1727 The algorithm used is the algorithm from git's patch-delta.c
1728 The algorithm used is the algorithm from git's patch-delta.c
1728 """
1729 """
1729 def deltahead(binchunk):
1730 def deltahead(binchunk):
1730 i = 0
1731 i = 0
1731 for c in binchunk:
1732 for c in binchunk:
1732 i += 1
1733 i += 1
1733 if not (ord(c) & 0x80):
1734 if not (ord(c) & 0x80):
1734 return i
1735 return i
1735 return i
1736 return i
1736 out = ""
1737 out = ""
1737 s = deltahead(binchunk)
1738 s = deltahead(binchunk)
1738 binchunk = binchunk[s:]
1739 binchunk = binchunk[s:]
1739 s = deltahead(binchunk)
1740 s = deltahead(binchunk)
1740 binchunk = binchunk[s:]
1741 binchunk = binchunk[s:]
1741 i = 0
1742 i = 0
1742 while i < len(binchunk):
1743 while i < len(binchunk):
1743 cmd = ord(binchunk[i])
1744 cmd = ord(binchunk[i])
1744 i += 1
1745 i += 1
1745 if (cmd & 0x80):
1746 if (cmd & 0x80):
1746 offset = 0
1747 offset = 0
1747 size = 0
1748 size = 0
1748 if (cmd & 0x01):
1749 if (cmd & 0x01):
1749 offset = ord(binchunk[i])
1750 offset = ord(binchunk[i])
1750 i += 1
1751 i += 1
1751 if (cmd & 0x02):
1752 if (cmd & 0x02):
1752 offset |= ord(binchunk[i]) << 8
1753 offset |= ord(binchunk[i]) << 8
1753 i += 1
1754 i += 1
1754 if (cmd & 0x04):
1755 if (cmd & 0x04):
1755 offset |= ord(binchunk[i]) << 16
1756 offset |= ord(binchunk[i]) << 16
1756 i += 1
1757 i += 1
1757 if (cmd & 0x08):
1758 if (cmd & 0x08):
1758 offset |= ord(binchunk[i]) << 24
1759 offset |= ord(binchunk[i]) << 24
1759 i += 1
1760 i += 1
1760 if (cmd & 0x10):
1761 if (cmd & 0x10):
1761 size = ord(binchunk[i])
1762 size = ord(binchunk[i])
1762 i += 1
1763 i += 1
1763 if (cmd & 0x20):
1764 if (cmd & 0x20):
1764 size |= ord(binchunk[i]) << 8
1765 size |= ord(binchunk[i]) << 8
1765 i += 1
1766 i += 1
1766 if (cmd & 0x40):
1767 if (cmd & 0x40):
1767 size |= ord(binchunk[i]) << 16
1768 size |= ord(binchunk[i]) << 16
1768 i += 1
1769 i += 1
1769 if size == 0:
1770 if size == 0:
1770 size = 0x10000
1771 size = 0x10000
1771 offset_end = offset + size
1772 offset_end = offset + size
1772 out += data[offset:offset_end]
1773 out += data[offset:offset_end]
1773 elif cmd != 0:
1774 elif cmd != 0:
1774 offset_end = i + cmd
1775 offset_end = i + cmd
1775 out += binchunk[i:offset_end]
1776 out += binchunk[i:offset_end]
1776 i += cmd
1777 i += cmd
1777 else:
1778 else:
1778 raise PatchError(_('unexpected delta opcode 0'))
1779 raise PatchError(_('unexpected delta opcode 0'))
1779 return out
1780 return out
1780
1781
1781 def applydiff(ui, fp, backend, store, strip=1, prefix='', eolmode='strict'):
1782 def applydiff(ui, fp, backend, store, strip=1, prefix='', eolmode='strict'):
1782 """Reads a patch from fp and tries to apply it.
1783 """Reads a patch from fp and tries to apply it.
1783
1784
1784 Returns 0 for a clean patch, -1 if any rejects were found and 1 if
1785 Returns 0 for a clean patch, -1 if any rejects were found and 1 if
1785 there was any fuzz.
1786 there was any fuzz.
1786
1787
1787 If 'eolmode' is 'strict', the patch content and patched file are
1788 If 'eolmode' is 'strict', the patch content and patched file are
1788 read in binary mode. Otherwise, line endings are ignored when
1789 read in binary mode. Otherwise, line endings are ignored when
1789 patching then normalized according to 'eolmode'.
1790 patching then normalized according to 'eolmode'.
1790 """
1791 """
1791 return _applydiff(ui, fp, patchfile, backend, store, strip=strip,
1792 return _applydiff(ui, fp, patchfile, backend, store, strip=strip,
1792 prefix=prefix, eolmode=eolmode)
1793 prefix=prefix, eolmode=eolmode)
1793
1794
1794 def _applydiff(ui, fp, patcher, backend, store, strip=1, prefix='',
1795 def _applydiff(ui, fp, patcher, backend, store, strip=1, prefix='',
1795 eolmode='strict'):
1796 eolmode='strict'):
1796
1797
1797 if prefix:
1798 if prefix:
1798 # clean up double slashes, lack of trailing slashes, etc
1799 prefix = pathutil.canonpath(backend.repo.root, backend.repo.getcwd(),
1799 prefix = util.normpath(prefix) + '/'
1800 prefix)
1801 if prefix != '':
1802 prefix += '/'
1800 def pstrip(p):
1803 def pstrip(p):
1801 return pathtransform(p, strip - 1, prefix)[1]
1804 return pathtransform(p, strip - 1, prefix)[1]
1802
1805
1803 rejects = 0
1806 rejects = 0
1804 err = 0
1807 err = 0
1805 current_file = None
1808 current_file = None
1806
1809
1807 for state, values in iterhunks(fp):
1810 for state, values in iterhunks(fp):
1808 if state == 'hunk':
1811 if state == 'hunk':
1809 if not current_file:
1812 if not current_file:
1810 continue
1813 continue
1811 ret = current_file.apply(values)
1814 ret = current_file.apply(values)
1812 if ret > 0:
1815 if ret > 0:
1813 err = 1
1816 err = 1
1814 elif state == 'file':
1817 elif state == 'file':
1815 if current_file:
1818 if current_file:
1816 rejects += current_file.close()
1819 rejects += current_file.close()
1817 current_file = None
1820 current_file = None
1818 afile, bfile, first_hunk, gp = values
1821 afile, bfile, first_hunk, gp = values
1819 if gp:
1822 if gp:
1820 gp.path = pstrip(gp.path)
1823 gp.path = pstrip(gp.path)
1821 if gp.oldpath:
1824 if gp.oldpath:
1822 gp.oldpath = pstrip(gp.oldpath)
1825 gp.oldpath = pstrip(gp.oldpath)
1823 else:
1826 else:
1824 gp = makepatchmeta(backend, afile, bfile, first_hunk, strip,
1827 gp = makepatchmeta(backend, afile, bfile, first_hunk, strip,
1825 prefix)
1828 prefix)
1826 if gp.op == 'RENAME':
1829 if gp.op == 'RENAME':
1827 backend.unlink(gp.oldpath)
1830 backend.unlink(gp.oldpath)
1828 if not first_hunk:
1831 if not first_hunk:
1829 if gp.op == 'DELETE':
1832 if gp.op == 'DELETE':
1830 backend.unlink(gp.path)
1833 backend.unlink(gp.path)
1831 continue
1834 continue
1832 data, mode = None, None
1835 data, mode = None, None
1833 if gp.op in ('RENAME', 'COPY'):
1836 if gp.op in ('RENAME', 'COPY'):
1834 data, mode = store.getfile(gp.oldpath)[:2]
1837 data, mode = store.getfile(gp.oldpath)[:2]
1835 # FIXME: failing getfile has never been handled here
1838 # FIXME: failing getfile has never been handled here
1836 assert data is not None
1839 assert data is not None
1837 if gp.mode:
1840 if gp.mode:
1838 mode = gp.mode
1841 mode = gp.mode
1839 if gp.op == 'ADD':
1842 if gp.op == 'ADD':
1840 # Added files without content have no hunk and
1843 # Added files without content have no hunk and
1841 # must be created
1844 # must be created
1842 data = ''
1845 data = ''
1843 if data or mode:
1846 if data or mode:
1844 if (gp.op in ('ADD', 'RENAME', 'COPY')
1847 if (gp.op in ('ADD', 'RENAME', 'COPY')
1845 and backend.exists(gp.path)):
1848 and backend.exists(gp.path)):
1846 raise PatchError(_("cannot create %s: destination "
1849 raise PatchError(_("cannot create %s: destination "
1847 "already exists") % gp.path)
1850 "already exists") % gp.path)
1848 backend.setfile(gp.path, data, mode, gp.oldpath)
1851 backend.setfile(gp.path, data, mode, gp.oldpath)
1849 continue
1852 continue
1850 try:
1853 try:
1851 current_file = patcher(ui, gp, backend, store,
1854 current_file = patcher(ui, gp, backend, store,
1852 eolmode=eolmode)
1855 eolmode=eolmode)
1853 except PatchError, inst:
1856 except PatchError, inst:
1854 ui.warn(str(inst) + '\n')
1857 ui.warn(str(inst) + '\n')
1855 current_file = None
1858 current_file = None
1856 rejects += 1
1859 rejects += 1
1857 continue
1860 continue
1858 elif state == 'git':
1861 elif state == 'git':
1859 for gp in values:
1862 for gp in values:
1860 path = pstrip(gp.oldpath)
1863 path = pstrip(gp.oldpath)
1861 data, mode = backend.getfile(path)
1864 data, mode = backend.getfile(path)
1862 if data is None:
1865 if data is None:
1863 # The error ignored here will trigger a getfile()
1866 # The error ignored here will trigger a getfile()
1864 # error in a place more appropriate for error
1867 # error in a place more appropriate for error
1865 # handling, and will not interrupt the patching
1868 # handling, and will not interrupt the patching
1866 # process.
1869 # process.
1867 pass
1870 pass
1868 else:
1871 else:
1869 store.setfile(path, data, mode)
1872 store.setfile(path, data, mode)
1870 else:
1873 else:
1871 raise util.Abort(_('unsupported parser state: %s') % state)
1874 raise util.Abort(_('unsupported parser state: %s') % state)
1872
1875
1873 if current_file:
1876 if current_file:
1874 rejects += current_file.close()
1877 rejects += current_file.close()
1875
1878
1876 if rejects:
1879 if rejects:
1877 return -1
1880 return -1
1878 return err
1881 return err
1879
1882
1880 def _externalpatch(ui, repo, patcher, patchname, strip, files,
1883 def _externalpatch(ui, repo, patcher, patchname, strip, files,
1881 similarity):
1884 similarity):
1882 """use <patcher> to apply <patchname> to the working directory.
1885 """use <patcher> to apply <patchname> to the working directory.
1883 returns whether patch was applied with fuzz factor."""
1886 returns whether patch was applied with fuzz factor."""
1884
1887
1885 fuzz = False
1888 fuzz = False
1886 args = []
1889 args = []
1887 cwd = repo.root
1890 cwd = repo.root
1888 if cwd:
1891 if cwd:
1889 args.append('-d %s' % util.shellquote(cwd))
1892 args.append('-d %s' % util.shellquote(cwd))
1890 fp = util.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
1893 fp = util.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
1891 util.shellquote(patchname)))
1894 util.shellquote(patchname)))
1892 try:
1895 try:
1893 for line in fp:
1896 for line in fp:
1894 line = line.rstrip()
1897 line = line.rstrip()
1895 ui.note(line + '\n')
1898 ui.note(line + '\n')
1896 if line.startswith('patching file '):
1899 if line.startswith('patching file '):
1897 pf = util.parsepatchoutput(line)
1900 pf = util.parsepatchoutput(line)
1898 printed_file = False
1901 printed_file = False
1899 files.add(pf)
1902 files.add(pf)
1900 elif line.find('with fuzz') >= 0:
1903 elif line.find('with fuzz') >= 0:
1901 fuzz = True
1904 fuzz = True
1902 if not printed_file:
1905 if not printed_file:
1903 ui.warn(pf + '\n')
1906 ui.warn(pf + '\n')
1904 printed_file = True
1907 printed_file = True
1905 ui.warn(line + '\n')
1908 ui.warn(line + '\n')
1906 elif line.find('saving rejects to file') >= 0:
1909 elif line.find('saving rejects to file') >= 0:
1907 ui.warn(line + '\n')
1910 ui.warn(line + '\n')
1908 elif line.find('FAILED') >= 0:
1911 elif line.find('FAILED') >= 0:
1909 if not printed_file:
1912 if not printed_file:
1910 ui.warn(pf + '\n')
1913 ui.warn(pf + '\n')
1911 printed_file = True
1914 printed_file = True
1912 ui.warn(line + '\n')
1915 ui.warn(line + '\n')
1913 finally:
1916 finally:
1914 if files:
1917 if files:
1915 scmutil.marktouched(repo, files, similarity)
1918 scmutil.marktouched(repo, files, similarity)
1916 code = fp.close()
1919 code = fp.close()
1917 if code:
1920 if code:
1918 raise PatchError(_("patch command failed: %s") %
1921 raise PatchError(_("patch command failed: %s") %
1919 util.explainexit(code)[0])
1922 util.explainexit(code)[0])
1920 return fuzz
1923 return fuzz
1921
1924
1922 def patchbackend(ui, backend, patchobj, strip, prefix, files=None,
1925 def patchbackend(ui, backend, patchobj, strip, prefix, files=None,
1923 eolmode='strict'):
1926 eolmode='strict'):
1924 if files is None:
1927 if files is None:
1925 files = set()
1928 files = set()
1926 if eolmode is None:
1929 if eolmode is None:
1927 eolmode = ui.config('patch', 'eol', 'strict')
1930 eolmode = ui.config('patch', 'eol', 'strict')
1928 if eolmode.lower() not in eolmodes:
1931 if eolmode.lower() not in eolmodes:
1929 raise util.Abort(_('unsupported line endings type: %s') % eolmode)
1932 raise util.Abort(_('unsupported line endings type: %s') % eolmode)
1930 eolmode = eolmode.lower()
1933 eolmode = eolmode.lower()
1931
1934
1932 store = filestore()
1935 store = filestore()
1933 try:
1936 try:
1934 fp = open(patchobj, 'rb')
1937 fp = open(patchobj, 'rb')
1935 except TypeError:
1938 except TypeError:
1936 fp = patchobj
1939 fp = patchobj
1937 try:
1940 try:
1938 ret = applydiff(ui, fp, backend, store, strip=strip, prefix=prefix,
1941 ret = applydiff(ui, fp, backend, store, strip=strip, prefix=prefix,
1939 eolmode=eolmode)
1942 eolmode=eolmode)
1940 finally:
1943 finally:
1941 if fp != patchobj:
1944 if fp != patchobj:
1942 fp.close()
1945 fp.close()
1943 files.update(backend.close())
1946 files.update(backend.close())
1944 store.close()
1947 store.close()
1945 if ret < 0:
1948 if ret < 0:
1946 raise PatchError(_('patch failed to apply'))
1949 raise PatchError(_('patch failed to apply'))
1947 return ret > 0
1950 return ret > 0
1948
1951
1949 def internalpatch(ui, repo, patchobj, strip, prefix='', files=None,
1952 def internalpatch(ui, repo, patchobj, strip, prefix='', files=None,
1950 eolmode='strict', similarity=0):
1953 eolmode='strict', similarity=0):
1951 """use builtin patch to apply <patchobj> to the working directory.
1954 """use builtin patch to apply <patchobj> to the working directory.
1952 returns whether patch was applied with fuzz factor."""
1955 returns whether patch was applied with fuzz factor."""
1953 backend = workingbackend(ui, repo, similarity)
1956 backend = workingbackend(ui, repo, similarity)
1954 return patchbackend(ui, backend, patchobj, strip, prefix, files, eolmode)
1957 return patchbackend(ui, backend, patchobj, strip, prefix, files, eolmode)
1955
1958
1956 def patchrepo(ui, repo, ctx, store, patchobj, strip, prefix, files=None,
1959 def patchrepo(ui, repo, ctx, store, patchobj, strip, prefix, files=None,
1957 eolmode='strict'):
1960 eolmode='strict'):
1958 backend = repobackend(ui, repo, ctx, store)
1961 backend = repobackend(ui, repo, ctx, store)
1959 return patchbackend(ui, backend, patchobj, strip, prefix, files, eolmode)
1962 return patchbackend(ui, backend, patchobj, strip, prefix, files, eolmode)
1960
1963
1961 def patch(ui, repo, patchname, strip=1, prefix='', files=None, eolmode='strict',
1964 def patch(ui, repo, patchname, strip=1, prefix='', files=None, eolmode='strict',
1962 similarity=0):
1965 similarity=0):
1963 """Apply <patchname> to the working directory.
1966 """Apply <patchname> to the working directory.
1964
1967
1965 'eolmode' specifies how end of lines should be handled. It can be:
1968 'eolmode' specifies how end of lines should be handled. It can be:
1966 - 'strict': inputs are read in binary mode, EOLs are preserved
1969 - 'strict': inputs are read in binary mode, EOLs are preserved
1967 - 'crlf': EOLs are ignored when patching and reset to CRLF
1970 - 'crlf': EOLs are ignored when patching and reset to CRLF
1968 - 'lf': EOLs are ignored when patching and reset to LF
1971 - 'lf': EOLs are ignored when patching and reset to LF
1969 - None: get it from user settings, default to 'strict'
1972 - None: get it from user settings, default to 'strict'
1970 'eolmode' is ignored when using an external patcher program.
1973 'eolmode' is ignored when using an external patcher program.
1971
1974
1972 Returns whether patch was applied with fuzz factor.
1975 Returns whether patch was applied with fuzz factor.
1973 """
1976 """
1974 patcher = ui.config('ui', 'patch')
1977 patcher = ui.config('ui', 'patch')
1975 if files is None:
1978 if files is None:
1976 files = set()
1979 files = set()
1977 if patcher:
1980 if patcher:
1978 return _externalpatch(ui, repo, patcher, patchname, strip,
1981 return _externalpatch(ui, repo, patcher, patchname, strip,
1979 files, similarity)
1982 files, similarity)
1980 return internalpatch(ui, repo, patchname, strip, prefix, files, eolmode,
1983 return internalpatch(ui, repo, patchname, strip, prefix, files, eolmode,
1981 similarity)
1984 similarity)
1982
1985
1983 def changedfiles(ui, repo, patchpath, strip=1):
1986 def changedfiles(ui, repo, patchpath, strip=1):
1984 backend = fsbackend(ui, repo.root)
1987 backend = fsbackend(ui, repo.root)
1985 fp = open(patchpath, 'rb')
1988 fp = open(patchpath, 'rb')
1986 try:
1989 try:
1987 changed = set()
1990 changed = set()
1988 for state, values in iterhunks(fp):
1991 for state, values in iterhunks(fp):
1989 if state == 'file':
1992 if state == 'file':
1990 afile, bfile, first_hunk, gp = values
1993 afile, bfile, first_hunk, gp = values
1991 if gp:
1994 if gp:
1992 gp.path = pathtransform(gp.path, strip - 1, '')[1]
1995 gp.path = pathtransform(gp.path, strip - 1, '')[1]
1993 if gp.oldpath:
1996 if gp.oldpath:
1994 gp.oldpath = pathtransform(gp.oldpath, strip - 1, '')[1]
1997 gp.oldpath = pathtransform(gp.oldpath, strip - 1, '')[1]
1995 else:
1998 else:
1996 gp = makepatchmeta(backend, afile, bfile, first_hunk, strip,
1999 gp = makepatchmeta(backend, afile, bfile, first_hunk, strip,
1997 '')
2000 '')
1998 changed.add(gp.path)
2001 changed.add(gp.path)
1999 if gp.op == 'RENAME':
2002 if gp.op == 'RENAME':
2000 changed.add(gp.oldpath)
2003 changed.add(gp.oldpath)
2001 elif state not in ('hunk', 'git'):
2004 elif state not in ('hunk', 'git'):
2002 raise util.Abort(_('unsupported parser state: %s') % state)
2005 raise util.Abort(_('unsupported parser state: %s') % state)
2003 return changed
2006 return changed
2004 finally:
2007 finally:
2005 fp.close()
2008 fp.close()
2006
2009
2007 class GitDiffRequired(Exception):
2010 class GitDiffRequired(Exception):
2008 pass
2011 pass
2009
2012
2010 def diffallopts(ui, opts=None, untrusted=False, section='diff'):
2013 def diffallopts(ui, opts=None, untrusted=False, section='diff'):
2011 '''return diffopts with all features supported and parsed'''
2014 '''return diffopts with all features supported and parsed'''
2012 return difffeatureopts(ui, opts=opts, untrusted=untrusted, section=section,
2015 return difffeatureopts(ui, opts=opts, untrusted=untrusted, section=section,
2013 git=True, whitespace=True, formatchanging=True)
2016 git=True, whitespace=True, formatchanging=True)
2014
2017
2015 diffopts = diffallopts
2018 diffopts = diffallopts
2016
2019
2017 def difffeatureopts(ui, opts=None, untrusted=False, section='diff', git=False,
2020 def difffeatureopts(ui, opts=None, untrusted=False, section='diff', git=False,
2018 whitespace=False, formatchanging=False):
2021 whitespace=False, formatchanging=False):
2019 '''return diffopts with only opted-in features parsed
2022 '''return diffopts with only opted-in features parsed
2020
2023
2021 Features:
2024 Features:
2022 - git: git-style diffs
2025 - git: git-style diffs
2023 - whitespace: whitespace options like ignoreblanklines and ignorews
2026 - whitespace: whitespace options like ignoreblanklines and ignorews
2024 - formatchanging: options that will likely break or cause correctness issues
2027 - formatchanging: options that will likely break or cause correctness issues
2025 with most diff parsers
2028 with most diff parsers
2026 '''
2029 '''
2027 def get(key, name=None, getter=ui.configbool, forceplain=None):
2030 def get(key, name=None, getter=ui.configbool, forceplain=None):
2028 if opts:
2031 if opts:
2029 v = opts.get(key)
2032 v = opts.get(key)
2030 if v:
2033 if v:
2031 return v
2034 return v
2032 if forceplain is not None and ui.plain():
2035 if forceplain is not None and ui.plain():
2033 return forceplain
2036 return forceplain
2034 return getter(section, name or key, None, untrusted=untrusted)
2037 return getter(section, name or key, None, untrusted=untrusted)
2035
2038
2036 # core options, expected to be understood by every diff parser
2039 # core options, expected to be understood by every diff parser
2037 buildopts = {
2040 buildopts = {
2038 'nodates': get('nodates'),
2041 'nodates': get('nodates'),
2039 'showfunc': get('show_function', 'showfunc'),
2042 'showfunc': get('show_function', 'showfunc'),
2040 'context': get('unified', getter=ui.config),
2043 'context': get('unified', getter=ui.config),
2041 }
2044 }
2042
2045
2043 if git:
2046 if git:
2044 buildopts['git'] = get('git')
2047 buildopts['git'] = get('git')
2045 if whitespace:
2048 if whitespace:
2046 buildopts['ignorews'] = get('ignore_all_space', 'ignorews')
2049 buildopts['ignorews'] = get('ignore_all_space', 'ignorews')
2047 buildopts['ignorewsamount'] = get('ignore_space_change',
2050 buildopts['ignorewsamount'] = get('ignore_space_change',
2048 'ignorewsamount')
2051 'ignorewsamount')
2049 buildopts['ignoreblanklines'] = get('ignore_blank_lines',
2052 buildopts['ignoreblanklines'] = get('ignore_blank_lines',
2050 'ignoreblanklines')
2053 'ignoreblanklines')
2051 if formatchanging:
2054 if formatchanging:
2052 buildopts['text'] = opts and opts.get('text')
2055 buildopts['text'] = opts and opts.get('text')
2053 buildopts['nobinary'] = get('nobinary')
2056 buildopts['nobinary'] = get('nobinary')
2054 buildopts['noprefix'] = get('noprefix', forceplain=False)
2057 buildopts['noprefix'] = get('noprefix', forceplain=False)
2055
2058
2056 return mdiff.diffopts(**buildopts)
2059 return mdiff.diffopts(**buildopts)
2057
2060
2058 def diff(repo, node1=None, node2=None, match=None, changes=None, opts=None,
2061 def diff(repo, node1=None, node2=None, match=None, changes=None, opts=None,
2059 losedatafn=None, prefix=''):
2062 losedatafn=None, prefix=''):
2060 '''yields diff of changes to files between two nodes, or node and
2063 '''yields diff of changes to files between two nodes, or node and
2061 working directory.
2064 working directory.
2062
2065
2063 if node1 is None, use first dirstate parent instead.
2066 if node1 is None, use first dirstate parent instead.
2064 if node2 is None, compare node1 with working directory.
2067 if node2 is None, compare node1 with working directory.
2065
2068
2066 losedatafn(**kwarg) is a callable run when opts.upgrade=True and
2069 losedatafn(**kwarg) is a callable run when opts.upgrade=True and
2067 every time some change cannot be represented with the current
2070 every time some change cannot be represented with the current
2068 patch format. Return False to upgrade to git patch format, True to
2071 patch format. Return False to upgrade to git patch format, True to
2069 accept the loss or raise an exception to abort the diff. It is
2072 accept the loss or raise an exception to abort the diff. It is
2070 called with the name of current file being diffed as 'fn'. If set
2073 called with the name of current file being diffed as 'fn'. If set
2071 to None, patches will always be upgraded to git format when
2074 to None, patches will always be upgraded to git format when
2072 necessary.
2075 necessary.
2073
2076
2074 prefix is a filename prefix that is prepended to all filenames on
2077 prefix is a filename prefix that is prepended to all filenames on
2075 display (used for subrepos).
2078 display (used for subrepos).
2076 '''
2079 '''
2077
2080
2078 if opts is None:
2081 if opts is None:
2079 opts = mdiff.defaultopts
2082 opts = mdiff.defaultopts
2080
2083
2081 if not node1 and not node2:
2084 if not node1 and not node2:
2082 node1 = repo.dirstate.p1()
2085 node1 = repo.dirstate.p1()
2083
2086
2084 def lrugetfilectx():
2087 def lrugetfilectx():
2085 cache = {}
2088 cache = {}
2086 order = util.deque()
2089 order = util.deque()
2087 def getfilectx(f, ctx):
2090 def getfilectx(f, ctx):
2088 fctx = ctx.filectx(f, filelog=cache.get(f))
2091 fctx = ctx.filectx(f, filelog=cache.get(f))
2089 if f not in cache:
2092 if f not in cache:
2090 if len(cache) > 20:
2093 if len(cache) > 20:
2091 del cache[order.popleft()]
2094 del cache[order.popleft()]
2092 cache[f] = fctx.filelog()
2095 cache[f] = fctx.filelog()
2093 else:
2096 else:
2094 order.remove(f)
2097 order.remove(f)
2095 order.append(f)
2098 order.append(f)
2096 return fctx
2099 return fctx
2097 return getfilectx
2100 return getfilectx
2098 getfilectx = lrugetfilectx()
2101 getfilectx = lrugetfilectx()
2099
2102
2100 ctx1 = repo[node1]
2103 ctx1 = repo[node1]
2101 ctx2 = repo[node2]
2104 ctx2 = repo[node2]
2102
2105
2103 if not changes:
2106 if not changes:
2104 changes = repo.status(ctx1, ctx2, match=match)
2107 changes = repo.status(ctx1, ctx2, match=match)
2105 modified, added, removed = changes[:3]
2108 modified, added, removed = changes[:3]
2106
2109
2107 if not modified and not added and not removed:
2110 if not modified and not added and not removed:
2108 return []
2111 return []
2109
2112
2110 if repo.ui.debugflag:
2113 if repo.ui.debugflag:
2111 hexfunc = hex
2114 hexfunc = hex
2112 else:
2115 else:
2113 hexfunc = short
2116 hexfunc = short
2114 revs = [hexfunc(node) for node in [ctx1.node(), ctx2.node()] if node]
2117 revs = [hexfunc(node) for node in [ctx1.node(), ctx2.node()] if node]
2115
2118
2116 copy = {}
2119 copy = {}
2117 if opts.git or opts.upgrade:
2120 if opts.git or opts.upgrade:
2118 copy = copies.pathcopies(ctx1, ctx2)
2121 copy = copies.pathcopies(ctx1, ctx2)
2119
2122
2120 def difffn(opts, losedata):
2123 def difffn(opts, losedata):
2121 return trydiff(repo, revs, ctx1, ctx2, modified, added, removed,
2124 return trydiff(repo, revs, ctx1, ctx2, modified, added, removed,
2122 copy, getfilectx, opts, losedata, prefix)
2125 copy, getfilectx, opts, losedata, prefix)
2123 if opts.upgrade and not opts.git:
2126 if opts.upgrade and not opts.git:
2124 try:
2127 try:
2125 def losedata(fn):
2128 def losedata(fn):
2126 if not losedatafn or not losedatafn(fn=fn):
2129 if not losedatafn or not losedatafn(fn=fn):
2127 raise GitDiffRequired
2130 raise GitDiffRequired
2128 # Buffer the whole output until we are sure it can be generated
2131 # Buffer the whole output until we are sure it can be generated
2129 return list(difffn(opts.copy(git=False), losedata))
2132 return list(difffn(opts.copy(git=False), losedata))
2130 except GitDiffRequired:
2133 except GitDiffRequired:
2131 return difffn(opts.copy(git=True), None)
2134 return difffn(opts.copy(git=True), None)
2132 else:
2135 else:
2133 return difffn(opts, None)
2136 return difffn(opts, None)
2134
2137
2135 def difflabel(func, *args, **kw):
2138 def difflabel(func, *args, **kw):
2136 '''yields 2-tuples of (output, label) based on the output of func()'''
2139 '''yields 2-tuples of (output, label) based on the output of func()'''
2137 headprefixes = [('diff', 'diff.diffline'),
2140 headprefixes = [('diff', 'diff.diffline'),
2138 ('copy', 'diff.extended'),
2141 ('copy', 'diff.extended'),
2139 ('rename', 'diff.extended'),
2142 ('rename', 'diff.extended'),
2140 ('old', 'diff.extended'),
2143 ('old', 'diff.extended'),
2141 ('new', 'diff.extended'),
2144 ('new', 'diff.extended'),
2142 ('deleted', 'diff.extended'),
2145 ('deleted', 'diff.extended'),
2143 ('---', 'diff.file_a'),
2146 ('---', 'diff.file_a'),
2144 ('+++', 'diff.file_b')]
2147 ('+++', 'diff.file_b')]
2145 textprefixes = [('@', 'diff.hunk'),
2148 textprefixes = [('@', 'diff.hunk'),
2146 ('-', 'diff.deleted'),
2149 ('-', 'diff.deleted'),
2147 ('+', 'diff.inserted')]
2150 ('+', 'diff.inserted')]
2148 head = False
2151 head = False
2149 for chunk in func(*args, **kw):
2152 for chunk in func(*args, **kw):
2150 lines = chunk.split('\n')
2153 lines = chunk.split('\n')
2151 for i, line in enumerate(lines):
2154 for i, line in enumerate(lines):
2152 if i != 0:
2155 if i != 0:
2153 yield ('\n', '')
2156 yield ('\n', '')
2154 if head:
2157 if head:
2155 if line.startswith('@'):
2158 if line.startswith('@'):
2156 head = False
2159 head = False
2157 else:
2160 else:
2158 if line and line[0] not in ' +-@\\':
2161 if line and line[0] not in ' +-@\\':
2159 head = True
2162 head = True
2160 stripline = line
2163 stripline = line
2161 diffline = False
2164 diffline = False
2162 if not head and line and line[0] in '+-':
2165 if not head and line and line[0] in '+-':
2163 # highlight tabs and trailing whitespace, but only in
2166 # highlight tabs and trailing whitespace, but only in
2164 # changed lines
2167 # changed lines
2165 stripline = line.rstrip()
2168 stripline = line.rstrip()
2166 diffline = True
2169 diffline = True
2167
2170
2168 prefixes = textprefixes
2171 prefixes = textprefixes
2169 if head:
2172 if head:
2170 prefixes = headprefixes
2173 prefixes = headprefixes
2171 for prefix, label in prefixes:
2174 for prefix, label in prefixes:
2172 if stripline.startswith(prefix):
2175 if stripline.startswith(prefix):
2173 if diffline:
2176 if diffline:
2174 for token in tabsplitter.findall(stripline):
2177 for token in tabsplitter.findall(stripline):
2175 if '\t' == token[0]:
2178 if '\t' == token[0]:
2176 yield (token, 'diff.tab')
2179 yield (token, 'diff.tab')
2177 else:
2180 else:
2178 yield (token, label)
2181 yield (token, label)
2179 else:
2182 else:
2180 yield (stripline, label)
2183 yield (stripline, label)
2181 break
2184 break
2182 else:
2185 else:
2183 yield (line, '')
2186 yield (line, '')
2184 if line != stripline:
2187 if line != stripline:
2185 yield (line[len(stripline):], 'diff.trailingwhitespace')
2188 yield (line[len(stripline):], 'diff.trailingwhitespace')
2186
2189
2187 def diffui(*args, **kw):
2190 def diffui(*args, **kw):
2188 '''like diff(), but yields 2-tuples of (output, label) for ui.write()'''
2191 '''like diff(), but yields 2-tuples of (output, label) for ui.write()'''
2189 return difflabel(diff, *args, **kw)
2192 return difflabel(diff, *args, **kw)
2190
2193
2191 def _filepairs(ctx1, modified, added, removed, copy, opts):
2194 def _filepairs(ctx1, modified, added, removed, copy, opts):
2192 '''generates tuples (f1, f2, copyop), where f1 is the name of the file
2195 '''generates tuples (f1, f2, copyop), where f1 is the name of the file
2193 before and f2 is the the name after. For added files, f1 will be None,
2196 before and f2 is the the name after. For added files, f1 will be None,
2194 and for removed files, f2 will be None. copyop may be set to None, 'copy'
2197 and for removed files, f2 will be None. copyop may be set to None, 'copy'
2195 or 'rename' (the latter two only if opts.git is set).'''
2198 or 'rename' (the latter two only if opts.git is set).'''
2196 gone = set()
2199 gone = set()
2197
2200
2198 copyto = dict([(v, k) for k, v in copy.items()])
2201 copyto = dict([(v, k) for k, v in copy.items()])
2199
2202
2200 addedset, removedset = set(added), set(removed)
2203 addedset, removedset = set(added), set(removed)
2201 # Fix up added, since merged-in additions appear as
2204 # Fix up added, since merged-in additions appear as
2202 # modifications during merges
2205 # modifications during merges
2203 for f in modified:
2206 for f in modified:
2204 if f not in ctx1:
2207 if f not in ctx1:
2205 addedset.add(f)
2208 addedset.add(f)
2206
2209
2207 for f in sorted(modified + added + removed):
2210 for f in sorted(modified + added + removed):
2208 copyop = None
2211 copyop = None
2209 f1, f2 = f, f
2212 f1, f2 = f, f
2210 if f in addedset:
2213 if f in addedset:
2211 f1 = None
2214 f1 = None
2212 if f in copy:
2215 if f in copy:
2213 if opts.git:
2216 if opts.git:
2214 f1 = copy[f]
2217 f1 = copy[f]
2215 if f1 in removedset and f1 not in gone:
2218 if f1 in removedset and f1 not in gone:
2216 copyop = 'rename'
2219 copyop = 'rename'
2217 gone.add(f1)
2220 gone.add(f1)
2218 else:
2221 else:
2219 copyop = 'copy'
2222 copyop = 'copy'
2220 elif f in removedset:
2223 elif f in removedset:
2221 f2 = None
2224 f2 = None
2222 if opts.git:
2225 if opts.git:
2223 # have we already reported a copy above?
2226 # have we already reported a copy above?
2224 if (f in copyto and copyto[f] in addedset
2227 if (f in copyto and copyto[f] in addedset
2225 and copy[copyto[f]] == f):
2228 and copy[copyto[f]] == f):
2226 continue
2229 continue
2227 yield f1, f2, copyop
2230 yield f1, f2, copyop
2228
2231
2229 def trydiff(repo, revs, ctx1, ctx2, modified, added, removed,
2232 def trydiff(repo, revs, ctx1, ctx2, modified, added, removed,
2230 copy, getfilectx, opts, losedatafn, prefix):
2233 copy, getfilectx, opts, losedatafn, prefix):
2231 '''given input data, generate a diff and yield it in blocks
2234 '''given input data, generate a diff and yield it in blocks
2232
2235
2233 If generating a diff would lose data like flags or binary data and
2236 If generating a diff would lose data like flags or binary data and
2234 losedatafn is not None, it will be called.
2237 losedatafn is not None, it will be called.
2235
2238
2236 prefix is added to every path in the diff output.'''
2239 prefix is added to every path in the diff output.'''
2237
2240
2238 def gitindex(text):
2241 def gitindex(text):
2239 if not text:
2242 if not text:
2240 text = ""
2243 text = ""
2241 l = len(text)
2244 l = len(text)
2242 s = util.sha1('blob %d\0' % l)
2245 s = util.sha1('blob %d\0' % l)
2243 s.update(text)
2246 s.update(text)
2244 return s.hexdigest()
2247 return s.hexdigest()
2245
2248
2246 if opts.noprefix:
2249 if opts.noprefix:
2247 aprefix = bprefix = ''
2250 aprefix = bprefix = ''
2248 else:
2251 else:
2249 aprefix = 'a/'
2252 aprefix = 'a/'
2250 bprefix = 'b/'
2253 bprefix = 'b/'
2251
2254
2252 def diffline(f, revs):
2255 def diffline(f, revs):
2253 revinfo = ' '.join(["-r %s" % rev for rev in revs])
2256 revinfo = ' '.join(["-r %s" % rev for rev in revs])
2254 return 'diff %s %s' % (revinfo, f)
2257 return 'diff %s %s' % (revinfo, f)
2255
2258
2256 date1 = util.datestr(ctx1.date())
2259 date1 = util.datestr(ctx1.date())
2257 date2 = util.datestr(ctx2.date())
2260 date2 = util.datestr(ctx2.date())
2258
2261
2259 gitmode = {'l': '120000', 'x': '100755', '': '100644'}
2262 gitmode = {'l': '120000', 'x': '100755', '': '100644'}
2260
2263
2261 for f1, f2, copyop in _filepairs(
2264 for f1, f2, copyop in _filepairs(
2262 ctx1, modified, added, removed, copy, opts):
2265 ctx1, modified, added, removed, copy, opts):
2263 content1 = None
2266 content1 = None
2264 content2 = None
2267 content2 = None
2265 flag1 = None
2268 flag1 = None
2266 flag2 = None
2269 flag2 = None
2267 if f1:
2270 if f1:
2268 content1 = getfilectx(f1, ctx1).data()
2271 content1 = getfilectx(f1, ctx1).data()
2269 if opts.git or losedatafn:
2272 if opts.git or losedatafn:
2270 flag1 = ctx1.flags(f1)
2273 flag1 = ctx1.flags(f1)
2271 if f2:
2274 if f2:
2272 content2 = getfilectx(f2, ctx2).data()
2275 content2 = getfilectx(f2, ctx2).data()
2273 if opts.git or losedatafn:
2276 if opts.git or losedatafn:
2274 flag2 = ctx2.flags(f2)
2277 flag2 = ctx2.flags(f2)
2275 binary = False
2278 binary = False
2276 if opts.git or losedatafn:
2279 if opts.git or losedatafn:
2277 binary = util.binary(content1) or util.binary(content2)
2280 binary = util.binary(content1) or util.binary(content2)
2278
2281
2279 if losedatafn and not opts.git:
2282 if losedatafn and not opts.git:
2280 if (binary or
2283 if (binary or
2281 # copy/rename
2284 # copy/rename
2282 f2 in copy or
2285 f2 in copy or
2283 # empty file creation
2286 # empty file creation
2284 (not f1 and not content2) or
2287 (not f1 and not content2) or
2285 # empty file deletion
2288 # empty file deletion
2286 (not content1 and not f2) or
2289 (not content1 and not f2) or
2287 # create with flags
2290 # create with flags
2288 (not f1 and flag2) or
2291 (not f1 and flag2) or
2289 # change flags
2292 # change flags
2290 (f1 and f2 and flag1 != flag2)):
2293 (f1 and f2 and flag1 != flag2)):
2291 losedatafn(f2 or f1)
2294 losedatafn(f2 or f1)
2292
2295
2293 path1 = posixpath.join(prefix, f1 or f2)
2296 path1 = posixpath.join(prefix, f1 or f2)
2294 path2 = posixpath.join(prefix, f2 or f1)
2297 path2 = posixpath.join(prefix, f2 or f1)
2295 header = []
2298 header = []
2296 if opts.git:
2299 if opts.git:
2297 header.append('diff --git %s%s %s%s' %
2300 header.append('diff --git %s%s %s%s' %
2298 (aprefix, path1, bprefix, path2))
2301 (aprefix, path1, bprefix, path2))
2299 if not f1: # added
2302 if not f1: # added
2300 header.append('new file mode %s' % gitmode[flag2])
2303 header.append('new file mode %s' % gitmode[flag2])
2301 elif not f2: # removed
2304 elif not f2: # removed
2302 header.append('deleted file mode %s' % gitmode[flag1])
2305 header.append('deleted file mode %s' % gitmode[flag1])
2303 else: # modified/copied/renamed
2306 else: # modified/copied/renamed
2304 mode1, mode2 = gitmode[flag1], gitmode[flag2]
2307 mode1, mode2 = gitmode[flag1], gitmode[flag2]
2305 if mode1 != mode2:
2308 if mode1 != mode2:
2306 header.append('old mode %s' % mode1)
2309 header.append('old mode %s' % mode1)
2307 header.append('new mode %s' % mode2)
2310 header.append('new mode %s' % mode2)
2308 if copyop is not None:
2311 if copyop is not None:
2309 header.append('%s from %s' % (copyop, path1))
2312 header.append('%s from %s' % (copyop, path1))
2310 header.append('%s to %s' % (copyop, path2))
2313 header.append('%s to %s' % (copyop, path2))
2311 elif revs and not repo.ui.quiet:
2314 elif revs and not repo.ui.quiet:
2312 header.append(diffline(path1, revs))
2315 header.append(diffline(path1, revs))
2313
2316
2314 if binary and opts.git and not opts.nobinary:
2317 if binary and opts.git and not opts.nobinary:
2315 text = mdiff.b85diff(content1, content2)
2318 text = mdiff.b85diff(content1, content2)
2316 if text:
2319 if text:
2317 header.append('index %s..%s' %
2320 header.append('index %s..%s' %
2318 (gitindex(content1), gitindex(content2)))
2321 (gitindex(content1), gitindex(content2)))
2319 else:
2322 else:
2320 text = mdiff.unidiff(content1, date1,
2323 text = mdiff.unidiff(content1, date1,
2321 content2, date2,
2324 content2, date2,
2322 path1, path2, opts=opts)
2325 path1, path2, opts=opts)
2323 if header and (text or len(header) > 1):
2326 if header and (text or len(header) > 1):
2324 yield '\n'.join(header) + '\n'
2327 yield '\n'.join(header) + '\n'
2325 if text:
2328 if text:
2326 yield text
2329 yield text
2327
2330
2328 def diffstatsum(stats):
2331 def diffstatsum(stats):
2329 maxfile, maxtotal, addtotal, removetotal, binary = 0, 0, 0, 0, False
2332 maxfile, maxtotal, addtotal, removetotal, binary = 0, 0, 0, 0, False
2330 for f, a, r, b in stats:
2333 for f, a, r, b in stats:
2331 maxfile = max(maxfile, encoding.colwidth(f))
2334 maxfile = max(maxfile, encoding.colwidth(f))
2332 maxtotal = max(maxtotal, a + r)
2335 maxtotal = max(maxtotal, a + r)
2333 addtotal += a
2336 addtotal += a
2334 removetotal += r
2337 removetotal += r
2335 binary = binary or b
2338 binary = binary or b
2336
2339
2337 return maxfile, maxtotal, addtotal, removetotal, binary
2340 return maxfile, maxtotal, addtotal, removetotal, binary
2338
2341
2339 def diffstatdata(lines):
2342 def diffstatdata(lines):
2340 diffre = re.compile('^diff .*-r [a-z0-9]+\s(.*)$')
2343 diffre = re.compile('^diff .*-r [a-z0-9]+\s(.*)$')
2341
2344
2342 results = []
2345 results = []
2343 filename, adds, removes, isbinary = None, 0, 0, False
2346 filename, adds, removes, isbinary = None, 0, 0, False
2344
2347
2345 def addresult():
2348 def addresult():
2346 if filename:
2349 if filename:
2347 results.append((filename, adds, removes, isbinary))
2350 results.append((filename, adds, removes, isbinary))
2348
2351
2349 for line in lines:
2352 for line in lines:
2350 if line.startswith('diff'):
2353 if line.startswith('diff'):
2351 addresult()
2354 addresult()
2352 # set numbers to 0 anyway when starting new file
2355 # set numbers to 0 anyway when starting new file
2353 adds, removes, isbinary = 0, 0, False
2356 adds, removes, isbinary = 0, 0, False
2354 if line.startswith('diff --git a/'):
2357 if line.startswith('diff --git a/'):
2355 filename = gitre.search(line).group(2)
2358 filename = gitre.search(line).group(2)
2356 elif line.startswith('diff -r'):
2359 elif line.startswith('diff -r'):
2357 # format: "diff -r ... -r ... filename"
2360 # format: "diff -r ... -r ... filename"
2358 filename = diffre.search(line).group(1)
2361 filename = diffre.search(line).group(1)
2359 elif line.startswith('+') and not line.startswith('+++ '):
2362 elif line.startswith('+') and not line.startswith('+++ '):
2360 adds += 1
2363 adds += 1
2361 elif line.startswith('-') and not line.startswith('--- '):
2364 elif line.startswith('-') and not line.startswith('--- '):
2362 removes += 1
2365 removes += 1
2363 elif (line.startswith('GIT binary patch') or
2366 elif (line.startswith('GIT binary patch') or
2364 line.startswith('Binary file')):
2367 line.startswith('Binary file')):
2365 isbinary = True
2368 isbinary = True
2366 addresult()
2369 addresult()
2367 return results
2370 return results
2368
2371
2369 def diffstat(lines, width=80, git=False):
2372 def diffstat(lines, width=80, git=False):
2370 output = []
2373 output = []
2371 stats = diffstatdata(lines)
2374 stats = diffstatdata(lines)
2372 maxname, maxtotal, totaladds, totalremoves, hasbinary = diffstatsum(stats)
2375 maxname, maxtotal, totaladds, totalremoves, hasbinary = diffstatsum(stats)
2373
2376
2374 countwidth = len(str(maxtotal))
2377 countwidth = len(str(maxtotal))
2375 if hasbinary and countwidth < 3:
2378 if hasbinary and countwidth < 3:
2376 countwidth = 3
2379 countwidth = 3
2377 graphwidth = width - countwidth - maxname - 6
2380 graphwidth = width - countwidth - maxname - 6
2378 if graphwidth < 10:
2381 if graphwidth < 10:
2379 graphwidth = 10
2382 graphwidth = 10
2380
2383
2381 def scale(i):
2384 def scale(i):
2382 if maxtotal <= graphwidth:
2385 if maxtotal <= graphwidth:
2383 return i
2386 return i
2384 # If diffstat runs out of room it doesn't print anything,
2387 # If diffstat runs out of room it doesn't print anything,
2385 # which isn't very useful, so always print at least one + or -
2388 # which isn't very useful, so always print at least one + or -
2386 # if there were at least some changes.
2389 # if there were at least some changes.
2387 return max(i * graphwidth // maxtotal, int(bool(i)))
2390 return max(i * graphwidth // maxtotal, int(bool(i)))
2388
2391
2389 for filename, adds, removes, isbinary in stats:
2392 for filename, adds, removes, isbinary in stats:
2390 if isbinary:
2393 if isbinary:
2391 count = 'Bin'
2394 count = 'Bin'
2392 else:
2395 else:
2393 count = adds + removes
2396 count = adds + removes
2394 pluses = '+' * scale(adds)
2397 pluses = '+' * scale(adds)
2395 minuses = '-' * scale(removes)
2398 minuses = '-' * scale(removes)
2396 output.append(' %s%s | %*s %s%s\n' %
2399 output.append(' %s%s | %*s %s%s\n' %
2397 (filename, ' ' * (maxname - encoding.colwidth(filename)),
2400 (filename, ' ' * (maxname - encoding.colwidth(filename)),
2398 countwidth, count, pluses, minuses))
2401 countwidth, count, pluses, minuses))
2399
2402
2400 if stats:
2403 if stats:
2401 output.append(_(' %d files changed, %d insertions(+), '
2404 output.append(_(' %d files changed, %d insertions(+), '
2402 '%d deletions(-)\n')
2405 '%d deletions(-)\n')
2403 % (len(stats), totaladds, totalremoves))
2406 % (len(stats), totaladds, totalremoves))
2404
2407
2405 return ''.join(output)
2408 return ''.join(output)
2406
2409
2407 def diffstatui(*args, **kw):
2410 def diffstatui(*args, **kw):
2408 '''like diffstat(), but yields 2-tuples of (output, label) for
2411 '''like diffstat(), but yields 2-tuples of (output, label) for
2409 ui.write()
2412 ui.write()
2410 '''
2413 '''
2411
2414
2412 for line in diffstat(*args, **kw).splitlines():
2415 for line in diffstat(*args, **kw).splitlines():
2413 if line and line[-1] in '+-':
2416 if line and line[-1] in '+-':
2414 name, graph = line.rsplit(' ', 1)
2417 name, graph = line.rsplit(' ', 1)
2415 yield (name + ' ', '')
2418 yield (name + ' ', '')
2416 m = re.search(r'\++', graph)
2419 m = re.search(r'\++', graph)
2417 if m:
2420 if m:
2418 yield (m.group(0), 'diffstat.inserted')
2421 yield (m.group(0), 'diffstat.inserted')
2419 m = re.search(r'-+', graph)
2422 m = re.search(r'-+', graph)
2420 if m:
2423 if m:
2421 yield (m.group(0), 'diffstat.deleted')
2424 yield (m.group(0), 'diffstat.deleted')
2422 else:
2425 else:
2423 yield (line, '')
2426 yield (line, '')
2424 yield ('\n', '')
2427 yield ('\n', '')
@@ -1,798 +1,825
1 $ hg init repo
1 $ hg init repo
2 $ cd repo
2 $ cd repo
3
3
4 New file:
4 New file:
5
5
6 $ hg import -d "1000000 0" -mnew - <<EOF
6 $ hg import -d "1000000 0" -mnew - <<EOF
7 > diff --git a/new b/new
7 > diff --git a/new b/new
8 > new file mode 100644
8 > new file mode 100644
9 > index 0000000..7898192
9 > index 0000000..7898192
10 > --- /dev/null
10 > --- /dev/null
11 > +++ b/new
11 > +++ b/new
12 > @@ -0,0 +1 @@
12 > @@ -0,0 +1 @@
13 > +a
13 > +a
14 > EOF
14 > EOF
15 applying patch from stdin
15 applying patch from stdin
16
16
17 $ hg tip -q
17 $ hg tip -q
18 0:ae3ee40d2079
18 0:ae3ee40d2079
19
19
20 New empty file:
20 New empty file:
21
21
22 $ hg import -d "1000000 0" -mempty - <<EOF
22 $ hg import -d "1000000 0" -mempty - <<EOF
23 > diff --git a/empty b/empty
23 > diff --git a/empty b/empty
24 > new file mode 100644
24 > new file mode 100644
25 > EOF
25 > EOF
26 applying patch from stdin
26 applying patch from stdin
27
27
28 $ hg tip -q
28 $ hg tip -q
29 1:ab199dc869b5
29 1:ab199dc869b5
30
30
31 $ hg locate empty
31 $ hg locate empty
32 empty
32 empty
33
33
34 chmod +x:
34 chmod +x:
35
35
36 $ hg import -d "1000000 0" -msetx - <<EOF
36 $ hg import -d "1000000 0" -msetx - <<EOF
37 > diff --git a/new b/new
37 > diff --git a/new b/new
38 > old mode 100644
38 > old mode 100644
39 > new mode 100755
39 > new mode 100755
40 > EOF
40 > EOF
41 applying patch from stdin
41 applying patch from stdin
42
42
43 #if execbit
43 #if execbit
44 $ hg tip -q
44 $ hg tip -q
45 2:3a34410f282e
45 2:3a34410f282e
46 $ test -x new
46 $ test -x new
47 $ hg rollback -q
47 $ hg rollback -q
48 #else
48 #else
49 $ hg tip -q
49 $ hg tip -q
50 1:ab199dc869b5
50 1:ab199dc869b5
51 #endif
51 #endif
52
52
53 Copy and removing x bit:
53 Copy and removing x bit:
54
54
55 $ hg import -f -d "1000000 0" -mcopy - <<EOF
55 $ hg import -f -d "1000000 0" -mcopy - <<EOF
56 > diff --git a/new b/copy
56 > diff --git a/new b/copy
57 > old mode 100755
57 > old mode 100755
58 > new mode 100644
58 > new mode 100644
59 > similarity index 100%
59 > similarity index 100%
60 > copy from new
60 > copy from new
61 > copy to copy
61 > copy to copy
62 > diff --git a/new b/copyx
62 > diff --git a/new b/copyx
63 > similarity index 100%
63 > similarity index 100%
64 > copy from new
64 > copy from new
65 > copy to copyx
65 > copy to copyx
66 > EOF
66 > EOF
67 applying patch from stdin
67 applying patch from stdin
68
68
69 $ test -f copy
69 $ test -f copy
70 #if execbit
70 #if execbit
71 $ test ! -x copy
71 $ test ! -x copy
72 $ test -x copyx
72 $ test -x copyx
73 $ hg tip -q
73 $ hg tip -q
74 2:21dfaae65c71
74 2:21dfaae65c71
75 #else
75 #else
76 $ hg tip -q
76 $ hg tip -q
77 2:0efdaa8e3bf3
77 2:0efdaa8e3bf3
78 #endif
78 #endif
79
79
80 $ hg up -qCr1
80 $ hg up -qCr1
81 $ hg rollback -q
81 $ hg rollback -q
82
82
83 Copy (like above but independent of execbit):
83 Copy (like above but independent of execbit):
84
84
85 $ hg import -d "1000000 0" -mcopy - <<EOF
85 $ hg import -d "1000000 0" -mcopy - <<EOF
86 > diff --git a/new b/copy
86 > diff --git a/new b/copy
87 > similarity index 100%
87 > similarity index 100%
88 > copy from new
88 > copy from new
89 > copy to copy
89 > copy to copy
90 > diff --git a/new b/copyx
90 > diff --git a/new b/copyx
91 > similarity index 100%
91 > similarity index 100%
92 > copy from new
92 > copy from new
93 > copy to copyx
93 > copy to copyx
94 > EOF
94 > EOF
95 applying patch from stdin
95 applying patch from stdin
96
96
97 $ hg tip -q
97 $ hg tip -q
98 2:0efdaa8e3bf3
98 2:0efdaa8e3bf3
99 $ test -f copy
99 $ test -f copy
100
100
101 $ cat copy
101 $ cat copy
102 a
102 a
103
103
104 $ hg cat copy
104 $ hg cat copy
105 a
105 a
106
106
107 Rename:
107 Rename:
108
108
109 $ hg import -d "1000000 0" -mrename - <<EOF
109 $ hg import -d "1000000 0" -mrename - <<EOF
110 > diff --git a/copy b/rename
110 > diff --git a/copy b/rename
111 > similarity index 100%
111 > similarity index 100%
112 > rename from copy
112 > rename from copy
113 > rename to rename
113 > rename to rename
114 > EOF
114 > EOF
115 applying patch from stdin
115 applying patch from stdin
116
116
117 $ hg tip -q
117 $ hg tip -q
118 3:b1f57753fad2
118 3:b1f57753fad2
119
119
120 $ hg locate
120 $ hg locate
121 copyx
121 copyx
122 empty
122 empty
123 new
123 new
124 rename
124 rename
125
125
126 Delete:
126 Delete:
127
127
128 $ hg import -d "1000000 0" -mdelete - <<EOF
128 $ hg import -d "1000000 0" -mdelete - <<EOF
129 > diff --git a/copyx b/copyx
129 > diff --git a/copyx b/copyx
130 > deleted file mode 100755
130 > deleted file mode 100755
131 > index 7898192..0000000
131 > index 7898192..0000000
132 > --- a/copyx
132 > --- a/copyx
133 > +++ /dev/null
133 > +++ /dev/null
134 > @@ -1 +0,0 @@
134 > @@ -1 +0,0 @@
135 > -a
135 > -a
136 > EOF
136 > EOF
137 applying patch from stdin
137 applying patch from stdin
138
138
139 $ hg tip -q
139 $ hg tip -q
140 4:1bd1da94b9b2
140 4:1bd1da94b9b2
141
141
142 $ hg locate
142 $ hg locate
143 empty
143 empty
144 new
144 new
145 rename
145 rename
146
146
147 $ test -f copyx
147 $ test -f copyx
148 [1]
148 [1]
149
149
150 Regular diff:
150 Regular diff:
151
151
152 $ hg import -d "1000000 0" -mregular - <<EOF
152 $ hg import -d "1000000 0" -mregular - <<EOF
153 > diff --git a/rename b/rename
153 > diff --git a/rename b/rename
154 > index 7898192..72e1fe3 100644
154 > index 7898192..72e1fe3 100644
155 > --- a/rename
155 > --- a/rename
156 > +++ b/rename
156 > +++ b/rename
157 > @@ -1 +1,5 @@
157 > @@ -1 +1,5 @@
158 > a
158 > a
159 > +a
159 > +a
160 > +a
160 > +a
161 > +a
161 > +a
162 > +a
162 > +a
163 > EOF
163 > EOF
164 applying patch from stdin
164 applying patch from stdin
165
165
166 $ hg tip -q
166 $ hg tip -q
167 5:46fe99cb3035
167 5:46fe99cb3035
168
168
169 Copy and modify:
169 Copy and modify:
170
170
171 $ hg import -d "1000000 0" -mcopymod - <<EOF
171 $ hg import -d "1000000 0" -mcopymod - <<EOF
172 > diff --git a/rename b/copy2
172 > diff --git a/rename b/copy2
173 > similarity index 80%
173 > similarity index 80%
174 > copy from rename
174 > copy from rename
175 > copy to copy2
175 > copy to copy2
176 > index 72e1fe3..b53c148 100644
176 > index 72e1fe3..b53c148 100644
177 > --- a/rename
177 > --- a/rename
178 > +++ b/copy2
178 > +++ b/copy2
179 > @@ -1,5 +1,5 @@
179 > @@ -1,5 +1,5 @@
180 > a
180 > a
181 > a
181 > a
182 > -a
182 > -a
183 > +b
183 > +b
184 > a
184 > a
185 > a
185 > a
186 > EOF
186 > EOF
187 applying patch from stdin
187 applying patch from stdin
188
188
189 $ hg tip -q
189 $ hg tip -q
190 6:ffeb3197c12d
190 6:ffeb3197c12d
191
191
192 $ hg cat copy2
192 $ hg cat copy2
193 a
193 a
194 a
194 a
195 b
195 b
196 a
196 a
197 a
197 a
198
198
199 Rename and modify:
199 Rename and modify:
200
200
201 $ hg import -d "1000000 0" -mrenamemod - <<EOF
201 $ hg import -d "1000000 0" -mrenamemod - <<EOF
202 > diff --git a/copy2 b/rename2
202 > diff --git a/copy2 b/rename2
203 > similarity index 80%
203 > similarity index 80%
204 > rename from copy2
204 > rename from copy2
205 > rename to rename2
205 > rename to rename2
206 > index b53c148..8f81e29 100644
206 > index b53c148..8f81e29 100644
207 > --- a/copy2
207 > --- a/copy2
208 > +++ b/rename2
208 > +++ b/rename2
209 > @@ -1,5 +1,5 @@
209 > @@ -1,5 +1,5 @@
210 > a
210 > a
211 > a
211 > a
212 > b
212 > b
213 > -a
213 > -a
214 > +c
214 > +c
215 > a
215 > a
216 > EOF
216 > EOF
217 applying patch from stdin
217 applying patch from stdin
218
218
219 $ hg tip -q
219 $ hg tip -q
220 7:401aede9e6bb
220 7:401aede9e6bb
221
221
222 $ hg locate copy2
222 $ hg locate copy2
223 [1]
223 [1]
224 $ hg cat rename2
224 $ hg cat rename2
225 a
225 a
226 a
226 a
227 b
227 b
228 c
228 c
229 a
229 a
230
230
231 One file renamed multiple times:
231 One file renamed multiple times:
232
232
233 $ hg import -d "1000000 0" -mmultirenames - <<EOF
233 $ hg import -d "1000000 0" -mmultirenames - <<EOF
234 > diff --git a/rename2 b/rename3
234 > diff --git a/rename2 b/rename3
235 > rename from rename2
235 > rename from rename2
236 > rename to rename3
236 > rename to rename3
237 > diff --git a/rename2 b/rename3-2
237 > diff --git a/rename2 b/rename3-2
238 > rename from rename2
238 > rename from rename2
239 > rename to rename3-2
239 > rename to rename3-2
240 > EOF
240 > EOF
241 applying patch from stdin
241 applying patch from stdin
242
242
243 $ hg tip -q
243 $ hg tip -q
244 8:2ef727e684e8
244 8:2ef727e684e8
245
245
246 $ hg log -vr. --template '{rev} {files} / {file_copies}\n'
246 $ hg log -vr. --template '{rev} {files} / {file_copies}\n'
247 8 rename2 rename3 rename3-2 / rename3 (rename2)rename3-2 (rename2)
247 8 rename2 rename3 rename3-2 / rename3 (rename2)rename3-2 (rename2)
248
248
249 $ hg locate rename2 rename3 rename3-2
249 $ hg locate rename2 rename3 rename3-2
250 rename3
250 rename3
251 rename3-2
251 rename3-2
252
252
253 $ hg cat rename3
253 $ hg cat rename3
254 a
254 a
255 a
255 a
256 b
256 b
257 c
257 c
258 a
258 a
259
259
260 $ hg cat rename3-2
260 $ hg cat rename3-2
261 a
261 a
262 a
262 a
263 b
263 b
264 c
264 c
265 a
265 a
266
266
267 $ echo foo > foo
267 $ echo foo > foo
268 $ hg add foo
268 $ hg add foo
269 $ hg ci -m 'add foo'
269 $ hg ci -m 'add foo'
270
270
271 Binary files and regular patch hunks:
271 Binary files and regular patch hunks:
272
272
273 $ hg import -d "1000000 0" -m binaryregular - <<EOF
273 $ hg import -d "1000000 0" -m binaryregular - <<EOF
274 > diff --git a/binary b/binary
274 > diff --git a/binary b/binary
275 > new file mode 100644
275 > new file mode 100644
276 > index 0000000000000000000000000000000000000000..593f4708db84ac8fd0f5cc47c634f38c013fe9e4
276 > index 0000000000000000000000000000000000000000..593f4708db84ac8fd0f5cc47c634f38c013fe9e4
277 > GIT binary patch
277 > GIT binary patch
278 > literal 4
278 > literal 4
279 > Lc\${NkU|;|M00aO5
279 > Lc\${NkU|;|M00aO5
280 >
280 >
281 > diff --git a/foo b/foo2
281 > diff --git a/foo b/foo2
282 > rename from foo
282 > rename from foo
283 > rename to foo2
283 > rename to foo2
284 > EOF
284 > EOF
285 applying patch from stdin
285 applying patch from stdin
286
286
287 $ hg tip -q
287 $ hg tip -q
288 10:27377172366e
288 10:27377172366e
289
289
290 $ cat foo2
290 $ cat foo2
291 foo
291 foo
292
292
293 $ hg manifest --debug | grep binary
293 $ hg manifest --debug | grep binary
294 045c85ba38952325e126c70962cc0f9d9077bc67 644 binary
294 045c85ba38952325e126c70962cc0f9d9077bc67 644 binary
295
295
296 Multiple binary files:
296 Multiple binary files:
297
297
298 $ hg import -d "1000000 0" -m multibinary - <<EOF
298 $ hg import -d "1000000 0" -m multibinary - <<EOF
299 > diff --git a/mbinary1 b/mbinary1
299 > diff --git a/mbinary1 b/mbinary1
300 > new file mode 100644
300 > new file mode 100644
301 > index 0000000000000000000000000000000000000000..593f4708db84ac8fd0f5cc47c634f38c013fe9e4
301 > index 0000000000000000000000000000000000000000..593f4708db84ac8fd0f5cc47c634f38c013fe9e4
302 > GIT binary patch
302 > GIT binary patch
303 > literal 4
303 > literal 4
304 > Lc\${NkU|;|M00aO5
304 > Lc\${NkU|;|M00aO5
305 >
305 >
306 > diff --git a/mbinary2 b/mbinary2
306 > diff --git a/mbinary2 b/mbinary2
307 > new file mode 100644
307 > new file mode 100644
308 > index 0000000000000000000000000000000000000000..112363ac1917b417ffbd7f376ca786a1e5fa7490
308 > index 0000000000000000000000000000000000000000..112363ac1917b417ffbd7f376ca786a1e5fa7490
309 > GIT binary patch
309 > GIT binary patch
310 > literal 5
310 > literal 5
311 > Mc\${NkU|\`?^000jF3jhEB
311 > Mc\${NkU|\`?^000jF3jhEB
312 >
312 >
313 > EOF
313 > EOF
314 applying patch from stdin
314 applying patch from stdin
315
315
316 $ hg tip -q
316 $ hg tip -q
317 11:18b73a84b4ab
317 11:18b73a84b4ab
318
318
319 $ hg manifest --debug | grep mbinary
319 $ hg manifest --debug | grep mbinary
320 045c85ba38952325e126c70962cc0f9d9077bc67 644 mbinary1
320 045c85ba38952325e126c70962cc0f9d9077bc67 644 mbinary1
321 a874b471193996e7cb034bb301cac7bdaf3e3f46 644 mbinary2
321 a874b471193996e7cb034bb301cac7bdaf3e3f46 644 mbinary2
322
322
323 Binary file and delta hunk (we build the patch using this sed hack to
323 Binary file and delta hunk (we build the patch using this sed hack to
324 avoid an unquoted ^, which check-code says breaks sh on Solaris):
324 avoid an unquoted ^, which check-code says breaks sh on Solaris):
325
325
326 $ sed 's/ caret /^/g;s/ dollarparen /$(/g' > quote-hack.patch <<'EOF'
326 $ sed 's/ caret /^/g;s/ dollarparen /$(/g' > quote-hack.patch <<'EOF'
327 > diff --git a/delta b/delta
327 > diff --git a/delta b/delta
328 > new file mode 100644
328 > new file mode 100644
329 > index 0000000000000000000000000000000000000000..8c9b7831b231c2600843e303e66b521353a200b3
329 > index 0000000000000000000000000000000000000000..8c9b7831b231c2600843e303e66b521353a200b3
330 > GIT binary patch
330 > GIT binary patch
331 > literal 3749
331 > literal 3749
332 > zcmV;W4qEYvP)<h;3K|Lk000e1NJLTq006iE002D*0ssI2kt{U(0000PbVXQnQ*UN;
332 > zcmV;W4qEYvP)<h;3K|Lk000e1NJLTq006iE002D*0ssI2kt{U(0000PbVXQnQ*UN;
333 > zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU=M@d9MRCwC#oC!>o#}>x{(W-y~UN*tK
333 > zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU=M@d9MRCwC#oC!>o#}>x{(W-y~UN*tK
334 > z%A%sxiUy2Ys)0Vm#ueArYKoYqX;GuiqZpgirM6nCVoYk?YNAz3G~z;BZ~@~&OQEe4
334 > z%A%sxiUy2Ys)0Vm#ueArYKoYqX;GuiqZpgirM6nCVoYk?YNAz3G~z;BZ~@~&OQEe4
335 > zmGvS5isFJI;Pd_7J+EKxyHZeu` caret t4r2>F;h-+VK3{_{WoGv8dSpFDYDrA%3UX03pt
335 > zmGvS5isFJI;Pd_7J+EKxyHZeu` caret t4r2>F;h-+VK3{_{WoGv8dSpFDYDrA%3UX03pt
336 > zOaVoi0*W#P6lDr1$`nwPDWE7*rhuYM0Y#YtiZTThWeO<D6i}2YpqR<%$s>bRRaI42
336 > zOaVoi0*W#P6lDr1$`nwPDWE7*rhuYM0Y#YtiZTThWeO<D6i}2YpqR<%$s>bRRaI42
337 > zS3iFIxJ8Q=EnBv1Z7?pBw_bLjJb3V+tgP(Tty_2R-mR#p04x78n2n7MSOFyt4i1iv
337 > zS3iFIxJ8Q=EnBv1Z7?pBw_bLjJb3V+tgP(Tty_2R-mR#p04x78n2n7MSOFyt4i1iv
338 > zjxH`PPEJmgD7U?IK&h;(EGQ@_DJc<@01=4fiNXHcKZ8LhZQ8T}E3U4tUS3}OrcgQW
338 > zjxH`PPEJmgD7U?IK&h;(EGQ@_DJc<@01=4fiNXHcKZ8LhZQ8T}E3U4tUS3}OrcgQW
339 > zWdX{K8#l7Ev&#$ysR)G#0*rC+<WGZ3?CtG4bm-ve>Dj$|_qJ`@D*stNP_AFUe&x!Q
339 > zWdX{K8#l7Ev&#$ysR)G#0*rC+<WGZ3?CtG4bm-ve>Dj$|_qJ`@D*stNP_AFUe&x!Q
340 > zJ9q9B7Z=ym)MyZ?Tg1ROunUYr81nV?B@!tYS~5_|%gfW#(_s<4UN1!Q?Dv8d>g#m6
340 > zJ9q9B7Z=ym)MyZ?Tg1ROunUYr81nV?B@!tYS~5_|%gfW#(_s<4UN1!Q?Dv8d>g#m6
341 > z%*@R2@bI2JdnzxQ!EDU`$eQY!tgI~Zn$prz;gaXNod5*5p(1Bz=P$qfvZ$y?dC@X~
341 > z%*@R2@bI2JdnzxQ!EDU`$eQY!tgI~Zn$prz;gaXNod5*5p(1Bz=P$qfvZ$y?dC@X~
342 > zlAD+NAKhB{=;6bMwzjqn>9mavvKOGd`s%A+fBiL>Q;xJWpa72C+}u{JTHUX>{~}Qj
342 > zlAD+NAKhB{=;6bMwzjqn>9mavvKOGd`s%A+fBiL>Q;xJWpa72C+}u{JTHUX>{~}Qj
343 > zUb%hyHgN~c?cBLjInvUALMD9g-aXt54ZL8AOCvXL-V6!~ijR*kEG$&Mv?!pE61OlI
343 > zUb%hyHgN~c?cBLjInvUALMD9g-aXt54ZL8AOCvXL-V6!~ijR*kEG$&Mv?!pE61OlI
344 > z8nzMSPE8F7bH|Py*RNl1VUCggq<V)>@_6gkEeiz7{rmTeuNTW6+KVS#0FG%IHf-3L
344 > z8nzMSPE8F7bH|Py*RNl1VUCggq<V)>@_6gkEeiz7{rmTeuNTW6+KVS#0FG%IHf-3L
345 > zGiS21vn>WCCr+GLx caret !uNetzB6u3o(w6&1C2?_LW8ij$+$sZ*zZ`|US3H@8N~%&V%Z
345 > zGiS21vn>WCCr+GLx caret !uNetzB6u3o(w6&1C2?_LW8ij$+$sZ*zZ`|US3H@8N~%&V%Z
346 > zAeA0HdhFS=$6|nzn3%YH`SN<>DQRO;Qc caret )dfdvA caret 5u`Xf;Zzu<ZQHgG?28V-#s<;T
346 > zAeA0HdhFS=$6|nzn3%YH`SN<>DQRO;Qc caret )dfdvA caret 5u`Xf;Zzu<ZQHgG?28V-#s<;T
347 > zzkh#LA)v7gpoE5ou3o*GoUUF%b#iht&kl9d0)><$FE1}ACr68;uCA`6DrGmz_U+rp
347 > zzkh#LA)v7gpoE5ou3o*GoUUF%b#iht&kl9d0)><$FE1}ACr68;uCA`6DrGmz_U+rp
348 > zL>Rx;X_yhk$fP_yJrTCQ|NgsW0A<985g&c@k-NKly<>mgU8n||ZPPV<`SN8#%$+-T
348 > zL>Rx;X_yhk$fP_yJrTCQ|NgsW0A<985g&c@k-NKly<>mgU8n||ZPPV<`SN8#%$+-T
349 > zfP$T!ou8jypFVwnzqhxyUvIxXd-wF~*U!ht=hCH1wzjqn9x#)IrhDa;S0JbK caret z_$W
349 > zfP$T!ou8jypFVwnzqhxyUvIxXd-wF~*U!ht=hCH1wzjqn9x#)IrhDa;S0JbK caret z_$W
350 > zd(8rX@;7|t*;GJ5h$SZ{v(}+UBEs$4w~?{@9%`_Z<P<kox5bMWuUWH(sF9hONgd$Q
350 > zd(8rX@;7|t*;GJ5h$SZ{v(}+UBEs$4w~?{@9%`_Z<P<kox5bMWuUWH(sF9hONgd$Q
351 > zunCgwT@1|CU9+;X caret 4z&|M~@yw23Ay50NFWn=FqF%yLZEUty;AT2??1oV@B)Nt))J7
351 > zunCgwT@1|CU9+;X caret 4z&|M~@yw23Ay50NFWn=FqF%yLZEUty;AT2??1oV@B)Nt))J7
352 > zh>{5j2@f7T=-an%L_`E)h;mZ4D_5>?7tjQtVPRo2XU-&;mX(!l-MSTJP4XWY82JAC
352 > zh>{5j2@f7T=-an%L_`E)h;mZ4D_5>?7tjQtVPRo2XU-&;mX(!l-MSTJP4XWY82JAC
353 > z@57+y&!1=P{Mn{W8)-HzEsgAtd63}Cazc>O6vGb>51%@9DzbyI3?4j~$ijmT95_IS
353 > z@57+y&!1=P{Mn{W8)-HzEsgAtd63}Cazc>O6vGb>51%@9DzbyI3?4j~$ijmT95_IS
354 > zS#r!LCDW%*4-O7CGnkr$xXR1RQ&UrA<CQt} caret 73NL%zk`)Jk!yxUAt-1r}ggLn-Zq}
354 > zS#r!LCDW%*4-O7CGnkr$xXR1RQ&UrA<CQt} caret 73NL%zk`)Jk!yxUAt-1r}ggLn-Zq}
355 > z*s){8pw68;i+kiG%CpBKYSJLLFyq&*U8}qDp+kpe&6<Vp(Z58%l#~>ZK?&s7y?b}i
355 > z*s){8pw68;i+kiG%CpBKYSJLLFyq&*U8}qDp+kpe&6<Vp(Z58%l#~>ZK?&s7y?b}i
356 > zuwcOgO%x-27A;y785zknl_{sU;E6v$8{pWmVS{KaJPpu`i;HP$#flY@u~Ua~K3%tN
356 > zuwcOgO%x-27A;y785zknl_{sU;E6v$8{pWmVS{KaJPpu`i;HP$#flY@u~Ua~K3%tN
357 > z-LhrNh{9SoHgDd%WXTc$$~Dq{?AWou3!H&?V8K{ caret {P9Ot5vecD?%1&-E-ntBFj87(
357 > z-LhrNh{9SoHgDd%WXTc$$~Dq{?AWou3!H&?V8K{ caret {P9Ot5vecD?%1&-E-ntBFj87(
358 > zy5`QE%QRX7qcHC%1{Ua}M~}L6=`wQUNEQ=I;qc+ZMMXtK2T+0os;jEco;}OV9z1w3
358 > zy5`QE%QRX7qcHC%1{Ua}M~}L6=`wQUNEQ=I;qc+ZMMXtK2T+0os;jEco;}OV9z1w3
359 > zARqv caret bm-85xnRCng3OT|MyVSmR3ND7 caret ?KaQGG! caret (aTbo1N;Nz;X3Q9FJbwK6`0?Yp
359 > zARqv caret bm-85xnRCng3OT|MyVSmR3ND7 caret ?KaQGG! caret (aTbo1N;Nz;X3Q9FJbwK6`0?Yp
360 > zj*X2ac;Pw3!I2|JShDaF>-gJmzm1NLj){rk&o|$E caret WAsfrK=x&@B!`w7Hik81sPz4
360 > zj*X2ac;Pw3!I2|JShDaF>-gJmzm1NLj){rk&o|$E caret WAsfrK=x&@B!`w7Hik81sPz4
361 > zuJTaiCppM>-+c!wPzcUw)5@?J4U-u|pJ~xbWUe-C+60k caret 7>9!)56DbjmA~`OJJ40v
361 > zuJTaiCppM>-+c!wPzcUw)5@?J4U-u|pJ~xbWUe-C+60k caret 7>9!)56DbjmA~`OJJ40v
362 > zu3hCA7eJXZWeN|1iJLu87$;+fS8+Kq6O`aT)*_x@sY#t7LxwoEcVw*)cWhhQW@l%!
362 > zu3hCA7eJXZWeN|1iJLu87$;+fS8+Kq6O`aT)*_x@sY#t7LxwoEcVw*)cWhhQW@l%!
363 > z{#Z=y+qcK@%z{p*D=8_Fcg278AnH3fI5;~yGu?9TscxXaaP*4$f<LIv! caret 5Lfr%vKg
363 > z{#Z=y+qcK@%z{p*D=8_Fcg278AnH3fI5;~yGu?9TscxXaaP*4$f<LIv! caret 5Lfr%vKg
364 > zpxmunH#%=+ICMvZA~wyNH%~eMl!-g caret R!cYJ#WmLq5N8viz#J%%LPtkO?V)tZ81cp>
364 > zpxmunH#%=+ICMvZA~wyNH%~eMl!-g caret R!cYJ#WmLq5N8viz#J%%LPtkO?V)tZ81cp>
365 > z{ALK?fNPePmd;289&M8Q3>YwgZX5GcGY&n>K1<x)!`;Qjg&}bb!Lrnl@xH#kS~VYE
365 > z{ALK?fNPePmd;289&M8Q3>YwgZX5GcGY&n>K1<x)!`;Qjg&}bb!Lrnl@xH#kS~VYE
366 > zpJmIJO`A3iy+Y3X`k>cY-@}Iw2Onq`=!ba3eATgs3yg3Wej=+P-Z8WF#w=RXvS@J3
366 > zpJmIJO`A3iy+Y3X`k>cY-@}Iw2Onq`=!ba3eATgs3yg3Wej=+P-Z8WF#w=RXvS@J3
367 > zEyhVTj-gO?kfDu1g9afo<RkPrYzG#_yF41IFxF%Ylg>9lx6<clPweR-b7Hn+r)e1l
367 > zEyhVTj-gO?kfDu1g9afo<RkPrYzG#_yF41IFxF%Ylg>9lx6<clPweR-b7Hn+r)e1l
368 > zO6c6FbNt@;;*w$z;N|H>h{czme)_4V6UC4hv**kX2@L caret Bgds dollarparen &P7M4dhfmWe)!=B
368 > zO6c6FbNt@;;*w$z;N|H>h{czme)_4V6UC4hv**kX2@L caret Bgds dollarparen &P7M4dhfmWe)!=B
369 > zR3X=Y{P9N}p@-##@1ZNW1YbVaiP~D@8m&<dzEP&cO|87Ju#j*=;wH~Exr>i*Hpp&@
369 > zR3X=Y{P9N}p@-##@1ZNW1YbVaiP~D@8m&<dzEP&cO|87Ju#j*=;wH~Exr>i*Hpp&@
370 > z`9!Sj+O;byD~s8qZ>6QB8uv7Bpn&&?xe;;e<M4F8KEID&pT7QmqoSgq&06adp5T=U
370 > z`9!Sj+O;byD~s8qZ>6QB8uv7Bpn&&?xe;;e<M4F8KEID&pT7QmqoSgq&06adp5T=U
371 > z6DH*4=AB7C1D9Amu?ia-wtxSAlmTEO96XHx)-+rKP;ip$pukuSJGW3P1aUmc2yo%)
371 > z6DH*4=AB7C1D9Amu?ia-wtxSAlmTEO96XHx)-+rKP;ip$pukuSJGW3P1aUmc2yo%)
372 > z&<t3F>d1X+1qzaag-%x+eKHx{?Afz3GBQSw9u0lw<mB+I#v11TKRpKWQS+lvVL7=u
372 > z&<t3F>d1X+1qzaag-%x+eKHx{?Afz3GBQSw9u0lw<mB+I#v11TKRpKWQS+lvVL7=u
373 > zHr6)1ynEF<i3kO6A8&ppPMo-F=PnWfXkSj@i*7J6C<F}wR?s(O0niC?t+6;+k}pPq
373 > zHr6)1ynEF<i3kO6A8&ppPMo-F=PnWfXkSj@i*7J6C<F}wR?s(O0niC?t+6;+k}pPq
374 > zrok&TPU40rL0ZYDwenNrrmPZ`gjo@DEF`7 caret cKP||pUr;+r)hyn9O37=xA`3%Bj-ih
374 > zrok&TPU40rL0ZYDwenNrrmPZ`gjo@DEF`7 caret cKP||pUr;+r)hyn9O37=xA`3%Bj-ih
375 > z+1usk<%5G-y+R?tA`qY=)6&vNjL{P?QzHg%P%>`ZxP=QB%DHY6L26?36V_p caret {}n$q
375 > z+1usk<%5G-y+R?tA`qY=)6&vNjL{P?QzHg%P%>`ZxP=QB%DHY6L26?36V_p caret {}n$q
376 > z3@9W=KmGI*Ng_Q#AzA%-z|Z caret |#oW(hkfgpuS$RKRhlrarX%efMMCs}GLChec5+y{6
376 > z3@9W=KmGI*Ng_Q#AzA%-z|Z caret |#oW(hkfgpuS$RKRhlrarX%efMMCs}GLChec5+y{6
377 > z1Qnxim_C-fmQuaAK_NUHUBV&;1c0V)wji<RcdZ*aAWTwyt>hVnlt caret asFCe0&a@tqp
377 > z1Qnxim_C-fmQuaAK_NUHUBV&;1c0V)wji<RcdZ*aAWTwyt>hVnlt caret asFCe0&a@tqp
378 > zEEy;$L}D$X6)wfQNl8gu6Z>oB3_RrP=gTyK2@@w#LbQfLNHj>Q&z(C5wUFhK+}0aV
378 > zEEy;$L}D$X6)wfQNl8gu6Z>oB3_RrP=gTyK2@@w#LbQfLNHj>Q&z(C5wUFhK+}0aV
379 > zSohlc=7K+spN<ctf}5KgKqNyJDNP9;LZd)nTE=9|6Xdr9%Hzk63-tL2c9FD*rsyYY
379 > zSohlc=7K+spN<ctf}5KgKqNyJDNP9;LZd)nTE=9|6Xdr9%Hzk63-tL2c9FD*rsyYY
380 > z!}t+Yljq7-p$X;4_YL?6d;mdY3R##o1e%rlPxrsMh8|;sKTr~ caret QD#sw3&vS$FwlTk
380 > z!}t+Yljq7-p$X;4_YL?6d;mdY3R##o1e%rlPxrsMh8|;sKTr~ caret QD#sw3&vS$FwlTk
381 > zp1#Gw!Qo-$LtvpXt#ApV0g) caret F=qFB`VB!W297x=$mr<$>rco3v$QKih_xN!k6;M=@
381 > zp1#Gw!Qo-$LtvpXt#ApV0g) caret F=qFB`VB!W297x=$mr<$>rco3v$QKih_xN!k6;M=@
382 > zCr?gDNQj7tm@;JwD;Ty&NlBSCYZk(b3dZeN8D4h2{r20dSFc7;(>E&r`s=TVtzpB4
382 > zCr?gDNQj7tm@;JwD;Ty&NlBSCYZk(b3dZeN8D4h2{r20dSFc7;(>E&r`s=TVtzpB4
383 > zk+ caret N&zCAiRns(?p6iBlk9v&h{1ve(FNtc)td51M>)TkXhc6{>5C)`fS$&)A1*CP1%
383 > zk+ caret N&zCAiRns(?p6iBlk9v&h{1ve(FNtc)td51M>)TkXhc6{>5C)`fS$&)A1*CP1%
384 > zld+peue4aYbg3C0!+4mu+}vE caret j_feX+ZijvffBI7Ofh#RZ*U3<3J5(+nfRCzexqQ5
384 > zld+peue4aYbg3C0!+4mu+}vE caret j_feX+ZijvffBI7Ofh#RZ*U3<3J5(+nfRCzexqQ5
385 > zgM&##Y4Dd{e%ZKjqrbm@|Ni}l4jo!AqtFynj3Xsd$o caret ?yV4$|UQ(j&UWCH>M=o_&N
385 > zgM&##Y4Dd{e%ZKjqrbm@|Ni}l4jo!AqtFynj3Xsd$o caret ?yV4$|UQ(j&UWCH>M=o_&N
386 > zmclXc3i|Q#<;#EoG>~V}4unTHbUK}u=y4;rA3S&vzC3 caret aJP!&D4RvvGfoyo(>C>la
386 > zmclXc3i|Q#<;#EoG>~V}4unTHbUK}u=y4;rA3S&vzC3 caret aJP!&D4RvvGfoyo(>C>la
387 > zijP<=v>X{3Ne&2BXo}DV8l0V-jdv`$am0ubG{Wuh%CTd|l9Q7m;G&|U@#Dvbhlj(d
387 > zijP<=v>X{3Ne&2BXo}DV8l0V-jdv`$am0ubG{Wuh%CTd|l9Q7m;G&|U@#Dvbhlj(d
388 > zg6W{3ATxYt#T?)3;SmIgOP4M|Dki~I_TX7SxP0x}wI~DQI7Lhm2BI7gph(aPIFAd;
388 > zg6W{3ATxYt#T?)3;SmIgOP4M|Dki~I_TX7SxP0x}wI~DQI7Lhm2BI7gph(aPIFAd;
389 > zQ&UsF`Q{rOz+z=87c5v%@5u~d6dWV5OlX`oH3cAH&UlvsZUEo(Q(P|lKs17rXvaiU
389 > zQ&UsF`Q{rOz+z=87c5v%@5u~d6dWV5OlX`oH3cAH&UlvsZUEo(Q(P|lKs17rXvaiU
390 > zQcj}IEufi1+Bnh6&(EhF{7O3vLHp`jjlp0J<M1kh$+$2xGm~Zk7OY7(q=&Rdhq*RG
390 > zQcj}IEufi1+Bnh6&(EhF{7O3vLHp`jjlp0J<M1kh$+$2xGm~Zk7OY7(q=&Rdhq*RG
391 > zwrmcd5MnP}xByB_)P@{J>DR9x6;`cUwPM8z){yooNiXPOc9_{W-gtwxE5TUg0vJk6
391 > zwrmcd5MnP}xByB_)P@{J>DR9x6;`cUwPM8z){yooNiXPOc9_{W-gtwxE5TUg0vJk6
392 > zO#JGruV&1cL6VGK2?+_YQr4`+EY8;Sm$9U$uuGRN=uj3k7?O9b+R~J7t_y*K64ZnI
392 > zO#JGruV&1cL6VGK2?+_YQr4`+EY8;Sm$9U$uuGRN=uj3k7?O9b+R~J7t_y*K64ZnI
393 > zM+{aE<b(v?vSmw;9zFP!aE266zHIhlmdI@ caret xa6o2jwdRk54a$>pcRbC29ZyG!Cfdp
393 > zM+{aE<b(v?vSmw;9zFP!aE266zHIhlmdI@ caret xa6o2jwdRk54a$>pcRbC29ZyG!Cfdp
394 > zutFf`Q`vljgo!(wHf=)F#m2_MIuj;L(2ja2YsQRX+rswV{d<H`Ar;(@%aNa9VPU8Z
394 > zutFf`Q`vljgo!(wHf=)F#m2_MIuj;L(2ja2YsQRX+rswV{d<H`Ar;(@%aNa9VPU8Z
395 > z;tq*`y}dm#NDJHKlV}uTIm!_vAq5E7!X-p{P=Z=Sh668>PuVS1*6e}OwOiMc;u3OQ
395 > z;tq*`y}dm#NDJHKlV}uTIm!_vAq5E7!X-p{P=Z=Sh668>PuVS1*6e}OwOiMc;u3OQ
396 > z@Bs)w3=lzfKoufH$SFuPG@uZ4NOnM#+=8LnQ2Q4zUd+nM+OT26;lqbN{P07dhH{jH
396 > z@Bs)w3=lzfKoufH$SFuPG@uZ4NOnM#+=8LnQ2Q4zUd+nM+OT26;lqbN{P07dhH{jH
397 > zManE8 caret dLms-Q2;1kB<*Q1a3f8kZr;xX=!Qro@`~@xN*Qj>gx;i;0Z24!~i2uLb`}v
397 > zManE8 caret dLms-Q2;1kB<*Q1a3f8kZr;xX=!Qro@`~@xN*Qj>gx;i;0Z24!~i2uLb`}v
398 > zA?R$|wvC+m caret Ups=*(4lDh*=UN8{5h(A?p#D caret 2N$8u4Z55!q?ZAh(iEEng9_Zi>IgO
398 > zA?R$|wvC+m caret Ups=*(4lDh*=UN8{5h(A?p#D caret 2N$8u4Z55!q?ZAh(iEEng9_Zi>IgO
399 > z#~**JC8hE4@n{hO&8btT5F*?nC_%LhA3i)PDhh-pB_&1wGrDIl caret *=8x3n&;akBf caret -
399 > z#~**JC8hE4@n{hO&8btT5F*?nC_%LhA3i)PDhh-pB_&1wGrDIl caret *=8x3n&;akBf caret -
400 > zJd&86kq$%%907v caret tgWoQdwI`|oNK%VvU~S#C<o caret F?6c48?Cjj#-4P<>HFD%&|Ni~t
400 > zJd&86kq$%%907v caret tgWoQdwI`|oNK%VvU~S#C<o caret F?6c48?Cjj#-4P<>HFD%&|Ni~t
401 > zKJ(|#H`$<5W+6ZkBb213rXonKZLB+X> caret L}J@W6osP3piLD_5?R!`S}*{xLBzFiL4@
401 > zKJ(|#H`$<5W+6ZkBb213rXonKZLB+X> caret L}J@W6osP3piLD_5?R!`S}*{xLBzFiL4@
402 > zX+}l{`A%?f@T5tT%ztu60p;)be`fWC`tP@WpO=?cpf8Xuf1OSj6d3f@Ki(ovDYq%0
402 > zX+}l{`A%?f@T5tT%ztu60p;)be`fWC`tP@WpO=?cpf8Xuf1OSj6d3f@Ki(ovDYq%0
403 > z{4ZSe`kOay5@=lAT!}vFzxyemC{sXDrhuYM0Y#ZI1r%ipD9W11{w=@&xgJ}t2x;ep
403 > z{4ZSe`kOay5@=lAT!}vFzxyemC{sXDrhuYM0Y#ZI1r%ipD9W11{w=@&xgJ}t2x;ep
404 > P00000NkvXXu0mjfZ5|Er
404 > P00000NkvXXu0mjfZ5|Er
405 >
405 >
406 > literal 0
406 > literal 0
407 > HcmV?d00001
407 > HcmV?d00001
408 >
408 >
409 > EOF
409 > EOF
410 $ hg import -d "1000000 0" -m delta quote-hack.patch
410 $ hg import -d "1000000 0" -m delta quote-hack.patch
411 applying quote-hack.patch
411 applying quote-hack.patch
412 $ rm quote-hack.patch
412 $ rm quote-hack.patch
413
413
414 $ hg manifest --debug | grep delta
414 $ hg manifest --debug | grep delta
415 9600f98bb60ce732634d126aaa4ac1ec959c573e 644 delta
415 9600f98bb60ce732634d126aaa4ac1ec959c573e 644 delta
416
416
417 $ hg import -d "1000000 0" -m delta - <<'EOF'
417 $ hg import -d "1000000 0" -m delta - <<'EOF'
418 > diff --git a/delta b/delta
418 > diff --git a/delta b/delta
419 > index 8c9b7831b231c2600843e303e66b521353a200b3..0021dd95bc0dba53c39ce81377126d43731d68df 100644
419 > index 8c9b7831b231c2600843e303e66b521353a200b3..0021dd95bc0dba53c39ce81377126d43731d68df 100644
420 > GIT binary patch
420 > GIT binary patch
421 > delta 49
421 > delta 49
422 > zcmZ1~yHs|=21Z8J$r~9bFdA-lVv=EEw4WT$qRf2QSa5SIOAHI6(&k4T8H|kLo4vWB
422 > zcmZ1~yHs|=21Z8J$r~9bFdA-lVv=EEw4WT$qRf2QSa5SIOAHI6(&k4T8H|kLo4vWB
423 > FSO9ZT4bA`n
423 > FSO9ZT4bA`n
424 >
424 >
425 > delta 49
425 > delta 49
426 > zcmV-10M7rV9i<(xumJ(}ld%Di0Xefm0vrMXpOaq%BLm9I%d>?9Tm%6Vv*HM70RcC&
426 > zcmV-10M7rV9i<(xumJ(}ld%Di0Xefm0vrMXpOaq%BLm9I%d>?9Tm%6Vv*HM70RcC&
427 > HOA1;9yU-AD
427 > HOA1;9yU-AD
428 >
428 >
429 > EOF
429 > EOF
430 applying patch from stdin
430 applying patch from stdin
431
431
432 $ hg manifest --debug | grep delta
432 $ hg manifest --debug | grep delta
433 56094bbea136dcf8dbd4088f6af469bde1a98b75 644 delta
433 56094bbea136dcf8dbd4088f6af469bde1a98b75 644 delta
434
434
435 Filenames with spaces:
435 Filenames with spaces:
436
436
437 $ sed 's,EOL$,,g' <<EOF | hg import -d "1000000 0" -m spaces -
437 $ sed 's,EOL$,,g' <<EOF | hg import -d "1000000 0" -m spaces -
438 > diff --git a/foo bar b/foo bar
438 > diff --git a/foo bar b/foo bar
439 > new file mode 100644
439 > new file mode 100644
440 > index 0000000..257cc56
440 > index 0000000..257cc56
441 > --- /dev/null
441 > --- /dev/null
442 > +++ b/foo bar EOL
442 > +++ b/foo bar EOL
443 > @@ -0,0 +1 @@
443 > @@ -0,0 +1 @@
444 > +foo
444 > +foo
445 > EOF
445 > EOF
446 applying patch from stdin
446 applying patch from stdin
447
447
448 $ hg tip -q
448 $ hg tip -q
449 14:4b79479c9a6d
449 14:4b79479c9a6d
450
450
451 $ cat "foo bar"
451 $ cat "foo bar"
452 foo
452 foo
453
453
454 Copy then modify the original file:
454 Copy then modify the original file:
455
455
456 $ hg import -d "1000000 0" -m copy-mod-orig - <<EOF
456 $ hg import -d "1000000 0" -m copy-mod-orig - <<EOF
457 > diff --git a/foo2 b/foo2
457 > diff --git a/foo2 b/foo2
458 > index 257cc56..fe08ec6 100644
458 > index 257cc56..fe08ec6 100644
459 > --- a/foo2
459 > --- a/foo2
460 > +++ b/foo2
460 > +++ b/foo2
461 > @@ -1 +1,2 @@
461 > @@ -1 +1,2 @@
462 > foo
462 > foo
463 > +new line
463 > +new line
464 > diff --git a/foo2 b/foo3
464 > diff --git a/foo2 b/foo3
465 > similarity index 100%
465 > similarity index 100%
466 > copy from foo2
466 > copy from foo2
467 > copy to foo3
467 > copy to foo3
468 > EOF
468 > EOF
469 applying patch from stdin
469 applying patch from stdin
470
470
471 $ hg tip -q
471 $ hg tip -q
472 15:9cbe44af4ae9
472 15:9cbe44af4ae9
473
473
474 $ cat foo3
474 $ cat foo3
475 foo
475 foo
476
476
477 Move text file and patch as binary
477 Move text file and patch as binary
478
478
479 $ echo a > text2
479 $ echo a > text2
480 $ hg ci -Am0
480 $ hg ci -Am0
481 adding text2
481 adding text2
482 $ hg import -d "1000000 0" -m rename-as-binary - <<"EOF"
482 $ hg import -d "1000000 0" -m rename-as-binary - <<"EOF"
483 > diff --git a/text2 b/binary2
483 > diff --git a/text2 b/binary2
484 > rename from text2
484 > rename from text2
485 > rename to binary2
485 > rename to binary2
486 > index 78981922613b2afb6025042ff6bd878ac1994e85..10efcb362e9f3b3420fcfbfc0e37f3dc16e29757
486 > index 78981922613b2afb6025042ff6bd878ac1994e85..10efcb362e9f3b3420fcfbfc0e37f3dc16e29757
487 > GIT binary patch
487 > GIT binary patch
488 > literal 5
488 > literal 5
489 > Mc$`b*O5$Pw00T?_*Z=?k
489 > Mc$`b*O5$Pw00T?_*Z=?k
490 >
490 >
491 > EOF
491 > EOF
492 applying patch from stdin
492 applying patch from stdin
493
493
494 $ cat binary2
494 $ cat binary2
495 a
495 a
496 b
496 b
497 \x00 (no-eol) (esc)
497 \x00 (no-eol) (esc)
498
498
499 $ hg st --copies --change .
499 $ hg st --copies --change .
500 A binary2
500 A binary2
501 text2
501 text2
502 R text2
502 R text2
503
503
504 Invalid base85 content
504 Invalid base85 content
505
505
506 $ hg rollback
506 $ hg rollback
507 repository tip rolled back to revision 16 (undo import)
507 repository tip rolled back to revision 16 (undo import)
508 working directory now based on revision 16
508 working directory now based on revision 16
509 $ hg revert -aq
509 $ hg revert -aq
510 $ hg import -d "1000000 0" -m invalid-binary - <<"EOF"
510 $ hg import -d "1000000 0" -m invalid-binary - <<"EOF"
511 > diff --git a/text2 b/binary2
511 > diff --git a/text2 b/binary2
512 > rename from text2
512 > rename from text2
513 > rename to binary2
513 > rename to binary2
514 > index 78981922613b2afb6025042ff6bd878ac1994e85..10efcb362e9f3b3420fcfbfc0e37f3dc16e29757
514 > index 78981922613b2afb6025042ff6bd878ac1994e85..10efcb362e9f3b3420fcfbfc0e37f3dc16e29757
515 > GIT binary patch
515 > GIT binary patch
516 > literal 5
516 > literal 5
517 > Mc$`b*O.$Pw00T?_*Z=?k
517 > Mc$`b*O.$Pw00T?_*Z=?k
518 >
518 >
519 > EOF
519 > EOF
520 applying patch from stdin
520 applying patch from stdin
521 abort: could not decode "binary2" binary patch: bad base85 character at position 6
521 abort: could not decode "binary2" binary patch: bad base85 character at position 6
522 [255]
522 [255]
523
523
524 $ hg revert -aq
524 $ hg revert -aq
525 $ hg import -d "1000000 0" -m rename-as-binary - <<"EOF"
525 $ hg import -d "1000000 0" -m rename-as-binary - <<"EOF"
526 > diff --git a/text2 b/binary2
526 > diff --git a/text2 b/binary2
527 > rename from text2
527 > rename from text2
528 > rename to binary2
528 > rename to binary2
529 > index 78981922613b2afb6025042ff6bd878ac1994e85..10efcb362e9f3b3420fcfbfc0e37f3dc16e29757
529 > index 78981922613b2afb6025042ff6bd878ac1994e85..10efcb362e9f3b3420fcfbfc0e37f3dc16e29757
530 > GIT binary patch
530 > GIT binary patch
531 > literal 6
531 > literal 6
532 > Mc$`b*O5$Pw00T?_*Z=?k
532 > Mc$`b*O5$Pw00T?_*Z=?k
533 >
533 >
534 > EOF
534 > EOF
535 applying patch from stdin
535 applying patch from stdin
536 abort: "binary2" length is 5 bytes, should be 6
536 abort: "binary2" length is 5 bytes, should be 6
537 [255]
537 [255]
538
538
539 $ hg revert -aq
539 $ hg revert -aq
540 $ hg import -d "1000000 0" -m rename-as-binary - <<"EOF"
540 $ hg import -d "1000000 0" -m rename-as-binary - <<"EOF"
541 > diff --git a/text2 b/binary2
541 > diff --git a/text2 b/binary2
542 > rename from text2
542 > rename from text2
543 > rename to binary2
543 > rename to binary2
544 > index 78981922613b2afb6025042ff6bd878ac1994e85..10efcb362e9f3b3420fcfbfc0e37f3dc16e29757
544 > index 78981922613b2afb6025042ff6bd878ac1994e85..10efcb362e9f3b3420fcfbfc0e37f3dc16e29757
545 > GIT binary patch
545 > GIT binary patch
546 > Mc$`b*O5$Pw00T?_*Z=?k
546 > Mc$`b*O5$Pw00T?_*Z=?k
547 >
547 >
548 > EOF
548 > EOF
549 applying patch from stdin
549 applying patch from stdin
550 abort: could not extract "binary2" binary data
550 abort: could not extract "binary2" binary data
551 [255]
551 [255]
552
552
553 Simulate a copy/paste turning LF into CRLF (issue2870)
553 Simulate a copy/paste turning LF into CRLF (issue2870)
554
554
555 $ hg revert -aq
555 $ hg revert -aq
556 $ cat > binary.diff <<"EOF"
556 $ cat > binary.diff <<"EOF"
557 > diff --git a/text2 b/binary2
557 > diff --git a/text2 b/binary2
558 > rename from text2
558 > rename from text2
559 > rename to binary2
559 > rename to binary2
560 > index 78981922613b2afb6025042ff6bd878ac1994e85..10efcb362e9f3b3420fcfbfc0e37f3dc16e29757
560 > index 78981922613b2afb6025042ff6bd878ac1994e85..10efcb362e9f3b3420fcfbfc0e37f3dc16e29757
561 > GIT binary patch
561 > GIT binary patch
562 > literal 5
562 > literal 5
563 > Mc$`b*O5$Pw00T?_*Z=?k
563 > Mc$`b*O5$Pw00T?_*Z=?k
564 >
564 >
565 > EOF
565 > EOF
566 >>> fp = file('binary.diff', 'rb')
566 >>> fp = file('binary.diff', 'rb')
567 >>> data = fp.read()
567 >>> data = fp.read()
568 >>> fp.close()
568 >>> fp.close()
569 >>> file('binary.diff', 'wb').write(data.replace('\n', '\r\n'))
569 >>> file('binary.diff', 'wb').write(data.replace('\n', '\r\n'))
570 $ rm binary2
570 $ rm binary2
571 $ hg import --no-commit binary.diff
571 $ hg import --no-commit binary.diff
572 applying binary.diff
572 applying binary.diff
573
573
574 $ cd ..
574 $ cd ..
575
575
576 Consecutive import with renames (issue2459)
576 Consecutive import with renames (issue2459)
577
577
578 $ hg init issue2459
578 $ hg init issue2459
579 $ cd issue2459
579 $ cd issue2459
580 $ hg import --no-commit --force - <<EOF
580 $ hg import --no-commit --force - <<EOF
581 > diff --git a/a b/a
581 > diff --git a/a b/a
582 > new file mode 100644
582 > new file mode 100644
583 > EOF
583 > EOF
584 applying patch from stdin
584 applying patch from stdin
585 $ hg import --no-commit --force - <<EOF
585 $ hg import --no-commit --force - <<EOF
586 > diff --git a/a b/b
586 > diff --git a/a b/b
587 > rename from a
587 > rename from a
588 > rename to b
588 > rename to b
589 > EOF
589 > EOF
590 applying patch from stdin
590 applying patch from stdin
591 a has not been committed yet, so no copy data will be stored for b.
591 a has not been committed yet, so no copy data will be stored for b.
592 $ hg debugstate
592 $ hg debugstate
593 a 0 -1 unset b
593 a 0 -1 unset b
594 $ hg ci -m done
594 $ hg ci -m done
595 $ cd ..
595 $ cd ..
596
596
597 Renames and strip
597 Renames and strip
598
598
599 $ hg init renameandstrip
599 $ hg init renameandstrip
600 $ cd renameandstrip
600 $ cd renameandstrip
601 $ echo a > a
601 $ echo a > a
602 $ hg ci -Am adda
602 $ hg ci -Am adda
603 adding a
603 adding a
604 $ hg import --no-commit -p2 - <<EOF
604 $ hg import --no-commit -p2 - <<EOF
605 > diff --git a/foo/a b/foo/b
605 > diff --git a/foo/a b/foo/b
606 > rename from foo/a
606 > rename from foo/a
607 > rename to foo/b
607 > rename to foo/b
608 > EOF
608 > EOF
609 applying patch from stdin
609 applying patch from stdin
610 $ hg st --copies
610 $ hg st --copies
611 A b
611 A b
612 a
612 a
613 R a
613 R a
614
614
615 Prefix with strip, renames, creates etc
615 Prefix with strip, renames, creates etc
616
616
617 $ hg revert -aC
617 $ hg revert -aC
618 undeleting a
618 undeleting a
619 forgetting b
619 forgetting b
620 $ rm b
620 $ rm b
621 $ mkdir -p dir/dir2
621 $ mkdir -p dir/dir2
622 $ echo b > dir/dir2/b
622 $ echo b > dir/dir2/b
623 $ echo c > dir/dir2/c
623 $ echo c > dir/dir2/c
624 $ echo d > dir/d
624 $ echo d > dir/d
625 $ hg ci -Am addbcd
625 $ hg ci -Am addbcd
626 adding dir/d
626 adding dir/d
627 adding dir/dir2/b
627 adding dir/dir2/b
628 adding dir/dir2/c
628 adding dir/dir2/c
629
630 prefix '.' is the same as no prefix
631 $ hg import --no-commit --prefix . - <<EOF
632 > diff --git a/dir/a b/dir/a
633 > --- /dev/null
634 > +++ b/dir/a
635 > @@ -0,0 +1 @@
636 > +aaaa
637 > diff --git a/dir/d b/dir/d
638 > --- a/dir/d
639 > +++ b/dir/d
640 > @@ -1,1 +1,2 @@
641 > d
642 > +dddd
643 > EOF
644 applying patch from stdin
645 $ cat dir/a
646 aaaa
647 $ cat dir/d
648 d
649 dddd
650 $ hg revert -aC
651 forgetting dir/a (glob)
652 reverting dir/d (glob)
653 $ rm dir/a
654
655 prefix with default strip
629 $ hg import --no-commit --prefix dir/ - <<EOF
656 $ hg import --no-commit --prefix dir/ - <<EOF
630 > diff --git a/a b/a
657 > diff --git a/a b/a
631 > --- /dev/null
658 > --- /dev/null
632 > +++ b/a
659 > +++ b/a
633 > @@ -0,0 +1 @@
660 > @@ -0,0 +1 @@
634 > +aaa
661 > +aaa
635 > diff --git a/d b/d
662 > diff --git a/d b/d
636 > --- a/d
663 > --- a/d
637 > +++ b/d
664 > +++ b/d
638 > @@ -1,1 +1,2 @@
665 > @@ -1,1 +1,2 @@
639 > d
666 > d
640 > +dd
667 > +dd
641 > EOF
668 > EOF
642 applying patch from stdin
669 applying patch from stdin
643 $ cat dir/a
670 $ cat dir/a
644 aaa
671 aaa
645 $ cat dir/d
672 $ cat dir/d
646 d
673 d
647 dd
674 dd
648 $ hg revert -aC
675 $ hg revert -aC
649 forgetting dir/a (glob)
676 forgetting dir/a (glob)
650 reverting dir/d (glob)
677 reverting dir/d (glob)
651 $ rm dir/a
678 $ rm dir/a
652 (test that prefixes are relative to the root)
679 (test that prefixes are relative to the cwd)
653 $ mkdir tmpdir
680 $ mkdir tmpdir
654 $ cd tmpdir
681 $ cd tmpdir
655 $ hg import --no-commit -p2 --prefix dir/ - <<EOF
682 $ hg import --no-commit -p2 --prefix ../dir/ - <<EOF
656 > diff --git a/foo/a b/foo/a
683 > diff --git a/foo/a b/foo/a
657 > new file mode 100644
684 > new file mode 100644
658 > --- /dev/null
685 > --- /dev/null
659 > +++ b/foo/a
686 > +++ b/foo/a
660 > @@ -0,0 +1 @@
687 > @@ -0,0 +1 @@
661 > +a
688 > +a
662 > diff --git a/foo/dir2/b b/foo/dir2/b2
689 > diff --git a/foo/dir2/b b/foo/dir2/b2
663 > rename from foo/dir2/b
690 > rename from foo/dir2/b
664 > rename to foo/dir2/b2
691 > rename to foo/dir2/b2
665 > diff --git a/foo/dir2/c b/foo/dir2/c
692 > diff --git a/foo/dir2/c b/foo/dir2/c
666 > --- a/foo/dir2/c
693 > --- a/foo/dir2/c
667 > +++ b/foo/dir2/c
694 > +++ b/foo/dir2/c
668 > @@ -0,0 +1 @@
695 > @@ -0,0 +1 @@
669 > +cc
696 > +cc
670 > diff --git a/foo/d b/foo/d
697 > diff --git a/foo/d b/foo/d
671 > deleted file mode 100644
698 > deleted file mode 100644
672 > --- a/foo/d
699 > --- a/foo/d
673 > +++ /dev/null
700 > +++ /dev/null
674 > @@ -1,1 +0,0 @@
701 > @@ -1,1 +0,0 @@
675 > -d
702 > -d
676 > EOF
703 > EOF
677 applying patch from stdin
704 applying patch from stdin
678 $ hg st --copies
705 $ hg st --copies
679 M dir/dir2/c
706 M dir/dir2/c
680 A dir/a
707 A dir/a
681 A dir/dir2/b2
708 A dir/dir2/b2
682 dir/dir2/b
709 dir/dir2/b
683 R dir/d
710 R dir/d
684 R dir/dir2/b
711 R dir/dir2/b
685 $ cd ..
712 $ cd ..
686
713
687 Renames, similarity and git diff
714 Renames, similarity and git diff
688
715
689 $ hg revert -aC
716 $ hg revert -aC
690 forgetting dir/a (glob)
717 forgetting dir/a (glob)
691 undeleting dir/d (glob)
718 undeleting dir/d (glob)
692 undeleting dir/dir2/b (glob)
719 undeleting dir/dir2/b (glob)
693 forgetting dir/dir2/b2 (glob)
720 forgetting dir/dir2/b2 (glob)
694 reverting dir/dir2/c (glob)
721 reverting dir/dir2/c (glob)
695 $ rm dir/a dir/dir2/b2
722 $ rm dir/a dir/dir2/b2
696 $ hg import --similarity 90 --no-commit - <<EOF
723 $ hg import --similarity 90 --no-commit - <<EOF
697 > diff --git a/a b/b
724 > diff --git a/a b/b
698 > rename from a
725 > rename from a
699 > rename to b
726 > rename to b
700 > EOF
727 > EOF
701 applying patch from stdin
728 applying patch from stdin
702 $ hg st --copies
729 $ hg st --copies
703 A b
730 A b
704 a
731 a
705 R a
732 R a
706 $ cd ..
733 $ cd ..
707
734
708 Pure copy with existing destination
735 Pure copy with existing destination
709
736
710 $ hg init copytoexisting
737 $ hg init copytoexisting
711 $ cd copytoexisting
738 $ cd copytoexisting
712 $ echo a > a
739 $ echo a > a
713 $ echo b > b
740 $ echo b > b
714 $ hg ci -Am add
741 $ hg ci -Am add
715 adding a
742 adding a
716 adding b
743 adding b
717 $ hg import --no-commit - <<EOF
744 $ hg import --no-commit - <<EOF
718 > diff --git a/a b/b
745 > diff --git a/a b/b
719 > copy from a
746 > copy from a
720 > copy to b
747 > copy to b
721 > EOF
748 > EOF
722 applying patch from stdin
749 applying patch from stdin
723 abort: cannot create b: destination already exists
750 abort: cannot create b: destination already exists
724 [255]
751 [255]
725 $ cat b
752 $ cat b
726 b
753 b
727
754
728 Copy and changes with existing destination
755 Copy and changes with existing destination
729
756
730 $ hg import --no-commit - <<EOF
757 $ hg import --no-commit - <<EOF
731 > diff --git a/a b/b
758 > diff --git a/a b/b
732 > copy from a
759 > copy from a
733 > copy to b
760 > copy to b
734 > --- a/a
761 > --- a/a
735 > +++ b/b
762 > +++ b/b
736 > @@ -1,1 +1,2 @@
763 > @@ -1,1 +1,2 @@
737 > a
764 > a
738 > +b
765 > +b
739 > EOF
766 > EOF
740 applying patch from stdin
767 applying patch from stdin
741 cannot create b: destination already exists
768 cannot create b: destination already exists
742 1 out of 1 hunks FAILED -- saving rejects to file b.rej
769 1 out of 1 hunks FAILED -- saving rejects to file b.rej
743 abort: patch failed to apply
770 abort: patch failed to apply
744 [255]
771 [255]
745 $ cat b
772 $ cat b
746 b
773 b
747
774
748 #if symlink
775 #if symlink
749
776
750 $ ln -s b linkb
777 $ ln -s b linkb
751 $ hg add linkb
778 $ hg add linkb
752 $ hg ci -m addlinkb
779 $ hg ci -m addlinkb
753 $ hg import --no-commit - <<EOF
780 $ hg import --no-commit - <<EOF
754 > diff --git a/linkb b/linkb
781 > diff --git a/linkb b/linkb
755 > deleted file mode 120000
782 > deleted file mode 120000
756 > --- a/linkb
783 > --- a/linkb
757 > +++ /dev/null
784 > +++ /dev/null
758 > @@ -1,1 +0,0 @@
785 > @@ -1,1 +0,0 @@
759 > -badhunk
786 > -badhunk
760 > \ No newline at end of file
787 > \ No newline at end of file
761 > EOF
788 > EOF
762 applying patch from stdin
789 applying patch from stdin
763 patching file linkb
790 patching file linkb
764 Hunk #1 FAILED at 0
791 Hunk #1 FAILED at 0
765 1 out of 1 hunks FAILED -- saving rejects to file linkb.rej
792 1 out of 1 hunks FAILED -- saving rejects to file linkb.rej
766 abort: patch failed to apply
793 abort: patch failed to apply
767 [255]
794 [255]
768 $ hg st
795 $ hg st
769 ? b.rej
796 ? b.rej
770 ? linkb.rej
797 ? linkb.rej
771
798
772 #endif
799 #endif
773
800
774 Test corner case involving copies and multiple hunks (issue3384)
801 Test corner case involving copies and multiple hunks (issue3384)
775
802
776 $ hg revert -qa
803 $ hg revert -qa
777 $ hg import --no-commit - <<EOF
804 $ hg import --no-commit - <<EOF
778 > diff --git a/a b/c
805 > diff --git a/a b/c
779 > copy from a
806 > copy from a
780 > copy to c
807 > copy to c
781 > --- a/a
808 > --- a/a
782 > +++ b/c
809 > +++ b/c
783 > @@ -1,1 +1,2 @@
810 > @@ -1,1 +1,2 @@
784 > a
811 > a
785 > +a
812 > +a
786 > @@ -2,1 +2,2 @@
813 > @@ -2,1 +2,2 @@
787 > a
814 > a
788 > +a
815 > +a
789 > diff --git a/a b/a
816 > diff --git a/a b/a
790 > --- a/a
817 > --- a/a
791 > +++ b/a
818 > +++ b/a
792 > @@ -1,1 +1,2 @@
819 > @@ -1,1 +1,2 @@
793 > a
820 > a
794 > +b
821 > +b
795 > EOF
822 > EOF
796 applying patch from stdin
823 applying patch from stdin
797
824
798 $ cd ..
825 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now