##// END OF EJS Templates
merge with stable
Yuya Nishihara -
r40712:1a6bb5a8 merge default
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,6203 +1,6204 b''
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 __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import difflib
10 import difflib
11 import errno
11 import errno
12 import os
12 import os
13 import re
13 import re
14 import sys
14 import sys
15
15
16 from .i18n import _
16 from .i18n import _
17 from .node import (
17 from .node import (
18 hex,
18 hex,
19 nullid,
19 nullid,
20 nullrev,
20 nullrev,
21 short,
21 short,
22 wdirhex,
22 wdirhex,
23 wdirrev,
23 wdirrev,
24 )
24 )
25 from . import (
25 from . import (
26 archival,
26 archival,
27 bookmarks,
27 bookmarks,
28 bundle2,
28 bundle2,
29 changegroup,
29 changegroup,
30 cmdutil,
30 cmdutil,
31 copies,
31 copies,
32 debugcommands as debugcommandsmod,
32 debugcommands as debugcommandsmod,
33 destutil,
33 destutil,
34 dirstateguard,
34 dirstateguard,
35 discovery,
35 discovery,
36 encoding,
36 encoding,
37 error,
37 error,
38 exchange,
38 exchange,
39 extensions,
39 extensions,
40 filemerge,
40 filemerge,
41 formatter,
41 formatter,
42 graphmod,
42 graphmod,
43 hbisect,
43 hbisect,
44 help,
44 help,
45 hg,
45 hg,
46 logcmdutil,
46 logcmdutil,
47 merge as mergemod,
47 merge as mergemod,
48 narrowspec,
48 narrowspec,
49 obsolete,
49 obsolete,
50 obsutil,
50 obsutil,
51 patch,
51 patch,
52 phases,
52 phases,
53 pycompat,
53 pycompat,
54 rcutil,
54 rcutil,
55 registrar,
55 registrar,
56 repair,
56 repair,
57 revsetlang,
57 revsetlang,
58 rewriteutil,
58 rewriteutil,
59 scmutil,
59 scmutil,
60 server,
60 server,
61 state as statemod,
61 state as statemod,
62 streamclone,
62 streamclone,
63 tags as tagsmod,
63 tags as tagsmod,
64 templatekw,
64 templatekw,
65 ui as uimod,
65 ui as uimod,
66 util,
66 util,
67 wireprotoserver,
67 wireprotoserver,
68 )
68 )
69 from .utils import (
69 from .utils import (
70 dateutil,
70 dateutil,
71 stringutil,
71 stringutil,
72 )
72 )
73
73
74 table = {}
74 table = {}
75 table.update(debugcommandsmod.command._table)
75 table.update(debugcommandsmod.command._table)
76
76
77 command = registrar.command(table)
77 command = registrar.command(table)
78 INTENT_READONLY = registrar.INTENT_READONLY
78 INTENT_READONLY = registrar.INTENT_READONLY
79
79
80 # common command options
80 # common command options
81
81
82 globalopts = [
82 globalopts = [
83 ('R', 'repository', '',
83 ('R', 'repository', '',
84 _('repository root directory or name of overlay bundle file'),
84 _('repository root directory or name of overlay bundle file'),
85 _('REPO')),
85 _('REPO')),
86 ('', 'cwd', '',
86 ('', 'cwd', '',
87 _('change working directory'), _('DIR')),
87 _('change working directory'), _('DIR')),
88 ('y', 'noninteractive', None,
88 ('y', 'noninteractive', None,
89 _('do not prompt, automatically pick the first choice for all prompts')),
89 _('do not prompt, automatically pick the first choice for all prompts')),
90 ('q', 'quiet', None, _('suppress output')),
90 ('q', 'quiet', None, _('suppress output')),
91 ('v', 'verbose', None, _('enable additional output')),
91 ('v', 'verbose', None, _('enable additional output')),
92 ('', 'color', '',
92 ('', 'color', '',
93 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
93 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
94 # and should not be translated
94 # and should not be translated
95 _("when to colorize (boolean, always, auto, never, or debug)"),
95 _("when to colorize (boolean, always, auto, never, or debug)"),
96 _('TYPE')),
96 _('TYPE')),
97 ('', 'config', [],
97 ('', 'config', [],
98 _('set/override config option (use \'section.name=value\')'),
98 _('set/override config option (use \'section.name=value\')'),
99 _('CONFIG')),
99 _('CONFIG')),
100 ('', 'debug', None, _('enable debugging output')),
100 ('', 'debug', None, _('enable debugging output')),
101 ('', 'debugger', None, _('start debugger')),
101 ('', 'debugger', None, _('start debugger')),
102 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
102 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
103 _('ENCODE')),
103 _('ENCODE')),
104 ('', 'encodingmode', encoding.encodingmode,
104 ('', 'encodingmode', encoding.encodingmode,
105 _('set the charset encoding mode'), _('MODE')),
105 _('set the charset encoding mode'), _('MODE')),
106 ('', 'traceback', None, _('always print a traceback on exception')),
106 ('', 'traceback', None, _('always print a traceback on exception')),
107 ('', 'time', None, _('time how long the command takes')),
107 ('', 'time', None, _('time how long the command takes')),
108 ('', 'profile', None, _('print command execution profile')),
108 ('', 'profile', None, _('print command execution profile')),
109 ('', 'version', None, _('output version information and exit')),
109 ('', 'version', None, _('output version information and exit')),
110 ('h', 'help', None, _('display help and exit')),
110 ('h', 'help', None, _('display help and exit')),
111 ('', 'hidden', False, _('consider hidden changesets')),
111 ('', 'hidden', False, _('consider hidden changesets')),
112 ('', 'pager', 'auto',
112 ('', 'pager', 'auto',
113 _("when to paginate (boolean, always, auto, or never)"), _('TYPE')),
113 _("when to paginate (boolean, always, auto, or never)"), _('TYPE')),
114 ]
114 ]
115
115
116 dryrunopts = cmdutil.dryrunopts
116 dryrunopts = cmdutil.dryrunopts
117 remoteopts = cmdutil.remoteopts
117 remoteopts = cmdutil.remoteopts
118 walkopts = cmdutil.walkopts
118 walkopts = cmdutil.walkopts
119 commitopts = cmdutil.commitopts
119 commitopts = cmdutil.commitopts
120 commitopts2 = cmdutil.commitopts2
120 commitopts2 = cmdutil.commitopts2
121 formatteropts = cmdutil.formatteropts
121 formatteropts = cmdutil.formatteropts
122 templateopts = cmdutil.templateopts
122 templateopts = cmdutil.templateopts
123 logopts = cmdutil.logopts
123 logopts = cmdutil.logopts
124 diffopts = cmdutil.diffopts
124 diffopts = cmdutil.diffopts
125 diffwsopts = cmdutil.diffwsopts
125 diffwsopts = cmdutil.diffwsopts
126 diffopts2 = cmdutil.diffopts2
126 diffopts2 = cmdutil.diffopts2
127 mergetoolopts = cmdutil.mergetoolopts
127 mergetoolopts = cmdutil.mergetoolopts
128 similarityopts = cmdutil.similarityopts
128 similarityopts = cmdutil.similarityopts
129 subrepoopts = cmdutil.subrepoopts
129 subrepoopts = cmdutil.subrepoopts
130 debugrevlogopts = cmdutil.debugrevlogopts
130 debugrevlogopts = cmdutil.debugrevlogopts
131
131
132 # Commands start here, listed alphabetically
132 # Commands start here, listed alphabetically
133
133
134 @command('add',
134 @command('add',
135 walkopts + subrepoopts + dryrunopts,
135 walkopts + subrepoopts + dryrunopts,
136 _('[OPTION]... [FILE]...'),
136 _('[OPTION]... [FILE]...'),
137 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
137 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
138 helpbasic=True, inferrepo=True)
138 helpbasic=True, inferrepo=True)
139 def add(ui, repo, *pats, **opts):
139 def add(ui, repo, *pats, **opts):
140 """add the specified files on the next commit
140 """add the specified files on the next commit
141
141
142 Schedule files to be version controlled and added to the
142 Schedule files to be version controlled and added to the
143 repository.
143 repository.
144
144
145 The files will be added to the repository at the next commit. To
145 The files will be added to the repository at the next commit. To
146 undo an add before that, see :hg:`forget`.
146 undo an add before that, see :hg:`forget`.
147
147
148 If no names are given, add all files to the repository (except
148 If no names are given, add all files to the repository (except
149 files matching ``.hgignore``).
149 files matching ``.hgignore``).
150
150
151 .. container:: verbose
151 .. container:: verbose
152
152
153 Examples:
153 Examples:
154
154
155 - New (unknown) files are added
155 - New (unknown) files are added
156 automatically by :hg:`add`::
156 automatically by :hg:`add`::
157
157
158 $ ls
158 $ ls
159 foo.c
159 foo.c
160 $ hg status
160 $ hg status
161 ? foo.c
161 ? foo.c
162 $ hg add
162 $ hg add
163 adding foo.c
163 adding foo.c
164 $ hg status
164 $ hg status
165 A foo.c
165 A foo.c
166
166
167 - Specific files to be added can be specified::
167 - Specific files to be added can be specified::
168
168
169 $ ls
169 $ ls
170 bar.c foo.c
170 bar.c foo.c
171 $ hg status
171 $ hg status
172 ? bar.c
172 ? bar.c
173 ? foo.c
173 ? foo.c
174 $ hg add bar.c
174 $ hg add bar.c
175 $ hg status
175 $ hg status
176 A bar.c
176 A bar.c
177 ? foo.c
177 ? foo.c
178
178
179 Returns 0 if all files are successfully added.
179 Returns 0 if all files are successfully added.
180 """
180 """
181
181
182 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
182 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
183 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
183 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
184 return rejected and 1 or 0
184 return rejected and 1 or 0
185
185
186 @command('addremove',
186 @command('addremove',
187 similarityopts + subrepoopts + walkopts + dryrunopts,
187 similarityopts + subrepoopts + walkopts + dryrunopts,
188 _('[OPTION]... [FILE]...'),
188 _('[OPTION]... [FILE]...'),
189 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
189 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
190 inferrepo=True)
190 inferrepo=True)
191 def addremove(ui, repo, *pats, **opts):
191 def addremove(ui, repo, *pats, **opts):
192 """add all new files, delete all missing files
192 """add all new files, delete all missing files
193
193
194 Add all new files and remove all missing files from the
194 Add all new files and remove all missing files from the
195 repository.
195 repository.
196
196
197 Unless names are given, new files are ignored if they match any of
197 Unless names are given, new files are ignored if they match any of
198 the patterns in ``.hgignore``. As with add, these changes take
198 the patterns in ``.hgignore``. As with add, these changes take
199 effect at the next commit.
199 effect at the next commit.
200
200
201 Use the -s/--similarity option to detect renamed files. This
201 Use the -s/--similarity option to detect renamed files. This
202 option takes a percentage between 0 (disabled) and 100 (files must
202 option takes a percentage between 0 (disabled) and 100 (files must
203 be identical) as its parameter. With a parameter greater than 0,
203 be identical) as its parameter. With a parameter greater than 0,
204 this compares every removed file with every added file and records
204 this compares every removed file with every added file and records
205 those similar enough as renames. Detecting renamed files this way
205 those similar enough as renames. Detecting renamed files this way
206 can be expensive. After using this option, :hg:`status -C` can be
206 can be expensive. After using this option, :hg:`status -C` can be
207 used to check which files were identified as moved or renamed. If
207 used to check which files were identified as moved or renamed. If
208 not specified, -s/--similarity defaults to 100 and only renames of
208 not specified, -s/--similarity defaults to 100 and only renames of
209 identical files are detected.
209 identical files are detected.
210
210
211 .. container:: verbose
211 .. container:: verbose
212
212
213 Examples:
213 Examples:
214
214
215 - A number of files (bar.c and foo.c) are new,
215 - A number of files (bar.c and foo.c) are new,
216 while foobar.c has been removed (without using :hg:`remove`)
216 while foobar.c has been removed (without using :hg:`remove`)
217 from the repository::
217 from the repository::
218
218
219 $ ls
219 $ ls
220 bar.c foo.c
220 bar.c foo.c
221 $ hg status
221 $ hg status
222 ! foobar.c
222 ! foobar.c
223 ? bar.c
223 ? bar.c
224 ? foo.c
224 ? foo.c
225 $ hg addremove
225 $ hg addremove
226 adding bar.c
226 adding bar.c
227 adding foo.c
227 adding foo.c
228 removing foobar.c
228 removing foobar.c
229 $ hg status
229 $ hg status
230 A bar.c
230 A bar.c
231 A foo.c
231 A foo.c
232 R foobar.c
232 R foobar.c
233
233
234 - A file foobar.c was moved to foo.c without using :hg:`rename`.
234 - A file foobar.c was moved to foo.c without using :hg:`rename`.
235 Afterwards, it was edited slightly::
235 Afterwards, it was edited slightly::
236
236
237 $ ls
237 $ ls
238 foo.c
238 foo.c
239 $ hg status
239 $ hg status
240 ! foobar.c
240 ! foobar.c
241 ? foo.c
241 ? foo.c
242 $ hg addremove --similarity 90
242 $ hg addremove --similarity 90
243 removing foobar.c
243 removing foobar.c
244 adding foo.c
244 adding foo.c
245 recording removal of foobar.c as rename to foo.c (94% similar)
245 recording removal of foobar.c as rename to foo.c (94% similar)
246 $ hg status -C
246 $ hg status -C
247 A foo.c
247 A foo.c
248 foobar.c
248 foobar.c
249 R foobar.c
249 R foobar.c
250
250
251 Returns 0 if all files are successfully added.
251 Returns 0 if all files are successfully added.
252 """
252 """
253 opts = pycompat.byteskwargs(opts)
253 opts = pycompat.byteskwargs(opts)
254 if not opts.get('similarity'):
254 if not opts.get('similarity'):
255 opts['similarity'] = '100'
255 opts['similarity'] = '100'
256 matcher = scmutil.match(repo[None], pats, opts)
256 matcher = scmutil.match(repo[None], pats, opts)
257 return scmutil.addremove(repo, matcher, "", opts)
257 return scmutil.addremove(repo, matcher, "", opts)
258
258
259 @command('annotate|blame',
259 @command('annotate|blame',
260 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
260 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
261 ('', 'follow', None,
261 ('', 'follow', None,
262 _('follow copies/renames and list the filename (DEPRECATED)')),
262 _('follow copies/renames and list the filename (DEPRECATED)')),
263 ('', 'no-follow', None, _("don't follow copies and renames")),
263 ('', 'no-follow', None, _("don't follow copies and renames")),
264 ('a', 'text', None, _('treat all files as text')),
264 ('a', 'text', None, _('treat all files as text')),
265 ('u', 'user', None, _('list the author (long with -v)')),
265 ('u', 'user', None, _('list the author (long with -v)')),
266 ('f', 'file', None, _('list the filename')),
266 ('f', 'file', None, _('list the filename')),
267 ('d', 'date', None, _('list the date (short with -q)')),
267 ('d', 'date', None, _('list the date (short with -q)')),
268 ('n', 'number', None, _('list the revision number (default)')),
268 ('n', 'number', None, _('list the revision number (default)')),
269 ('c', 'changeset', None, _('list the changeset')),
269 ('c', 'changeset', None, _('list the changeset')),
270 ('l', 'line-number', None, _('show line number at the first appearance')),
270 ('l', 'line-number', None, _('show line number at the first appearance')),
271 ('', 'skip', [], _('revision to not display (EXPERIMENTAL)'), _('REV')),
271 ('', 'skip', [], _('revision to not display (EXPERIMENTAL)'), _('REV')),
272 ] + diffwsopts + walkopts + formatteropts,
272 ] + diffwsopts + walkopts + formatteropts,
273 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
273 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
274 helpcategory=command.CATEGORY_FILE_CONTENTS,
274 helpcategory=command.CATEGORY_FILE_CONTENTS,
275 helpbasic=True, inferrepo=True)
275 helpbasic=True, inferrepo=True)
276 def annotate(ui, repo, *pats, **opts):
276 def annotate(ui, repo, *pats, **opts):
277 """show changeset information by line for each file
277 """show changeset information by line for each file
278
278
279 List changes in files, showing the revision id responsible for
279 List changes in files, showing the revision id responsible for
280 each line.
280 each line.
281
281
282 This command is useful for discovering when a change was made and
282 This command is useful for discovering when a change was made and
283 by whom.
283 by whom.
284
284
285 If you include --file, --user, or --date, the revision number is
285 If you include --file, --user, or --date, the revision number is
286 suppressed unless you also include --number.
286 suppressed unless you also include --number.
287
287
288 Without the -a/--text option, annotate will avoid processing files
288 Without the -a/--text option, annotate will avoid processing files
289 it detects as binary. With -a, annotate will annotate the file
289 it detects as binary. With -a, annotate will annotate the file
290 anyway, although the results will probably be neither useful
290 anyway, although the results will probably be neither useful
291 nor desirable.
291 nor desirable.
292
292
293 .. container:: verbose
293 .. container:: verbose
294
294
295 Template:
295 Template:
296
296
297 The following keywords are supported in addition to the common template
297 The following keywords are supported in addition to the common template
298 keywords and functions. See also :hg:`help templates`.
298 keywords and functions. See also :hg:`help templates`.
299
299
300 :lines: List of lines with annotation data.
300 :lines: List of lines with annotation data.
301 :path: String. Repository-absolute path of the specified file.
301 :path: String. Repository-absolute path of the specified file.
302
302
303 And each entry of ``{lines}`` provides the following sub-keywords in
303 And each entry of ``{lines}`` provides the following sub-keywords in
304 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
304 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
305
305
306 :line: String. Line content.
306 :line: String. Line content.
307 :lineno: Integer. Line number at that revision.
307 :lineno: Integer. Line number at that revision.
308 :path: String. Repository-absolute path of the file at that revision.
308 :path: String. Repository-absolute path of the file at that revision.
309
309
310 See :hg:`help templates.operators` for the list expansion syntax.
310 See :hg:`help templates.operators` for the list expansion syntax.
311
311
312 Returns 0 on success.
312 Returns 0 on success.
313 """
313 """
314 opts = pycompat.byteskwargs(opts)
314 opts = pycompat.byteskwargs(opts)
315 if not pats:
315 if not pats:
316 raise error.Abort(_('at least one filename or pattern is required'))
316 raise error.Abort(_('at least one filename or pattern is required'))
317
317
318 if opts.get('follow'):
318 if opts.get('follow'):
319 # --follow is deprecated and now just an alias for -f/--file
319 # --follow is deprecated and now just an alias for -f/--file
320 # to mimic the behavior of Mercurial before version 1.5
320 # to mimic the behavior of Mercurial before version 1.5
321 opts['file'] = True
321 opts['file'] = True
322
322
323 rev = opts.get('rev')
323 rev = opts.get('rev')
324 if rev:
324 if rev:
325 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
325 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
326 ctx = scmutil.revsingle(repo, rev)
326 ctx = scmutil.revsingle(repo, rev)
327
327
328 rootfm = ui.formatter('annotate', opts)
328 rootfm = ui.formatter('annotate', opts)
329 if ui.debugflag:
329 if ui.debugflag:
330 shorthex = pycompat.identity
330 shorthex = pycompat.identity
331 else:
331 else:
332 def shorthex(h):
332 def shorthex(h):
333 return h[:12]
333 return h[:12]
334 if ui.quiet:
334 if ui.quiet:
335 datefunc = dateutil.shortdate
335 datefunc = dateutil.shortdate
336 else:
336 else:
337 datefunc = dateutil.datestr
337 datefunc = dateutil.datestr
338 if ctx.rev() is None:
338 if ctx.rev() is None:
339 if opts.get('changeset'):
339 if opts.get('changeset'):
340 # omit "+" suffix which is appended to node hex
340 # omit "+" suffix which is appended to node hex
341 def formatrev(rev):
341 def formatrev(rev):
342 if rev == wdirrev:
342 if rev == wdirrev:
343 return '%d' % ctx.p1().rev()
343 return '%d' % ctx.p1().rev()
344 else:
344 else:
345 return '%d' % rev
345 return '%d' % rev
346 else:
346 else:
347 def formatrev(rev):
347 def formatrev(rev):
348 if rev == wdirrev:
348 if rev == wdirrev:
349 return '%d+' % ctx.p1().rev()
349 return '%d+' % ctx.p1().rev()
350 else:
350 else:
351 return '%d ' % rev
351 return '%d ' % rev
352 def formathex(h):
352 def formathex(h):
353 if h == wdirhex:
353 if h == wdirhex:
354 return '%s+' % shorthex(hex(ctx.p1().node()))
354 return '%s+' % shorthex(hex(ctx.p1().node()))
355 else:
355 else:
356 return '%s ' % shorthex(h)
356 return '%s ' % shorthex(h)
357 else:
357 else:
358 formatrev = b'%d'.__mod__
358 formatrev = b'%d'.__mod__
359 formathex = shorthex
359 formathex = shorthex
360
360
361 opmap = [('user', ' ', lambda x: x.fctx.user(), ui.shortuser),
361 opmap = [('user', ' ', lambda x: x.fctx.user(), ui.shortuser),
362 ('rev', ' ', lambda x: scmutil.intrev(x.fctx), formatrev),
362 ('rev', ' ', lambda x: scmutil.intrev(x.fctx), formatrev),
363 ('node', ' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
363 ('node', ' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
364 ('date', ' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
364 ('date', ' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
365 ('path', ' ', lambda x: x.fctx.path(), pycompat.bytestr),
365 ('path', ' ', lambda x: x.fctx.path(), pycompat.bytestr),
366 ('lineno', ':', lambda x: x.lineno, pycompat.bytestr),
366 ('lineno', ':', lambda x: x.lineno, pycompat.bytestr),
367 ]
367 ]
368 opnamemap = {'rev': 'number', 'node': 'changeset', 'path': 'file',
368 opnamemap = {'rev': 'number', 'node': 'changeset', 'path': 'file',
369 'lineno': 'line_number'}
369 'lineno': 'line_number'}
370
370
371 if (not opts.get('user') and not opts.get('changeset')
371 if (not opts.get('user') and not opts.get('changeset')
372 and not opts.get('date') and not opts.get('file')):
372 and not opts.get('date') and not opts.get('file')):
373 opts['number'] = True
373 opts['number'] = True
374
374
375 linenumber = opts.get('line_number') is not None
375 linenumber = opts.get('line_number') is not None
376 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
376 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
377 raise error.Abort(_('at least one of -n/-c is required for -l'))
377 raise error.Abort(_('at least one of -n/-c is required for -l'))
378
378
379 ui.pager('annotate')
379 ui.pager('annotate')
380
380
381 if rootfm.isplain():
381 if rootfm.isplain():
382 def makefunc(get, fmt):
382 def makefunc(get, fmt):
383 return lambda x: fmt(get(x))
383 return lambda x: fmt(get(x))
384 else:
384 else:
385 def makefunc(get, fmt):
385 def makefunc(get, fmt):
386 return get
386 return get
387 datahint = rootfm.datahint()
387 datahint = rootfm.datahint()
388 funcmap = [(makefunc(get, fmt), sep) for fn, sep, get, fmt in opmap
388 funcmap = [(makefunc(get, fmt), sep) for fn, sep, get, fmt in opmap
389 if opts.get(opnamemap.get(fn, fn)) or fn in datahint]
389 if opts.get(opnamemap.get(fn, fn)) or fn in datahint]
390 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
390 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
391 fields = ' '.join(fn for fn, sep, get, fmt in opmap
391 fields = ' '.join(fn for fn, sep, get, fmt in opmap
392 if opts.get(opnamemap.get(fn, fn)) or fn in datahint)
392 if opts.get(opnamemap.get(fn, fn)) or fn in datahint)
393
393
394 def bad(x, y):
394 def bad(x, y):
395 raise error.Abort("%s: %s" % (x, y))
395 raise error.Abort("%s: %s" % (x, y))
396
396
397 m = scmutil.match(ctx, pats, opts, badfn=bad)
397 m = scmutil.match(ctx, pats, opts, badfn=bad)
398
398
399 follow = not opts.get('no_follow')
399 follow = not opts.get('no_follow')
400 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
400 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
401 whitespace=True)
401 whitespace=True)
402 skiprevs = opts.get('skip')
402 skiprevs = opts.get('skip')
403 if skiprevs:
403 if skiprevs:
404 skiprevs = scmutil.revrange(repo, skiprevs)
404 skiprevs = scmutil.revrange(repo, skiprevs)
405
405
406 for abs in ctx.walk(m):
406 for abs in ctx.walk(m):
407 fctx = ctx[abs]
407 fctx = ctx[abs]
408 rootfm.startitem()
408 rootfm.startitem()
409 rootfm.data(path=abs)
409 rootfm.data(path=abs)
410 if not opts.get('text') and fctx.isbinary():
410 if not opts.get('text') and fctx.isbinary():
411 rootfm.plain(_("%s: binary file\n")
411 rootfm.plain(_("%s: binary file\n")
412 % ((pats and m.rel(abs)) or abs))
412 % ((pats and m.rel(abs)) or abs))
413 continue
413 continue
414
414
415 fm = rootfm.nested('lines', tmpl='{rev}: {line}')
415 fm = rootfm.nested('lines', tmpl='{rev}: {line}')
416 lines = fctx.annotate(follow=follow, skiprevs=skiprevs,
416 lines = fctx.annotate(follow=follow, skiprevs=skiprevs,
417 diffopts=diffopts)
417 diffopts=diffopts)
418 if not lines:
418 if not lines:
419 fm.end()
419 fm.end()
420 continue
420 continue
421 formats = []
421 formats = []
422 pieces = []
422 pieces = []
423
423
424 for f, sep in funcmap:
424 for f, sep in funcmap:
425 l = [f(n) for n in lines]
425 l = [f(n) for n in lines]
426 if fm.isplain():
426 if fm.isplain():
427 sizes = [encoding.colwidth(x) for x in l]
427 sizes = [encoding.colwidth(x) for x in l]
428 ml = max(sizes)
428 ml = max(sizes)
429 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
429 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
430 else:
430 else:
431 formats.append(['%s' for x in l])
431 formats.append(['%s' for x in l])
432 pieces.append(l)
432 pieces.append(l)
433
433
434 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
434 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
435 fm.startitem()
435 fm.startitem()
436 fm.context(fctx=n.fctx)
436 fm.context(fctx=n.fctx)
437 fm.write(fields, "".join(f), *p)
437 fm.write(fields, "".join(f), *p)
438 if n.skip:
438 if n.skip:
439 fmt = "* %s"
439 fmt = "* %s"
440 else:
440 else:
441 fmt = ": %s"
441 fmt = ": %s"
442 fm.write('line', fmt, n.text)
442 fm.write('line', fmt, n.text)
443
443
444 if not lines[-1].text.endswith('\n'):
444 if not lines[-1].text.endswith('\n'):
445 fm.plain('\n')
445 fm.plain('\n')
446 fm.end()
446 fm.end()
447
447
448 rootfm.end()
448 rootfm.end()
449
449
450 @command('archive',
450 @command('archive',
451 [('', 'no-decode', None, _('do not pass files through decoders')),
451 [('', 'no-decode', None, _('do not pass files through decoders')),
452 ('p', 'prefix', '', _('directory prefix for files in archive'),
452 ('p', 'prefix', '', _('directory prefix for files in archive'),
453 _('PREFIX')),
453 _('PREFIX')),
454 ('r', 'rev', '', _('revision to distribute'), _('REV')),
454 ('r', 'rev', '', _('revision to distribute'), _('REV')),
455 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
455 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
456 ] + subrepoopts + walkopts,
456 ] + subrepoopts + walkopts,
457 _('[OPTION]... DEST'),
457 _('[OPTION]... DEST'),
458 helpcategory=command.CATEGORY_IMPORT_EXPORT)
458 helpcategory=command.CATEGORY_IMPORT_EXPORT)
459 def archive(ui, repo, dest, **opts):
459 def archive(ui, repo, dest, **opts):
460 '''create an unversioned archive of a repository revision
460 '''create an unversioned archive of a repository revision
461
461
462 By default, the revision used is the parent of the working
462 By default, the revision used is the parent of the working
463 directory; use -r/--rev to specify a different revision.
463 directory; use -r/--rev to specify a different revision.
464
464
465 The archive type is automatically detected based on file
465 The archive type is automatically detected based on file
466 extension (to override, use -t/--type).
466 extension (to override, use -t/--type).
467
467
468 .. container:: verbose
468 .. container:: verbose
469
469
470 Examples:
470 Examples:
471
471
472 - create a zip file containing the 1.0 release::
472 - create a zip file containing the 1.0 release::
473
473
474 hg archive -r 1.0 project-1.0.zip
474 hg archive -r 1.0 project-1.0.zip
475
475
476 - create a tarball excluding .hg files::
476 - create a tarball excluding .hg files::
477
477
478 hg archive project.tar.gz -X ".hg*"
478 hg archive project.tar.gz -X ".hg*"
479
479
480 Valid types are:
480 Valid types are:
481
481
482 :``files``: a directory full of files (default)
482 :``files``: a directory full of files (default)
483 :``tar``: tar archive, uncompressed
483 :``tar``: tar archive, uncompressed
484 :``tbz2``: tar archive, compressed using bzip2
484 :``tbz2``: tar archive, compressed using bzip2
485 :``tgz``: tar archive, compressed using gzip
485 :``tgz``: tar archive, compressed using gzip
486 :``uzip``: zip archive, uncompressed
486 :``uzip``: zip archive, uncompressed
487 :``zip``: zip archive, compressed using deflate
487 :``zip``: zip archive, compressed using deflate
488
488
489 The exact name of the destination archive or directory is given
489 The exact name of the destination archive or directory is given
490 using a format string; see :hg:`help export` for details.
490 using a format string; see :hg:`help export` for details.
491
491
492 Each member added to an archive file has a directory prefix
492 Each member added to an archive file has a directory prefix
493 prepended. Use -p/--prefix to specify a format string for the
493 prepended. Use -p/--prefix to specify a format string for the
494 prefix. The default is the basename of the archive, with suffixes
494 prefix. The default is the basename of the archive, with suffixes
495 removed.
495 removed.
496
496
497 Returns 0 on success.
497 Returns 0 on success.
498 '''
498 '''
499
499
500 opts = pycompat.byteskwargs(opts)
500 opts = pycompat.byteskwargs(opts)
501 rev = opts.get('rev')
501 rev = opts.get('rev')
502 if rev:
502 if rev:
503 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
503 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
504 ctx = scmutil.revsingle(repo, rev)
504 ctx = scmutil.revsingle(repo, rev)
505 if not ctx:
505 if not ctx:
506 raise error.Abort(_('no working directory: please specify a revision'))
506 raise error.Abort(_('no working directory: please specify a revision'))
507 node = ctx.node()
507 node = ctx.node()
508 dest = cmdutil.makefilename(ctx, dest)
508 dest = cmdutil.makefilename(ctx, dest)
509 if os.path.realpath(dest) == repo.root:
509 if os.path.realpath(dest) == repo.root:
510 raise error.Abort(_('repository root cannot be destination'))
510 raise error.Abort(_('repository root cannot be destination'))
511
511
512 kind = opts.get('type') or archival.guesskind(dest) or 'files'
512 kind = opts.get('type') or archival.guesskind(dest) or 'files'
513 prefix = opts.get('prefix')
513 prefix = opts.get('prefix')
514
514
515 if dest == '-':
515 if dest == '-':
516 if kind == 'files':
516 if kind == 'files':
517 raise error.Abort(_('cannot archive plain files to stdout'))
517 raise error.Abort(_('cannot archive plain files to stdout'))
518 dest = cmdutil.makefileobj(ctx, dest)
518 dest = cmdutil.makefileobj(ctx, dest)
519 if not prefix:
519 if not prefix:
520 prefix = os.path.basename(repo.root) + '-%h'
520 prefix = os.path.basename(repo.root) + '-%h'
521
521
522 prefix = cmdutil.makefilename(ctx, prefix)
522 prefix = cmdutil.makefilename(ctx, prefix)
523 match = scmutil.match(ctx, [], opts)
523 match = scmutil.match(ctx, [], opts)
524 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
524 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
525 match, prefix, subrepos=opts.get('subrepos'))
525 match, prefix, subrepos=opts.get('subrepos'))
526
526
527 @command('backout',
527 @command('backout',
528 [('', 'merge', None, _('merge with old dirstate parent after backout')),
528 [('', 'merge', None, _('merge with old dirstate parent after backout')),
529 ('', 'commit', None,
529 ('', 'commit', None,
530 _('commit if no conflicts were encountered (DEPRECATED)')),
530 _('commit if no conflicts were encountered (DEPRECATED)')),
531 ('', 'no-commit', None, _('do not commit')),
531 ('', 'no-commit', None, _('do not commit')),
532 ('', 'parent', '',
532 ('', 'parent', '',
533 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
533 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
534 ('r', 'rev', '', _('revision to backout'), _('REV')),
534 ('r', 'rev', '', _('revision to backout'), _('REV')),
535 ('e', 'edit', False, _('invoke editor on commit messages')),
535 ('e', 'edit', False, _('invoke editor on commit messages')),
536 ] + mergetoolopts + walkopts + commitopts + commitopts2,
536 ] + mergetoolopts + walkopts + commitopts + commitopts2,
537 _('[OPTION]... [-r] REV'),
537 _('[OPTION]... [-r] REV'),
538 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT)
538 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT)
539 def backout(ui, repo, node=None, rev=None, **opts):
539 def backout(ui, repo, node=None, rev=None, **opts):
540 '''reverse effect of earlier changeset
540 '''reverse effect of earlier changeset
541
541
542 Prepare a new changeset with the effect of REV undone in the
542 Prepare a new changeset with the effect of REV undone in the
543 current working directory. If no conflicts were encountered,
543 current working directory. If no conflicts were encountered,
544 it will be committed immediately.
544 it will be committed immediately.
545
545
546 If REV is the parent of the working directory, then this new changeset
546 If REV is the parent of the working directory, then this new changeset
547 is committed automatically (unless --no-commit is specified).
547 is committed automatically (unless --no-commit is specified).
548
548
549 .. note::
549 .. note::
550
550
551 :hg:`backout` cannot be used to fix either an unwanted or
551 :hg:`backout` cannot be used to fix either an unwanted or
552 incorrect merge.
552 incorrect merge.
553
553
554 .. container:: verbose
554 .. container:: verbose
555
555
556 Examples:
556 Examples:
557
557
558 - Reverse the effect of the parent of the working directory.
558 - Reverse the effect of the parent of the working directory.
559 This backout will be committed immediately::
559 This backout will be committed immediately::
560
560
561 hg backout -r .
561 hg backout -r .
562
562
563 - Reverse the effect of previous bad revision 23::
563 - Reverse the effect of previous bad revision 23::
564
564
565 hg backout -r 23
565 hg backout -r 23
566
566
567 - Reverse the effect of previous bad revision 23 and
567 - Reverse the effect of previous bad revision 23 and
568 leave changes uncommitted::
568 leave changes uncommitted::
569
569
570 hg backout -r 23 --no-commit
570 hg backout -r 23 --no-commit
571 hg commit -m "Backout revision 23"
571 hg commit -m "Backout revision 23"
572
572
573 By default, the pending changeset will have one parent,
573 By default, the pending changeset will have one parent,
574 maintaining a linear history. With --merge, the pending
574 maintaining a linear history. With --merge, the pending
575 changeset will instead have two parents: the old parent of the
575 changeset will instead have two parents: the old parent of the
576 working directory and a new child of REV that simply undoes REV.
576 working directory and a new child of REV that simply undoes REV.
577
577
578 Before version 1.7, the behavior without --merge was equivalent
578 Before version 1.7, the behavior without --merge was equivalent
579 to specifying --merge followed by :hg:`update --clean .` to
579 to specifying --merge followed by :hg:`update --clean .` to
580 cancel the merge and leave the child of REV as a head to be
580 cancel the merge and leave the child of REV as a head to be
581 merged separately.
581 merged separately.
582
582
583 See :hg:`help dates` for a list of formats valid for -d/--date.
583 See :hg:`help dates` for a list of formats valid for -d/--date.
584
584
585 See :hg:`help revert` for a way to restore files to the state
585 See :hg:`help revert` for a way to restore files to the state
586 of another revision.
586 of another revision.
587
587
588 Returns 0 on success, 1 if nothing to backout or there are unresolved
588 Returns 0 on success, 1 if nothing to backout or there are unresolved
589 files.
589 files.
590 '''
590 '''
591 with repo.wlock(), repo.lock():
591 with repo.wlock(), repo.lock():
592 return _dobackout(ui, repo, node, rev, **opts)
592 return _dobackout(ui, repo, node, rev, **opts)
593
593
594 def _dobackout(ui, repo, node=None, rev=None, **opts):
594 def _dobackout(ui, repo, node=None, rev=None, **opts):
595 opts = pycompat.byteskwargs(opts)
595 opts = pycompat.byteskwargs(opts)
596 if opts.get('commit') and opts.get('no_commit'):
596 if opts.get('commit') and opts.get('no_commit'):
597 raise error.Abort(_("cannot use --commit with --no-commit"))
597 raise error.Abort(_("cannot use --commit with --no-commit"))
598 if opts.get('merge') and opts.get('no_commit'):
598 if opts.get('merge') and opts.get('no_commit'):
599 raise error.Abort(_("cannot use --merge with --no-commit"))
599 raise error.Abort(_("cannot use --merge with --no-commit"))
600
600
601 if rev and node:
601 if rev and node:
602 raise error.Abort(_("please specify just one revision"))
602 raise error.Abort(_("please specify just one revision"))
603
603
604 if not rev:
604 if not rev:
605 rev = node
605 rev = node
606
606
607 if not rev:
607 if not rev:
608 raise error.Abort(_("please specify a revision to backout"))
608 raise error.Abort(_("please specify a revision to backout"))
609
609
610 date = opts.get('date')
610 date = opts.get('date')
611 if date:
611 if date:
612 opts['date'] = dateutil.parsedate(date)
612 opts['date'] = dateutil.parsedate(date)
613
613
614 cmdutil.checkunfinished(repo)
614 cmdutil.checkunfinished(repo)
615 cmdutil.bailifchanged(repo)
615 cmdutil.bailifchanged(repo)
616 node = scmutil.revsingle(repo, rev).node()
616 node = scmutil.revsingle(repo, rev).node()
617
617
618 op1, op2 = repo.dirstate.parents()
618 op1, op2 = repo.dirstate.parents()
619 if not repo.changelog.isancestor(node, op1):
619 if not repo.changelog.isancestor(node, op1):
620 raise error.Abort(_('cannot backout change that is not an ancestor'))
620 raise error.Abort(_('cannot backout change that is not an ancestor'))
621
621
622 p1, p2 = repo.changelog.parents(node)
622 p1, p2 = repo.changelog.parents(node)
623 if p1 == nullid:
623 if p1 == nullid:
624 raise error.Abort(_('cannot backout a change with no parents'))
624 raise error.Abort(_('cannot backout a change with no parents'))
625 if p2 != nullid:
625 if p2 != nullid:
626 if not opts.get('parent'):
626 if not opts.get('parent'):
627 raise error.Abort(_('cannot backout a merge changeset'))
627 raise error.Abort(_('cannot backout a merge changeset'))
628 p = repo.lookup(opts['parent'])
628 p = repo.lookup(opts['parent'])
629 if p not in (p1, p2):
629 if p not in (p1, p2):
630 raise error.Abort(_('%s is not a parent of %s') %
630 raise error.Abort(_('%s is not a parent of %s') %
631 (short(p), short(node)))
631 (short(p), short(node)))
632 parent = p
632 parent = p
633 else:
633 else:
634 if opts.get('parent'):
634 if opts.get('parent'):
635 raise error.Abort(_('cannot use --parent on non-merge changeset'))
635 raise error.Abort(_('cannot use --parent on non-merge changeset'))
636 parent = p1
636 parent = p1
637
637
638 # the backout should appear on the same branch
638 # the backout should appear on the same branch
639 branch = repo.dirstate.branch()
639 branch = repo.dirstate.branch()
640 bheads = repo.branchheads(branch)
640 bheads = repo.branchheads(branch)
641 rctx = scmutil.revsingle(repo, hex(parent))
641 rctx = scmutil.revsingle(repo, hex(parent))
642 if not opts.get('merge') and op1 != node:
642 if not opts.get('merge') and op1 != node:
643 with dirstateguard.dirstateguard(repo, 'backout'):
643 with dirstateguard.dirstateguard(repo, 'backout'):
644 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
644 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
645 with ui.configoverride(overrides, 'backout'):
645 with ui.configoverride(overrides, 'backout'):
646 stats = mergemod.update(repo, parent, branchmerge=True,
646 stats = mergemod.update(repo, parent, branchmerge=True,
647 force=True, ancestor=node,
647 force=True, ancestor=node,
648 mergeancestor=False)
648 mergeancestor=False)
649 repo.setparents(op1, op2)
649 repo.setparents(op1, op2)
650 hg._showstats(repo, stats)
650 hg._showstats(repo, stats)
651 if stats.unresolvedcount:
651 if stats.unresolvedcount:
652 repo.ui.status(_("use 'hg resolve' to retry unresolved "
652 repo.ui.status(_("use 'hg resolve' to retry unresolved "
653 "file merges\n"))
653 "file merges\n"))
654 return 1
654 return 1
655 else:
655 else:
656 hg.clean(repo, node, show_stats=False)
656 hg.clean(repo, node, show_stats=False)
657 repo.dirstate.setbranch(branch)
657 repo.dirstate.setbranch(branch)
658 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
658 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
659
659
660 if opts.get('no_commit'):
660 if opts.get('no_commit'):
661 msg = _("changeset %s backed out, "
661 msg = _("changeset %s backed out, "
662 "don't forget to commit.\n")
662 "don't forget to commit.\n")
663 ui.status(msg % short(node))
663 ui.status(msg % short(node))
664 return 0
664 return 0
665
665
666 def commitfunc(ui, repo, message, match, opts):
666 def commitfunc(ui, repo, message, match, opts):
667 editform = 'backout'
667 editform = 'backout'
668 e = cmdutil.getcommiteditor(editform=editform,
668 e = cmdutil.getcommiteditor(editform=editform,
669 **pycompat.strkwargs(opts))
669 **pycompat.strkwargs(opts))
670 if not message:
670 if not message:
671 # we don't translate commit messages
671 # we don't translate commit messages
672 message = "Backed out changeset %s" % short(node)
672 message = "Backed out changeset %s" % short(node)
673 e = cmdutil.getcommiteditor(edit=True, editform=editform)
673 e = cmdutil.getcommiteditor(edit=True, editform=editform)
674 return repo.commit(message, opts.get('user'), opts.get('date'),
674 return repo.commit(message, opts.get('user'), opts.get('date'),
675 match, editor=e)
675 match, editor=e)
676 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
676 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
677 if not newnode:
677 if not newnode:
678 ui.status(_("nothing changed\n"))
678 ui.status(_("nothing changed\n"))
679 return 1
679 return 1
680 cmdutil.commitstatus(repo, newnode, branch, bheads)
680 cmdutil.commitstatus(repo, newnode, branch, bheads)
681
681
682 def nice(node):
682 def nice(node):
683 return '%d:%s' % (repo.changelog.rev(node), short(node))
683 return '%d:%s' % (repo.changelog.rev(node), short(node))
684 ui.status(_('changeset %s backs out changeset %s\n') %
684 ui.status(_('changeset %s backs out changeset %s\n') %
685 (nice(repo.changelog.tip()), nice(node)))
685 (nice(repo.changelog.tip()), nice(node)))
686 if opts.get('merge') and op1 != node:
686 if opts.get('merge') and op1 != node:
687 hg.clean(repo, op1, show_stats=False)
687 hg.clean(repo, op1, show_stats=False)
688 ui.status(_('merging with changeset %s\n')
688 ui.status(_('merging with changeset %s\n')
689 % nice(repo.changelog.tip()))
689 % nice(repo.changelog.tip()))
690 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
690 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
691 with ui.configoverride(overrides, 'backout'):
691 with ui.configoverride(overrides, 'backout'):
692 return hg.merge(repo, hex(repo.changelog.tip()))
692 return hg.merge(repo, hex(repo.changelog.tip()))
693 return 0
693 return 0
694
694
695 @command('bisect',
695 @command('bisect',
696 [('r', 'reset', False, _('reset bisect state')),
696 [('r', 'reset', False, _('reset bisect state')),
697 ('g', 'good', False, _('mark changeset good')),
697 ('g', 'good', False, _('mark changeset good')),
698 ('b', 'bad', False, _('mark changeset bad')),
698 ('b', 'bad', False, _('mark changeset bad')),
699 ('s', 'skip', False, _('skip testing changeset')),
699 ('s', 'skip', False, _('skip testing changeset')),
700 ('e', 'extend', False, _('extend the bisect range')),
700 ('e', 'extend', False, _('extend the bisect range')),
701 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
701 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
702 ('U', 'noupdate', False, _('do not update to target'))],
702 ('U', 'noupdate', False, _('do not update to target'))],
703 _("[-gbsr] [-U] [-c CMD] [REV]"),
703 _("[-gbsr] [-U] [-c CMD] [REV]"),
704 helpcategory=command.CATEGORY_CHANGE_NAVIGATION)
704 helpcategory=command.CATEGORY_CHANGE_NAVIGATION)
705 def bisect(ui, repo, rev=None, extra=None, command=None,
705 def bisect(ui, repo, rev=None, extra=None, command=None,
706 reset=None, good=None, bad=None, skip=None, extend=None,
706 reset=None, good=None, bad=None, skip=None, extend=None,
707 noupdate=None):
707 noupdate=None):
708 """subdivision search of changesets
708 """subdivision search of changesets
709
709
710 This command helps to find changesets which introduce problems. To
710 This command helps to find changesets which introduce problems. To
711 use, mark the earliest changeset you know exhibits the problem as
711 use, mark the earliest changeset you know exhibits the problem as
712 bad, then mark the latest changeset which is free from the problem
712 bad, then mark the latest changeset which is free from the problem
713 as good. Bisect will update your working directory to a revision
713 as good. Bisect will update your working directory to a revision
714 for testing (unless the -U/--noupdate option is specified). Once
714 for testing (unless the -U/--noupdate option is specified). Once
715 you have performed tests, mark the working directory as good or
715 you have performed tests, mark the working directory as good or
716 bad, and bisect will either update to another candidate changeset
716 bad, and bisect will either update to another candidate changeset
717 or announce that it has found the bad revision.
717 or announce that it has found the bad revision.
718
718
719 As a shortcut, you can also use the revision argument to mark a
719 As a shortcut, you can also use the revision argument to mark a
720 revision as good or bad without checking it out first.
720 revision as good or bad without checking it out first.
721
721
722 If you supply a command, it will be used for automatic bisection.
722 If you supply a command, it will be used for automatic bisection.
723 The environment variable HG_NODE will contain the ID of the
723 The environment variable HG_NODE will contain the ID of the
724 changeset being tested. The exit status of the command will be
724 changeset being tested. The exit status of the command will be
725 used to mark revisions as good or bad: status 0 means good, 125
725 used to mark revisions as good or bad: status 0 means good, 125
726 means to skip the revision, 127 (command not found) will abort the
726 means to skip the revision, 127 (command not found) will abort the
727 bisection, and any other non-zero exit status means the revision
727 bisection, and any other non-zero exit status means the revision
728 is bad.
728 is bad.
729
729
730 .. container:: verbose
730 .. container:: verbose
731
731
732 Some examples:
732 Some examples:
733
733
734 - start a bisection with known bad revision 34, and good revision 12::
734 - start a bisection with known bad revision 34, and good revision 12::
735
735
736 hg bisect --bad 34
736 hg bisect --bad 34
737 hg bisect --good 12
737 hg bisect --good 12
738
738
739 - advance the current bisection by marking current revision as good or
739 - advance the current bisection by marking current revision as good or
740 bad::
740 bad::
741
741
742 hg bisect --good
742 hg bisect --good
743 hg bisect --bad
743 hg bisect --bad
744
744
745 - mark the current revision, or a known revision, to be skipped (e.g. if
745 - mark the current revision, or a known revision, to be skipped (e.g. if
746 that revision is not usable because of another issue)::
746 that revision is not usable because of another issue)::
747
747
748 hg bisect --skip
748 hg bisect --skip
749 hg bisect --skip 23
749 hg bisect --skip 23
750
750
751 - skip all revisions that do not touch directories ``foo`` or ``bar``::
751 - skip all revisions that do not touch directories ``foo`` or ``bar``::
752
752
753 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
753 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
754
754
755 - forget the current bisection::
755 - forget the current bisection::
756
756
757 hg bisect --reset
757 hg bisect --reset
758
758
759 - use 'make && make tests' to automatically find the first broken
759 - use 'make && make tests' to automatically find the first broken
760 revision::
760 revision::
761
761
762 hg bisect --reset
762 hg bisect --reset
763 hg bisect --bad 34
763 hg bisect --bad 34
764 hg bisect --good 12
764 hg bisect --good 12
765 hg bisect --command "make && make tests"
765 hg bisect --command "make && make tests"
766
766
767 - see all changesets whose states are already known in the current
767 - see all changesets whose states are already known in the current
768 bisection::
768 bisection::
769
769
770 hg log -r "bisect(pruned)"
770 hg log -r "bisect(pruned)"
771
771
772 - see the changeset currently being bisected (especially useful
772 - see the changeset currently being bisected (especially useful
773 if running with -U/--noupdate)::
773 if running with -U/--noupdate)::
774
774
775 hg log -r "bisect(current)"
775 hg log -r "bisect(current)"
776
776
777 - see all changesets that took part in the current bisection::
777 - see all changesets that took part in the current bisection::
778
778
779 hg log -r "bisect(range)"
779 hg log -r "bisect(range)"
780
780
781 - you can even get a nice graph::
781 - you can even get a nice graph::
782
782
783 hg log --graph -r "bisect(range)"
783 hg log --graph -r "bisect(range)"
784
784
785 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
785 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
786
786
787 Returns 0 on success.
787 Returns 0 on success.
788 """
788 """
789 # backward compatibility
789 # backward compatibility
790 if rev in "good bad reset init".split():
790 if rev in "good bad reset init".split():
791 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
791 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
792 cmd, rev, extra = rev, extra, None
792 cmd, rev, extra = rev, extra, None
793 if cmd == "good":
793 if cmd == "good":
794 good = True
794 good = True
795 elif cmd == "bad":
795 elif cmd == "bad":
796 bad = True
796 bad = True
797 else:
797 else:
798 reset = True
798 reset = True
799 elif extra:
799 elif extra:
800 raise error.Abort(_('incompatible arguments'))
800 raise error.Abort(_('incompatible arguments'))
801
801
802 incompatibles = {
802 incompatibles = {
803 '--bad': bad,
803 '--bad': bad,
804 '--command': bool(command),
804 '--command': bool(command),
805 '--extend': extend,
805 '--extend': extend,
806 '--good': good,
806 '--good': good,
807 '--reset': reset,
807 '--reset': reset,
808 '--skip': skip,
808 '--skip': skip,
809 }
809 }
810
810
811 enabled = [x for x in incompatibles if incompatibles[x]]
811 enabled = [x for x in incompatibles if incompatibles[x]]
812
812
813 if len(enabled) > 1:
813 if len(enabled) > 1:
814 raise error.Abort(_('%s and %s are incompatible') %
814 raise error.Abort(_('%s and %s are incompatible') %
815 tuple(sorted(enabled)[0:2]))
815 tuple(sorted(enabled)[0:2]))
816
816
817 if reset:
817 if reset:
818 hbisect.resetstate(repo)
818 hbisect.resetstate(repo)
819 return
819 return
820
820
821 state = hbisect.load_state(repo)
821 state = hbisect.load_state(repo)
822
822
823 # update state
823 # update state
824 if good or bad or skip:
824 if good or bad or skip:
825 if rev:
825 if rev:
826 nodes = [repo[i].node() for i in scmutil.revrange(repo, [rev])]
826 nodes = [repo[i].node() for i in scmutil.revrange(repo, [rev])]
827 else:
827 else:
828 nodes = [repo.lookup('.')]
828 nodes = [repo.lookup('.')]
829 if good:
829 if good:
830 state['good'] += nodes
830 state['good'] += nodes
831 elif bad:
831 elif bad:
832 state['bad'] += nodes
832 state['bad'] += nodes
833 elif skip:
833 elif skip:
834 state['skip'] += nodes
834 state['skip'] += nodes
835 hbisect.save_state(repo, state)
835 hbisect.save_state(repo, state)
836 if not (state['good'] and state['bad']):
836 if not (state['good'] and state['bad']):
837 return
837 return
838
838
839 def mayupdate(repo, node, show_stats=True):
839 def mayupdate(repo, node, show_stats=True):
840 """common used update sequence"""
840 """common used update sequence"""
841 if noupdate:
841 if noupdate:
842 return
842 return
843 cmdutil.checkunfinished(repo)
843 cmdutil.checkunfinished(repo)
844 cmdutil.bailifchanged(repo)
844 cmdutil.bailifchanged(repo)
845 return hg.clean(repo, node, show_stats=show_stats)
845 return hg.clean(repo, node, show_stats=show_stats)
846
846
847 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
847 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
848
848
849 if command:
849 if command:
850 changesets = 1
850 changesets = 1
851 if noupdate:
851 if noupdate:
852 try:
852 try:
853 node = state['current'][0]
853 node = state['current'][0]
854 except LookupError:
854 except LookupError:
855 raise error.Abort(_('current bisect revision is unknown - '
855 raise error.Abort(_('current bisect revision is unknown - '
856 'start a new bisect to fix'))
856 'start a new bisect to fix'))
857 else:
857 else:
858 node, p2 = repo.dirstate.parents()
858 node, p2 = repo.dirstate.parents()
859 if p2 != nullid:
859 if p2 != nullid:
860 raise error.Abort(_('current bisect revision is a merge'))
860 raise error.Abort(_('current bisect revision is a merge'))
861 if rev:
861 if rev:
862 node = repo[scmutil.revsingle(repo, rev, node)].node()
862 node = repo[scmutil.revsingle(repo, rev, node)].node()
863 try:
863 try:
864 while changesets:
864 while changesets:
865 # update state
865 # update state
866 state['current'] = [node]
866 state['current'] = [node]
867 hbisect.save_state(repo, state)
867 hbisect.save_state(repo, state)
868 status = ui.system(command, environ={'HG_NODE': hex(node)},
868 status = ui.system(command, environ={'HG_NODE': hex(node)},
869 blockedtag='bisect_check')
869 blockedtag='bisect_check')
870 if status == 125:
870 if status == 125:
871 transition = "skip"
871 transition = "skip"
872 elif status == 0:
872 elif status == 0:
873 transition = "good"
873 transition = "good"
874 # status < 0 means process was killed
874 # status < 0 means process was killed
875 elif status == 127:
875 elif status == 127:
876 raise error.Abort(_("failed to execute %s") % command)
876 raise error.Abort(_("failed to execute %s") % command)
877 elif status < 0:
877 elif status < 0:
878 raise error.Abort(_("%s killed") % command)
878 raise error.Abort(_("%s killed") % command)
879 else:
879 else:
880 transition = "bad"
880 transition = "bad"
881 state[transition].append(node)
881 state[transition].append(node)
882 ctx = repo[node]
882 ctx = repo[node]
883 ui.status(_('changeset %d:%s: %s\n') % (ctx.rev(), ctx,
883 ui.status(_('changeset %d:%s: %s\n') % (ctx.rev(), ctx,
884 transition))
884 transition))
885 hbisect.checkstate(state)
885 hbisect.checkstate(state)
886 # bisect
886 # bisect
887 nodes, changesets, bgood = hbisect.bisect(repo, state)
887 nodes, changesets, bgood = hbisect.bisect(repo, state)
888 # update to next check
888 # update to next check
889 node = nodes[0]
889 node = nodes[0]
890 mayupdate(repo, node, show_stats=False)
890 mayupdate(repo, node, show_stats=False)
891 finally:
891 finally:
892 state['current'] = [node]
892 state['current'] = [node]
893 hbisect.save_state(repo, state)
893 hbisect.save_state(repo, state)
894 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
894 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
895 return
895 return
896
896
897 hbisect.checkstate(state)
897 hbisect.checkstate(state)
898
898
899 # actually bisect
899 # actually bisect
900 nodes, changesets, good = hbisect.bisect(repo, state)
900 nodes, changesets, good = hbisect.bisect(repo, state)
901 if extend:
901 if extend:
902 if not changesets:
902 if not changesets:
903 extendnode = hbisect.extendrange(repo, state, nodes, good)
903 extendnode = hbisect.extendrange(repo, state, nodes, good)
904 if extendnode is not None:
904 if extendnode is not None:
905 ui.write(_("Extending search to changeset %d:%s\n")
905 ui.write(_("Extending search to changeset %d:%s\n")
906 % (extendnode.rev(), extendnode))
906 % (extendnode.rev(), extendnode))
907 state['current'] = [extendnode.node()]
907 state['current'] = [extendnode.node()]
908 hbisect.save_state(repo, state)
908 hbisect.save_state(repo, state)
909 return mayupdate(repo, extendnode.node())
909 return mayupdate(repo, extendnode.node())
910 raise error.Abort(_("nothing to extend"))
910 raise error.Abort(_("nothing to extend"))
911
911
912 if changesets == 0:
912 if changesets == 0:
913 hbisect.printresult(ui, repo, state, displayer, nodes, good)
913 hbisect.printresult(ui, repo, state, displayer, nodes, good)
914 else:
914 else:
915 assert len(nodes) == 1 # only a single node can be tested next
915 assert len(nodes) == 1 # only a single node can be tested next
916 node = nodes[0]
916 node = nodes[0]
917 # compute the approximate number of remaining tests
917 # compute the approximate number of remaining tests
918 tests, size = 0, 2
918 tests, size = 0, 2
919 while size <= changesets:
919 while size <= changesets:
920 tests, size = tests + 1, size * 2
920 tests, size = tests + 1, size * 2
921 rev = repo.changelog.rev(node)
921 rev = repo.changelog.rev(node)
922 ui.write(_("Testing changeset %d:%s "
922 ui.write(_("Testing changeset %d:%s "
923 "(%d changesets remaining, ~%d tests)\n")
923 "(%d changesets remaining, ~%d tests)\n")
924 % (rev, short(node), changesets, tests))
924 % (rev, short(node), changesets, tests))
925 state['current'] = [node]
925 state['current'] = [node]
926 hbisect.save_state(repo, state)
926 hbisect.save_state(repo, state)
927 return mayupdate(repo, node)
927 return mayupdate(repo, node)
928
928
929 @command('bookmarks|bookmark',
929 @command('bookmarks|bookmark',
930 [('f', 'force', False, _('force')),
930 [('f', 'force', False, _('force')),
931 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
931 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
932 ('d', 'delete', False, _('delete a given bookmark')),
932 ('d', 'delete', False, _('delete a given bookmark')),
933 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
933 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
934 ('i', 'inactive', False, _('mark a bookmark inactive')),
934 ('i', 'inactive', False, _('mark a bookmark inactive')),
935 ('l', 'list', False, _('list existing bookmarks')),
935 ('l', 'list', False, _('list existing bookmarks')),
936 ] + formatteropts,
936 ] + formatteropts,
937 _('hg bookmarks [OPTIONS]... [NAME]...'),
937 _('hg bookmarks [OPTIONS]... [NAME]...'),
938 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
938 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
939 def bookmark(ui, repo, *names, **opts):
939 def bookmark(ui, repo, *names, **opts):
940 '''create a new bookmark or list existing bookmarks
940 '''create a new bookmark or list existing bookmarks
941
941
942 Bookmarks are labels on changesets to help track lines of development.
942 Bookmarks are labels on changesets to help track lines of development.
943 Bookmarks are unversioned and can be moved, renamed and deleted.
943 Bookmarks are unversioned and can be moved, renamed and deleted.
944 Deleting or moving a bookmark has no effect on the associated changesets.
944 Deleting or moving a bookmark has no effect on the associated changesets.
945
945
946 Creating or updating to a bookmark causes it to be marked as 'active'.
946 Creating or updating to a bookmark causes it to be marked as 'active'.
947 The active bookmark is indicated with a '*'.
947 The active bookmark is indicated with a '*'.
948 When a commit is made, the active bookmark will advance to the new commit.
948 When a commit is made, the active bookmark will advance to the new commit.
949 A plain :hg:`update` will also advance an active bookmark, if possible.
949 A plain :hg:`update` will also advance an active bookmark, if possible.
950 Updating away from a bookmark will cause it to be deactivated.
950 Updating away from a bookmark will cause it to be deactivated.
951
951
952 Bookmarks can be pushed and pulled between repositories (see
952 Bookmarks can be pushed and pulled between repositories (see
953 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
953 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
954 diverged, a new 'divergent bookmark' of the form 'name@path' will
954 diverged, a new 'divergent bookmark' of the form 'name@path' will
955 be created. Using :hg:`merge` will resolve the divergence.
955 be created. Using :hg:`merge` will resolve the divergence.
956
956
957 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
957 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
958 the active bookmark's name.
958 the active bookmark's name.
959
959
960 A bookmark named '@' has the special property that :hg:`clone` will
960 A bookmark named '@' has the special property that :hg:`clone` will
961 check it out by default if it exists.
961 check it out by default if it exists.
962
962
963 .. container:: verbose
963 .. container:: verbose
964
964
965 Template:
965 Template:
966
966
967 The following keywords are supported in addition to the common template
967 The following keywords are supported in addition to the common template
968 keywords and functions such as ``{bookmark}``. See also
968 keywords and functions such as ``{bookmark}``. See also
969 :hg:`help templates`.
969 :hg:`help templates`.
970
970
971 :active: Boolean. True if the bookmark is active.
971 :active: Boolean. True if the bookmark is active.
972
972
973 Examples:
973 Examples:
974
974
975 - create an active bookmark for a new line of development::
975 - create an active bookmark for a new line of development::
976
976
977 hg book new-feature
977 hg book new-feature
978
978
979 - create an inactive bookmark as a place marker::
979 - create an inactive bookmark as a place marker::
980
980
981 hg book -i reviewed
981 hg book -i reviewed
982
982
983 - create an inactive bookmark on another changeset::
983 - create an inactive bookmark on another changeset::
984
984
985 hg book -r .^ tested
985 hg book -r .^ tested
986
986
987 - rename bookmark turkey to dinner::
987 - rename bookmark turkey to dinner::
988
988
989 hg book -m turkey dinner
989 hg book -m turkey dinner
990
990
991 - move the '@' bookmark from another branch::
991 - move the '@' bookmark from another branch::
992
992
993 hg book -f @
993 hg book -f @
994
994
995 - print only the active bookmark name::
995 - print only the active bookmark name::
996
996
997 hg book -ql .
997 hg book -ql .
998 '''
998 '''
999 opts = pycompat.byteskwargs(opts)
999 opts = pycompat.byteskwargs(opts)
1000 force = opts.get('force')
1000 force = opts.get('force')
1001 rev = opts.get('rev')
1001 rev = opts.get('rev')
1002 inactive = opts.get('inactive') # meaning add/rename to inactive bookmark
1002 inactive = opts.get('inactive') # meaning add/rename to inactive bookmark
1003
1003
1004 selactions = [k for k in ['delete', 'rename', 'list'] if opts.get(k)]
1004 selactions = [k for k in ['delete', 'rename', 'list'] if opts.get(k)]
1005 if len(selactions) > 1:
1005 if len(selactions) > 1:
1006 raise error.Abort(_('--%s and --%s are incompatible')
1006 raise error.Abort(_('--%s and --%s are incompatible')
1007 % tuple(selactions[:2]))
1007 % tuple(selactions[:2]))
1008 if selactions:
1008 if selactions:
1009 action = selactions[0]
1009 action = selactions[0]
1010 elif names or rev:
1010 elif names or rev:
1011 action = 'add'
1011 action = 'add'
1012 elif inactive:
1012 elif inactive:
1013 action = 'inactive' # meaning deactivate
1013 action = 'inactive' # meaning deactivate
1014 else:
1014 else:
1015 action = 'list'
1015 action = 'list'
1016
1016
1017 if rev and action in {'delete', 'rename', 'list'}:
1017 if rev and action in {'delete', 'rename', 'list'}:
1018 raise error.Abort(_("--rev is incompatible with --%s") % action)
1018 raise error.Abort(_("--rev is incompatible with --%s") % action)
1019 if inactive and action in {'delete', 'list'}:
1019 if inactive and action in {'delete', 'list'}:
1020 raise error.Abort(_("--inactive is incompatible with --%s") % action)
1020 raise error.Abort(_("--inactive is incompatible with --%s") % action)
1021 if not names and action in {'add', 'delete'}:
1021 if not names and action in {'add', 'delete'}:
1022 raise error.Abort(_("bookmark name required"))
1022 raise error.Abort(_("bookmark name required"))
1023
1023
1024 if action in {'add', 'delete', 'rename', 'inactive'}:
1024 if action in {'add', 'delete', 'rename', 'inactive'}:
1025 with repo.wlock(), repo.lock(), repo.transaction('bookmark') as tr:
1025 with repo.wlock(), repo.lock(), repo.transaction('bookmark') as tr:
1026 if action == 'delete':
1026 if action == 'delete':
1027 names = pycompat.maplist(repo._bookmarks.expandname, names)
1027 names = pycompat.maplist(repo._bookmarks.expandname, names)
1028 bookmarks.delete(repo, tr, names)
1028 bookmarks.delete(repo, tr, names)
1029 elif action == 'rename':
1029 elif action == 'rename':
1030 if not names:
1030 if not names:
1031 raise error.Abort(_("new bookmark name required"))
1031 raise error.Abort(_("new bookmark name required"))
1032 elif len(names) > 1:
1032 elif len(names) > 1:
1033 raise error.Abort(_("only one new bookmark name allowed"))
1033 raise error.Abort(_("only one new bookmark name allowed"))
1034 oldname = repo._bookmarks.expandname(opts['rename'])
1034 oldname = repo._bookmarks.expandname(opts['rename'])
1035 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1035 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1036 elif action == 'add':
1036 elif action == 'add':
1037 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1037 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1038 elif action == 'inactive':
1038 elif action == 'inactive':
1039 if len(repo._bookmarks) == 0:
1039 if len(repo._bookmarks) == 0:
1040 ui.status(_("no bookmarks set\n"))
1040 ui.status(_("no bookmarks set\n"))
1041 elif not repo._activebookmark:
1041 elif not repo._activebookmark:
1042 ui.status(_("no active bookmark\n"))
1042 ui.status(_("no active bookmark\n"))
1043 else:
1043 else:
1044 bookmarks.deactivate(repo)
1044 bookmarks.deactivate(repo)
1045 elif action == 'list':
1045 elif action == 'list':
1046 names = pycompat.maplist(repo._bookmarks.expandname, names)
1046 names = pycompat.maplist(repo._bookmarks.expandname, names)
1047 with ui.formatter('bookmarks', opts) as fm:
1047 with ui.formatter('bookmarks', opts) as fm:
1048 bookmarks.printbookmarks(ui, repo, fm, names)
1048 bookmarks.printbookmarks(ui, repo, fm, names)
1049 else:
1049 else:
1050 raise error.ProgrammingError('invalid action: %s' % action)
1050 raise error.ProgrammingError('invalid action: %s' % action)
1051
1051
1052 @command('branch',
1052 @command('branch',
1053 [('f', 'force', None,
1053 [('f', 'force', None,
1054 _('set branch name even if it shadows an existing branch')),
1054 _('set branch name even if it shadows an existing branch')),
1055 ('C', 'clean', None, _('reset branch name to parent branch name')),
1055 ('C', 'clean', None, _('reset branch name to parent branch name')),
1056 ('r', 'rev', [], _('change branches of the given revs (EXPERIMENTAL)')),
1056 ('r', 'rev', [], _('change branches of the given revs (EXPERIMENTAL)')),
1057 ],
1057 ],
1058 _('[-fC] [NAME]'),
1058 _('[-fC] [NAME]'),
1059 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
1059 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
1060 def branch(ui, repo, label=None, **opts):
1060 def branch(ui, repo, label=None, **opts):
1061 """set or show the current branch name
1061 """set or show the current branch name
1062
1062
1063 .. note::
1063 .. note::
1064
1064
1065 Branch names are permanent and global. Use :hg:`bookmark` to create a
1065 Branch names are permanent and global. Use :hg:`bookmark` to create a
1066 light-weight bookmark instead. See :hg:`help glossary` for more
1066 light-weight bookmark instead. See :hg:`help glossary` for more
1067 information about named branches and bookmarks.
1067 information about named branches and bookmarks.
1068
1068
1069 With no argument, show the current branch name. With one argument,
1069 With no argument, show the current branch name. With one argument,
1070 set the working directory branch name (the branch will not exist
1070 set the working directory branch name (the branch will not exist
1071 in the repository until the next commit). Standard practice
1071 in the repository until the next commit). Standard practice
1072 recommends that primary development take place on the 'default'
1072 recommends that primary development take place on the 'default'
1073 branch.
1073 branch.
1074
1074
1075 Unless -f/--force is specified, branch will not let you set a
1075 Unless -f/--force is specified, branch will not let you set a
1076 branch name that already exists.
1076 branch name that already exists.
1077
1077
1078 Use -C/--clean to reset the working directory branch to that of
1078 Use -C/--clean to reset the working directory branch to that of
1079 the parent of the working directory, negating a previous branch
1079 the parent of the working directory, negating a previous branch
1080 change.
1080 change.
1081
1081
1082 Use the command :hg:`update` to switch to an existing branch. Use
1082 Use the command :hg:`update` to switch to an existing branch. Use
1083 :hg:`commit --close-branch` to mark this branch head as closed.
1083 :hg:`commit --close-branch` to mark this branch head as closed.
1084 When all heads of a branch are closed, the branch will be
1084 When all heads of a branch are closed, the branch will be
1085 considered closed.
1085 considered closed.
1086
1086
1087 Returns 0 on success.
1087 Returns 0 on success.
1088 """
1088 """
1089 opts = pycompat.byteskwargs(opts)
1089 opts = pycompat.byteskwargs(opts)
1090 revs = opts.get('rev')
1090 revs = opts.get('rev')
1091 if label:
1091 if label:
1092 label = label.strip()
1092 label = label.strip()
1093
1093
1094 if not opts.get('clean') and not label:
1094 if not opts.get('clean') and not label:
1095 if revs:
1095 if revs:
1096 raise error.Abort(_("no branch name specified for the revisions"))
1096 raise error.Abort(_("no branch name specified for the revisions"))
1097 ui.write("%s\n" % repo.dirstate.branch())
1097 ui.write("%s\n" % repo.dirstate.branch())
1098 return
1098 return
1099
1099
1100 with repo.wlock():
1100 with repo.wlock():
1101 if opts.get('clean'):
1101 if opts.get('clean'):
1102 label = repo[None].p1().branch()
1102 label = repo[None].p1().branch()
1103 repo.dirstate.setbranch(label)
1103 repo.dirstate.setbranch(label)
1104 ui.status(_('reset working directory to branch %s\n') % label)
1104 ui.status(_('reset working directory to branch %s\n') % label)
1105 elif label:
1105 elif label:
1106
1106
1107 scmutil.checknewlabel(repo, label, 'branch')
1107 scmutil.checknewlabel(repo, label, 'branch')
1108 if revs:
1108 if revs:
1109 return cmdutil.changebranch(ui, repo, revs, label)
1109 return cmdutil.changebranch(ui, repo, revs, label)
1110
1110
1111 if not opts.get('force') and label in repo.branchmap():
1111 if not opts.get('force') and label in repo.branchmap():
1112 if label not in [p.branch() for p in repo[None].parents()]:
1112 if label not in [p.branch() for p in repo[None].parents()]:
1113 raise error.Abort(_('a branch of the same name already'
1113 raise error.Abort(_('a branch of the same name already'
1114 ' exists'),
1114 ' exists'),
1115 # i18n: "it" refers to an existing branch
1115 # i18n: "it" refers to an existing branch
1116 hint=_("use 'hg update' to switch to it"))
1116 hint=_("use 'hg update' to switch to it"))
1117
1117
1118 repo.dirstate.setbranch(label)
1118 repo.dirstate.setbranch(label)
1119 ui.status(_('marked working directory as branch %s\n') % label)
1119 ui.status(_('marked working directory as branch %s\n') % label)
1120
1120
1121 # find any open named branches aside from default
1121 # find any open named branches aside from default
1122 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1122 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1123 if n != "default" and not c]
1123 if n != "default" and not c]
1124 if not others:
1124 if not others:
1125 ui.status(_('(branches are permanent and global, '
1125 ui.status(_('(branches are permanent and global, '
1126 'did you want a bookmark?)\n'))
1126 'did you want a bookmark?)\n'))
1127
1127
1128 @command('branches',
1128 @command('branches',
1129 [('a', 'active', False,
1129 [('a', 'active', False,
1130 _('show only branches that have unmerged heads (DEPRECATED)')),
1130 _('show only branches that have unmerged heads (DEPRECATED)')),
1131 ('c', 'closed', False, _('show normal and closed branches')),
1131 ('c', 'closed', False, _('show normal and closed branches')),
1132 ] + formatteropts,
1132 ] + formatteropts,
1133 _('[-c]'),
1133 _('[-c]'),
1134 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1134 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1135 intents={INTENT_READONLY})
1135 intents={INTENT_READONLY})
1136 def branches(ui, repo, active=False, closed=False, **opts):
1136 def branches(ui, repo, active=False, closed=False, **opts):
1137 """list repository named branches
1137 """list repository named branches
1138
1138
1139 List the repository's named branches, indicating which ones are
1139 List the repository's named branches, indicating which ones are
1140 inactive. If -c/--closed is specified, also list branches which have
1140 inactive. If -c/--closed is specified, also list branches which have
1141 been marked closed (see :hg:`commit --close-branch`).
1141 been marked closed (see :hg:`commit --close-branch`).
1142
1142
1143 Use the command :hg:`update` to switch to an existing branch.
1143 Use the command :hg:`update` to switch to an existing branch.
1144
1144
1145 .. container:: verbose
1145 .. container:: verbose
1146
1146
1147 Template:
1147 Template:
1148
1148
1149 The following keywords are supported in addition to the common template
1149 The following keywords are supported in addition to the common template
1150 keywords and functions such as ``{branch}``. See also
1150 keywords and functions such as ``{branch}``. See also
1151 :hg:`help templates`.
1151 :hg:`help templates`.
1152
1152
1153 :active: Boolean. True if the branch is active.
1153 :active: Boolean. True if the branch is active.
1154 :closed: Boolean. True if the branch is closed.
1154 :closed: Boolean. True if the branch is closed.
1155 :current: Boolean. True if it is the current branch.
1155 :current: Boolean. True if it is the current branch.
1156
1156
1157 Returns 0.
1157 Returns 0.
1158 """
1158 """
1159
1159
1160 opts = pycompat.byteskwargs(opts)
1160 opts = pycompat.byteskwargs(opts)
1161 ui.pager('branches')
1161 ui.pager('branches')
1162 fm = ui.formatter('branches', opts)
1162 fm = ui.formatter('branches', opts)
1163 hexfunc = fm.hexfunc
1163 hexfunc = fm.hexfunc
1164
1164
1165 allheads = set(repo.heads())
1165 allheads = set(repo.heads())
1166 branches = []
1166 branches = []
1167 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1167 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1168 isactive = False
1168 isactive = False
1169 if not isclosed:
1169 if not isclosed:
1170 openheads = set(repo.branchmap().iteropen(heads))
1170 openheads = set(repo.branchmap().iteropen(heads))
1171 isactive = bool(openheads & allheads)
1171 isactive = bool(openheads & allheads)
1172 branches.append((tag, repo[tip], isactive, not isclosed))
1172 branches.append((tag, repo[tip], isactive, not isclosed))
1173 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1173 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1174 reverse=True)
1174 reverse=True)
1175
1175
1176 for tag, ctx, isactive, isopen in branches:
1176 for tag, ctx, isactive, isopen in branches:
1177 if active and not isactive:
1177 if active and not isactive:
1178 continue
1178 continue
1179 if isactive:
1179 if isactive:
1180 label = 'branches.active'
1180 label = 'branches.active'
1181 notice = ''
1181 notice = ''
1182 elif not isopen:
1182 elif not isopen:
1183 if not closed:
1183 if not closed:
1184 continue
1184 continue
1185 label = 'branches.closed'
1185 label = 'branches.closed'
1186 notice = _(' (closed)')
1186 notice = _(' (closed)')
1187 else:
1187 else:
1188 label = 'branches.inactive'
1188 label = 'branches.inactive'
1189 notice = _(' (inactive)')
1189 notice = _(' (inactive)')
1190 current = (tag == repo.dirstate.branch())
1190 current = (tag == repo.dirstate.branch())
1191 if current:
1191 if current:
1192 label = 'branches.current'
1192 label = 'branches.current'
1193
1193
1194 fm.startitem()
1194 fm.startitem()
1195 fm.write('branch', '%s', tag, label=label)
1195 fm.write('branch', '%s', tag, label=label)
1196 rev = ctx.rev()
1196 rev = ctx.rev()
1197 padsize = max(31 - len("%d" % rev) - encoding.colwidth(tag), 0)
1197 padsize = max(31 - len("%d" % rev) - encoding.colwidth(tag), 0)
1198 fmt = ' ' * padsize + ' %d:%s'
1198 fmt = ' ' * padsize + ' %d:%s'
1199 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1199 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1200 label='log.changeset changeset.%s' % ctx.phasestr())
1200 label='log.changeset changeset.%s' % ctx.phasestr())
1201 fm.context(ctx=ctx)
1201 fm.context(ctx=ctx)
1202 fm.data(active=isactive, closed=not isopen, current=current)
1202 fm.data(active=isactive, closed=not isopen, current=current)
1203 if not ui.quiet:
1203 if not ui.quiet:
1204 fm.plain(notice)
1204 fm.plain(notice)
1205 fm.plain('\n')
1205 fm.plain('\n')
1206 fm.end()
1206 fm.end()
1207
1207
1208 @command('bundle',
1208 @command('bundle',
1209 [('f', 'force', None, _('run even when the destination is unrelated')),
1209 [('f', 'force', None, _('run even when the destination is unrelated')),
1210 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1210 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1211 _('REV')),
1211 _('REV')),
1212 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1212 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1213 _('BRANCH')),
1213 _('BRANCH')),
1214 ('', 'base', [],
1214 ('', 'base', [],
1215 _('a base changeset assumed to be available at the destination'),
1215 _('a base changeset assumed to be available at the destination'),
1216 _('REV')),
1216 _('REV')),
1217 ('a', 'all', None, _('bundle all changesets in the repository')),
1217 ('a', 'all', None, _('bundle all changesets in the repository')),
1218 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1218 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1219 ] + remoteopts,
1219 ] + remoteopts,
1220 _('[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'),
1220 _('[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'),
1221 helpcategory=command.CATEGORY_IMPORT_EXPORT)
1221 helpcategory=command.CATEGORY_IMPORT_EXPORT)
1222 def bundle(ui, repo, fname, dest=None, **opts):
1222 def bundle(ui, repo, fname, dest=None, **opts):
1223 """create a bundle file
1223 """create a bundle file
1224
1224
1225 Generate a bundle file containing data to be transferred to another
1225 Generate a bundle file containing data to be transferred to another
1226 repository.
1226 repository.
1227
1227
1228 To create a bundle containing all changesets, use -a/--all
1228 To create a bundle containing all changesets, use -a/--all
1229 (or --base null). Otherwise, hg assumes the destination will have
1229 (or --base null). Otherwise, hg assumes the destination will have
1230 all the nodes you specify with --base parameters. Otherwise, hg
1230 all the nodes you specify with --base parameters. Otherwise, hg
1231 will assume the repository has all the nodes in destination, or
1231 will assume the repository has all the nodes in destination, or
1232 default-push/default if no destination is specified, where destination
1232 default-push/default if no destination is specified, where destination
1233 is the repository you provide through DEST option.
1233 is the repository you provide through DEST option.
1234
1234
1235 You can change bundle format with the -t/--type option. See
1235 You can change bundle format with the -t/--type option. See
1236 :hg:`help bundlespec` for documentation on this format. By default,
1236 :hg:`help bundlespec` for documentation on this format. By default,
1237 the most appropriate format is used and compression defaults to
1237 the most appropriate format is used and compression defaults to
1238 bzip2.
1238 bzip2.
1239
1239
1240 The bundle file can then be transferred using conventional means
1240 The bundle file can then be transferred using conventional means
1241 and applied to another repository with the unbundle or pull
1241 and applied to another repository with the unbundle or pull
1242 command. This is useful when direct push and pull are not
1242 command. This is useful when direct push and pull are not
1243 available or when exporting an entire repository is undesirable.
1243 available or when exporting an entire repository is undesirable.
1244
1244
1245 Applying bundles preserves all changeset contents including
1245 Applying bundles preserves all changeset contents including
1246 permissions, copy/rename information, and revision history.
1246 permissions, copy/rename information, and revision history.
1247
1247
1248 Returns 0 on success, 1 if no changes found.
1248 Returns 0 on success, 1 if no changes found.
1249 """
1249 """
1250 opts = pycompat.byteskwargs(opts)
1250 opts = pycompat.byteskwargs(opts)
1251 revs = None
1251 revs = None
1252 if 'rev' in opts:
1252 if 'rev' in opts:
1253 revstrings = opts['rev']
1253 revstrings = opts['rev']
1254 revs = scmutil.revrange(repo, revstrings)
1254 revs = scmutil.revrange(repo, revstrings)
1255 if revstrings and not revs:
1255 if revstrings and not revs:
1256 raise error.Abort(_('no commits to bundle'))
1256 raise error.Abort(_('no commits to bundle'))
1257
1257
1258 bundletype = opts.get('type', 'bzip2').lower()
1258 bundletype = opts.get('type', 'bzip2').lower()
1259 try:
1259 try:
1260 bundlespec = exchange.parsebundlespec(repo, bundletype, strict=False)
1260 bundlespec = exchange.parsebundlespec(repo, bundletype, strict=False)
1261 except error.UnsupportedBundleSpecification as e:
1261 except error.UnsupportedBundleSpecification as e:
1262 raise error.Abort(pycompat.bytestr(e),
1262 raise error.Abort(pycompat.bytestr(e),
1263 hint=_("see 'hg help bundlespec' for supported "
1263 hint=_("see 'hg help bundlespec' for supported "
1264 "values for --type"))
1264 "values for --type"))
1265 cgversion = bundlespec.contentopts["cg.version"]
1265 cgversion = bundlespec.contentopts["cg.version"]
1266
1266
1267 # Packed bundles are a pseudo bundle format for now.
1267 # Packed bundles are a pseudo bundle format for now.
1268 if cgversion == 's1':
1268 if cgversion == 's1':
1269 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1269 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1270 hint=_("use 'hg debugcreatestreamclonebundle'"))
1270 hint=_("use 'hg debugcreatestreamclonebundle'"))
1271
1271
1272 if opts.get('all'):
1272 if opts.get('all'):
1273 if dest:
1273 if dest:
1274 raise error.Abort(_("--all is incompatible with specifying "
1274 raise error.Abort(_("--all is incompatible with specifying "
1275 "a destination"))
1275 "a destination"))
1276 if opts.get('base'):
1276 if opts.get('base'):
1277 ui.warn(_("ignoring --base because --all was specified\n"))
1277 ui.warn(_("ignoring --base because --all was specified\n"))
1278 base = [nullrev]
1278 base = [nullrev]
1279 else:
1279 else:
1280 base = scmutil.revrange(repo, opts.get('base'))
1280 base = scmutil.revrange(repo, opts.get('base'))
1281 if cgversion not in changegroup.supportedoutgoingversions(repo):
1281 if cgversion not in changegroup.supportedoutgoingversions(repo):
1282 raise error.Abort(_("repository does not support bundle version %s") %
1282 raise error.Abort(_("repository does not support bundle version %s") %
1283 cgversion)
1283 cgversion)
1284
1284
1285 if base:
1285 if base:
1286 if dest:
1286 if dest:
1287 raise error.Abort(_("--base is incompatible with specifying "
1287 raise error.Abort(_("--base is incompatible with specifying "
1288 "a destination"))
1288 "a destination"))
1289 common = [repo[rev].node() for rev in base]
1289 common = [repo[rev].node() for rev in base]
1290 heads = [repo[r].node() for r in revs] if revs else None
1290 heads = [repo[r].node() for r in revs] if revs else None
1291 outgoing = discovery.outgoing(repo, common, heads)
1291 outgoing = discovery.outgoing(repo, common, heads)
1292 else:
1292 else:
1293 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1293 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1294 dest, branches = hg.parseurl(dest, opts.get('branch'))
1294 dest, branches = hg.parseurl(dest, opts.get('branch'))
1295 other = hg.peer(repo, opts, dest)
1295 other = hg.peer(repo, opts, dest)
1296 revs = [repo[r].hex() for r in revs]
1296 revs = [repo[r].hex() for r in revs]
1297 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1297 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1298 heads = revs and pycompat.maplist(repo.lookup, revs) or revs
1298 heads = revs and pycompat.maplist(repo.lookup, revs) or revs
1299 outgoing = discovery.findcommonoutgoing(repo, other,
1299 outgoing = discovery.findcommonoutgoing(repo, other,
1300 onlyheads=heads,
1300 onlyheads=heads,
1301 force=opts.get('force'),
1301 force=opts.get('force'),
1302 portable=True)
1302 portable=True)
1303
1303
1304 if not outgoing.missing:
1304 if not outgoing.missing:
1305 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1305 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1306 return 1
1306 return 1
1307
1307
1308 if cgversion == '01': #bundle1
1308 if cgversion == '01': #bundle1
1309 bversion = 'HG10' + bundlespec.wirecompression
1309 bversion = 'HG10' + bundlespec.wirecompression
1310 bcompression = None
1310 bcompression = None
1311 elif cgversion in ('02', '03'):
1311 elif cgversion in ('02', '03'):
1312 bversion = 'HG20'
1312 bversion = 'HG20'
1313 bcompression = bundlespec.wirecompression
1313 bcompression = bundlespec.wirecompression
1314 else:
1314 else:
1315 raise error.ProgrammingError(
1315 raise error.ProgrammingError(
1316 'bundle: unexpected changegroup version %s' % cgversion)
1316 'bundle: unexpected changegroup version %s' % cgversion)
1317
1317
1318 # TODO compression options should be derived from bundlespec parsing.
1318 # TODO compression options should be derived from bundlespec parsing.
1319 # This is a temporary hack to allow adjusting bundle compression
1319 # This is a temporary hack to allow adjusting bundle compression
1320 # level without a) formalizing the bundlespec changes to declare it
1320 # level without a) formalizing the bundlespec changes to declare it
1321 # b) introducing a command flag.
1321 # b) introducing a command flag.
1322 compopts = {}
1322 compopts = {}
1323 complevel = ui.configint('experimental',
1323 complevel = ui.configint('experimental',
1324 'bundlecomplevel.' + bundlespec.compression)
1324 'bundlecomplevel.' + bundlespec.compression)
1325 if complevel is None:
1325 if complevel is None:
1326 complevel = ui.configint('experimental', 'bundlecomplevel')
1326 complevel = ui.configint('experimental', 'bundlecomplevel')
1327 if complevel is not None:
1327 if complevel is not None:
1328 compopts['level'] = complevel
1328 compopts['level'] = complevel
1329
1329
1330 # Allow overriding the bundling of obsmarker in phases through
1330 # Allow overriding the bundling of obsmarker in phases through
1331 # configuration while we don't have a bundle version that include them
1331 # configuration while we don't have a bundle version that include them
1332 if repo.ui.configbool('experimental', 'evolution.bundle-obsmarker'):
1332 if repo.ui.configbool('experimental', 'evolution.bundle-obsmarker'):
1333 bundlespec.contentopts['obsolescence'] = True
1333 bundlespec.contentopts['obsolescence'] = True
1334 if repo.ui.configbool('experimental', 'bundle-phases'):
1334 if repo.ui.configbool('experimental', 'bundle-phases'):
1335 bundlespec.contentopts['phases'] = True
1335 bundlespec.contentopts['phases'] = True
1336
1336
1337 bundle2.writenewbundle(ui, repo, 'bundle', fname, bversion, outgoing,
1337 bundle2.writenewbundle(ui, repo, 'bundle', fname, bversion, outgoing,
1338 bundlespec.contentopts, compression=bcompression,
1338 bundlespec.contentopts, compression=bcompression,
1339 compopts=compopts)
1339 compopts=compopts)
1340
1340
1341 @command('cat',
1341 @command('cat',
1342 [('o', 'output', '',
1342 [('o', 'output', '',
1343 _('print output to file with formatted name'), _('FORMAT')),
1343 _('print output to file with formatted name'), _('FORMAT')),
1344 ('r', 'rev', '', _('print the given revision'), _('REV')),
1344 ('r', 'rev', '', _('print the given revision'), _('REV')),
1345 ('', 'decode', None, _('apply any matching decode filter')),
1345 ('', 'decode', None, _('apply any matching decode filter')),
1346 ] + walkopts + formatteropts,
1346 ] + walkopts + formatteropts,
1347 _('[OPTION]... FILE...'),
1347 _('[OPTION]... FILE...'),
1348 helpcategory=command.CATEGORY_FILE_CONTENTS,
1348 helpcategory=command.CATEGORY_FILE_CONTENTS,
1349 inferrepo=True,
1349 inferrepo=True,
1350 intents={INTENT_READONLY})
1350 intents={INTENT_READONLY})
1351 def cat(ui, repo, file1, *pats, **opts):
1351 def cat(ui, repo, file1, *pats, **opts):
1352 """output the current or given revision of files
1352 """output the current or given revision of files
1353
1353
1354 Print the specified files as they were at the given revision. If
1354 Print the specified files as they were at the given revision. If
1355 no revision is given, the parent of the working directory is used.
1355 no revision is given, the parent of the working directory is used.
1356
1356
1357 Output may be to a file, in which case the name of the file is
1357 Output may be to a file, in which case the name of the file is
1358 given using a template string. See :hg:`help templates`. In addition
1358 given using a template string. See :hg:`help templates`. In addition
1359 to the common template keywords, the following formatting rules are
1359 to the common template keywords, the following formatting rules are
1360 supported:
1360 supported:
1361
1361
1362 :``%%``: literal "%" character
1362 :``%%``: literal "%" character
1363 :``%s``: basename of file being printed
1363 :``%s``: basename of file being printed
1364 :``%d``: dirname of file being printed, or '.' if in repository root
1364 :``%d``: dirname of file being printed, or '.' if in repository root
1365 :``%p``: root-relative path name of file being printed
1365 :``%p``: root-relative path name of file being printed
1366 :``%H``: changeset hash (40 hexadecimal digits)
1366 :``%H``: changeset hash (40 hexadecimal digits)
1367 :``%R``: changeset revision number
1367 :``%R``: changeset revision number
1368 :``%h``: short-form changeset hash (12 hexadecimal digits)
1368 :``%h``: short-form changeset hash (12 hexadecimal digits)
1369 :``%r``: zero-padded changeset revision number
1369 :``%r``: zero-padded changeset revision number
1370 :``%b``: basename of the exporting repository
1370 :``%b``: basename of the exporting repository
1371 :``\\``: literal "\\" character
1371 :``\\``: literal "\\" character
1372
1372
1373 .. container:: verbose
1373 .. container:: verbose
1374
1374
1375 Template:
1375 Template:
1376
1376
1377 The following keywords are supported in addition to the common template
1377 The following keywords are supported in addition to the common template
1378 keywords and functions. See also :hg:`help templates`.
1378 keywords and functions. See also :hg:`help templates`.
1379
1379
1380 :data: String. File content.
1380 :data: String. File content.
1381 :path: String. Repository-absolute path of the file.
1381 :path: String. Repository-absolute path of the file.
1382
1382
1383 Returns 0 on success.
1383 Returns 0 on success.
1384 """
1384 """
1385 opts = pycompat.byteskwargs(opts)
1385 opts = pycompat.byteskwargs(opts)
1386 rev = opts.get('rev')
1386 rev = opts.get('rev')
1387 if rev:
1387 if rev:
1388 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
1388 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
1389 ctx = scmutil.revsingle(repo, rev)
1389 ctx = scmutil.revsingle(repo, rev)
1390 m = scmutil.match(ctx, (file1,) + pats, opts)
1390 m = scmutil.match(ctx, (file1,) + pats, opts)
1391 fntemplate = opts.pop('output', '')
1391 fntemplate = opts.pop('output', '')
1392 if cmdutil.isstdiofilename(fntemplate):
1392 if cmdutil.isstdiofilename(fntemplate):
1393 fntemplate = ''
1393 fntemplate = ''
1394
1394
1395 if fntemplate:
1395 if fntemplate:
1396 fm = formatter.nullformatter(ui, 'cat', opts)
1396 fm = formatter.nullformatter(ui, 'cat', opts)
1397 else:
1397 else:
1398 ui.pager('cat')
1398 ui.pager('cat')
1399 fm = ui.formatter('cat', opts)
1399 fm = ui.formatter('cat', opts)
1400 with fm:
1400 with fm:
1401 return cmdutil.cat(ui, repo, ctx, m, fm, fntemplate, '',
1401 return cmdutil.cat(ui, repo, ctx, m, fm, fntemplate, '',
1402 **pycompat.strkwargs(opts))
1402 **pycompat.strkwargs(opts))
1403
1403
1404 @command('clone',
1404 @command('clone',
1405 [('U', 'noupdate', None, _('the clone will include an empty working '
1405 [('U', 'noupdate', None, _('the clone will include an empty working '
1406 'directory (only a repository)')),
1406 'directory (only a repository)')),
1407 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1407 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1408 _('REV')),
1408 _('REV')),
1409 ('r', 'rev', [], _('do not clone everything, but include this changeset'
1409 ('r', 'rev', [], _('do not clone everything, but include this changeset'
1410 ' and its ancestors'), _('REV')),
1410 ' and its ancestors'), _('REV')),
1411 ('b', 'branch', [], _('do not clone everything, but include this branch\'s'
1411 ('b', 'branch', [], _('do not clone everything, but include this branch\'s'
1412 ' changesets and their ancestors'), _('BRANCH')),
1412 ' changesets and their ancestors'), _('BRANCH')),
1413 ('', 'pull', None, _('use pull protocol to copy metadata')),
1413 ('', 'pull', None, _('use pull protocol to copy metadata')),
1414 ('', 'uncompressed', None,
1414 ('', 'uncompressed', None,
1415 _('an alias to --stream (DEPRECATED)')),
1415 _('an alias to --stream (DEPRECATED)')),
1416 ('', 'stream', None,
1416 ('', 'stream', None,
1417 _('clone with minimal data processing')),
1417 _('clone with minimal data processing')),
1418 ] + remoteopts,
1418 ] + remoteopts,
1419 _('[OPTION]... SOURCE [DEST]'),
1419 _('[OPTION]... SOURCE [DEST]'),
1420 helpcategory=command.CATEGORY_REPO_CREATION,
1420 helpcategory=command.CATEGORY_REPO_CREATION,
1421 helpbasic=True, norepo=True)
1421 helpbasic=True, norepo=True)
1422 def clone(ui, source, dest=None, **opts):
1422 def clone(ui, source, dest=None, **opts):
1423 """make a copy of an existing repository
1423 """make a copy of an existing repository
1424
1424
1425 Create a copy of an existing repository in a new directory.
1425 Create a copy of an existing repository in a new directory.
1426
1426
1427 If no destination directory name is specified, it defaults to the
1427 If no destination directory name is specified, it defaults to the
1428 basename of the source.
1428 basename of the source.
1429
1429
1430 The location of the source is added to the new repository's
1430 The location of the source is added to the new repository's
1431 ``.hg/hgrc`` file, as the default to be used for future pulls.
1431 ``.hg/hgrc`` file, as the default to be used for future pulls.
1432
1432
1433 Only local paths and ``ssh://`` URLs are supported as
1433 Only local paths and ``ssh://`` URLs are supported as
1434 destinations. For ``ssh://`` destinations, no working directory or
1434 destinations. For ``ssh://`` destinations, no working directory or
1435 ``.hg/hgrc`` will be created on the remote side.
1435 ``.hg/hgrc`` will be created on the remote side.
1436
1436
1437 If the source repository has a bookmark called '@' set, that
1437 If the source repository has a bookmark called '@' set, that
1438 revision will be checked out in the new repository by default.
1438 revision will be checked out in the new repository by default.
1439
1439
1440 To check out a particular version, use -u/--update, or
1440 To check out a particular version, use -u/--update, or
1441 -U/--noupdate to create a clone with no working directory.
1441 -U/--noupdate to create a clone with no working directory.
1442
1442
1443 To pull only a subset of changesets, specify one or more revisions
1443 To pull only a subset of changesets, specify one or more revisions
1444 identifiers with -r/--rev or branches with -b/--branch. The
1444 identifiers with -r/--rev or branches with -b/--branch. The
1445 resulting clone will contain only the specified changesets and
1445 resulting clone will contain only the specified changesets and
1446 their ancestors. These options (or 'clone src#rev dest') imply
1446 their ancestors. These options (or 'clone src#rev dest') imply
1447 --pull, even for local source repositories.
1447 --pull, even for local source repositories.
1448
1448
1449 In normal clone mode, the remote normalizes repository data into a common
1449 In normal clone mode, the remote normalizes repository data into a common
1450 exchange format and the receiving end translates this data into its local
1450 exchange format and the receiving end translates this data into its local
1451 storage format. --stream activates a different clone mode that essentially
1451 storage format. --stream activates a different clone mode that essentially
1452 copies repository files from the remote with minimal data processing. This
1452 copies repository files from the remote with minimal data processing. This
1453 significantly reduces the CPU cost of a clone both remotely and locally.
1453 significantly reduces the CPU cost of a clone both remotely and locally.
1454 However, it often increases the transferred data size by 30-40%. This can
1454 However, it often increases the transferred data size by 30-40%. This can
1455 result in substantially faster clones where I/O throughput is plentiful,
1455 result in substantially faster clones where I/O throughput is plentiful,
1456 especially for larger repositories. A side-effect of --stream clones is
1456 especially for larger repositories. A side-effect of --stream clones is
1457 that storage settings and requirements on the remote are applied locally:
1457 that storage settings and requirements on the remote are applied locally:
1458 a modern client may inherit legacy or inefficient storage used by the
1458 a modern client may inherit legacy or inefficient storage used by the
1459 remote or a legacy Mercurial client may not be able to clone from a
1459 remote or a legacy Mercurial client may not be able to clone from a
1460 modern Mercurial remote.
1460 modern Mercurial remote.
1461
1461
1462 .. note::
1462 .. note::
1463
1463
1464 Specifying a tag will include the tagged changeset but not the
1464 Specifying a tag will include the tagged changeset but not the
1465 changeset containing the tag.
1465 changeset containing the tag.
1466
1466
1467 .. container:: verbose
1467 .. container:: verbose
1468
1468
1469 For efficiency, hardlinks are used for cloning whenever the
1469 For efficiency, hardlinks are used for cloning whenever the
1470 source and destination are on the same filesystem (note this
1470 source and destination are on the same filesystem (note this
1471 applies only to the repository data, not to the working
1471 applies only to the repository data, not to the working
1472 directory). Some filesystems, such as AFS, implement hardlinking
1472 directory). Some filesystems, such as AFS, implement hardlinking
1473 incorrectly, but do not report errors. In these cases, use the
1473 incorrectly, but do not report errors. In these cases, use the
1474 --pull option to avoid hardlinking.
1474 --pull option to avoid hardlinking.
1475
1475
1476 Mercurial will update the working directory to the first applicable
1476 Mercurial will update the working directory to the first applicable
1477 revision from this list:
1477 revision from this list:
1478
1478
1479 a) null if -U or the source repository has no changesets
1479 a) null if -U or the source repository has no changesets
1480 b) if -u . and the source repository is local, the first parent of
1480 b) if -u . and the source repository is local, the first parent of
1481 the source repository's working directory
1481 the source repository's working directory
1482 c) the changeset specified with -u (if a branch name, this means the
1482 c) the changeset specified with -u (if a branch name, this means the
1483 latest head of that branch)
1483 latest head of that branch)
1484 d) the changeset specified with -r
1484 d) the changeset specified with -r
1485 e) the tipmost head specified with -b
1485 e) the tipmost head specified with -b
1486 f) the tipmost head specified with the url#branch source syntax
1486 f) the tipmost head specified with the url#branch source syntax
1487 g) the revision marked with the '@' bookmark, if present
1487 g) the revision marked with the '@' bookmark, if present
1488 h) the tipmost head of the default branch
1488 h) the tipmost head of the default branch
1489 i) tip
1489 i) tip
1490
1490
1491 When cloning from servers that support it, Mercurial may fetch
1491 When cloning from servers that support it, Mercurial may fetch
1492 pre-generated data from a server-advertised URL or inline from the
1492 pre-generated data from a server-advertised URL or inline from the
1493 same stream. When this is done, hooks operating on incoming changesets
1493 same stream. When this is done, hooks operating on incoming changesets
1494 and changegroups may fire more than once, once for each pre-generated
1494 and changegroups may fire more than once, once for each pre-generated
1495 bundle and as well as for any additional remaining data. In addition,
1495 bundle and as well as for any additional remaining data. In addition,
1496 if an error occurs, the repository may be rolled back to a partial
1496 if an error occurs, the repository may be rolled back to a partial
1497 clone. This behavior may change in future releases.
1497 clone. This behavior may change in future releases.
1498 See :hg:`help -e clonebundles` for more.
1498 See :hg:`help -e clonebundles` for more.
1499
1499
1500 Examples:
1500 Examples:
1501
1501
1502 - clone a remote repository to a new directory named hg/::
1502 - clone a remote repository to a new directory named hg/::
1503
1503
1504 hg clone https://www.mercurial-scm.org/repo/hg/
1504 hg clone https://www.mercurial-scm.org/repo/hg/
1505
1505
1506 - create a lightweight local clone::
1506 - create a lightweight local clone::
1507
1507
1508 hg clone project/ project-feature/
1508 hg clone project/ project-feature/
1509
1509
1510 - clone from an absolute path on an ssh server (note double-slash)::
1510 - clone from an absolute path on an ssh server (note double-slash)::
1511
1511
1512 hg clone ssh://user@server//home/projects/alpha/
1512 hg clone ssh://user@server//home/projects/alpha/
1513
1513
1514 - do a streaming clone while checking out a specified version::
1514 - do a streaming clone while checking out a specified version::
1515
1515
1516 hg clone --stream http://server/repo -u 1.5
1516 hg clone --stream http://server/repo -u 1.5
1517
1517
1518 - create a repository without changesets after a particular revision::
1518 - create a repository without changesets after a particular revision::
1519
1519
1520 hg clone -r 04e544 experimental/ good/
1520 hg clone -r 04e544 experimental/ good/
1521
1521
1522 - clone (and track) a particular named branch::
1522 - clone (and track) a particular named branch::
1523
1523
1524 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1524 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1525
1525
1526 See :hg:`help urls` for details on specifying URLs.
1526 See :hg:`help urls` for details on specifying URLs.
1527
1527
1528 Returns 0 on success.
1528 Returns 0 on success.
1529 """
1529 """
1530 opts = pycompat.byteskwargs(opts)
1530 opts = pycompat.byteskwargs(opts)
1531 if opts.get('noupdate') and opts.get('updaterev'):
1531 if opts.get('noupdate') and opts.get('updaterev'):
1532 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1532 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1533
1533
1534 # --include/--exclude can come from narrow or sparse.
1534 # --include/--exclude can come from narrow or sparse.
1535 includepats, excludepats = None, None
1535 includepats, excludepats = None, None
1536
1536
1537 # hg.clone() differentiates between None and an empty set. So make sure
1537 # hg.clone() differentiates between None and an empty set. So make sure
1538 # patterns are sets if narrow is requested without patterns.
1538 # patterns are sets if narrow is requested without patterns.
1539 if opts.get('narrow'):
1539 if opts.get('narrow'):
1540 includepats = set()
1540 includepats = set()
1541 excludepats = set()
1541 excludepats = set()
1542
1542
1543 if opts.get('include'):
1543 if opts.get('include'):
1544 includepats = narrowspec.parsepatterns(opts.get('include'))
1544 includepats = narrowspec.parsepatterns(opts.get('include'))
1545 if opts.get('exclude'):
1545 if opts.get('exclude'):
1546 excludepats = narrowspec.parsepatterns(opts.get('exclude'))
1546 excludepats = narrowspec.parsepatterns(opts.get('exclude'))
1547
1547
1548 r = hg.clone(ui, opts, source, dest,
1548 r = hg.clone(ui, opts, source, dest,
1549 pull=opts.get('pull'),
1549 pull=opts.get('pull'),
1550 stream=opts.get('stream') or opts.get('uncompressed'),
1550 stream=opts.get('stream') or opts.get('uncompressed'),
1551 revs=opts.get('rev'),
1551 revs=opts.get('rev'),
1552 update=opts.get('updaterev') or not opts.get('noupdate'),
1552 update=opts.get('updaterev') or not opts.get('noupdate'),
1553 branch=opts.get('branch'),
1553 branch=opts.get('branch'),
1554 shareopts=opts.get('shareopts'),
1554 shareopts=opts.get('shareopts'),
1555 storeincludepats=includepats,
1555 storeincludepats=includepats,
1556 storeexcludepats=excludepats,
1556 storeexcludepats=excludepats,
1557 depth=opts.get('depth') or None)
1557 depth=opts.get('depth') or None)
1558
1558
1559 return r is None
1559 return r is None
1560
1560
1561 @command('commit|ci',
1561 @command('commit|ci',
1562 [('A', 'addremove', None,
1562 [('A', 'addremove', None,
1563 _('mark new/missing files as added/removed before committing')),
1563 _('mark new/missing files as added/removed before committing')),
1564 ('', 'close-branch', None,
1564 ('', 'close-branch', None,
1565 _('mark a branch head as closed')),
1565 _('mark a branch head as closed')),
1566 ('', 'amend', None, _('amend the parent of the working directory')),
1566 ('', 'amend', None, _('amend the parent of the working directory')),
1567 ('s', 'secret', None, _('use the secret phase for committing')),
1567 ('s', 'secret', None, _('use the secret phase for committing')),
1568 ('e', 'edit', None, _('invoke editor on commit messages')),
1568 ('e', 'edit', None, _('invoke editor on commit messages')),
1569 ('i', 'interactive', None, _('use interactive mode')),
1569 ('i', 'interactive', None, _('use interactive mode')),
1570 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1570 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1571 _('[OPTION]... [FILE]...'),
1571 _('[OPTION]... [FILE]...'),
1572 helpcategory=command.CATEGORY_COMMITTING, helpbasic=True,
1572 helpcategory=command.CATEGORY_COMMITTING, helpbasic=True,
1573 inferrepo=True)
1573 inferrepo=True)
1574 def commit(ui, repo, *pats, **opts):
1574 def commit(ui, repo, *pats, **opts):
1575 """commit the specified files or all outstanding changes
1575 """commit the specified files or all outstanding changes
1576
1576
1577 Commit changes to the given files into the repository. Unlike a
1577 Commit changes to the given files into the repository. Unlike a
1578 centralized SCM, this operation is a local operation. See
1578 centralized SCM, this operation is a local operation. See
1579 :hg:`push` for a way to actively distribute your changes.
1579 :hg:`push` for a way to actively distribute your changes.
1580
1580
1581 If a list of files is omitted, all changes reported by :hg:`status`
1581 If a list of files is omitted, all changes reported by :hg:`status`
1582 will be committed.
1582 will be committed.
1583
1583
1584 If you are committing the result of a merge, do not provide any
1584 If you are committing the result of a merge, do not provide any
1585 filenames or -I/-X filters.
1585 filenames or -I/-X filters.
1586
1586
1587 If no commit message is specified, Mercurial starts your
1587 If no commit message is specified, Mercurial starts your
1588 configured editor where you can enter a message. In case your
1588 configured editor where you can enter a message. In case your
1589 commit fails, you will find a backup of your message in
1589 commit fails, you will find a backup of your message in
1590 ``.hg/last-message.txt``.
1590 ``.hg/last-message.txt``.
1591
1591
1592 The --close-branch flag can be used to mark the current branch
1592 The --close-branch flag can be used to mark the current branch
1593 head closed. When all heads of a branch are closed, the branch
1593 head closed. When all heads of a branch are closed, the branch
1594 will be considered closed and no longer listed.
1594 will be considered closed and no longer listed.
1595
1595
1596 The --amend flag can be used to amend the parent of the
1596 The --amend flag can be used to amend the parent of the
1597 working directory with a new commit that contains the changes
1597 working directory with a new commit that contains the changes
1598 in the parent in addition to those currently reported by :hg:`status`,
1598 in the parent in addition to those currently reported by :hg:`status`,
1599 if there are any. The old commit is stored in a backup bundle in
1599 if there are any. The old commit is stored in a backup bundle in
1600 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1600 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1601 on how to restore it).
1601 on how to restore it).
1602
1602
1603 Message, user and date are taken from the amended commit unless
1603 Message, user and date are taken from the amended commit unless
1604 specified. When a message isn't specified on the command line,
1604 specified. When a message isn't specified on the command line,
1605 the editor will open with the message of the amended commit.
1605 the editor will open with the message of the amended commit.
1606
1606
1607 It is not possible to amend public changesets (see :hg:`help phases`)
1607 It is not possible to amend public changesets (see :hg:`help phases`)
1608 or changesets that have children.
1608 or changesets that have children.
1609
1609
1610 See :hg:`help dates` for a list of formats valid for -d/--date.
1610 See :hg:`help dates` for a list of formats valid for -d/--date.
1611
1611
1612 Returns 0 on success, 1 if nothing changed.
1612 Returns 0 on success, 1 if nothing changed.
1613
1613
1614 .. container:: verbose
1614 .. container:: verbose
1615
1615
1616 Examples:
1616 Examples:
1617
1617
1618 - commit all files ending in .py::
1618 - commit all files ending in .py::
1619
1619
1620 hg commit --include "set:**.py"
1620 hg commit --include "set:**.py"
1621
1621
1622 - commit all non-binary files::
1622 - commit all non-binary files::
1623
1623
1624 hg commit --exclude "set:binary()"
1624 hg commit --exclude "set:binary()"
1625
1625
1626 - amend the current commit and set the date to now::
1626 - amend the current commit and set the date to now::
1627
1627
1628 hg commit --amend --date now
1628 hg commit --amend --date now
1629 """
1629 """
1630 with repo.wlock(), repo.lock():
1630 with repo.wlock(), repo.lock():
1631 return _docommit(ui, repo, *pats, **opts)
1631 return _docommit(ui, repo, *pats, **opts)
1632
1632
1633 def _docommit(ui, repo, *pats, **opts):
1633 def _docommit(ui, repo, *pats, **opts):
1634 if opts.get(r'interactive'):
1634 if opts.get(r'interactive'):
1635 opts.pop(r'interactive')
1635 opts.pop(r'interactive')
1636 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1636 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1637 cmdutil.recordfilter, *pats,
1637 cmdutil.recordfilter, *pats,
1638 **opts)
1638 **opts)
1639 # ret can be 0 (no changes to record) or the value returned by
1639 # ret can be 0 (no changes to record) or the value returned by
1640 # commit(), 1 if nothing changed or None on success.
1640 # commit(), 1 if nothing changed or None on success.
1641 return 1 if ret == 0 else ret
1641 return 1 if ret == 0 else ret
1642
1642
1643 opts = pycompat.byteskwargs(opts)
1643 opts = pycompat.byteskwargs(opts)
1644 if opts.get('subrepos'):
1644 if opts.get('subrepos'):
1645 if opts.get('amend'):
1645 if opts.get('amend'):
1646 raise error.Abort(_('cannot amend with --subrepos'))
1646 raise error.Abort(_('cannot amend with --subrepos'))
1647 # Let --subrepos on the command line override config setting.
1647 # Let --subrepos on the command line override config setting.
1648 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1648 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1649
1649
1650 cmdutil.checkunfinished(repo, commit=True)
1650 cmdutil.checkunfinished(repo, commit=True)
1651
1651
1652 branch = repo[None].branch()
1652 branch = repo[None].branch()
1653 bheads = repo.branchheads(branch)
1653 bheads = repo.branchheads(branch)
1654
1654
1655 extra = {}
1655 extra = {}
1656 if opts.get('close_branch'):
1656 if opts.get('close_branch'):
1657 extra['close'] = '1'
1657 extra['close'] = '1'
1658
1658
1659 if not bheads:
1659 if not bheads:
1660 raise error.Abort(_('can only close branch heads'))
1660 raise error.Abort(_('can only close branch heads'))
1661 elif opts.get('amend'):
1661 elif opts.get('amend'):
1662 if repo[None].parents()[0].p1().branch() != branch and \
1662 if repo[None].parents()[0].p1().branch() != branch and \
1663 repo[None].parents()[0].p2().branch() != branch:
1663 repo[None].parents()[0].p2().branch() != branch:
1664 raise error.Abort(_('can only close branch heads'))
1664 raise error.Abort(_('can only close branch heads'))
1665
1665
1666 if opts.get('amend'):
1666 if opts.get('amend'):
1667 if ui.configbool('ui', 'commitsubrepos'):
1667 if ui.configbool('ui', 'commitsubrepos'):
1668 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1668 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1669
1669
1670 old = repo['.']
1670 old = repo['.']
1671 rewriteutil.precheck(repo, [old.rev()], 'amend')
1671 rewriteutil.precheck(repo, [old.rev()], 'amend')
1672
1672
1673 # Currently histedit gets confused if an amend happens while histedit
1673 # Currently histedit gets confused if an amend happens while histedit
1674 # is in progress. Since we have a checkunfinished command, we are
1674 # is in progress. Since we have a checkunfinished command, we are
1675 # temporarily honoring it.
1675 # temporarily honoring it.
1676 #
1676 #
1677 # Note: eventually this guard will be removed. Please do not expect
1677 # Note: eventually this guard will be removed. Please do not expect
1678 # this behavior to remain.
1678 # this behavior to remain.
1679 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1679 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1680 cmdutil.checkunfinished(repo)
1680 cmdutil.checkunfinished(repo)
1681
1681
1682 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
1682 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
1683 if node == old.node():
1683 if node == old.node():
1684 ui.status(_("nothing changed\n"))
1684 ui.status(_("nothing changed\n"))
1685 return 1
1685 return 1
1686 else:
1686 else:
1687 def commitfunc(ui, repo, message, match, opts):
1687 def commitfunc(ui, repo, message, match, opts):
1688 overrides = {}
1688 overrides = {}
1689 if opts.get('secret'):
1689 if opts.get('secret'):
1690 overrides[('phases', 'new-commit')] = 'secret'
1690 overrides[('phases', 'new-commit')] = 'secret'
1691
1691
1692 baseui = repo.baseui
1692 baseui = repo.baseui
1693 with baseui.configoverride(overrides, 'commit'):
1693 with baseui.configoverride(overrides, 'commit'):
1694 with ui.configoverride(overrides, 'commit'):
1694 with ui.configoverride(overrides, 'commit'):
1695 editform = cmdutil.mergeeditform(repo[None],
1695 editform = cmdutil.mergeeditform(repo[None],
1696 'commit.normal')
1696 'commit.normal')
1697 editor = cmdutil.getcommiteditor(
1697 editor = cmdutil.getcommiteditor(
1698 editform=editform, **pycompat.strkwargs(opts))
1698 editform=editform, **pycompat.strkwargs(opts))
1699 return repo.commit(message,
1699 return repo.commit(message,
1700 opts.get('user'),
1700 opts.get('user'),
1701 opts.get('date'),
1701 opts.get('date'),
1702 match,
1702 match,
1703 editor=editor,
1703 editor=editor,
1704 extra=extra)
1704 extra=extra)
1705
1705
1706 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1706 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1707
1707
1708 if not node:
1708 if not node:
1709 stat = cmdutil.postcommitstatus(repo, pats, opts)
1709 stat = cmdutil.postcommitstatus(repo, pats, opts)
1710 if stat[3]:
1710 if stat[3]:
1711 ui.status(_("nothing changed (%d missing files, see "
1711 ui.status(_("nothing changed (%d missing files, see "
1712 "'hg status')\n") % len(stat[3]))
1712 "'hg status')\n") % len(stat[3]))
1713 else:
1713 else:
1714 ui.status(_("nothing changed\n"))
1714 ui.status(_("nothing changed\n"))
1715 return 1
1715 return 1
1716
1716
1717 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1717 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1718
1718
1719 @command('config|showconfig|debugconfig',
1719 @command('config|showconfig|debugconfig',
1720 [('u', 'untrusted', None, _('show untrusted configuration options')),
1720 [('u', 'untrusted', None, _('show untrusted configuration options')),
1721 ('e', 'edit', None, _('edit user config')),
1721 ('e', 'edit', None, _('edit user config')),
1722 ('l', 'local', None, _('edit repository config')),
1722 ('l', 'local', None, _('edit repository config')),
1723 ('g', 'global', None, _('edit global config'))] + formatteropts,
1723 ('g', 'global', None, _('edit global config'))] + formatteropts,
1724 _('[-u] [NAME]...'),
1724 _('[-u] [NAME]...'),
1725 helpcategory=command.CATEGORY_HELP,
1725 helpcategory=command.CATEGORY_HELP,
1726 optionalrepo=True,
1726 optionalrepo=True,
1727 intents={INTENT_READONLY})
1727 intents={INTENT_READONLY})
1728 def config(ui, repo, *values, **opts):
1728 def config(ui, repo, *values, **opts):
1729 """show combined config settings from all hgrc files
1729 """show combined config settings from all hgrc files
1730
1730
1731 With no arguments, print names and values of all config items.
1731 With no arguments, print names and values of all config items.
1732
1732
1733 With one argument of the form section.name, print just the value
1733 With one argument of the form section.name, print just the value
1734 of that config item.
1734 of that config item.
1735
1735
1736 With multiple arguments, print names and values of all config
1736 With multiple arguments, print names and values of all config
1737 items with matching section names or section.names.
1737 items with matching section names or section.names.
1738
1738
1739 With --edit, start an editor on the user-level config file. With
1739 With --edit, start an editor on the user-level config file. With
1740 --global, edit the system-wide config file. With --local, edit the
1740 --global, edit the system-wide config file. With --local, edit the
1741 repository-level config file.
1741 repository-level config file.
1742
1742
1743 With --debug, the source (filename and line number) is printed
1743 With --debug, the source (filename and line number) is printed
1744 for each config item.
1744 for each config item.
1745
1745
1746 See :hg:`help config` for more information about config files.
1746 See :hg:`help config` for more information about config files.
1747
1747
1748 .. container:: verbose
1748 .. container:: verbose
1749
1749
1750 Template:
1750 Template:
1751
1751
1752 The following keywords are supported. See also :hg:`help templates`.
1752 The following keywords are supported. See also :hg:`help templates`.
1753
1753
1754 :name: String. Config name.
1754 :name: String. Config name.
1755 :source: String. Filename and line number where the item is defined.
1755 :source: String. Filename and line number where the item is defined.
1756 :value: String. Config value.
1756 :value: String. Config value.
1757
1757
1758 Returns 0 on success, 1 if NAME does not exist.
1758 Returns 0 on success, 1 if NAME does not exist.
1759
1759
1760 """
1760 """
1761
1761
1762 opts = pycompat.byteskwargs(opts)
1762 opts = pycompat.byteskwargs(opts)
1763 if opts.get('edit') or opts.get('local') or opts.get('global'):
1763 if opts.get('edit') or opts.get('local') or opts.get('global'):
1764 if opts.get('local') and opts.get('global'):
1764 if opts.get('local') and opts.get('global'):
1765 raise error.Abort(_("can't use --local and --global together"))
1765 raise error.Abort(_("can't use --local and --global together"))
1766
1766
1767 if opts.get('local'):
1767 if opts.get('local'):
1768 if not repo:
1768 if not repo:
1769 raise error.Abort(_("can't use --local outside a repository"))
1769 raise error.Abort(_("can't use --local outside a repository"))
1770 paths = [repo.vfs.join('hgrc')]
1770 paths = [repo.vfs.join('hgrc')]
1771 elif opts.get('global'):
1771 elif opts.get('global'):
1772 paths = rcutil.systemrcpath()
1772 paths = rcutil.systemrcpath()
1773 else:
1773 else:
1774 paths = rcutil.userrcpath()
1774 paths = rcutil.userrcpath()
1775
1775
1776 for f in paths:
1776 for f in paths:
1777 if os.path.exists(f):
1777 if os.path.exists(f):
1778 break
1778 break
1779 else:
1779 else:
1780 if opts.get('global'):
1780 if opts.get('global'):
1781 samplehgrc = uimod.samplehgrcs['global']
1781 samplehgrc = uimod.samplehgrcs['global']
1782 elif opts.get('local'):
1782 elif opts.get('local'):
1783 samplehgrc = uimod.samplehgrcs['local']
1783 samplehgrc = uimod.samplehgrcs['local']
1784 else:
1784 else:
1785 samplehgrc = uimod.samplehgrcs['user']
1785 samplehgrc = uimod.samplehgrcs['user']
1786
1786
1787 f = paths[0]
1787 f = paths[0]
1788 fp = open(f, "wb")
1788 fp = open(f, "wb")
1789 fp.write(util.tonativeeol(samplehgrc))
1789 fp.write(util.tonativeeol(samplehgrc))
1790 fp.close()
1790 fp.close()
1791
1791
1792 editor = ui.geteditor()
1792 editor = ui.geteditor()
1793 ui.system("%s \"%s\"" % (editor, f),
1793 ui.system("%s \"%s\"" % (editor, f),
1794 onerr=error.Abort, errprefix=_("edit failed"),
1794 onerr=error.Abort, errprefix=_("edit failed"),
1795 blockedtag='config_edit')
1795 blockedtag='config_edit')
1796 return
1796 return
1797 ui.pager('config')
1797 ui.pager('config')
1798 fm = ui.formatter('config', opts)
1798 fm = ui.formatter('config', opts)
1799 for t, f in rcutil.rccomponents():
1799 for t, f in rcutil.rccomponents():
1800 if t == 'path':
1800 if t == 'path':
1801 ui.debug('read config from: %s\n' % f)
1801 ui.debug('read config from: %s\n' % f)
1802 elif t == 'items':
1802 elif t == 'items':
1803 for section, name, value, source in f:
1803 for section, name, value, source in f:
1804 ui.debug('set config by: %s\n' % source)
1804 ui.debug('set config by: %s\n' % source)
1805 else:
1805 else:
1806 raise error.ProgrammingError('unknown rctype: %s' % t)
1806 raise error.ProgrammingError('unknown rctype: %s' % t)
1807 untrusted = bool(opts.get('untrusted'))
1807 untrusted = bool(opts.get('untrusted'))
1808
1808
1809 selsections = selentries = []
1809 selsections = selentries = []
1810 if values:
1810 if values:
1811 selsections = [v for v in values if '.' not in v]
1811 selsections = [v for v in values if '.' not in v]
1812 selentries = [v for v in values if '.' in v]
1812 selentries = [v for v in values if '.' in v]
1813 uniquesel = (len(selentries) == 1 and not selsections)
1813 uniquesel = (len(selentries) == 1 and not selsections)
1814 selsections = set(selsections)
1814 selsections = set(selsections)
1815 selentries = set(selentries)
1815 selentries = set(selentries)
1816
1816
1817 matched = False
1817 matched = False
1818 for section, name, value in ui.walkconfig(untrusted=untrusted):
1818 for section, name, value in ui.walkconfig(untrusted=untrusted):
1819 source = ui.configsource(section, name, untrusted)
1819 source = ui.configsource(section, name, untrusted)
1820 value = pycompat.bytestr(value)
1820 value = pycompat.bytestr(value)
1821 if fm.isplain():
1821 if fm.isplain():
1822 source = source or 'none'
1822 source = source or 'none'
1823 value = value.replace('\n', '\\n')
1823 value = value.replace('\n', '\\n')
1824 entryname = section + '.' + name
1824 entryname = section + '.' + name
1825 if values and not (section in selsections or entryname in selentries):
1825 if values and not (section in selsections or entryname in selentries):
1826 continue
1826 continue
1827 fm.startitem()
1827 fm.startitem()
1828 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1828 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1829 if uniquesel:
1829 if uniquesel:
1830 fm.data(name=entryname)
1830 fm.data(name=entryname)
1831 fm.write('value', '%s\n', value)
1831 fm.write('value', '%s\n', value)
1832 else:
1832 else:
1833 fm.write('name value', '%s=%s\n', entryname, value)
1833 fm.write('name value', '%s=%s\n', entryname, value)
1834 matched = True
1834 matched = True
1835 fm.end()
1835 fm.end()
1836 if matched:
1836 if matched:
1837 return 0
1837 return 0
1838 return 1
1838 return 1
1839
1839
1840 @command('copy|cp',
1840 @command('copy|cp',
1841 [('A', 'after', None, _('record a copy that has already occurred')),
1841 [('A', 'after', None, _('record a copy that has already occurred')),
1842 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1842 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1843 ] + walkopts + dryrunopts,
1843 ] + walkopts + dryrunopts,
1844 _('[OPTION]... [SOURCE]... DEST'),
1844 _('[OPTION]... [SOURCE]... DEST'),
1845 helpcategory=command.CATEGORY_FILE_CONTENTS)
1845 helpcategory=command.CATEGORY_FILE_CONTENTS)
1846 def copy(ui, repo, *pats, **opts):
1846 def copy(ui, repo, *pats, **opts):
1847 """mark files as copied for the next commit
1847 """mark files as copied for the next commit
1848
1848
1849 Mark dest as having copies of source files. If dest is a
1849 Mark dest as having copies of source files. If dest is a
1850 directory, copies are put in that directory. If dest is a file,
1850 directory, copies are put in that directory. If dest is a file,
1851 the source must be a single file.
1851 the source must be a single file.
1852
1852
1853 By default, this command copies the contents of files as they
1853 By default, this command copies the contents of files as they
1854 exist in the working directory. If invoked with -A/--after, the
1854 exist in the working directory. If invoked with -A/--after, the
1855 operation is recorded, but no copying is performed.
1855 operation is recorded, but no copying is performed.
1856
1856
1857 This command takes effect with the next commit. To undo a copy
1857 This command takes effect with the next commit. To undo a copy
1858 before that, see :hg:`revert`.
1858 before that, see :hg:`revert`.
1859
1859
1860 Returns 0 on success, 1 if errors are encountered.
1860 Returns 0 on success, 1 if errors are encountered.
1861 """
1861 """
1862 opts = pycompat.byteskwargs(opts)
1862 opts = pycompat.byteskwargs(opts)
1863 with repo.wlock(False):
1863 with repo.wlock(False):
1864 return cmdutil.copy(ui, repo, pats, opts)
1864 return cmdutil.copy(ui, repo, pats, opts)
1865
1865
1866 @command(
1866 @command(
1867 'debugcommands', [], _('[COMMAND]'),
1867 'debugcommands', [], _('[COMMAND]'),
1868 helpcategory=command.CATEGORY_HELP,
1868 helpcategory=command.CATEGORY_HELP,
1869 norepo=True)
1869 norepo=True)
1870 def debugcommands(ui, cmd='', *args):
1870 def debugcommands(ui, cmd='', *args):
1871 """list all available commands and options"""
1871 """list all available commands and options"""
1872 for cmd, vals in sorted(table.iteritems()):
1872 for cmd, vals in sorted(table.iteritems()):
1873 cmd = cmd.split('|')[0]
1873 cmd = cmd.split('|')[0]
1874 opts = ', '.join([i[1] for i in vals[1]])
1874 opts = ', '.join([i[1] for i in vals[1]])
1875 ui.write('%s: %s\n' % (cmd, opts))
1875 ui.write('%s: %s\n' % (cmd, opts))
1876
1876
1877 @command('debugcomplete',
1877 @command('debugcomplete',
1878 [('o', 'options', None, _('show the command options'))],
1878 [('o', 'options', None, _('show the command options'))],
1879 _('[-o] CMD'),
1879 _('[-o] CMD'),
1880 helpcategory=command.CATEGORY_HELP,
1880 helpcategory=command.CATEGORY_HELP,
1881 norepo=True)
1881 norepo=True)
1882 def debugcomplete(ui, cmd='', **opts):
1882 def debugcomplete(ui, cmd='', **opts):
1883 """returns the completion list associated with the given command"""
1883 """returns the completion list associated with the given command"""
1884
1884
1885 if opts.get(r'options'):
1885 if opts.get(r'options'):
1886 options = []
1886 options = []
1887 otables = [globalopts]
1887 otables = [globalopts]
1888 if cmd:
1888 if cmd:
1889 aliases, entry = cmdutil.findcmd(cmd, table, False)
1889 aliases, entry = cmdutil.findcmd(cmd, table, False)
1890 otables.append(entry[1])
1890 otables.append(entry[1])
1891 for t in otables:
1891 for t in otables:
1892 for o in t:
1892 for o in t:
1893 if "(DEPRECATED)" in o[3]:
1893 if "(DEPRECATED)" in o[3]:
1894 continue
1894 continue
1895 if o[0]:
1895 if o[0]:
1896 options.append('-%s' % o[0])
1896 options.append('-%s' % o[0])
1897 options.append('--%s' % o[1])
1897 options.append('--%s' % o[1])
1898 ui.write("%s\n" % "\n".join(options))
1898 ui.write("%s\n" % "\n".join(options))
1899 return
1899 return
1900
1900
1901 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1901 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1902 if ui.verbose:
1902 if ui.verbose:
1903 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1903 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1904 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1904 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1905
1905
1906 @command('diff',
1906 @command('diff',
1907 [('r', 'rev', [], _('revision'), _('REV')),
1907 [('r', 'rev', [], _('revision'), _('REV')),
1908 ('c', 'change', '', _('change made by revision'), _('REV'))
1908 ('c', 'change', '', _('change made by revision'), _('REV'))
1909 ] + diffopts + diffopts2 + walkopts + subrepoopts,
1909 ] + diffopts + diffopts2 + walkopts + subrepoopts,
1910 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
1910 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
1911 helpcategory=command.CATEGORY_FILE_CONTENTS,
1911 helpcategory=command.CATEGORY_FILE_CONTENTS,
1912 helpbasic=True, inferrepo=True, intents={INTENT_READONLY})
1912 helpbasic=True, inferrepo=True, intents={INTENT_READONLY})
1913 def diff(ui, repo, *pats, **opts):
1913 def diff(ui, repo, *pats, **opts):
1914 """diff repository (or selected files)
1914 """diff repository (or selected files)
1915
1915
1916 Show differences between revisions for the specified files.
1916 Show differences between revisions for the specified files.
1917
1917
1918 Differences between files are shown using the unified diff format.
1918 Differences between files are shown using the unified diff format.
1919
1919
1920 .. note::
1920 .. note::
1921
1921
1922 :hg:`diff` may generate unexpected results for merges, as it will
1922 :hg:`diff` may generate unexpected results for merges, as it will
1923 default to comparing against the working directory's first
1923 default to comparing against the working directory's first
1924 parent changeset if no revisions are specified.
1924 parent changeset if no revisions are specified.
1925
1925
1926 When two revision arguments are given, then changes are shown
1926 When two revision arguments are given, then changes are shown
1927 between those revisions. If only one revision is specified then
1927 between those revisions. If only one revision is specified then
1928 that revision is compared to the working directory, and, when no
1928 that revision is compared to the working directory, and, when no
1929 revisions are specified, the working directory files are compared
1929 revisions are specified, the working directory files are compared
1930 to its first parent.
1930 to its first parent.
1931
1931
1932 Alternatively you can specify -c/--change with a revision to see
1932 Alternatively you can specify -c/--change with a revision to see
1933 the changes in that changeset relative to its first parent.
1933 the changes in that changeset relative to its first parent.
1934
1934
1935 Without the -a/--text option, diff will avoid generating diffs of
1935 Without the -a/--text option, diff will avoid generating diffs of
1936 files it detects as binary. With -a, diff will generate a diff
1936 files it detects as binary. With -a, diff will generate a diff
1937 anyway, probably with undesirable results.
1937 anyway, probably with undesirable results.
1938
1938
1939 Use the -g/--git option to generate diffs in the git extended diff
1939 Use the -g/--git option to generate diffs in the git extended diff
1940 format. For more information, read :hg:`help diffs`.
1940 format. For more information, read :hg:`help diffs`.
1941
1941
1942 .. container:: verbose
1942 .. container:: verbose
1943
1943
1944 Examples:
1944 Examples:
1945
1945
1946 - compare a file in the current working directory to its parent::
1946 - compare a file in the current working directory to its parent::
1947
1947
1948 hg diff foo.c
1948 hg diff foo.c
1949
1949
1950 - compare two historical versions of a directory, with rename info::
1950 - compare two historical versions of a directory, with rename info::
1951
1951
1952 hg diff --git -r 1.0:1.2 lib/
1952 hg diff --git -r 1.0:1.2 lib/
1953
1953
1954 - get change stats relative to the last change on some date::
1954 - get change stats relative to the last change on some date::
1955
1955
1956 hg diff --stat -r "date('may 2')"
1956 hg diff --stat -r "date('may 2')"
1957
1957
1958 - diff all newly-added files that contain a keyword::
1958 - diff all newly-added files that contain a keyword::
1959
1959
1960 hg diff "set:added() and grep(GNU)"
1960 hg diff "set:added() and grep(GNU)"
1961
1961
1962 - compare a revision and its parents::
1962 - compare a revision and its parents::
1963
1963
1964 hg diff -c 9353 # compare against first parent
1964 hg diff -c 9353 # compare against first parent
1965 hg diff -r 9353^:9353 # same using revset syntax
1965 hg diff -r 9353^:9353 # same using revset syntax
1966 hg diff -r 9353^2:9353 # compare against the second parent
1966 hg diff -r 9353^2:9353 # compare against the second parent
1967
1967
1968 Returns 0 on success.
1968 Returns 0 on success.
1969 """
1969 """
1970
1970
1971 opts = pycompat.byteskwargs(opts)
1971 opts = pycompat.byteskwargs(opts)
1972 revs = opts.get('rev')
1972 revs = opts.get('rev')
1973 change = opts.get('change')
1973 change = opts.get('change')
1974 stat = opts.get('stat')
1974 stat = opts.get('stat')
1975 reverse = opts.get('reverse')
1975 reverse = opts.get('reverse')
1976
1976
1977 if revs and change:
1977 if revs and change:
1978 msg = _('cannot specify --rev and --change at the same time')
1978 msg = _('cannot specify --rev and --change at the same time')
1979 raise error.Abort(msg)
1979 raise error.Abort(msg)
1980 elif change:
1980 elif change:
1981 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
1981 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
1982 ctx2 = scmutil.revsingle(repo, change, None)
1982 ctx2 = scmutil.revsingle(repo, change, None)
1983 ctx1 = ctx2.p1()
1983 ctx1 = ctx2.p1()
1984 else:
1984 else:
1985 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
1985 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
1986 ctx1, ctx2 = scmutil.revpair(repo, revs)
1986 ctx1, ctx2 = scmutil.revpair(repo, revs)
1987 node1, node2 = ctx1.node(), ctx2.node()
1987 node1, node2 = ctx1.node(), ctx2.node()
1988
1988
1989 if reverse:
1989 if reverse:
1990 node1, node2 = node2, node1
1990 node1, node2 = node2, node1
1991
1991
1992 diffopts = patch.diffallopts(ui, opts)
1992 diffopts = patch.diffallopts(ui, opts)
1993 m = scmutil.match(ctx2, pats, opts)
1993 m = scmutil.match(ctx2, pats, opts)
1994 m = repo.narrowmatch(m)
1994 m = repo.narrowmatch(m)
1995 ui.pager('diff')
1995 ui.pager('diff')
1996 logcmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1996 logcmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1997 listsubrepos=opts.get('subrepos'),
1997 listsubrepos=opts.get('subrepos'),
1998 root=opts.get('root'))
1998 root=opts.get('root'))
1999
1999
2000 @command('export',
2000 @command('export',
2001 [('B', 'bookmark', '',
2001 [('B', 'bookmark', '',
2002 _('export changes only reachable by given bookmark'), _('BOOKMARK')),
2002 _('export changes only reachable by given bookmark'), _('BOOKMARK')),
2003 ('o', 'output', '',
2003 ('o', 'output', '',
2004 _('print output to file with formatted name'), _('FORMAT')),
2004 _('print output to file with formatted name'), _('FORMAT')),
2005 ('', 'switch-parent', None, _('diff against the second parent')),
2005 ('', 'switch-parent', None, _('diff against the second parent')),
2006 ('r', 'rev', [], _('revisions to export'), _('REV')),
2006 ('r', 'rev', [], _('revisions to export'), _('REV')),
2007 ] + diffopts + formatteropts,
2007 ] + diffopts + formatteropts,
2008 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2008 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2009 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2009 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2010 helpbasic=True, intents={INTENT_READONLY})
2010 helpbasic=True, intents={INTENT_READONLY})
2011 def export(ui, repo, *changesets, **opts):
2011 def export(ui, repo, *changesets, **opts):
2012 """dump the header and diffs for one or more changesets
2012 """dump the header and diffs for one or more changesets
2013
2013
2014 Print the changeset header and diffs for one or more revisions.
2014 Print the changeset header and diffs for one or more revisions.
2015 If no revision is given, the parent of the working directory is used.
2015 If no revision is given, the parent of the working directory is used.
2016
2016
2017 The information shown in the changeset header is: author, date,
2017 The information shown in the changeset header is: author, date,
2018 branch name (if non-default), changeset hash, parent(s) and commit
2018 branch name (if non-default), changeset hash, parent(s) and commit
2019 comment.
2019 comment.
2020
2020
2021 .. note::
2021 .. note::
2022
2022
2023 :hg:`export` may generate unexpected diff output for merge
2023 :hg:`export` may generate unexpected diff output for merge
2024 changesets, as it will compare the merge changeset against its
2024 changesets, as it will compare the merge changeset against its
2025 first parent only.
2025 first parent only.
2026
2026
2027 Output may be to a file, in which case the name of the file is
2027 Output may be to a file, in which case the name of the file is
2028 given using a template string. See :hg:`help templates`. In addition
2028 given using a template string. See :hg:`help templates`. In addition
2029 to the common template keywords, the following formatting rules are
2029 to the common template keywords, the following formatting rules are
2030 supported:
2030 supported:
2031
2031
2032 :``%%``: literal "%" character
2032 :``%%``: literal "%" character
2033 :``%H``: changeset hash (40 hexadecimal digits)
2033 :``%H``: changeset hash (40 hexadecimal digits)
2034 :``%N``: number of patches being generated
2034 :``%N``: number of patches being generated
2035 :``%R``: changeset revision number
2035 :``%R``: changeset revision number
2036 :``%b``: basename of the exporting repository
2036 :``%b``: basename of the exporting repository
2037 :``%h``: short-form changeset hash (12 hexadecimal digits)
2037 :``%h``: short-form changeset hash (12 hexadecimal digits)
2038 :``%m``: first line of the commit message (only alphanumeric characters)
2038 :``%m``: first line of the commit message (only alphanumeric characters)
2039 :``%n``: zero-padded sequence number, starting at 1
2039 :``%n``: zero-padded sequence number, starting at 1
2040 :``%r``: zero-padded changeset revision number
2040 :``%r``: zero-padded changeset revision number
2041 :``\\``: literal "\\" character
2041 :``\\``: literal "\\" character
2042
2042
2043 Without the -a/--text option, export will avoid generating diffs
2043 Without the -a/--text option, export will avoid generating diffs
2044 of files it detects as binary. With -a, export will generate a
2044 of files it detects as binary. With -a, export will generate a
2045 diff anyway, probably with undesirable results.
2045 diff anyway, probably with undesirable results.
2046
2046
2047 With -B/--bookmark changesets reachable by the given bookmark are
2047 With -B/--bookmark changesets reachable by the given bookmark are
2048 selected.
2048 selected.
2049
2049
2050 Use the -g/--git option to generate diffs in the git extended diff
2050 Use the -g/--git option to generate diffs in the git extended diff
2051 format. See :hg:`help diffs` for more information.
2051 format. See :hg:`help diffs` for more information.
2052
2052
2053 With the --switch-parent option, the diff will be against the
2053 With the --switch-parent option, the diff will be against the
2054 second parent. It can be useful to review a merge.
2054 second parent. It can be useful to review a merge.
2055
2055
2056 .. container:: verbose
2056 .. container:: verbose
2057
2057
2058 Template:
2058 Template:
2059
2059
2060 The following keywords are supported in addition to the common template
2060 The following keywords are supported in addition to the common template
2061 keywords and functions. See also :hg:`help templates`.
2061 keywords and functions. See also :hg:`help templates`.
2062
2062
2063 :diff: String. Diff content.
2063 :diff: String. Diff content.
2064 :parents: List of strings. Parent nodes of the changeset.
2064 :parents: List of strings. Parent nodes of the changeset.
2065
2065
2066 Examples:
2066 Examples:
2067
2067
2068 - use export and import to transplant a bugfix to the current
2068 - use export and import to transplant a bugfix to the current
2069 branch::
2069 branch::
2070
2070
2071 hg export -r 9353 | hg import -
2071 hg export -r 9353 | hg import -
2072
2072
2073 - export all the changesets between two revisions to a file with
2073 - export all the changesets between two revisions to a file with
2074 rename information::
2074 rename information::
2075
2075
2076 hg export --git -r 123:150 > changes.txt
2076 hg export --git -r 123:150 > changes.txt
2077
2077
2078 - split outgoing changes into a series of patches with
2078 - split outgoing changes into a series of patches with
2079 descriptive names::
2079 descriptive names::
2080
2080
2081 hg export -r "outgoing()" -o "%n-%m.patch"
2081 hg export -r "outgoing()" -o "%n-%m.patch"
2082
2082
2083 Returns 0 on success.
2083 Returns 0 on success.
2084 """
2084 """
2085 opts = pycompat.byteskwargs(opts)
2085 opts = pycompat.byteskwargs(opts)
2086 bookmark = opts.get('bookmark')
2086 bookmark = opts.get('bookmark')
2087 changesets += tuple(opts.get('rev', []))
2087 changesets += tuple(opts.get('rev', []))
2088
2088
2089 if bookmark and changesets:
2089 if bookmark and changesets:
2090 raise error.Abort(_("-r and -B are mutually exclusive"))
2090 raise error.Abort(_("-r and -B are mutually exclusive"))
2091
2091
2092 if bookmark:
2092 if bookmark:
2093 if bookmark not in repo._bookmarks:
2093 if bookmark not in repo._bookmarks:
2094 raise error.Abort(_("bookmark '%s' not found") % bookmark)
2094 raise error.Abort(_("bookmark '%s' not found") % bookmark)
2095
2095
2096 revs = scmutil.bookmarkrevs(repo, bookmark)
2096 revs = scmutil.bookmarkrevs(repo, bookmark)
2097 else:
2097 else:
2098 if not changesets:
2098 if not changesets:
2099 changesets = ['.']
2099 changesets = ['.']
2100
2100
2101 repo = scmutil.unhidehashlikerevs(repo, changesets, 'nowarn')
2101 repo = scmutil.unhidehashlikerevs(repo, changesets, 'nowarn')
2102 revs = scmutil.revrange(repo, changesets)
2102 revs = scmutil.revrange(repo, changesets)
2103
2103
2104 if not revs:
2104 if not revs:
2105 raise error.Abort(_("export requires at least one changeset"))
2105 raise error.Abort(_("export requires at least one changeset"))
2106 if len(revs) > 1:
2106 if len(revs) > 1:
2107 ui.note(_('exporting patches:\n'))
2107 ui.note(_('exporting patches:\n'))
2108 else:
2108 else:
2109 ui.note(_('exporting patch:\n'))
2109 ui.note(_('exporting patch:\n'))
2110
2110
2111 fntemplate = opts.get('output')
2111 fntemplate = opts.get('output')
2112 if cmdutil.isstdiofilename(fntemplate):
2112 if cmdutil.isstdiofilename(fntemplate):
2113 fntemplate = ''
2113 fntemplate = ''
2114
2114
2115 if fntemplate:
2115 if fntemplate:
2116 fm = formatter.nullformatter(ui, 'export', opts)
2116 fm = formatter.nullformatter(ui, 'export', opts)
2117 else:
2117 else:
2118 ui.pager('export')
2118 ui.pager('export')
2119 fm = ui.formatter('export', opts)
2119 fm = ui.formatter('export', opts)
2120 with fm:
2120 with fm:
2121 cmdutil.export(repo, revs, fm, fntemplate=fntemplate,
2121 cmdutil.export(repo, revs, fm, fntemplate=fntemplate,
2122 switch_parent=opts.get('switch_parent'),
2122 switch_parent=opts.get('switch_parent'),
2123 opts=patch.diffallopts(ui, opts))
2123 opts=patch.diffallopts(ui, opts))
2124
2124
2125 @command('files',
2125 @command('files',
2126 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
2126 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
2127 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
2127 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
2128 ] + walkopts + formatteropts + subrepoopts,
2128 ] + walkopts + formatteropts + subrepoopts,
2129 _('[OPTION]... [FILE]...'),
2129 _('[OPTION]... [FILE]...'),
2130 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2130 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2131 intents={INTENT_READONLY})
2131 intents={INTENT_READONLY})
2132 def files(ui, repo, *pats, **opts):
2132 def files(ui, repo, *pats, **opts):
2133 """list tracked files
2133 """list tracked files
2134
2134
2135 Print files under Mercurial control in the working directory or
2135 Print files under Mercurial control in the working directory or
2136 specified revision for given files (excluding removed files).
2136 specified revision for given files (excluding removed files).
2137 Files can be specified as filenames or filesets.
2137 Files can be specified as filenames or filesets.
2138
2138
2139 If no files are given to match, this command prints the names
2139 If no files are given to match, this command prints the names
2140 of all files under Mercurial control.
2140 of all files under Mercurial control.
2141
2141
2142 .. container:: verbose
2142 .. container:: verbose
2143
2143
2144 Template:
2144 Template:
2145
2145
2146 The following keywords are supported in addition to the common template
2146 The following keywords are supported in addition to the common template
2147 keywords and functions. See also :hg:`help templates`.
2147 keywords and functions. See also :hg:`help templates`.
2148
2148
2149 :flags: String. Character denoting file's symlink and executable bits.
2149 :flags: String. Character denoting file's symlink and executable bits.
2150 :path: String. Repository-absolute path of the file.
2150 :path: String. Repository-absolute path of the file.
2151 :size: Integer. Size of the file in bytes.
2151 :size: Integer. Size of the file in bytes.
2152
2152
2153 Examples:
2153 Examples:
2154
2154
2155 - list all files under the current directory::
2155 - list all files under the current directory::
2156
2156
2157 hg files .
2157 hg files .
2158
2158
2159 - shows sizes and flags for current revision::
2159 - shows sizes and flags for current revision::
2160
2160
2161 hg files -vr .
2161 hg files -vr .
2162
2162
2163 - list all files named README::
2163 - list all files named README::
2164
2164
2165 hg files -I "**/README"
2165 hg files -I "**/README"
2166
2166
2167 - list all binary files::
2167 - list all binary files::
2168
2168
2169 hg files "set:binary()"
2169 hg files "set:binary()"
2170
2170
2171 - find files containing a regular expression::
2171 - find files containing a regular expression::
2172
2172
2173 hg files "set:grep('bob')"
2173 hg files "set:grep('bob')"
2174
2174
2175 - search tracked file contents with xargs and grep::
2175 - search tracked file contents with xargs and grep::
2176
2176
2177 hg files -0 | xargs -0 grep foo
2177 hg files -0 | xargs -0 grep foo
2178
2178
2179 See :hg:`help patterns` and :hg:`help filesets` for more information
2179 See :hg:`help patterns` and :hg:`help filesets` for more information
2180 on specifying file patterns.
2180 on specifying file patterns.
2181
2181
2182 Returns 0 if a match is found, 1 otherwise.
2182 Returns 0 if a match is found, 1 otherwise.
2183
2183
2184 """
2184 """
2185
2185
2186 opts = pycompat.byteskwargs(opts)
2186 opts = pycompat.byteskwargs(opts)
2187 rev = opts.get('rev')
2187 rev = opts.get('rev')
2188 if rev:
2188 if rev:
2189 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2189 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2190 ctx = scmutil.revsingle(repo, rev, None)
2190 ctx = scmutil.revsingle(repo, rev, None)
2191
2191
2192 end = '\n'
2192 end = '\n'
2193 if opts.get('print0'):
2193 if opts.get('print0'):
2194 end = '\0'
2194 end = '\0'
2195 fmt = '%s' + end
2195 fmt = '%s' + end
2196
2196
2197 m = scmutil.match(ctx, pats, opts)
2197 m = scmutil.match(ctx, pats, opts)
2198 ui.pager('files')
2198 ui.pager('files')
2199 with ui.formatter('files', opts) as fm:
2199 with ui.formatter('files', opts) as fm:
2200 return cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
2200 return cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
2201
2201
2202 @command(
2202 @command(
2203 'forget',
2203 'forget',
2204 [('i', 'interactive', None, _('use interactive mode')),
2204 [('i', 'interactive', None, _('use interactive mode')),
2205 ] + walkopts + dryrunopts,
2205 ] + walkopts + dryrunopts,
2206 _('[OPTION]... FILE...'),
2206 _('[OPTION]... FILE...'),
2207 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2207 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2208 helpbasic=True, inferrepo=True)
2208 helpbasic=True, inferrepo=True)
2209 def forget(ui, repo, *pats, **opts):
2209 def forget(ui, repo, *pats, **opts):
2210 """forget the specified files on the next commit
2210 """forget the specified files on the next commit
2211
2211
2212 Mark the specified files so they will no longer be tracked
2212 Mark the specified files so they will no longer be tracked
2213 after the next commit.
2213 after the next commit.
2214
2214
2215 This only removes files from the current branch, not from the
2215 This only removes files from the current branch, not from the
2216 entire project history, and it does not delete them from the
2216 entire project history, and it does not delete them from the
2217 working directory.
2217 working directory.
2218
2218
2219 To delete the file from the working directory, see :hg:`remove`.
2219 To delete the file from the working directory, see :hg:`remove`.
2220
2220
2221 To undo a forget before the next commit, see :hg:`add`.
2221 To undo a forget before the next commit, see :hg:`add`.
2222
2222
2223 .. container:: verbose
2223 .. container:: verbose
2224
2224
2225 Examples:
2225 Examples:
2226
2226
2227 - forget newly-added binary files::
2227 - forget newly-added binary files::
2228
2228
2229 hg forget "set:added() and binary()"
2229 hg forget "set:added() and binary()"
2230
2230
2231 - forget files that would be excluded by .hgignore::
2231 - forget files that would be excluded by .hgignore::
2232
2232
2233 hg forget "set:hgignore()"
2233 hg forget "set:hgignore()"
2234
2234
2235 Returns 0 on success.
2235 Returns 0 on success.
2236 """
2236 """
2237
2237
2238 opts = pycompat.byteskwargs(opts)
2238 opts = pycompat.byteskwargs(opts)
2239 if not pats:
2239 if not pats:
2240 raise error.Abort(_('no files specified'))
2240 raise error.Abort(_('no files specified'))
2241
2241
2242 m = scmutil.match(repo[None], pats, opts)
2242 m = scmutil.match(repo[None], pats, opts)
2243 dryrun, interactive = opts.get('dry_run'), opts.get('interactive')
2243 dryrun, interactive = opts.get('dry_run'), opts.get('interactive')
2244 rejected = cmdutil.forget(ui, repo, m, prefix="",
2244 rejected = cmdutil.forget(ui, repo, m, prefix="",
2245 explicitonly=False, dryrun=dryrun,
2245 explicitonly=False, dryrun=dryrun,
2246 interactive=interactive)[0]
2246 interactive=interactive)[0]
2247 return rejected and 1 or 0
2247 return rejected and 1 or 0
2248
2248
2249 @command(
2249 @command(
2250 'graft',
2250 'graft',
2251 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2251 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2252 ('', 'base', '',
2252 ('', 'base', '',
2253 _('base revision when doing the graft merge (ADVANCED)'), _('REV')),
2253 _('base revision when doing the graft merge (ADVANCED)'), _('REV')),
2254 ('c', 'continue', False, _('resume interrupted graft')),
2254 ('c', 'continue', False, _('resume interrupted graft')),
2255 ('', 'stop', False, _('stop interrupted graft')),
2255 ('', 'stop', False, _('stop interrupted graft')),
2256 ('', 'abort', False, _('abort interrupted graft')),
2256 ('', 'abort', False, _('abort interrupted graft')),
2257 ('e', 'edit', False, _('invoke editor on commit messages')),
2257 ('e', 'edit', False, _('invoke editor on commit messages')),
2258 ('', 'log', None, _('append graft info to log message')),
2258 ('', 'log', None, _('append graft info to log message')),
2259 ('', 'no-commit', None,
2259 ('', 'no-commit', None,
2260 _("don't commit, just apply the changes in working directory")),
2260 _("don't commit, just apply the changes in working directory")),
2261 ('f', 'force', False, _('force graft')),
2261 ('f', 'force', False, _('force graft')),
2262 ('D', 'currentdate', False,
2262 ('D', 'currentdate', False,
2263 _('record the current date as commit date')),
2263 _('record the current date as commit date')),
2264 ('U', 'currentuser', False,
2264 ('U', 'currentuser', False,
2265 _('record the current user as committer'))]
2265 _('record the current user as committer'))]
2266 + commitopts2 + mergetoolopts + dryrunopts,
2266 + commitopts2 + mergetoolopts + dryrunopts,
2267 _('[OPTION]... [-r REV]... REV...'),
2267 _('[OPTION]... [-r REV]... REV...'),
2268 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT)
2268 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT)
2269 def graft(ui, repo, *revs, **opts):
2269 def graft(ui, repo, *revs, **opts):
2270 '''copy changes from other branches onto the current branch
2270 '''copy changes from other branches onto the current branch
2271
2271
2272 This command uses Mercurial's merge logic to copy individual
2272 This command uses Mercurial's merge logic to copy individual
2273 changes from other branches without merging branches in the
2273 changes from other branches without merging branches in the
2274 history graph. This is sometimes known as 'backporting' or
2274 history graph. This is sometimes known as 'backporting' or
2275 'cherry-picking'. By default, graft will copy user, date, and
2275 'cherry-picking'. By default, graft will copy user, date, and
2276 description from the source changesets.
2276 description from the source changesets.
2277
2277
2278 Changesets that are ancestors of the current revision, that have
2278 Changesets that are ancestors of the current revision, that have
2279 already been grafted, or that are merges will be skipped.
2279 already been grafted, or that are merges will be skipped.
2280
2280
2281 If --log is specified, log messages will have a comment appended
2281 If --log is specified, log messages will have a comment appended
2282 of the form::
2282 of the form::
2283
2283
2284 (grafted from CHANGESETHASH)
2284 (grafted from CHANGESETHASH)
2285
2285
2286 If --force is specified, revisions will be grafted even if they
2286 If --force is specified, revisions will be grafted even if they
2287 are already ancestors of, or have been grafted to, the destination.
2287 are already ancestors of, or have been grafted to, the destination.
2288 This is useful when the revisions have since been backed out.
2288 This is useful when the revisions have since been backed out.
2289
2289
2290 If a graft merge results in conflicts, the graft process is
2290 If a graft merge results in conflicts, the graft process is
2291 interrupted so that the current merge can be manually resolved.
2291 interrupted so that the current merge can be manually resolved.
2292 Once all conflicts are addressed, the graft process can be
2292 Once all conflicts are addressed, the graft process can be
2293 continued with the -c/--continue option.
2293 continued with the -c/--continue option.
2294
2294
2295 The -c/--continue option reapplies all the earlier options.
2295 The -c/--continue option reapplies all the earlier options.
2296
2296
2297 .. container:: verbose
2297 .. container:: verbose
2298
2298
2299 The --base option exposes more of how graft internally uses merge with a
2299 The --base option exposes more of how graft internally uses merge with a
2300 custom base revision. --base can be used to specify another ancestor than
2300 custom base revision. --base can be used to specify another ancestor than
2301 the first and only parent.
2301 the first and only parent.
2302
2302
2303 The command::
2303 The command::
2304
2304
2305 hg graft -r 345 --base 234
2305 hg graft -r 345 --base 234
2306
2306
2307 is thus pretty much the same as::
2307 is thus pretty much the same as::
2308
2308
2309 hg diff -r 234 -r 345 | hg import
2309 hg diff -r 234 -r 345 | hg import
2310
2310
2311 but using merge to resolve conflicts and track moved files.
2311 but using merge to resolve conflicts and track moved files.
2312
2312
2313 The result of a merge can thus be backported as a single commit by
2313 The result of a merge can thus be backported as a single commit by
2314 specifying one of the merge parents as base, and thus effectively
2314 specifying one of the merge parents as base, and thus effectively
2315 grafting the changes from the other side.
2315 grafting the changes from the other side.
2316
2316
2317 It is also possible to collapse multiple changesets and clean up history
2317 It is also possible to collapse multiple changesets and clean up history
2318 by specifying another ancestor as base, much like rebase --collapse
2318 by specifying another ancestor as base, much like rebase --collapse
2319 --keep.
2319 --keep.
2320
2320
2321 The commit message can be tweaked after the fact using commit --amend .
2321 The commit message can be tweaked after the fact using commit --amend .
2322
2322
2323 For using non-ancestors as the base to backout changes, see the backout
2323 For using non-ancestors as the base to backout changes, see the backout
2324 command and the hidden --parent option.
2324 command and the hidden --parent option.
2325
2325
2326 .. container:: verbose
2326 .. container:: verbose
2327
2327
2328 Examples:
2328 Examples:
2329
2329
2330 - copy a single change to the stable branch and edit its description::
2330 - copy a single change to the stable branch and edit its description::
2331
2331
2332 hg update stable
2332 hg update stable
2333 hg graft --edit 9393
2333 hg graft --edit 9393
2334
2334
2335 - graft a range of changesets with one exception, updating dates::
2335 - graft a range of changesets with one exception, updating dates::
2336
2336
2337 hg graft -D "2085::2093 and not 2091"
2337 hg graft -D "2085::2093 and not 2091"
2338
2338
2339 - continue a graft after resolving conflicts::
2339 - continue a graft after resolving conflicts::
2340
2340
2341 hg graft -c
2341 hg graft -c
2342
2342
2343 - show the source of a grafted changeset::
2343 - show the source of a grafted changeset::
2344
2344
2345 hg log --debug -r .
2345 hg log --debug -r .
2346
2346
2347 - show revisions sorted by date::
2347 - show revisions sorted by date::
2348
2348
2349 hg log -r "sort(all(), date)"
2349 hg log -r "sort(all(), date)"
2350
2350
2351 - backport the result of a merge as a single commit::
2351 - backport the result of a merge as a single commit::
2352
2352
2353 hg graft -r 123 --base 123^
2353 hg graft -r 123 --base 123^
2354
2354
2355 - land a feature branch as one changeset::
2355 - land a feature branch as one changeset::
2356
2356
2357 hg up -cr default
2357 hg up -cr default
2358 hg graft -r featureX --base "ancestor('featureX', 'default')"
2358 hg graft -r featureX --base "ancestor('featureX', 'default')"
2359
2359
2360 See :hg:`help revisions` for more about specifying revisions.
2360 See :hg:`help revisions` for more about specifying revisions.
2361
2361
2362 Returns 0 on successful completion.
2362 Returns 0 on successful completion.
2363 '''
2363 '''
2364 with repo.wlock():
2364 with repo.wlock():
2365 return _dograft(ui, repo, *revs, **opts)
2365 return _dograft(ui, repo, *revs, **opts)
2366
2366
2367 def _dograft(ui, repo, *revs, **opts):
2367 def _dograft(ui, repo, *revs, **opts):
2368 opts = pycompat.byteskwargs(opts)
2368 opts = pycompat.byteskwargs(opts)
2369 if revs and opts.get('rev'):
2369 if revs and opts.get('rev'):
2370 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
2370 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
2371 'revision ordering!\n'))
2371 'revision ordering!\n'))
2372
2372
2373 revs = list(revs)
2373 revs = list(revs)
2374 revs.extend(opts.get('rev'))
2374 revs.extend(opts.get('rev'))
2375 basectx = None
2375 basectx = None
2376 if opts.get('base'):
2376 if opts.get('base'):
2377 basectx = scmutil.revsingle(repo, opts['base'], None)
2377 basectx = scmutil.revsingle(repo, opts['base'], None)
2378 # a dict of data to be stored in state file
2378 # a dict of data to be stored in state file
2379 statedata = {}
2379 statedata = {}
2380 # list of new nodes created by ongoing graft
2380 # list of new nodes created by ongoing graft
2381 statedata['newnodes'] = []
2381 statedata['newnodes'] = []
2382
2382
2383 if not opts.get('user') and opts.get('currentuser'):
2383 if not opts.get('user') and opts.get('currentuser'):
2384 opts['user'] = ui.username()
2384 opts['user'] = ui.username()
2385 if not opts.get('date') and opts.get('currentdate'):
2385 if not opts.get('date') and opts.get('currentdate'):
2386 opts['date'] = "%d %d" % dateutil.makedate()
2386 opts['date'] = "%d %d" % dateutil.makedate()
2387
2387
2388 editor = cmdutil.getcommiteditor(editform='graft',
2388 editor = cmdutil.getcommiteditor(editform='graft',
2389 **pycompat.strkwargs(opts))
2389 **pycompat.strkwargs(opts))
2390
2390
2391 cont = False
2391 cont = False
2392 if opts.get('no_commit'):
2392 if opts.get('no_commit'):
2393 if opts.get('edit'):
2393 if opts.get('edit'):
2394 raise error.Abort(_("cannot specify --no-commit and "
2394 raise error.Abort(_("cannot specify --no-commit and "
2395 "--edit together"))
2395 "--edit together"))
2396 if opts.get('currentuser'):
2396 if opts.get('currentuser'):
2397 raise error.Abort(_("cannot specify --no-commit and "
2397 raise error.Abort(_("cannot specify --no-commit and "
2398 "--currentuser together"))
2398 "--currentuser together"))
2399 if opts.get('currentdate'):
2399 if opts.get('currentdate'):
2400 raise error.Abort(_("cannot specify --no-commit and "
2400 raise error.Abort(_("cannot specify --no-commit and "
2401 "--currentdate together"))
2401 "--currentdate together"))
2402 if opts.get('log'):
2402 if opts.get('log'):
2403 raise error.Abort(_("cannot specify --no-commit and "
2403 raise error.Abort(_("cannot specify --no-commit and "
2404 "--log together"))
2404 "--log together"))
2405
2405
2406 graftstate = statemod.cmdstate(repo, 'graftstate')
2406 graftstate = statemod.cmdstate(repo, 'graftstate')
2407
2407
2408 if opts.get('stop'):
2408 if opts.get('stop'):
2409 if opts.get('continue'):
2409 if opts.get('continue'):
2410 raise error.Abort(_("cannot use '--continue' and "
2410 raise error.Abort(_("cannot use '--continue' and "
2411 "'--stop' together"))
2411 "'--stop' together"))
2412 if opts.get('abort'):
2412 if opts.get('abort'):
2413 raise error.Abort(_("cannot use '--abort' and '--stop' together"))
2413 raise error.Abort(_("cannot use '--abort' and '--stop' together"))
2414
2414
2415 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2415 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2416 opts.get('date'), opts.get('currentdate'),
2416 opts.get('date'), opts.get('currentdate'),
2417 opts.get('currentuser'), opts.get('rev'))):
2417 opts.get('currentuser'), opts.get('rev'))):
2418 raise error.Abort(_("cannot specify any other flag with '--stop'"))
2418 raise error.Abort(_("cannot specify any other flag with '--stop'"))
2419 return _stopgraft(ui, repo, graftstate)
2419 return _stopgraft(ui, repo, graftstate)
2420 elif opts.get('abort'):
2420 elif opts.get('abort'):
2421 if opts.get('continue'):
2421 if opts.get('continue'):
2422 raise error.Abort(_("cannot use '--continue' and "
2422 raise error.Abort(_("cannot use '--continue' and "
2423 "'--abort' together"))
2423 "'--abort' together"))
2424 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2424 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2425 opts.get('date'), opts.get('currentdate'),
2425 opts.get('date'), opts.get('currentdate'),
2426 opts.get('currentuser'), opts.get('rev'))):
2426 opts.get('currentuser'), opts.get('rev'))):
2427 raise error.Abort(_("cannot specify any other flag with '--abort'"))
2427 raise error.Abort(_("cannot specify any other flag with '--abort'"))
2428
2428
2429 return _abortgraft(ui, repo, graftstate)
2429 return _abortgraft(ui, repo, graftstate)
2430 elif opts.get('continue'):
2430 elif opts.get('continue'):
2431 cont = True
2431 cont = True
2432 if revs:
2432 if revs:
2433 raise error.Abort(_("can't specify --continue and revisions"))
2433 raise error.Abort(_("can't specify --continue and revisions"))
2434 # read in unfinished revisions
2434 # read in unfinished revisions
2435 if graftstate.exists():
2435 if graftstate.exists():
2436 statedata = _readgraftstate(repo, graftstate)
2436 statedata = _readgraftstate(repo, graftstate)
2437 if statedata.get('date'):
2437 if statedata.get('date'):
2438 opts['date'] = statedata['date']
2438 opts['date'] = statedata['date']
2439 if statedata.get('user'):
2439 if statedata.get('user'):
2440 opts['user'] = statedata['user']
2440 opts['user'] = statedata['user']
2441 if statedata.get('log'):
2441 if statedata.get('log'):
2442 opts['log'] = True
2442 opts['log'] = True
2443 if statedata.get('no_commit'):
2443 if statedata.get('no_commit'):
2444 opts['no_commit'] = statedata.get('no_commit')
2444 opts['no_commit'] = statedata.get('no_commit')
2445 nodes = statedata['nodes']
2445 nodes = statedata['nodes']
2446 revs = [repo[node].rev() for node in nodes]
2446 revs = [repo[node].rev() for node in nodes]
2447 else:
2447 else:
2448 cmdutil.wrongtooltocontinue(repo, _('graft'))
2448 cmdutil.wrongtooltocontinue(repo, _('graft'))
2449 else:
2449 else:
2450 if not revs:
2450 if not revs:
2451 raise error.Abort(_('no revisions specified'))
2451 raise error.Abort(_('no revisions specified'))
2452 cmdutil.checkunfinished(repo)
2452 cmdutil.checkunfinished(repo)
2453 cmdutil.bailifchanged(repo)
2453 cmdutil.bailifchanged(repo)
2454 revs = scmutil.revrange(repo, revs)
2454 revs = scmutil.revrange(repo, revs)
2455
2455
2456 skipped = set()
2456 skipped = set()
2457 if basectx is None:
2457 if basectx is None:
2458 # check for merges
2458 # check for merges
2459 for rev in repo.revs('%ld and merge()', revs):
2459 for rev in repo.revs('%ld and merge()', revs):
2460 ui.warn(_('skipping ungraftable merge revision %d\n') % rev)
2460 ui.warn(_('skipping ungraftable merge revision %d\n') % rev)
2461 skipped.add(rev)
2461 skipped.add(rev)
2462 revs = [r for r in revs if r not in skipped]
2462 revs = [r for r in revs if r not in skipped]
2463 if not revs:
2463 if not revs:
2464 return -1
2464 return -1
2465 if basectx is not None and len(revs) != 1:
2465 if basectx is not None and len(revs) != 1:
2466 raise error.Abort(_('only one revision allowed with --base '))
2466 raise error.Abort(_('only one revision allowed with --base '))
2467
2467
2468 # Don't check in the --continue case, in effect retaining --force across
2468 # Don't check in the --continue case, in effect retaining --force across
2469 # --continues. That's because without --force, any revisions we decided to
2469 # --continues. That's because without --force, any revisions we decided to
2470 # skip would have been filtered out here, so they wouldn't have made their
2470 # skip would have been filtered out here, so they wouldn't have made their
2471 # way to the graftstate. With --force, any revisions we would have otherwise
2471 # way to the graftstate. With --force, any revisions we would have otherwise
2472 # skipped would not have been filtered out, and if they hadn't been applied
2472 # skipped would not have been filtered out, and if they hadn't been applied
2473 # already, they'd have been in the graftstate.
2473 # already, they'd have been in the graftstate.
2474 if not (cont or opts.get('force')) and basectx is None:
2474 if not (cont or opts.get('force')) and basectx is None:
2475 # check for ancestors of dest branch
2475 # check for ancestors of dest branch
2476 crev = repo['.'].rev()
2476 crev = repo['.'].rev()
2477 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2477 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2478 # XXX make this lazy in the future
2478 # XXX make this lazy in the future
2479 # don't mutate while iterating, create a copy
2479 # don't mutate while iterating, create a copy
2480 for rev in list(revs):
2480 for rev in list(revs):
2481 if rev in ancestors:
2481 if rev in ancestors:
2482 ui.warn(_('skipping ancestor revision %d:%s\n') %
2482 ui.warn(_('skipping ancestor revision %d:%s\n') %
2483 (rev, repo[rev]))
2483 (rev, repo[rev]))
2484 # XXX remove on list is slow
2484 # XXX remove on list is slow
2485 revs.remove(rev)
2485 revs.remove(rev)
2486 if not revs:
2486 if not revs:
2487 return -1
2487 return -1
2488
2488
2489 # analyze revs for earlier grafts
2489 # analyze revs for earlier grafts
2490 ids = {}
2490 ids = {}
2491 for ctx in repo.set("%ld", revs):
2491 for ctx in repo.set("%ld", revs):
2492 ids[ctx.hex()] = ctx.rev()
2492 ids[ctx.hex()] = ctx.rev()
2493 n = ctx.extra().get('source')
2493 n = ctx.extra().get('source')
2494 if n:
2494 if n:
2495 ids[n] = ctx.rev()
2495 ids[n] = ctx.rev()
2496
2496
2497 # check ancestors for earlier grafts
2497 # check ancestors for earlier grafts
2498 ui.debug('scanning for duplicate grafts\n')
2498 ui.debug('scanning for duplicate grafts\n')
2499
2499
2500 # The only changesets we can be sure doesn't contain grafts of any
2500 # The only changesets we can be sure doesn't contain grafts of any
2501 # revs, are the ones that are common ancestors of *all* revs:
2501 # revs, are the ones that are common ancestors of *all* revs:
2502 for rev in repo.revs('only(%d,ancestor(%ld))', crev, revs):
2502 for rev in repo.revs('only(%d,ancestor(%ld))', crev, revs):
2503 ctx = repo[rev]
2503 ctx = repo[rev]
2504 n = ctx.extra().get('source')
2504 n = ctx.extra().get('source')
2505 if n in ids:
2505 if n in ids:
2506 try:
2506 try:
2507 r = repo[n].rev()
2507 r = repo[n].rev()
2508 except error.RepoLookupError:
2508 except error.RepoLookupError:
2509 r = None
2509 r = None
2510 if r in revs:
2510 if r in revs:
2511 ui.warn(_('skipping revision %d:%s '
2511 ui.warn(_('skipping revision %d:%s '
2512 '(already grafted to %d:%s)\n')
2512 '(already grafted to %d:%s)\n')
2513 % (r, repo[r], rev, ctx))
2513 % (r, repo[r], rev, ctx))
2514 revs.remove(r)
2514 revs.remove(r)
2515 elif ids[n] in revs:
2515 elif ids[n] in revs:
2516 if r is None:
2516 if r is None:
2517 ui.warn(_('skipping already grafted revision %d:%s '
2517 ui.warn(_('skipping already grafted revision %d:%s '
2518 '(%d:%s also has unknown origin %s)\n')
2518 '(%d:%s also has unknown origin %s)\n')
2519 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
2519 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
2520 else:
2520 else:
2521 ui.warn(_('skipping already grafted revision %d:%s '
2521 ui.warn(_('skipping already grafted revision %d:%s '
2522 '(%d:%s also has origin %d:%s)\n')
2522 '(%d:%s also has origin %d:%s)\n')
2523 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
2523 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
2524 revs.remove(ids[n])
2524 revs.remove(ids[n])
2525 elif ctx.hex() in ids:
2525 elif ctx.hex() in ids:
2526 r = ids[ctx.hex()]
2526 r = ids[ctx.hex()]
2527 ui.warn(_('skipping already grafted revision %d:%s '
2527 if r in revs:
2528 '(was grafted from %d:%s)\n') %
2528 ui.warn(_('skipping already grafted revision %d:%s '
2529 (r, repo[r], rev, ctx))
2529 '(was grafted from %d:%s)\n') %
2530 revs.remove(r)
2530 (r, repo[r], rev, ctx))
2531 revs.remove(r)
2531 if not revs:
2532 if not revs:
2532 return -1
2533 return -1
2533
2534
2534 if opts.get('no_commit'):
2535 if opts.get('no_commit'):
2535 statedata['no_commit'] = True
2536 statedata['no_commit'] = True
2536 for pos, ctx in enumerate(repo.set("%ld", revs)):
2537 for pos, ctx in enumerate(repo.set("%ld", revs)):
2537 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
2538 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
2538 ctx.description().split('\n', 1)[0])
2539 ctx.description().split('\n', 1)[0])
2539 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
2540 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
2540 if names:
2541 if names:
2541 desc += ' (%s)' % ' '.join(names)
2542 desc += ' (%s)' % ' '.join(names)
2542 ui.status(_('grafting %s\n') % desc)
2543 ui.status(_('grafting %s\n') % desc)
2543 if opts.get('dry_run'):
2544 if opts.get('dry_run'):
2544 continue
2545 continue
2545
2546
2546 source = ctx.extra().get('source')
2547 source = ctx.extra().get('source')
2547 extra = {}
2548 extra = {}
2548 if source:
2549 if source:
2549 extra['source'] = source
2550 extra['source'] = source
2550 extra['intermediate-source'] = ctx.hex()
2551 extra['intermediate-source'] = ctx.hex()
2551 else:
2552 else:
2552 extra['source'] = ctx.hex()
2553 extra['source'] = ctx.hex()
2553 user = ctx.user()
2554 user = ctx.user()
2554 if opts.get('user'):
2555 if opts.get('user'):
2555 user = opts['user']
2556 user = opts['user']
2556 statedata['user'] = user
2557 statedata['user'] = user
2557 date = ctx.date()
2558 date = ctx.date()
2558 if opts.get('date'):
2559 if opts.get('date'):
2559 date = opts['date']
2560 date = opts['date']
2560 statedata['date'] = date
2561 statedata['date'] = date
2561 message = ctx.description()
2562 message = ctx.description()
2562 if opts.get('log'):
2563 if opts.get('log'):
2563 message += '\n(grafted from %s)' % ctx.hex()
2564 message += '\n(grafted from %s)' % ctx.hex()
2564 statedata['log'] = True
2565 statedata['log'] = True
2565
2566
2566 # we don't merge the first commit when continuing
2567 # we don't merge the first commit when continuing
2567 if not cont:
2568 if not cont:
2568 # perform the graft merge with p1(rev) as 'ancestor'
2569 # perform the graft merge with p1(rev) as 'ancestor'
2569 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
2570 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
2570 base = ctx.p1() if basectx is None else basectx
2571 base = ctx.p1() if basectx is None else basectx
2571 with ui.configoverride(overrides, 'graft'):
2572 with ui.configoverride(overrides, 'graft'):
2572 stats = mergemod.graft(repo, ctx, base, ['local', 'graft'])
2573 stats = mergemod.graft(repo, ctx, base, ['local', 'graft'])
2573 # report any conflicts
2574 # report any conflicts
2574 if stats.unresolvedcount > 0:
2575 if stats.unresolvedcount > 0:
2575 # write out state for --continue
2576 # write out state for --continue
2576 nodes = [repo[rev].hex() for rev in revs[pos:]]
2577 nodes = [repo[rev].hex() for rev in revs[pos:]]
2577 statedata['nodes'] = nodes
2578 statedata['nodes'] = nodes
2578 stateversion = 1
2579 stateversion = 1
2579 graftstate.save(stateversion, statedata)
2580 graftstate.save(stateversion, statedata)
2580 hint = _("use 'hg resolve' and 'hg graft --continue'")
2581 hint = _("use 'hg resolve' and 'hg graft --continue'")
2581 raise error.Abort(
2582 raise error.Abort(
2582 _("unresolved conflicts, can't continue"),
2583 _("unresolved conflicts, can't continue"),
2583 hint=hint)
2584 hint=hint)
2584 else:
2585 else:
2585 cont = False
2586 cont = False
2586
2587
2587 # commit if --no-commit is false
2588 # commit if --no-commit is false
2588 if not opts.get('no_commit'):
2589 if not opts.get('no_commit'):
2589 node = repo.commit(text=message, user=user, date=date, extra=extra,
2590 node = repo.commit(text=message, user=user, date=date, extra=extra,
2590 editor=editor)
2591 editor=editor)
2591 if node is None:
2592 if node is None:
2592 ui.warn(
2593 ui.warn(
2593 _('note: graft of %d:%s created no changes to commit\n') %
2594 _('note: graft of %d:%s created no changes to commit\n') %
2594 (ctx.rev(), ctx))
2595 (ctx.rev(), ctx))
2595 # checking that newnodes exist because old state files won't have it
2596 # checking that newnodes exist because old state files won't have it
2596 elif statedata.get('newnodes') is not None:
2597 elif statedata.get('newnodes') is not None:
2597 statedata['newnodes'].append(node)
2598 statedata['newnodes'].append(node)
2598
2599
2599 # remove state when we complete successfully
2600 # remove state when we complete successfully
2600 if not opts.get('dry_run'):
2601 if not opts.get('dry_run'):
2601 graftstate.delete()
2602 graftstate.delete()
2602
2603
2603 return 0
2604 return 0
2604
2605
2605 def _abortgraft(ui, repo, graftstate):
2606 def _abortgraft(ui, repo, graftstate):
2606 """abort the interrupted graft and rollbacks to the state before interrupted
2607 """abort the interrupted graft and rollbacks to the state before interrupted
2607 graft"""
2608 graft"""
2608 if not graftstate.exists():
2609 if not graftstate.exists():
2609 raise error.Abort(_("no interrupted graft to abort"))
2610 raise error.Abort(_("no interrupted graft to abort"))
2610 statedata = _readgraftstate(repo, graftstate)
2611 statedata = _readgraftstate(repo, graftstate)
2611 newnodes = statedata.get('newnodes')
2612 newnodes = statedata.get('newnodes')
2612 if newnodes is None:
2613 if newnodes is None:
2613 # and old graft state which does not have all the data required to abort
2614 # and old graft state which does not have all the data required to abort
2614 # the graft
2615 # the graft
2615 raise error.Abort(_("cannot abort using an old graftstate"))
2616 raise error.Abort(_("cannot abort using an old graftstate"))
2616
2617
2617 # changeset from which graft operation was started
2618 # changeset from which graft operation was started
2618 startctx = None
2619 startctx = None
2619 if len(newnodes) > 0:
2620 if len(newnodes) > 0:
2620 startctx = repo[newnodes[0]].p1()
2621 startctx = repo[newnodes[0]].p1()
2621 else:
2622 else:
2622 startctx = repo['.']
2623 startctx = repo['.']
2623 # whether to strip or not
2624 # whether to strip or not
2624 cleanup = False
2625 cleanup = False
2625 if newnodes:
2626 if newnodes:
2626 newnodes = [repo[r].rev() for r in newnodes]
2627 newnodes = [repo[r].rev() for r in newnodes]
2627 cleanup = True
2628 cleanup = True
2628 # checking that none of the newnodes turned public or is public
2629 # checking that none of the newnodes turned public or is public
2629 immutable = [c for c in newnodes if not repo[c].mutable()]
2630 immutable = [c for c in newnodes if not repo[c].mutable()]
2630 if immutable:
2631 if immutable:
2631 repo.ui.warn(_("cannot clean up public changesets %s\n")
2632 repo.ui.warn(_("cannot clean up public changesets %s\n")
2632 % ', '.join(bytes(repo[r]) for r in immutable),
2633 % ', '.join(bytes(repo[r]) for r in immutable),
2633 hint=_("see 'hg help phases' for details"))
2634 hint=_("see 'hg help phases' for details"))
2634 cleanup = False
2635 cleanup = False
2635
2636
2636 # checking that no new nodes are created on top of grafted revs
2637 # checking that no new nodes are created on top of grafted revs
2637 desc = set(repo.changelog.descendants(newnodes))
2638 desc = set(repo.changelog.descendants(newnodes))
2638 if desc - set(newnodes):
2639 if desc - set(newnodes):
2639 repo.ui.warn(_("new changesets detected on destination "
2640 repo.ui.warn(_("new changesets detected on destination "
2640 "branch, can't strip\n"))
2641 "branch, can't strip\n"))
2641 cleanup = False
2642 cleanup = False
2642
2643
2643 if cleanup:
2644 if cleanup:
2644 with repo.wlock(), repo.lock():
2645 with repo.wlock(), repo.lock():
2645 hg.updaterepo(repo, startctx.node(), overwrite=True)
2646 hg.updaterepo(repo, startctx.node(), overwrite=True)
2646 # stripping the new nodes created
2647 # stripping the new nodes created
2647 strippoints = [c.node() for c in repo.set("roots(%ld)",
2648 strippoints = [c.node() for c in repo.set("roots(%ld)",
2648 newnodes)]
2649 newnodes)]
2649 repair.strip(repo.ui, repo, strippoints, backup=False)
2650 repair.strip(repo.ui, repo, strippoints, backup=False)
2650
2651
2651 if not cleanup:
2652 if not cleanup:
2652 # we don't update to the startnode if we can't strip
2653 # we don't update to the startnode if we can't strip
2653 startctx = repo['.']
2654 startctx = repo['.']
2654 hg.updaterepo(repo, startctx.node(), overwrite=True)
2655 hg.updaterepo(repo, startctx.node(), overwrite=True)
2655
2656
2656 ui.status(_("graft aborted\n"))
2657 ui.status(_("graft aborted\n"))
2657 ui.status(_("working directory is now at %s\n") % startctx.hex()[:12])
2658 ui.status(_("working directory is now at %s\n") % startctx.hex()[:12])
2658 graftstate.delete()
2659 graftstate.delete()
2659 return 0
2660 return 0
2660
2661
2661 def _readgraftstate(repo, graftstate):
2662 def _readgraftstate(repo, graftstate):
2662 """read the graft state file and return a dict of the data stored in it"""
2663 """read the graft state file and return a dict of the data stored in it"""
2663 try:
2664 try:
2664 return graftstate.read()
2665 return graftstate.read()
2665 except error.CorruptedState:
2666 except error.CorruptedState:
2666 nodes = repo.vfs.read('graftstate').splitlines()
2667 nodes = repo.vfs.read('graftstate').splitlines()
2667 return {'nodes': nodes}
2668 return {'nodes': nodes}
2668
2669
2669 def _stopgraft(ui, repo, graftstate):
2670 def _stopgraft(ui, repo, graftstate):
2670 """stop the interrupted graft"""
2671 """stop the interrupted graft"""
2671 if not graftstate.exists():
2672 if not graftstate.exists():
2672 raise error.Abort(_("no interrupted graft found"))
2673 raise error.Abort(_("no interrupted graft found"))
2673 pctx = repo['.']
2674 pctx = repo['.']
2674 hg.updaterepo(repo, pctx.node(), overwrite=True)
2675 hg.updaterepo(repo, pctx.node(), overwrite=True)
2675 graftstate.delete()
2676 graftstate.delete()
2676 ui.status(_("stopped the interrupted graft\n"))
2677 ui.status(_("stopped the interrupted graft\n"))
2677 ui.status(_("working directory is now at %s\n") % pctx.hex()[:12])
2678 ui.status(_("working directory is now at %s\n") % pctx.hex()[:12])
2678 return 0
2679 return 0
2679
2680
2680 @command('grep',
2681 @command('grep',
2681 [('0', 'print0', None, _('end fields with NUL')),
2682 [('0', 'print0', None, _('end fields with NUL')),
2682 ('', 'all', None, _('print all revisions that match (DEPRECATED) ')),
2683 ('', 'all', None, _('print all revisions that match (DEPRECATED) ')),
2683 ('', 'diff', None, _('print all revisions when the term was introduced '
2684 ('', 'diff', None, _('print all revisions when the term was introduced '
2684 'or removed')),
2685 'or removed')),
2685 ('a', 'text', None, _('treat all files as text')),
2686 ('a', 'text', None, _('treat all files as text')),
2686 ('f', 'follow', None,
2687 ('f', 'follow', None,
2687 _('follow changeset history,'
2688 _('follow changeset history,'
2688 ' or file history across copies and renames')),
2689 ' or file history across copies and renames')),
2689 ('i', 'ignore-case', None, _('ignore case when matching')),
2690 ('i', 'ignore-case', None, _('ignore case when matching')),
2690 ('l', 'files-with-matches', None,
2691 ('l', 'files-with-matches', None,
2691 _('print only filenames and revisions that match')),
2692 _('print only filenames and revisions that match')),
2692 ('n', 'line-number', None, _('print matching line numbers')),
2693 ('n', 'line-number', None, _('print matching line numbers')),
2693 ('r', 'rev', [],
2694 ('r', 'rev', [],
2694 _('only search files changed within revision range'), _('REV')),
2695 _('only search files changed within revision range'), _('REV')),
2695 ('', 'all-files', None,
2696 ('', 'all-files', None,
2696 _('include all files in the changeset while grepping (EXPERIMENTAL)')),
2697 _('include all files in the changeset while grepping (EXPERIMENTAL)')),
2697 ('u', 'user', None, _('list the author (long with -v)')),
2698 ('u', 'user', None, _('list the author (long with -v)')),
2698 ('d', 'date', None, _('list the date (short with -q)')),
2699 ('d', 'date', None, _('list the date (short with -q)')),
2699 ] + formatteropts + walkopts,
2700 ] + formatteropts + walkopts,
2700 _('[OPTION]... PATTERN [FILE]...'),
2701 _('[OPTION]... PATTERN [FILE]...'),
2701 helpcategory=command.CATEGORY_FILE_CONTENTS,
2702 helpcategory=command.CATEGORY_FILE_CONTENTS,
2702 inferrepo=True,
2703 inferrepo=True,
2703 intents={INTENT_READONLY})
2704 intents={INTENT_READONLY})
2704 def grep(ui, repo, pattern, *pats, **opts):
2705 def grep(ui, repo, pattern, *pats, **opts):
2705 """search revision history for a pattern in specified files
2706 """search revision history for a pattern in specified files
2706
2707
2707 Search revision history for a regular expression in the specified
2708 Search revision history for a regular expression in the specified
2708 files or the entire project.
2709 files or the entire project.
2709
2710
2710 By default, grep prints the most recent revision number for each
2711 By default, grep prints the most recent revision number for each
2711 file in which it finds a match. To get it to print every revision
2712 file in which it finds a match. To get it to print every revision
2712 that contains a change in match status ("-" for a match that becomes
2713 that contains a change in match status ("-" for a match that becomes
2713 a non-match, or "+" for a non-match that becomes a match), use the
2714 a non-match, or "+" for a non-match that becomes a match), use the
2714 --diff flag.
2715 --diff flag.
2715
2716
2716 PATTERN can be any Python (roughly Perl-compatible) regular
2717 PATTERN can be any Python (roughly Perl-compatible) regular
2717 expression.
2718 expression.
2718
2719
2719 If no FILEs are specified (and -f/--follow isn't set), all files in
2720 If no FILEs are specified (and -f/--follow isn't set), all files in
2720 the repository are searched, including those that don't exist in the
2721 the repository are searched, including those that don't exist in the
2721 current branch or have been deleted in a prior changeset.
2722 current branch or have been deleted in a prior changeset.
2722
2723
2723 .. container:: verbose
2724 .. container:: verbose
2724
2725
2725 Template:
2726 Template:
2726
2727
2727 The following keywords are supported in addition to the common template
2728 The following keywords are supported in addition to the common template
2728 keywords and functions. See also :hg:`help templates`.
2729 keywords and functions. See also :hg:`help templates`.
2729
2730
2730 :change: String. Character denoting insertion ``+`` or removal ``-``.
2731 :change: String. Character denoting insertion ``+`` or removal ``-``.
2731 Available if ``--diff`` is specified.
2732 Available if ``--diff`` is specified.
2732 :lineno: Integer. Line number of the match.
2733 :lineno: Integer. Line number of the match.
2733 :path: String. Repository-absolute path of the file.
2734 :path: String. Repository-absolute path of the file.
2734 :texts: List of text chunks.
2735 :texts: List of text chunks.
2735
2736
2736 And each entry of ``{texts}`` provides the following sub-keywords.
2737 And each entry of ``{texts}`` provides the following sub-keywords.
2737
2738
2738 :matched: Boolean. True if the chunk matches the specified pattern.
2739 :matched: Boolean. True if the chunk matches the specified pattern.
2739 :text: String. Chunk content.
2740 :text: String. Chunk content.
2740
2741
2741 See :hg:`help templates.operators` for the list expansion syntax.
2742 See :hg:`help templates.operators` for the list expansion syntax.
2742
2743
2743 Returns 0 if a match is found, 1 otherwise.
2744 Returns 0 if a match is found, 1 otherwise.
2744 """
2745 """
2745 opts = pycompat.byteskwargs(opts)
2746 opts = pycompat.byteskwargs(opts)
2746 diff = opts.get('all') or opts.get('diff')
2747 diff = opts.get('all') or opts.get('diff')
2747 all_files = opts.get('all_files')
2748 all_files = opts.get('all_files')
2748 if diff and opts.get('all_files'):
2749 if diff and opts.get('all_files'):
2749 raise error.Abort(_('--diff and --all-files are mutually exclusive'))
2750 raise error.Abort(_('--diff and --all-files are mutually exclusive'))
2750 # TODO: remove "not opts.get('rev')" if --all-files -rMULTIREV gets working
2751 # TODO: remove "not opts.get('rev')" if --all-files -rMULTIREV gets working
2751 if opts.get('all_files') is None and not opts.get('rev') and not diff:
2752 if opts.get('all_files') is None and not opts.get('rev') and not diff:
2752 # experimental config: commands.grep.all-files
2753 # experimental config: commands.grep.all-files
2753 opts['all_files'] = ui.configbool('commands', 'grep.all-files')
2754 opts['all_files'] = ui.configbool('commands', 'grep.all-files')
2754 plaingrep = opts.get('all_files') and not opts.get('rev')
2755 plaingrep = opts.get('all_files') and not opts.get('rev')
2755 if plaingrep:
2756 if plaingrep:
2756 opts['rev'] = ['wdir()']
2757 opts['rev'] = ['wdir()']
2757
2758
2758 reflags = re.M
2759 reflags = re.M
2759 if opts.get('ignore_case'):
2760 if opts.get('ignore_case'):
2760 reflags |= re.I
2761 reflags |= re.I
2761 try:
2762 try:
2762 regexp = util.re.compile(pattern, reflags)
2763 regexp = util.re.compile(pattern, reflags)
2763 except re.error as inst:
2764 except re.error as inst:
2764 ui.warn(_("grep: invalid match pattern: %s\n") % pycompat.bytestr(inst))
2765 ui.warn(_("grep: invalid match pattern: %s\n") % pycompat.bytestr(inst))
2765 return 1
2766 return 1
2766 sep, eol = ':', '\n'
2767 sep, eol = ':', '\n'
2767 if opts.get('print0'):
2768 if opts.get('print0'):
2768 sep = eol = '\0'
2769 sep = eol = '\0'
2769
2770
2770 getfile = util.lrucachefunc(repo.file)
2771 getfile = util.lrucachefunc(repo.file)
2771
2772
2772 def matchlines(body):
2773 def matchlines(body):
2773 begin = 0
2774 begin = 0
2774 linenum = 0
2775 linenum = 0
2775 while begin < len(body):
2776 while begin < len(body):
2776 match = regexp.search(body, begin)
2777 match = regexp.search(body, begin)
2777 if not match:
2778 if not match:
2778 break
2779 break
2779 mstart, mend = match.span()
2780 mstart, mend = match.span()
2780 linenum += body.count('\n', begin, mstart) + 1
2781 linenum += body.count('\n', begin, mstart) + 1
2781 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2782 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2782 begin = body.find('\n', mend) + 1 or len(body) + 1
2783 begin = body.find('\n', mend) + 1 or len(body) + 1
2783 lend = begin - 1
2784 lend = begin - 1
2784 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2785 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2785
2786
2786 class linestate(object):
2787 class linestate(object):
2787 def __init__(self, line, linenum, colstart, colend):
2788 def __init__(self, line, linenum, colstart, colend):
2788 self.line = line
2789 self.line = line
2789 self.linenum = linenum
2790 self.linenum = linenum
2790 self.colstart = colstart
2791 self.colstart = colstart
2791 self.colend = colend
2792 self.colend = colend
2792
2793
2793 def __hash__(self):
2794 def __hash__(self):
2794 return hash((self.linenum, self.line))
2795 return hash((self.linenum, self.line))
2795
2796
2796 def __eq__(self, other):
2797 def __eq__(self, other):
2797 return self.line == other.line
2798 return self.line == other.line
2798
2799
2799 def findpos(self):
2800 def findpos(self):
2800 """Iterate all (start, end) indices of matches"""
2801 """Iterate all (start, end) indices of matches"""
2801 yield self.colstart, self.colend
2802 yield self.colstart, self.colend
2802 p = self.colend
2803 p = self.colend
2803 while p < len(self.line):
2804 while p < len(self.line):
2804 m = regexp.search(self.line, p)
2805 m = regexp.search(self.line, p)
2805 if not m:
2806 if not m:
2806 break
2807 break
2807 yield m.span()
2808 yield m.span()
2808 p = m.end()
2809 p = m.end()
2809
2810
2810 matches = {}
2811 matches = {}
2811 copies = {}
2812 copies = {}
2812 def grepbody(fn, rev, body):
2813 def grepbody(fn, rev, body):
2813 matches[rev].setdefault(fn, [])
2814 matches[rev].setdefault(fn, [])
2814 m = matches[rev][fn]
2815 m = matches[rev][fn]
2815 for lnum, cstart, cend, line in matchlines(body):
2816 for lnum, cstart, cend, line in matchlines(body):
2816 s = linestate(line, lnum, cstart, cend)
2817 s = linestate(line, lnum, cstart, cend)
2817 m.append(s)
2818 m.append(s)
2818
2819
2819 def difflinestates(a, b):
2820 def difflinestates(a, b):
2820 sm = difflib.SequenceMatcher(None, a, b)
2821 sm = difflib.SequenceMatcher(None, a, b)
2821 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2822 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2822 if tag == r'insert':
2823 if tag == r'insert':
2823 for i in pycompat.xrange(blo, bhi):
2824 for i in pycompat.xrange(blo, bhi):
2824 yield ('+', b[i])
2825 yield ('+', b[i])
2825 elif tag == r'delete':
2826 elif tag == r'delete':
2826 for i in pycompat.xrange(alo, ahi):
2827 for i in pycompat.xrange(alo, ahi):
2827 yield ('-', a[i])
2828 yield ('-', a[i])
2828 elif tag == r'replace':
2829 elif tag == r'replace':
2829 for i in pycompat.xrange(alo, ahi):
2830 for i in pycompat.xrange(alo, ahi):
2830 yield ('-', a[i])
2831 yield ('-', a[i])
2831 for i in pycompat.xrange(blo, bhi):
2832 for i in pycompat.xrange(blo, bhi):
2832 yield ('+', b[i])
2833 yield ('+', b[i])
2833
2834
2834 def display(fm, fn, ctx, pstates, states):
2835 def display(fm, fn, ctx, pstates, states):
2835 rev = scmutil.intrev(ctx)
2836 rev = scmutil.intrev(ctx)
2836 if fm.isplain():
2837 if fm.isplain():
2837 formatuser = ui.shortuser
2838 formatuser = ui.shortuser
2838 else:
2839 else:
2839 formatuser = pycompat.bytestr
2840 formatuser = pycompat.bytestr
2840 if ui.quiet:
2841 if ui.quiet:
2841 datefmt = '%Y-%m-%d'
2842 datefmt = '%Y-%m-%d'
2842 else:
2843 else:
2843 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
2844 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
2844 found = False
2845 found = False
2845 @util.cachefunc
2846 @util.cachefunc
2846 def binary():
2847 def binary():
2847 flog = getfile(fn)
2848 flog = getfile(fn)
2848 try:
2849 try:
2849 return stringutil.binary(flog.read(ctx.filenode(fn)))
2850 return stringutil.binary(flog.read(ctx.filenode(fn)))
2850 except error.WdirUnsupported:
2851 except error.WdirUnsupported:
2851 return ctx[fn].isbinary()
2852 return ctx[fn].isbinary()
2852
2853
2853 fieldnamemap = {'filename': 'path', 'linenumber': 'lineno'}
2854 fieldnamemap = {'filename': 'path', 'linenumber': 'lineno'}
2854 if diff:
2855 if diff:
2855 iter = difflinestates(pstates, states)
2856 iter = difflinestates(pstates, states)
2856 else:
2857 else:
2857 iter = [('', l) for l in states]
2858 iter = [('', l) for l in states]
2858 for change, l in iter:
2859 for change, l in iter:
2859 fm.startitem()
2860 fm.startitem()
2860 fm.context(ctx=ctx)
2861 fm.context(ctx=ctx)
2861 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)))
2862 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)))
2862
2863
2863 cols = [
2864 cols = [
2864 ('filename', '%s', fn, True),
2865 ('filename', '%s', fn, True),
2865 ('rev', '%d', rev, not plaingrep),
2866 ('rev', '%d', rev, not plaingrep),
2866 ('linenumber', '%d', l.linenum, opts.get('line_number')),
2867 ('linenumber', '%d', l.linenum, opts.get('line_number')),
2867 ]
2868 ]
2868 if diff:
2869 if diff:
2869 cols.append(('change', '%s', change, True))
2870 cols.append(('change', '%s', change, True))
2870 cols.extend([
2871 cols.extend([
2871 ('user', '%s', formatuser(ctx.user()), opts.get('user')),
2872 ('user', '%s', formatuser(ctx.user()), opts.get('user')),
2872 ('date', '%s', fm.formatdate(ctx.date(), datefmt),
2873 ('date', '%s', fm.formatdate(ctx.date(), datefmt),
2873 opts.get('date')),
2874 opts.get('date')),
2874 ])
2875 ])
2875 lastcol = next(
2876 lastcol = next(
2876 name for name, fmt, data, cond in reversed(cols) if cond)
2877 name for name, fmt, data, cond in reversed(cols) if cond)
2877 for name, fmt, data, cond in cols:
2878 for name, fmt, data, cond in cols:
2878 field = fieldnamemap.get(name, name)
2879 field = fieldnamemap.get(name, name)
2879 fm.condwrite(cond, field, fmt, data, label='grep.%s' % name)
2880 fm.condwrite(cond, field, fmt, data, label='grep.%s' % name)
2880 if cond and name != lastcol:
2881 if cond and name != lastcol:
2881 fm.plain(sep, label='grep.sep')
2882 fm.plain(sep, label='grep.sep')
2882 if not opts.get('files_with_matches'):
2883 if not opts.get('files_with_matches'):
2883 fm.plain(sep, label='grep.sep')
2884 fm.plain(sep, label='grep.sep')
2884 if not opts.get('text') and binary():
2885 if not opts.get('text') and binary():
2885 fm.plain(_(" Binary file matches"))
2886 fm.plain(_(" Binary file matches"))
2886 else:
2887 else:
2887 displaymatches(fm.nested('texts', tmpl='{text}'), l)
2888 displaymatches(fm.nested('texts', tmpl='{text}'), l)
2888 fm.plain(eol)
2889 fm.plain(eol)
2889 found = True
2890 found = True
2890 if opts.get('files_with_matches'):
2891 if opts.get('files_with_matches'):
2891 break
2892 break
2892 return found
2893 return found
2893
2894
2894 def displaymatches(fm, l):
2895 def displaymatches(fm, l):
2895 p = 0
2896 p = 0
2896 for s, e in l.findpos():
2897 for s, e in l.findpos():
2897 if p < s:
2898 if p < s:
2898 fm.startitem()
2899 fm.startitem()
2899 fm.write('text', '%s', l.line[p:s])
2900 fm.write('text', '%s', l.line[p:s])
2900 fm.data(matched=False)
2901 fm.data(matched=False)
2901 fm.startitem()
2902 fm.startitem()
2902 fm.write('text', '%s', l.line[s:e], label='grep.match')
2903 fm.write('text', '%s', l.line[s:e], label='grep.match')
2903 fm.data(matched=True)
2904 fm.data(matched=True)
2904 p = e
2905 p = e
2905 if p < len(l.line):
2906 if p < len(l.line):
2906 fm.startitem()
2907 fm.startitem()
2907 fm.write('text', '%s', l.line[p:])
2908 fm.write('text', '%s', l.line[p:])
2908 fm.data(matched=False)
2909 fm.data(matched=False)
2909 fm.end()
2910 fm.end()
2910
2911
2911 skip = {}
2912 skip = {}
2912 revfiles = {}
2913 revfiles = {}
2913 match = scmutil.match(repo[None], pats, opts)
2914 match = scmutil.match(repo[None], pats, opts)
2914 found = False
2915 found = False
2915 follow = opts.get('follow')
2916 follow = opts.get('follow')
2916
2917
2917 def prep(ctx, fns):
2918 def prep(ctx, fns):
2918 rev = ctx.rev()
2919 rev = ctx.rev()
2919 pctx = ctx.p1()
2920 pctx = ctx.p1()
2920 parent = pctx.rev()
2921 parent = pctx.rev()
2921 matches.setdefault(rev, {})
2922 matches.setdefault(rev, {})
2922 matches.setdefault(parent, {})
2923 matches.setdefault(parent, {})
2923 files = revfiles.setdefault(rev, [])
2924 files = revfiles.setdefault(rev, [])
2924 for fn in fns:
2925 for fn in fns:
2925 flog = getfile(fn)
2926 flog = getfile(fn)
2926 try:
2927 try:
2927 fnode = ctx.filenode(fn)
2928 fnode = ctx.filenode(fn)
2928 except error.LookupError:
2929 except error.LookupError:
2929 continue
2930 continue
2930 try:
2931 try:
2931 copied = flog.renamed(fnode)
2932 copied = flog.renamed(fnode)
2932 except error.WdirUnsupported:
2933 except error.WdirUnsupported:
2933 copied = ctx[fn].renamed()
2934 copied = ctx[fn].renamed()
2934 copy = follow and copied and copied[0]
2935 copy = follow and copied and copied[0]
2935 if copy:
2936 if copy:
2936 copies.setdefault(rev, {})[fn] = copy
2937 copies.setdefault(rev, {})[fn] = copy
2937 if fn in skip:
2938 if fn in skip:
2938 if copy:
2939 if copy:
2939 skip[copy] = True
2940 skip[copy] = True
2940 continue
2941 continue
2941 files.append(fn)
2942 files.append(fn)
2942
2943
2943 if fn not in matches[rev]:
2944 if fn not in matches[rev]:
2944 try:
2945 try:
2945 content = flog.read(fnode)
2946 content = flog.read(fnode)
2946 except error.WdirUnsupported:
2947 except error.WdirUnsupported:
2947 content = ctx[fn].data()
2948 content = ctx[fn].data()
2948 grepbody(fn, rev, content)
2949 grepbody(fn, rev, content)
2949
2950
2950 pfn = copy or fn
2951 pfn = copy or fn
2951 if pfn not in matches[parent]:
2952 if pfn not in matches[parent]:
2952 try:
2953 try:
2953 fnode = pctx.filenode(pfn)
2954 fnode = pctx.filenode(pfn)
2954 grepbody(pfn, parent, flog.read(fnode))
2955 grepbody(pfn, parent, flog.read(fnode))
2955 except error.LookupError:
2956 except error.LookupError:
2956 pass
2957 pass
2957
2958
2958 ui.pager('grep')
2959 ui.pager('grep')
2959 fm = ui.formatter('grep', opts)
2960 fm = ui.formatter('grep', opts)
2960 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
2961 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
2961 rev = ctx.rev()
2962 rev = ctx.rev()
2962 parent = ctx.p1().rev()
2963 parent = ctx.p1().rev()
2963 for fn in sorted(revfiles.get(rev, [])):
2964 for fn in sorted(revfiles.get(rev, [])):
2964 states = matches[rev][fn]
2965 states = matches[rev][fn]
2965 copy = copies.get(rev, {}).get(fn)
2966 copy = copies.get(rev, {}).get(fn)
2966 if fn in skip:
2967 if fn in skip:
2967 if copy:
2968 if copy:
2968 skip[copy] = True
2969 skip[copy] = True
2969 continue
2970 continue
2970 pstates = matches.get(parent, {}).get(copy or fn, [])
2971 pstates = matches.get(parent, {}).get(copy or fn, [])
2971 if pstates or states:
2972 if pstates or states:
2972 r = display(fm, fn, ctx, pstates, states)
2973 r = display(fm, fn, ctx, pstates, states)
2973 found = found or r
2974 found = found or r
2974 if r and not diff and not all_files:
2975 if r and not diff and not all_files:
2975 skip[fn] = True
2976 skip[fn] = True
2976 if copy:
2977 if copy:
2977 skip[copy] = True
2978 skip[copy] = True
2978 del revfiles[rev]
2979 del revfiles[rev]
2979 # We will keep the matches dict for the duration of the window
2980 # We will keep the matches dict for the duration of the window
2980 # clear the matches dict once the window is over
2981 # clear the matches dict once the window is over
2981 if not revfiles:
2982 if not revfiles:
2982 matches.clear()
2983 matches.clear()
2983 fm.end()
2984 fm.end()
2984
2985
2985 return not found
2986 return not found
2986
2987
2987 @command('heads',
2988 @command('heads',
2988 [('r', 'rev', '',
2989 [('r', 'rev', '',
2989 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2990 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2990 ('t', 'topo', False, _('show topological heads only')),
2991 ('t', 'topo', False, _('show topological heads only')),
2991 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2992 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2992 ('c', 'closed', False, _('show normal and closed branch heads')),
2993 ('c', 'closed', False, _('show normal and closed branch heads')),
2993 ] + templateopts,
2994 ] + templateopts,
2994 _('[-ct] [-r STARTREV] [REV]...'),
2995 _('[-ct] [-r STARTREV] [REV]...'),
2995 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
2996 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
2996 intents={INTENT_READONLY})
2997 intents={INTENT_READONLY})
2997 def heads(ui, repo, *branchrevs, **opts):
2998 def heads(ui, repo, *branchrevs, **opts):
2998 """show branch heads
2999 """show branch heads
2999
3000
3000 With no arguments, show all open branch heads in the repository.
3001 With no arguments, show all open branch heads in the repository.
3001 Branch heads are changesets that have no descendants on the
3002 Branch heads are changesets that have no descendants on the
3002 same branch. They are where development generally takes place and
3003 same branch. They are where development generally takes place and
3003 are the usual targets for update and merge operations.
3004 are the usual targets for update and merge operations.
3004
3005
3005 If one or more REVs are given, only open branch heads on the
3006 If one or more REVs are given, only open branch heads on the
3006 branches associated with the specified changesets are shown. This
3007 branches associated with the specified changesets are shown. This
3007 means that you can use :hg:`heads .` to see the heads on the
3008 means that you can use :hg:`heads .` to see the heads on the
3008 currently checked-out branch.
3009 currently checked-out branch.
3009
3010
3010 If -c/--closed is specified, also show branch heads marked closed
3011 If -c/--closed is specified, also show branch heads marked closed
3011 (see :hg:`commit --close-branch`).
3012 (see :hg:`commit --close-branch`).
3012
3013
3013 If STARTREV is specified, only those heads that are descendants of
3014 If STARTREV is specified, only those heads that are descendants of
3014 STARTREV will be displayed.
3015 STARTREV will be displayed.
3015
3016
3016 If -t/--topo is specified, named branch mechanics will be ignored and only
3017 If -t/--topo is specified, named branch mechanics will be ignored and only
3017 topological heads (changesets with no children) will be shown.
3018 topological heads (changesets with no children) will be shown.
3018
3019
3019 Returns 0 if matching heads are found, 1 if not.
3020 Returns 0 if matching heads are found, 1 if not.
3020 """
3021 """
3021
3022
3022 opts = pycompat.byteskwargs(opts)
3023 opts = pycompat.byteskwargs(opts)
3023 start = None
3024 start = None
3024 rev = opts.get('rev')
3025 rev = opts.get('rev')
3025 if rev:
3026 if rev:
3026 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3027 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3027 start = scmutil.revsingle(repo, rev, None).node()
3028 start = scmutil.revsingle(repo, rev, None).node()
3028
3029
3029 if opts.get('topo'):
3030 if opts.get('topo'):
3030 heads = [repo[h] for h in repo.heads(start)]
3031 heads = [repo[h] for h in repo.heads(start)]
3031 else:
3032 else:
3032 heads = []
3033 heads = []
3033 for branch in repo.branchmap():
3034 for branch in repo.branchmap():
3034 heads += repo.branchheads(branch, start, opts.get('closed'))
3035 heads += repo.branchheads(branch, start, opts.get('closed'))
3035 heads = [repo[h] for h in heads]
3036 heads = [repo[h] for h in heads]
3036
3037
3037 if branchrevs:
3038 if branchrevs:
3038 branches = set(repo[r].branch()
3039 branches = set(repo[r].branch()
3039 for r in scmutil.revrange(repo, branchrevs))
3040 for r in scmutil.revrange(repo, branchrevs))
3040 heads = [h for h in heads if h.branch() in branches]
3041 heads = [h for h in heads if h.branch() in branches]
3041
3042
3042 if opts.get('active') and branchrevs:
3043 if opts.get('active') and branchrevs:
3043 dagheads = repo.heads(start)
3044 dagheads = repo.heads(start)
3044 heads = [h for h in heads if h.node() in dagheads]
3045 heads = [h for h in heads if h.node() in dagheads]
3045
3046
3046 if branchrevs:
3047 if branchrevs:
3047 haveheads = set(h.branch() for h in heads)
3048 haveheads = set(h.branch() for h in heads)
3048 if branches - haveheads:
3049 if branches - haveheads:
3049 headless = ', '.join(b for b in branches - haveheads)
3050 headless = ', '.join(b for b in branches - haveheads)
3050 msg = _('no open branch heads found on branches %s')
3051 msg = _('no open branch heads found on branches %s')
3051 if opts.get('rev'):
3052 if opts.get('rev'):
3052 msg += _(' (started at %s)') % opts['rev']
3053 msg += _(' (started at %s)') % opts['rev']
3053 ui.warn((msg + '\n') % headless)
3054 ui.warn((msg + '\n') % headless)
3054
3055
3055 if not heads:
3056 if not heads:
3056 return 1
3057 return 1
3057
3058
3058 ui.pager('heads')
3059 ui.pager('heads')
3059 heads = sorted(heads, key=lambda x: -x.rev())
3060 heads = sorted(heads, key=lambda x: -x.rev())
3060 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3061 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3061 for ctx in heads:
3062 for ctx in heads:
3062 displayer.show(ctx)
3063 displayer.show(ctx)
3063 displayer.close()
3064 displayer.close()
3064
3065
3065 @command('help',
3066 @command('help',
3066 [('e', 'extension', None, _('show only help for extensions')),
3067 [('e', 'extension', None, _('show only help for extensions')),
3067 ('c', 'command', None, _('show only help for commands')),
3068 ('c', 'command', None, _('show only help for commands')),
3068 ('k', 'keyword', None, _('show topics matching keyword')),
3069 ('k', 'keyword', None, _('show topics matching keyword')),
3069 ('s', 'system', [],
3070 ('s', 'system', [],
3070 _('show help for specific platform(s)'), _('PLATFORM')),
3071 _('show help for specific platform(s)'), _('PLATFORM')),
3071 ],
3072 ],
3072 _('[-eck] [-s PLATFORM] [TOPIC]'),
3073 _('[-eck] [-s PLATFORM] [TOPIC]'),
3073 helpcategory=command.CATEGORY_HELP,
3074 helpcategory=command.CATEGORY_HELP,
3074 norepo=True,
3075 norepo=True,
3075 intents={INTENT_READONLY})
3076 intents={INTENT_READONLY})
3076 def help_(ui, name=None, **opts):
3077 def help_(ui, name=None, **opts):
3077 """show help for a given topic or a help overview
3078 """show help for a given topic or a help overview
3078
3079
3079 With no arguments, print a list of commands with short help messages.
3080 With no arguments, print a list of commands with short help messages.
3080
3081
3081 Given a topic, extension, or command name, print help for that
3082 Given a topic, extension, or command name, print help for that
3082 topic.
3083 topic.
3083
3084
3084 Returns 0 if successful.
3085 Returns 0 if successful.
3085 """
3086 """
3086
3087
3087 keep = opts.get(r'system') or []
3088 keep = opts.get(r'system') or []
3088 if len(keep) == 0:
3089 if len(keep) == 0:
3089 if pycompat.sysplatform.startswith('win'):
3090 if pycompat.sysplatform.startswith('win'):
3090 keep.append('windows')
3091 keep.append('windows')
3091 elif pycompat.sysplatform == 'OpenVMS':
3092 elif pycompat.sysplatform == 'OpenVMS':
3092 keep.append('vms')
3093 keep.append('vms')
3093 elif pycompat.sysplatform == 'plan9':
3094 elif pycompat.sysplatform == 'plan9':
3094 keep.append('plan9')
3095 keep.append('plan9')
3095 else:
3096 else:
3096 keep.append('unix')
3097 keep.append('unix')
3097 keep.append(pycompat.sysplatform.lower())
3098 keep.append(pycompat.sysplatform.lower())
3098 if ui.verbose:
3099 if ui.verbose:
3099 keep.append('verbose')
3100 keep.append('verbose')
3100
3101
3101 commands = sys.modules[__name__]
3102 commands = sys.modules[__name__]
3102 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3103 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3103 ui.pager('help')
3104 ui.pager('help')
3104 ui.write(formatted)
3105 ui.write(formatted)
3105
3106
3106
3107
3107 @command('identify|id',
3108 @command('identify|id',
3108 [('r', 'rev', '',
3109 [('r', 'rev', '',
3109 _('identify the specified revision'), _('REV')),
3110 _('identify the specified revision'), _('REV')),
3110 ('n', 'num', None, _('show local revision number')),
3111 ('n', 'num', None, _('show local revision number')),
3111 ('i', 'id', None, _('show global revision id')),
3112 ('i', 'id', None, _('show global revision id')),
3112 ('b', 'branch', None, _('show branch')),
3113 ('b', 'branch', None, _('show branch')),
3113 ('t', 'tags', None, _('show tags')),
3114 ('t', 'tags', None, _('show tags')),
3114 ('B', 'bookmarks', None, _('show bookmarks')),
3115 ('B', 'bookmarks', None, _('show bookmarks')),
3115 ] + remoteopts + formatteropts,
3116 ] + remoteopts + formatteropts,
3116 _('[-nibtB] [-r REV] [SOURCE]'),
3117 _('[-nibtB] [-r REV] [SOURCE]'),
3117 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3118 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3118 optionalrepo=True,
3119 optionalrepo=True,
3119 intents={INTENT_READONLY})
3120 intents={INTENT_READONLY})
3120 def identify(ui, repo, source=None, rev=None,
3121 def identify(ui, repo, source=None, rev=None,
3121 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3122 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3122 """identify the working directory or specified revision
3123 """identify the working directory or specified revision
3123
3124
3124 Print a summary identifying the repository state at REV using one or
3125 Print a summary identifying the repository state at REV using one or
3125 two parent hash identifiers, followed by a "+" if the working
3126 two parent hash identifiers, followed by a "+" if the working
3126 directory has uncommitted changes, the branch name (if not default),
3127 directory has uncommitted changes, the branch name (if not default),
3127 a list of tags, and a list of bookmarks.
3128 a list of tags, and a list of bookmarks.
3128
3129
3129 When REV is not given, print a summary of the current state of the
3130 When REV is not given, print a summary of the current state of the
3130 repository including the working directory. Specify -r. to get information
3131 repository including the working directory. Specify -r. to get information
3131 of the working directory parent without scanning uncommitted changes.
3132 of the working directory parent without scanning uncommitted changes.
3132
3133
3133 Specifying a path to a repository root or Mercurial bundle will
3134 Specifying a path to a repository root or Mercurial bundle will
3134 cause lookup to operate on that repository/bundle.
3135 cause lookup to operate on that repository/bundle.
3135
3136
3136 .. container:: verbose
3137 .. container:: verbose
3137
3138
3138 Template:
3139 Template:
3139
3140
3140 The following keywords are supported in addition to the common template
3141 The following keywords are supported in addition to the common template
3141 keywords and functions. See also :hg:`help templates`.
3142 keywords and functions. See also :hg:`help templates`.
3142
3143
3143 :dirty: String. Character ``+`` denoting if the working directory has
3144 :dirty: String. Character ``+`` denoting if the working directory has
3144 uncommitted changes.
3145 uncommitted changes.
3145 :id: String. One or two nodes, optionally followed by ``+``.
3146 :id: String. One or two nodes, optionally followed by ``+``.
3146 :parents: List of strings. Parent nodes of the changeset.
3147 :parents: List of strings. Parent nodes of the changeset.
3147
3148
3148 Examples:
3149 Examples:
3149
3150
3150 - generate a build identifier for the working directory::
3151 - generate a build identifier for the working directory::
3151
3152
3152 hg id --id > build-id.dat
3153 hg id --id > build-id.dat
3153
3154
3154 - find the revision corresponding to a tag::
3155 - find the revision corresponding to a tag::
3155
3156
3156 hg id -n -r 1.3
3157 hg id -n -r 1.3
3157
3158
3158 - check the most recent revision of a remote repository::
3159 - check the most recent revision of a remote repository::
3159
3160
3160 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3161 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3161
3162
3162 See :hg:`log` for generating more information about specific revisions,
3163 See :hg:`log` for generating more information about specific revisions,
3163 including full hash identifiers.
3164 including full hash identifiers.
3164
3165
3165 Returns 0 if successful.
3166 Returns 0 if successful.
3166 """
3167 """
3167
3168
3168 opts = pycompat.byteskwargs(opts)
3169 opts = pycompat.byteskwargs(opts)
3169 if not repo and not source:
3170 if not repo and not source:
3170 raise error.Abort(_("there is no Mercurial repository here "
3171 raise error.Abort(_("there is no Mercurial repository here "
3171 "(.hg not found)"))
3172 "(.hg not found)"))
3172
3173
3173 default = not (num or id or branch or tags or bookmarks)
3174 default = not (num or id or branch or tags or bookmarks)
3174 output = []
3175 output = []
3175 revs = []
3176 revs = []
3176
3177
3177 if source:
3178 if source:
3178 source, branches = hg.parseurl(ui.expandpath(source))
3179 source, branches = hg.parseurl(ui.expandpath(source))
3179 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3180 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3180 repo = peer.local()
3181 repo = peer.local()
3181 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3182 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3182
3183
3183 fm = ui.formatter('identify', opts)
3184 fm = ui.formatter('identify', opts)
3184 fm.startitem()
3185 fm.startitem()
3185
3186
3186 if not repo:
3187 if not repo:
3187 if num or branch or tags:
3188 if num or branch or tags:
3188 raise error.Abort(
3189 raise error.Abort(
3189 _("can't query remote revision number, branch, or tags"))
3190 _("can't query remote revision number, branch, or tags"))
3190 if not rev and revs:
3191 if not rev and revs:
3191 rev = revs[0]
3192 rev = revs[0]
3192 if not rev:
3193 if not rev:
3193 rev = "tip"
3194 rev = "tip"
3194
3195
3195 remoterev = peer.lookup(rev)
3196 remoterev = peer.lookup(rev)
3196 hexrev = fm.hexfunc(remoterev)
3197 hexrev = fm.hexfunc(remoterev)
3197 if default or id:
3198 if default or id:
3198 output = [hexrev]
3199 output = [hexrev]
3199 fm.data(id=hexrev)
3200 fm.data(id=hexrev)
3200
3201
3201 @util.cachefunc
3202 @util.cachefunc
3202 def getbms():
3203 def getbms():
3203 bms = []
3204 bms = []
3204
3205
3205 if 'bookmarks' in peer.listkeys('namespaces'):
3206 if 'bookmarks' in peer.listkeys('namespaces'):
3206 hexremoterev = hex(remoterev)
3207 hexremoterev = hex(remoterev)
3207 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
3208 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
3208 if bmr == hexremoterev]
3209 if bmr == hexremoterev]
3209
3210
3210 return sorted(bms)
3211 return sorted(bms)
3211
3212
3212 if fm.isplain():
3213 if fm.isplain():
3213 if bookmarks:
3214 if bookmarks:
3214 output.extend(getbms())
3215 output.extend(getbms())
3215 elif default and not ui.quiet:
3216 elif default and not ui.quiet:
3216 # multiple bookmarks for a single parent separated by '/'
3217 # multiple bookmarks for a single parent separated by '/'
3217 bm = '/'.join(getbms())
3218 bm = '/'.join(getbms())
3218 if bm:
3219 if bm:
3219 output.append(bm)
3220 output.append(bm)
3220 else:
3221 else:
3221 fm.data(node=hex(remoterev))
3222 fm.data(node=hex(remoterev))
3222 if bookmarks or 'bookmarks' in fm.datahint():
3223 if bookmarks or 'bookmarks' in fm.datahint():
3223 fm.data(bookmarks=fm.formatlist(getbms(), name='bookmark'))
3224 fm.data(bookmarks=fm.formatlist(getbms(), name='bookmark'))
3224 else:
3225 else:
3225 if rev:
3226 if rev:
3226 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3227 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3227 ctx = scmutil.revsingle(repo, rev, None)
3228 ctx = scmutil.revsingle(repo, rev, None)
3228
3229
3229 if ctx.rev() is None:
3230 if ctx.rev() is None:
3230 ctx = repo[None]
3231 ctx = repo[None]
3231 parents = ctx.parents()
3232 parents = ctx.parents()
3232 taglist = []
3233 taglist = []
3233 for p in parents:
3234 for p in parents:
3234 taglist.extend(p.tags())
3235 taglist.extend(p.tags())
3235
3236
3236 dirty = ""
3237 dirty = ""
3237 if ctx.dirty(missing=True, merge=False, branch=False):
3238 if ctx.dirty(missing=True, merge=False, branch=False):
3238 dirty = '+'
3239 dirty = '+'
3239 fm.data(dirty=dirty)
3240 fm.data(dirty=dirty)
3240
3241
3241 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3242 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3242 if default or id:
3243 if default or id:
3243 output = ["%s%s" % ('+'.join(hexoutput), dirty)]
3244 output = ["%s%s" % ('+'.join(hexoutput), dirty)]
3244 fm.data(id="%s%s" % ('+'.join(hexoutput), dirty))
3245 fm.data(id="%s%s" % ('+'.join(hexoutput), dirty))
3245
3246
3246 if num:
3247 if num:
3247 numoutput = ["%d" % p.rev() for p in parents]
3248 numoutput = ["%d" % p.rev() for p in parents]
3248 output.append("%s%s" % ('+'.join(numoutput), dirty))
3249 output.append("%s%s" % ('+'.join(numoutput), dirty))
3249
3250
3250 fm.data(parents=fm.formatlist([fm.hexfunc(p.node())
3251 fm.data(parents=fm.formatlist([fm.hexfunc(p.node())
3251 for p in parents], name='node'))
3252 for p in parents], name='node'))
3252 else:
3253 else:
3253 hexoutput = fm.hexfunc(ctx.node())
3254 hexoutput = fm.hexfunc(ctx.node())
3254 if default or id:
3255 if default or id:
3255 output = [hexoutput]
3256 output = [hexoutput]
3256 fm.data(id=hexoutput)
3257 fm.data(id=hexoutput)
3257
3258
3258 if num:
3259 if num:
3259 output.append(pycompat.bytestr(ctx.rev()))
3260 output.append(pycompat.bytestr(ctx.rev()))
3260 taglist = ctx.tags()
3261 taglist = ctx.tags()
3261
3262
3262 if default and not ui.quiet:
3263 if default and not ui.quiet:
3263 b = ctx.branch()
3264 b = ctx.branch()
3264 if b != 'default':
3265 if b != 'default':
3265 output.append("(%s)" % b)
3266 output.append("(%s)" % b)
3266
3267
3267 # multiple tags for a single parent separated by '/'
3268 # multiple tags for a single parent separated by '/'
3268 t = '/'.join(taglist)
3269 t = '/'.join(taglist)
3269 if t:
3270 if t:
3270 output.append(t)
3271 output.append(t)
3271
3272
3272 # multiple bookmarks for a single parent separated by '/'
3273 # multiple bookmarks for a single parent separated by '/'
3273 bm = '/'.join(ctx.bookmarks())
3274 bm = '/'.join(ctx.bookmarks())
3274 if bm:
3275 if bm:
3275 output.append(bm)
3276 output.append(bm)
3276 else:
3277 else:
3277 if branch:
3278 if branch:
3278 output.append(ctx.branch())
3279 output.append(ctx.branch())
3279
3280
3280 if tags:
3281 if tags:
3281 output.extend(taglist)
3282 output.extend(taglist)
3282
3283
3283 if bookmarks:
3284 if bookmarks:
3284 output.extend(ctx.bookmarks())
3285 output.extend(ctx.bookmarks())
3285
3286
3286 fm.data(node=ctx.hex())
3287 fm.data(node=ctx.hex())
3287 fm.data(branch=ctx.branch())
3288 fm.data(branch=ctx.branch())
3288 fm.data(tags=fm.formatlist(taglist, name='tag', sep=':'))
3289 fm.data(tags=fm.formatlist(taglist, name='tag', sep=':'))
3289 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name='bookmark'))
3290 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name='bookmark'))
3290 fm.context(ctx=ctx)
3291 fm.context(ctx=ctx)
3291
3292
3292 fm.plain("%s\n" % ' '.join(output))
3293 fm.plain("%s\n" % ' '.join(output))
3293 fm.end()
3294 fm.end()
3294
3295
3295 @command('import|patch',
3296 @command('import|patch',
3296 [('p', 'strip', 1,
3297 [('p', 'strip', 1,
3297 _('directory strip option for patch. This has the same '
3298 _('directory strip option for patch. This has the same '
3298 'meaning as the corresponding patch option'), _('NUM')),
3299 'meaning as the corresponding patch option'), _('NUM')),
3299 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3300 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3300 ('e', 'edit', False, _('invoke editor on commit messages')),
3301 ('e', 'edit', False, _('invoke editor on commit messages')),
3301 ('f', 'force', None,
3302 ('f', 'force', None,
3302 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
3303 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
3303 ('', 'no-commit', None,
3304 ('', 'no-commit', None,
3304 _("don't commit, just update the working directory")),
3305 _("don't commit, just update the working directory")),
3305 ('', 'bypass', None,
3306 ('', 'bypass', None,
3306 _("apply patch without touching the working directory")),
3307 _("apply patch without touching the working directory")),
3307 ('', 'partial', None,
3308 ('', 'partial', None,
3308 _('commit even if some hunks fail')),
3309 _('commit even if some hunks fail')),
3309 ('', 'exact', None,
3310 ('', 'exact', None,
3310 _('abort if patch would apply lossily')),
3311 _('abort if patch would apply lossily')),
3311 ('', 'prefix', '',
3312 ('', 'prefix', '',
3312 _('apply patch to subdirectory'), _('DIR')),
3313 _('apply patch to subdirectory'), _('DIR')),
3313 ('', 'import-branch', None,
3314 ('', 'import-branch', None,
3314 _('use any branch information in patch (implied by --exact)'))] +
3315 _('use any branch information in patch (implied by --exact)'))] +
3315 commitopts + commitopts2 + similarityopts,
3316 commitopts + commitopts2 + similarityopts,
3316 _('[OPTION]... PATCH...'),
3317 _('[OPTION]... PATCH...'),
3317 helpcategory=command.CATEGORY_IMPORT_EXPORT)
3318 helpcategory=command.CATEGORY_IMPORT_EXPORT)
3318 def import_(ui, repo, patch1=None, *patches, **opts):
3319 def import_(ui, repo, patch1=None, *patches, **opts):
3319 """import an ordered set of patches
3320 """import an ordered set of patches
3320
3321
3321 Import a list of patches and commit them individually (unless
3322 Import a list of patches and commit them individually (unless
3322 --no-commit is specified).
3323 --no-commit is specified).
3323
3324
3324 To read a patch from standard input (stdin), use "-" as the patch
3325 To read a patch from standard input (stdin), use "-" as the patch
3325 name. If a URL is specified, the patch will be downloaded from
3326 name. If a URL is specified, the patch will be downloaded from
3326 there.
3327 there.
3327
3328
3328 Import first applies changes to the working directory (unless
3329 Import first applies changes to the working directory (unless
3329 --bypass is specified), import will abort if there are outstanding
3330 --bypass is specified), import will abort if there are outstanding
3330 changes.
3331 changes.
3331
3332
3332 Use --bypass to apply and commit patches directly to the
3333 Use --bypass to apply and commit patches directly to the
3333 repository, without affecting the working directory. Without
3334 repository, without affecting the working directory. Without
3334 --exact, patches will be applied on top of the working directory
3335 --exact, patches will be applied on top of the working directory
3335 parent revision.
3336 parent revision.
3336
3337
3337 You can import a patch straight from a mail message. Even patches
3338 You can import a patch straight from a mail message. Even patches
3338 as attachments work (to use the body part, it must have type
3339 as attachments work (to use the body part, it must have type
3339 text/plain or text/x-patch). From and Subject headers of email
3340 text/plain or text/x-patch). From and Subject headers of email
3340 message are used as default committer and commit message. All
3341 message are used as default committer and commit message. All
3341 text/plain body parts before first diff are added to the commit
3342 text/plain body parts before first diff are added to the commit
3342 message.
3343 message.
3343
3344
3344 If the imported patch was generated by :hg:`export`, user and
3345 If the imported patch was generated by :hg:`export`, user and
3345 description from patch override values from message headers and
3346 description from patch override values from message headers and
3346 body. Values given on command line with -m/--message and -u/--user
3347 body. Values given on command line with -m/--message and -u/--user
3347 override these.
3348 override these.
3348
3349
3349 If --exact is specified, import will set the working directory to
3350 If --exact is specified, import will set the working directory to
3350 the parent of each patch before applying it, and will abort if the
3351 the parent of each patch before applying it, and will abort if the
3351 resulting changeset has a different ID than the one recorded in
3352 resulting changeset has a different ID than the one recorded in
3352 the patch. This will guard against various ways that portable
3353 the patch. This will guard against various ways that portable
3353 patch formats and mail systems might fail to transfer Mercurial
3354 patch formats and mail systems might fail to transfer Mercurial
3354 data or metadata. See :hg:`bundle` for lossless transmission.
3355 data or metadata. See :hg:`bundle` for lossless transmission.
3355
3356
3356 Use --partial to ensure a changeset will be created from the patch
3357 Use --partial to ensure a changeset will be created from the patch
3357 even if some hunks fail to apply. Hunks that fail to apply will be
3358 even if some hunks fail to apply. Hunks that fail to apply will be
3358 written to a <target-file>.rej file. Conflicts can then be resolved
3359 written to a <target-file>.rej file. Conflicts can then be resolved
3359 by hand before :hg:`commit --amend` is run to update the created
3360 by hand before :hg:`commit --amend` is run to update the created
3360 changeset. This flag exists to let people import patches that
3361 changeset. This flag exists to let people import patches that
3361 partially apply without losing the associated metadata (author,
3362 partially apply without losing the associated metadata (author,
3362 date, description, ...).
3363 date, description, ...).
3363
3364
3364 .. note::
3365 .. note::
3365
3366
3366 When no hunks apply cleanly, :hg:`import --partial` will create
3367 When no hunks apply cleanly, :hg:`import --partial` will create
3367 an empty changeset, importing only the patch metadata.
3368 an empty changeset, importing only the patch metadata.
3368
3369
3369 With -s/--similarity, hg will attempt to discover renames and
3370 With -s/--similarity, hg will attempt to discover renames and
3370 copies in the patch in the same way as :hg:`addremove`.
3371 copies in the patch in the same way as :hg:`addremove`.
3371
3372
3372 It is possible to use external patch programs to perform the patch
3373 It is possible to use external patch programs to perform the patch
3373 by setting the ``ui.patch`` configuration option. For the default
3374 by setting the ``ui.patch`` configuration option. For the default
3374 internal tool, the fuzz can also be configured via ``patch.fuzz``.
3375 internal tool, the fuzz can also be configured via ``patch.fuzz``.
3375 See :hg:`help config` for more information about configuration
3376 See :hg:`help config` for more information about configuration
3376 files and how to use these options.
3377 files and how to use these options.
3377
3378
3378 See :hg:`help dates` for a list of formats valid for -d/--date.
3379 See :hg:`help dates` for a list of formats valid for -d/--date.
3379
3380
3380 .. container:: verbose
3381 .. container:: verbose
3381
3382
3382 Examples:
3383 Examples:
3383
3384
3384 - import a traditional patch from a website and detect renames::
3385 - import a traditional patch from a website and detect renames::
3385
3386
3386 hg import -s 80 http://example.com/bugfix.patch
3387 hg import -s 80 http://example.com/bugfix.patch
3387
3388
3388 - import a changeset from an hgweb server::
3389 - import a changeset from an hgweb server::
3389
3390
3390 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
3391 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
3391
3392
3392 - import all the patches in an Unix-style mbox::
3393 - import all the patches in an Unix-style mbox::
3393
3394
3394 hg import incoming-patches.mbox
3395 hg import incoming-patches.mbox
3395
3396
3396 - import patches from stdin::
3397 - import patches from stdin::
3397
3398
3398 hg import -
3399 hg import -
3399
3400
3400 - attempt to exactly restore an exported changeset (not always
3401 - attempt to exactly restore an exported changeset (not always
3401 possible)::
3402 possible)::
3402
3403
3403 hg import --exact proposed-fix.patch
3404 hg import --exact proposed-fix.patch
3404
3405
3405 - use an external tool to apply a patch which is too fuzzy for
3406 - use an external tool to apply a patch which is too fuzzy for
3406 the default internal tool.
3407 the default internal tool.
3407
3408
3408 hg import --config ui.patch="patch --merge" fuzzy.patch
3409 hg import --config ui.patch="patch --merge" fuzzy.patch
3409
3410
3410 - change the default fuzzing from 2 to a less strict 7
3411 - change the default fuzzing from 2 to a less strict 7
3411
3412
3412 hg import --config ui.fuzz=7 fuzz.patch
3413 hg import --config ui.fuzz=7 fuzz.patch
3413
3414
3414 Returns 0 on success, 1 on partial success (see --partial).
3415 Returns 0 on success, 1 on partial success (see --partial).
3415 """
3416 """
3416
3417
3417 opts = pycompat.byteskwargs(opts)
3418 opts = pycompat.byteskwargs(opts)
3418 if not patch1:
3419 if not patch1:
3419 raise error.Abort(_('need at least one patch to import'))
3420 raise error.Abort(_('need at least one patch to import'))
3420
3421
3421 patches = (patch1,) + patches
3422 patches = (patch1,) + patches
3422
3423
3423 date = opts.get('date')
3424 date = opts.get('date')
3424 if date:
3425 if date:
3425 opts['date'] = dateutil.parsedate(date)
3426 opts['date'] = dateutil.parsedate(date)
3426
3427
3427 exact = opts.get('exact')
3428 exact = opts.get('exact')
3428 update = not opts.get('bypass')
3429 update = not opts.get('bypass')
3429 if not update and opts.get('no_commit'):
3430 if not update and opts.get('no_commit'):
3430 raise error.Abort(_('cannot use --no-commit with --bypass'))
3431 raise error.Abort(_('cannot use --no-commit with --bypass'))
3431 try:
3432 try:
3432 sim = float(opts.get('similarity') or 0)
3433 sim = float(opts.get('similarity') or 0)
3433 except ValueError:
3434 except ValueError:
3434 raise error.Abort(_('similarity must be a number'))
3435 raise error.Abort(_('similarity must be a number'))
3435 if sim < 0 or sim > 100:
3436 if sim < 0 or sim > 100:
3436 raise error.Abort(_('similarity must be between 0 and 100'))
3437 raise error.Abort(_('similarity must be between 0 and 100'))
3437 if sim and not update:
3438 if sim and not update:
3438 raise error.Abort(_('cannot use --similarity with --bypass'))
3439 raise error.Abort(_('cannot use --similarity with --bypass'))
3439 if exact:
3440 if exact:
3440 if opts.get('edit'):
3441 if opts.get('edit'):
3441 raise error.Abort(_('cannot use --exact with --edit'))
3442 raise error.Abort(_('cannot use --exact with --edit'))
3442 if opts.get('prefix'):
3443 if opts.get('prefix'):
3443 raise error.Abort(_('cannot use --exact with --prefix'))
3444 raise error.Abort(_('cannot use --exact with --prefix'))
3444
3445
3445 base = opts["base"]
3446 base = opts["base"]
3446 msgs = []
3447 msgs = []
3447 ret = 0
3448 ret = 0
3448
3449
3449 with repo.wlock():
3450 with repo.wlock():
3450 if update:
3451 if update:
3451 cmdutil.checkunfinished(repo)
3452 cmdutil.checkunfinished(repo)
3452 if (exact or not opts.get('force')):
3453 if (exact or not opts.get('force')):
3453 cmdutil.bailifchanged(repo)
3454 cmdutil.bailifchanged(repo)
3454
3455
3455 if not opts.get('no_commit'):
3456 if not opts.get('no_commit'):
3456 lock = repo.lock
3457 lock = repo.lock
3457 tr = lambda: repo.transaction('import')
3458 tr = lambda: repo.transaction('import')
3458 dsguard = util.nullcontextmanager
3459 dsguard = util.nullcontextmanager
3459 else:
3460 else:
3460 lock = util.nullcontextmanager
3461 lock = util.nullcontextmanager
3461 tr = util.nullcontextmanager
3462 tr = util.nullcontextmanager
3462 dsguard = lambda: dirstateguard.dirstateguard(repo, 'import')
3463 dsguard = lambda: dirstateguard.dirstateguard(repo, 'import')
3463 with lock(), tr(), dsguard():
3464 with lock(), tr(), dsguard():
3464 parents = repo[None].parents()
3465 parents = repo[None].parents()
3465 for patchurl in patches:
3466 for patchurl in patches:
3466 if patchurl == '-':
3467 if patchurl == '-':
3467 ui.status(_('applying patch from stdin\n'))
3468 ui.status(_('applying patch from stdin\n'))
3468 patchfile = ui.fin
3469 patchfile = ui.fin
3469 patchurl = 'stdin' # for error message
3470 patchurl = 'stdin' # for error message
3470 else:
3471 else:
3471 patchurl = os.path.join(base, patchurl)
3472 patchurl = os.path.join(base, patchurl)
3472 ui.status(_('applying %s\n') % patchurl)
3473 ui.status(_('applying %s\n') % patchurl)
3473 patchfile = hg.openpath(ui, patchurl)
3474 patchfile = hg.openpath(ui, patchurl)
3474
3475
3475 haspatch = False
3476 haspatch = False
3476 for hunk in patch.split(patchfile):
3477 for hunk in patch.split(patchfile):
3477 with patch.extract(ui, hunk) as patchdata:
3478 with patch.extract(ui, hunk) as patchdata:
3478 msg, node, rej = cmdutil.tryimportone(ui, repo,
3479 msg, node, rej = cmdutil.tryimportone(ui, repo,
3479 patchdata,
3480 patchdata,
3480 parents, opts,
3481 parents, opts,
3481 msgs, hg.clean)
3482 msgs, hg.clean)
3482 if msg:
3483 if msg:
3483 haspatch = True
3484 haspatch = True
3484 ui.note(msg + '\n')
3485 ui.note(msg + '\n')
3485 if update or exact:
3486 if update or exact:
3486 parents = repo[None].parents()
3487 parents = repo[None].parents()
3487 else:
3488 else:
3488 parents = [repo[node]]
3489 parents = [repo[node]]
3489 if rej:
3490 if rej:
3490 ui.write_err(_("patch applied partially\n"))
3491 ui.write_err(_("patch applied partially\n"))
3491 ui.write_err(_("(fix the .rej files and run "
3492 ui.write_err(_("(fix the .rej files and run "
3492 "`hg commit --amend`)\n"))
3493 "`hg commit --amend`)\n"))
3493 ret = 1
3494 ret = 1
3494 break
3495 break
3495
3496
3496 if not haspatch:
3497 if not haspatch:
3497 raise error.Abort(_('%s: no diffs found') % patchurl)
3498 raise error.Abort(_('%s: no diffs found') % patchurl)
3498
3499
3499 if msgs:
3500 if msgs:
3500 repo.savecommitmessage('\n* * *\n'.join(msgs))
3501 repo.savecommitmessage('\n* * *\n'.join(msgs))
3501 return ret
3502 return ret
3502
3503
3503 @command('incoming|in',
3504 @command('incoming|in',
3504 [('f', 'force', None,
3505 [('f', 'force', None,
3505 _('run even if remote repository is unrelated')),
3506 _('run even if remote repository is unrelated')),
3506 ('n', 'newest-first', None, _('show newest record first')),
3507 ('n', 'newest-first', None, _('show newest record first')),
3507 ('', 'bundle', '',
3508 ('', 'bundle', '',
3508 _('file to store the bundles into'), _('FILE')),
3509 _('file to store the bundles into'), _('FILE')),
3509 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3510 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3510 ('B', 'bookmarks', False, _("compare bookmarks")),
3511 ('B', 'bookmarks', False, _("compare bookmarks")),
3511 ('b', 'branch', [],
3512 ('b', 'branch', [],
3512 _('a specific branch you would like to pull'), _('BRANCH')),
3513 _('a specific branch you would like to pull'), _('BRANCH')),
3513 ] + logopts + remoteopts + subrepoopts,
3514 ] + logopts + remoteopts + subrepoopts,
3514 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
3515 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
3515 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT)
3516 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT)
3516 def incoming(ui, repo, source="default", **opts):
3517 def incoming(ui, repo, source="default", **opts):
3517 """show new changesets found in source
3518 """show new changesets found in source
3518
3519
3519 Show new changesets found in the specified path/URL or the default
3520 Show new changesets found in the specified path/URL or the default
3520 pull location. These are the changesets that would have been pulled
3521 pull location. These are the changesets that would have been pulled
3521 by :hg:`pull` at the time you issued this command.
3522 by :hg:`pull` at the time you issued this command.
3522
3523
3523 See pull for valid source format details.
3524 See pull for valid source format details.
3524
3525
3525 .. container:: verbose
3526 .. container:: verbose
3526
3527
3527 With -B/--bookmarks, the result of bookmark comparison between
3528 With -B/--bookmarks, the result of bookmark comparison between
3528 local and remote repositories is displayed. With -v/--verbose,
3529 local and remote repositories is displayed. With -v/--verbose,
3529 status is also displayed for each bookmark like below::
3530 status is also displayed for each bookmark like below::
3530
3531
3531 BM1 01234567890a added
3532 BM1 01234567890a added
3532 BM2 1234567890ab advanced
3533 BM2 1234567890ab advanced
3533 BM3 234567890abc diverged
3534 BM3 234567890abc diverged
3534 BM4 34567890abcd changed
3535 BM4 34567890abcd changed
3535
3536
3536 The action taken locally when pulling depends on the
3537 The action taken locally when pulling depends on the
3537 status of each bookmark:
3538 status of each bookmark:
3538
3539
3539 :``added``: pull will create it
3540 :``added``: pull will create it
3540 :``advanced``: pull will update it
3541 :``advanced``: pull will update it
3541 :``diverged``: pull will create a divergent bookmark
3542 :``diverged``: pull will create a divergent bookmark
3542 :``changed``: result depends on remote changesets
3543 :``changed``: result depends on remote changesets
3543
3544
3544 From the point of view of pulling behavior, bookmark
3545 From the point of view of pulling behavior, bookmark
3545 existing only in the remote repository are treated as ``added``,
3546 existing only in the remote repository are treated as ``added``,
3546 even if it is in fact locally deleted.
3547 even if it is in fact locally deleted.
3547
3548
3548 .. container:: verbose
3549 .. container:: verbose
3549
3550
3550 For remote repository, using --bundle avoids downloading the
3551 For remote repository, using --bundle avoids downloading the
3551 changesets twice if the incoming is followed by a pull.
3552 changesets twice if the incoming is followed by a pull.
3552
3553
3553 Examples:
3554 Examples:
3554
3555
3555 - show incoming changes with patches and full description::
3556 - show incoming changes with patches and full description::
3556
3557
3557 hg incoming -vp
3558 hg incoming -vp
3558
3559
3559 - show incoming changes excluding merges, store a bundle::
3560 - show incoming changes excluding merges, store a bundle::
3560
3561
3561 hg in -vpM --bundle incoming.hg
3562 hg in -vpM --bundle incoming.hg
3562 hg pull incoming.hg
3563 hg pull incoming.hg
3563
3564
3564 - briefly list changes inside a bundle::
3565 - briefly list changes inside a bundle::
3565
3566
3566 hg in changes.hg -T "{desc|firstline}\\n"
3567 hg in changes.hg -T "{desc|firstline}\\n"
3567
3568
3568 Returns 0 if there are incoming changes, 1 otherwise.
3569 Returns 0 if there are incoming changes, 1 otherwise.
3569 """
3570 """
3570 opts = pycompat.byteskwargs(opts)
3571 opts = pycompat.byteskwargs(opts)
3571 if opts.get('graph'):
3572 if opts.get('graph'):
3572 logcmdutil.checkunsupportedgraphflags([], opts)
3573 logcmdutil.checkunsupportedgraphflags([], opts)
3573 def display(other, chlist, displayer):
3574 def display(other, chlist, displayer):
3574 revdag = logcmdutil.graphrevs(other, chlist, opts)
3575 revdag = logcmdutil.graphrevs(other, chlist, opts)
3575 logcmdutil.displaygraph(ui, repo, revdag, displayer,
3576 logcmdutil.displaygraph(ui, repo, revdag, displayer,
3576 graphmod.asciiedges)
3577 graphmod.asciiedges)
3577
3578
3578 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3579 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3579 return 0
3580 return 0
3580
3581
3581 if opts.get('bundle') and opts.get('subrepos'):
3582 if opts.get('bundle') and opts.get('subrepos'):
3582 raise error.Abort(_('cannot combine --bundle and --subrepos'))
3583 raise error.Abort(_('cannot combine --bundle and --subrepos'))
3583
3584
3584 if opts.get('bookmarks'):
3585 if opts.get('bookmarks'):
3585 source, branches = hg.parseurl(ui.expandpath(source),
3586 source, branches = hg.parseurl(ui.expandpath(source),
3586 opts.get('branch'))
3587 opts.get('branch'))
3587 other = hg.peer(repo, opts, source)
3588 other = hg.peer(repo, opts, source)
3588 if 'bookmarks' not in other.listkeys('namespaces'):
3589 if 'bookmarks' not in other.listkeys('namespaces'):
3589 ui.warn(_("remote doesn't support bookmarks\n"))
3590 ui.warn(_("remote doesn't support bookmarks\n"))
3590 return 0
3591 return 0
3591 ui.pager('incoming')
3592 ui.pager('incoming')
3592 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3593 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3593 return bookmarks.incoming(ui, repo, other)
3594 return bookmarks.incoming(ui, repo, other)
3594
3595
3595 repo._subtoppath = ui.expandpath(source)
3596 repo._subtoppath = ui.expandpath(source)
3596 try:
3597 try:
3597 return hg.incoming(ui, repo, source, opts)
3598 return hg.incoming(ui, repo, source, opts)
3598 finally:
3599 finally:
3599 del repo._subtoppath
3600 del repo._subtoppath
3600
3601
3601
3602
3602 @command('init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
3603 @command('init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
3603 helpcategory=command.CATEGORY_REPO_CREATION,
3604 helpcategory=command.CATEGORY_REPO_CREATION,
3604 helpbasic=True, norepo=True)
3605 helpbasic=True, norepo=True)
3605 def init(ui, dest=".", **opts):
3606 def init(ui, dest=".", **opts):
3606 """create a new repository in the given directory
3607 """create a new repository in the given directory
3607
3608
3608 Initialize a new repository in the given directory. If the given
3609 Initialize a new repository in the given directory. If the given
3609 directory does not exist, it will be created.
3610 directory does not exist, it will be created.
3610
3611
3611 If no directory is given, the current directory is used.
3612 If no directory is given, the current directory is used.
3612
3613
3613 It is possible to specify an ``ssh://`` URL as the destination.
3614 It is possible to specify an ``ssh://`` URL as the destination.
3614 See :hg:`help urls` for more information.
3615 See :hg:`help urls` for more information.
3615
3616
3616 Returns 0 on success.
3617 Returns 0 on success.
3617 """
3618 """
3618 opts = pycompat.byteskwargs(opts)
3619 opts = pycompat.byteskwargs(opts)
3619 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3620 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3620
3621
3621 @command('locate',
3622 @command('locate',
3622 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3623 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3623 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3624 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3624 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3625 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3625 ] + walkopts,
3626 ] + walkopts,
3626 _('[OPTION]... [PATTERN]...'),
3627 _('[OPTION]... [PATTERN]...'),
3627 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
3628 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
3628 def locate(ui, repo, *pats, **opts):
3629 def locate(ui, repo, *pats, **opts):
3629 """locate files matching specific patterns (DEPRECATED)
3630 """locate files matching specific patterns (DEPRECATED)
3630
3631
3631 Print files under Mercurial control in the working directory whose
3632 Print files under Mercurial control in the working directory whose
3632 names match the given patterns.
3633 names match the given patterns.
3633
3634
3634 By default, this command searches all directories in the working
3635 By default, this command searches all directories in the working
3635 directory. To search just the current directory and its
3636 directory. To search just the current directory and its
3636 subdirectories, use "--include .".
3637 subdirectories, use "--include .".
3637
3638
3638 If no patterns are given to match, this command prints the names
3639 If no patterns are given to match, this command prints the names
3639 of all files under Mercurial control in the working directory.
3640 of all files under Mercurial control in the working directory.
3640
3641
3641 If you want to feed the output of this command into the "xargs"
3642 If you want to feed the output of this command into the "xargs"
3642 command, use the -0 option to both this command and "xargs". This
3643 command, use the -0 option to both this command and "xargs". This
3643 will avoid the problem of "xargs" treating single filenames that
3644 will avoid the problem of "xargs" treating single filenames that
3644 contain whitespace as multiple filenames.
3645 contain whitespace as multiple filenames.
3645
3646
3646 See :hg:`help files` for a more versatile command.
3647 See :hg:`help files` for a more versatile command.
3647
3648
3648 Returns 0 if a match is found, 1 otherwise.
3649 Returns 0 if a match is found, 1 otherwise.
3649 """
3650 """
3650 opts = pycompat.byteskwargs(opts)
3651 opts = pycompat.byteskwargs(opts)
3651 if opts.get('print0'):
3652 if opts.get('print0'):
3652 end = '\0'
3653 end = '\0'
3653 else:
3654 else:
3654 end = '\n'
3655 end = '\n'
3655 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3656 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3656
3657
3657 ret = 1
3658 ret = 1
3658 m = scmutil.match(ctx, pats, opts, default='relglob',
3659 m = scmutil.match(ctx, pats, opts, default='relglob',
3659 badfn=lambda x, y: False)
3660 badfn=lambda x, y: False)
3660
3661
3661 ui.pager('locate')
3662 ui.pager('locate')
3662 if ctx.rev() is None:
3663 if ctx.rev() is None:
3663 # When run on the working copy, "locate" includes removed files, so
3664 # When run on the working copy, "locate" includes removed files, so
3664 # we get the list of files from the dirstate.
3665 # we get the list of files from the dirstate.
3665 filesgen = sorted(repo.dirstate.matches(m))
3666 filesgen = sorted(repo.dirstate.matches(m))
3666 else:
3667 else:
3667 filesgen = ctx.matches(m)
3668 filesgen = ctx.matches(m)
3668 for abs in filesgen:
3669 for abs in filesgen:
3669 if opts.get('fullpath'):
3670 if opts.get('fullpath'):
3670 ui.write(repo.wjoin(abs), end)
3671 ui.write(repo.wjoin(abs), end)
3671 else:
3672 else:
3672 ui.write(((pats and m.rel(abs)) or abs), end)
3673 ui.write(((pats and m.rel(abs)) or abs), end)
3673 ret = 0
3674 ret = 0
3674
3675
3675 return ret
3676 return ret
3676
3677
3677 @command('log|history',
3678 @command('log|history',
3678 [('f', 'follow', None,
3679 [('f', 'follow', None,
3679 _('follow changeset history, or file history across copies and renames')),
3680 _('follow changeset history, or file history across copies and renames')),
3680 ('', 'follow-first', None,
3681 ('', 'follow-first', None,
3681 _('only follow the first parent of merge changesets (DEPRECATED)')),
3682 _('only follow the first parent of merge changesets (DEPRECATED)')),
3682 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3683 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3683 ('C', 'copies', None, _('show copied files')),
3684 ('C', 'copies', None, _('show copied files')),
3684 ('k', 'keyword', [],
3685 ('k', 'keyword', [],
3685 _('do case-insensitive search for a given text'), _('TEXT')),
3686 _('do case-insensitive search for a given text'), _('TEXT')),
3686 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
3687 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
3687 ('L', 'line-range', [],
3688 ('L', 'line-range', [],
3688 _('follow line range of specified file (EXPERIMENTAL)'),
3689 _('follow line range of specified file (EXPERIMENTAL)'),
3689 _('FILE,RANGE')),
3690 _('FILE,RANGE')),
3690 ('', 'removed', None, _('include revisions where files were removed')),
3691 ('', 'removed', None, _('include revisions where files were removed')),
3691 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3692 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3692 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3693 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3693 ('', 'only-branch', [],
3694 ('', 'only-branch', [],
3694 _('show only changesets within the given named branch (DEPRECATED)'),
3695 _('show only changesets within the given named branch (DEPRECATED)'),
3695 _('BRANCH')),
3696 _('BRANCH')),
3696 ('b', 'branch', [],
3697 ('b', 'branch', [],
3697 _('show changesets within the given named branch'), _('BRANCH')),
3698 _('show changesets within the given named branch'), _('BRANCH')),
3698 ('P', 'prune', [],
3699 ('P', 'prune', [],
3699 _('do not display revision or any of its ancestors'), _('REV')),
3700 _('do not display revision or any of its ancestors'), _('REV')),
3700 ] + logopts + walkopts,
3701 ] + logopts + walkopts,
3701 _('[OPTION]... [FILE]'),
3702 _('[OPTION]... [FILE]'),
3702 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3703 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3703 helpbasic=True, inferrepo=True,
3704 helpbasic=True, inferrepo=True,
3704 intents={INTENT_READONLY})
3705 intents={INTENT_READONLY})
3705 def log(ui, repo, *pats, **opts):
3706 def log(ui, repo, *pats, **opts):
3706 """show revision history of entire repository or files
3707 """show revision history of entire repository or files
3707
3708
3708 Print the revision history of the specified files or the entire
3709 Print the revision history of the specified files or the entire
3709 project.
3710 project.
3710
3711
3711 If no revision range is specified, the default is ``tip:0`` unless
3712 If no revision range is specified, the default is ``tip:0`` unless
3712 --follow is set, in which case the working directory parent is
3713 --follow is set, in which case the working directory parent is
3713 used as the starting revision.
3714 used as the starting revision.
3714
3715
3715 File history is shown without following rename or copy history of
3716 File history is shown without following rename or copy history of
3716 files. Use -f/--follow with a filename to follow history across
3717 files. Use -f/--follow with a filename to follow history across
3717 renames and copies. --follow without a filename will only show
3718 renames and copies. --follow without a filename will only show
3718 ancestors of the starting revision.
3719 ancestors of the starting revision.
3719
3720
3720 By default this command prints revision number and changeset id,
3721 By default this command prints revision number and changeset id,
3721 tags, non-trivial parents, user, date and time, and a summary for
3722 tags, non-trivial parents, user, date and time, and a summary for
3722 each commit. When the -v/--verbose switch is used, the list of
3723 each commit. When the -v/--verbose switch is used, the list of
3723 changed files and full commit message are shown.
3724 changed files and full commit message are shown.
3724
3725
3725 With --graph the revisions are shown as an ASCII art DAG with the most
3726 With --graph the revisions are shown as an ASCII art DAG with the most
3726 recent changeset at the top.
3727 recent changeset at the top.
3727 'o' is a changeset, '@' is a working directory parent, '_' closes a branch,
3728 'o' is a changeset, '@' is a working directory parent, '_' closes a branch,
3728 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
3729 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
3729 changeset from the lines below is a parent of the 'o' merge on the same
3730 changeset from the lines below is a parent of the 'o' merge on the same
3730 line.
3731 line.
3731 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
3732 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
3732 of a '|' indicates one or more revisions in a path are omitted.
3733 of a '|' indicates one or more revisions in a path are omitted.
3733
3734
3734 .. container:: verbose
3735 .. container:: verbose
3735
3736
3736 Use -L/--line-range FILE,M:N options to follow the history of lines
3737 Use -L/--line-range FILE,M:N options to follow the history of lines
3737 from M to N in FILE. With -p/--patch only diff hunks affecting
3738 from M to N in FILE. With -p/--patch only diff hunks affecting
3738 specified line range will be shown. This option requires --follow;
3739 specified line range will be shown. This option requires --follow;
3739 it can be specified multiple times. Currently, this option is not
3740 it can be specified multiple times. Currently, this option is not
3740 compatible with --graph. This option is experimental.
3741 compatible with --graph. This option is experimental.
3741
3742
3742 .. note::
3743 .. note::
3743
3744
3744 :hg:`log --patch` may generate unexpected diff output for merge
3745 :hg:`log --patch` may generate unexpected diff output for merge
3745 changesets, as it will only compare the merge changeset against
3746 changesets, as it will only compare the merge changeset against
3746 its first parent. Also, only files different from BOTH parents
3747 its first parent. Also, only files different from BOTH parents
3747 will appear in files:.
3748 will appear in files:.
3748
3749
3749 .. note::
3750 .. note::
3750
3751
3751 For performance reasons, :hg:`log FILE` may omit duplicate changes
3752 For performance reasons, :hg:`log FILE` may omit duplicate changes
3752 made on branches and will not show removals or mode changes. To
3753 made on branches and will not show removals or mode changes. To
3753 see all such changes, use the --removed switch.
3754 see all such changes, use the --removed switch.
3754
3755
3755 .. container:: verbose
3756 .. container:: verbose
3756
3757
3757 .. note::
3758 .. note::
3758
3759
3759 The history resulting from -L/--line-range options depends on diff
3760 The history resulting from -L/--line-range options depends on diff
3760 options; for instance if white-spaces are ignored, respective changes
3761 options; for instance if white-spaces are ignored, respective changes
3761 with only white-spaces in specified line range will not be listed.
3762 with only white-spaces in specified line range will not be listed.
3762
3763
3763 .. container:: verbose
3764 .. container:: verbose
3764
3765
3765 Some examples:
3766 Some examples:
3766
3767
3767 - changesets with full descriptions and file lists::
3768 - changesets with full descriptions and file lists::
3768
3769
3769 hg log -v
3770 hg log -v
3770
3771
3771 - changesets ancestral to the working directory::
3772 - changesets ancestral to the working directory::
3772
3773
3773 hg log -f
3774 hg log -f
3774
3775
3775 - last 10 commits on the current branch::
3776 - last 10 commits on the current branch::
3776
3777
3777 hg log -l 10 -b .
3778 hg log -l 10 -b .
3778
3779
3779 - changesets showing all modifications of a file, including removals::
3780 - changesets showing all modifications of a file, including removals::
3780
3781
3781 hg log --removed file.c
3782 hg log --removed file.c
3782
3783
3783 - all changesets that touch a directory, with diffs, excluding merges::
3784 - all changesets that touch a directory, with diffs, excluding merges::
3784
3785
3785 hg log -Mp lib/
3786 hg log -Mp lib/
3786
3787
3787 - all revision numbers that match a keyword::
3788 - all revision numbers that match a keyword::
3788
3789
3789 hg log -k bug --template "{rev}\\n"
3790 hg log -k bug --template "{rev}\\n"
3790
3791
3791 - the full hash identifier of the working directory parent::
3792 - the full hash identifier of the working directory parent::
3792
3793
3793 hg log -r . --template "{node}\\n"
3794 hg log -r . --template "{node}\\n"
3794
3795
3795 - list available log templates::
3796 - list available log templates::
3796
3797
3797 hg log -T list
3798 hg log -T list
3798
3799
3799 - check if a given changeset is included in a tagged release::
3800 - check if a given changeset is included in a tagged release::
3800
3801
3801 hg log -r "a21ccf and ancestor(1.9)"
3802 hg log -r "a21ccf and ancestor(1.9)"
3802
3803
3803 - find all changesets by some user in a date range::
3804 - find all changesets by some user in a date range::
3804
3805
3805 hg log -k alice -d "may 2008 to jul 2008"
3806 hg log -k alice -d "may 2008 to jul 2008"
3806
3807
3807 - summary of all changesets after the last tag::
3808 - summary of all changesets after the last tag::
3808
3809
3809 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3810 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3810
3811
3811 - changesets touching lines 13 to 23 for file.c::
3812 - changesets touching lines 13 to 23 for file.c::
3812
3813
3813 hg log -L file.c,13:23
3814 hg log -L file.c,13:23
3814
3815
3815 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
3816 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
3816 main.c with patch::
3817 main.c with patch::
3817
3818
3818 hg log -L file.c,13:23 -L main.c,2:6 -p
3819 hg log -L file.c,13:23 -L main.c,2:6 -p
3819
3820
3820 See :hg:`help dates` for a list of formats valid for -d/--date.
3821 See :hg:`help dates` for a list of formats valid for -d/--date.
3821
3822
3822 See :hg:`help revisions` for more about specifying and ordering
3823 See :hg:`help revisions` for more about specifying and ordering
3823 revisions.
3824 revisions.
3824
3825
3825 See :hg:`help templates` for more about pre-packaged styles and
3826 See :hg:`help templates` for more about pre-packaged styles and
3826 specifying custom templates. The default template used by the log
3827 specifying custom templates. The default template used by the log
3827 command can be customized via the ``ui.logtemplate`` configuration
3828 command can be customized via the ``ui.logtemplate`` configuration
3828 setting.
3829 setting.
3829
3830
3830 Returns 0 on success.
3831 Returns 0 on success.
3831
3832
3832 """
3833 """
3833 opts = pycompat.byteskwargs(opts)
3834 opts = pycompat.byteskwargs(opts)
3834 linerange = opts.get('line_range')
3835 linerange = opts.get('line_range')
3835
3836
3836 if linerange and not opts.get('follow'):
3837 if linerange and not opts.get('follow'):
3837 raise error.Abort(_('--line-range requires --follow'))
3838 raise error.Abort(_('--line-range requires --follow'))
3838
3839
3839 if linerange and pats:
3840 if linerange and pats:
3840 # TODO: take pats as patterns with no line-range filter
3841 # TODO: take pats as patterns with no line-range filter
3841 raise error.Abort(
3842 raise error.Abort(
3842 _('FILE arguments are not compatible with --line-range option')
3843 _('FILE arguments are not compatible with --line-range option')
3843 )
3844 )
3844
3845
3845 repo = scmutil.unhidehashlikerevs(repo, opts.get('rev'), 'nowarn')
3846 repo = scmutil.unhidehashlikerevs(repo, opts.get('rev'), 'nowarn')
3846 revs, differ = logcmdutil.getrevs(repo, pats, opts)
3847 revs, differ = logcmdutil.getrevs(repo, pats, opts)
3847 if linerange:
3848 if linerange:
3848 # TODO: should follow file history from logcmdutil._initialrevs(),
3849 # TODO: should follow file history from logcmdutil._initialrevs(),
3849 # then filter the result by logcmdutil._makerevset() and --limit
3850 # then filter the result by logcmdutil._makerevset() and --limit
3850 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
3851 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
3851
3852
3852 getrenamed = None
3853 getrenamed = None
3853 if opts.get('copies'):
3854 if opts.get('copies'):
3854 endrev = None
3855 endrev = None
3855 if revs:
3856 if revs:
3856 endrev = revs.max() + 1
3857 endrev = revs.max() + 1
3857 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3858 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3858
3859
3859 ui.pager('log')
3860 ui.pager('log')
3860 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, differ,
3861 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, differ,
3861 buffered=True)
3862 buffered=True)
3862 if opts.get('graph'):
3863 if opts.get('graph'):
3863 displayfn = logcmdutil.displaygraphrevs
3864 displayfn = logcmdutil.displaygraphrevs
3864 else:
3865 else:
3865 displayfn = logcmdutil.displayrevs
3866 displayfn = logcmdutil.displayrevs
3866 displayfn(ui, repo, revs, displayer, getrenamed)
3867 displayfn(ui, repo, revs, displayer, getrenamed)
3867
3868
3868 @command('manifest',
3869 @command('manifest',
3869 [('r', 'rev', '', _('revision to display'), _('REV')),
3870 [('r', 'rev', '', _('revision to display'), _('REV')),
3870 ('', 'all', False, _("list files from all revisions"))]
3871 ('', 'all', False, _("list files from all revisions"))]
3871 + formatteropts,
3872 + formatteropts,
3872 _('[-r REV]'),
3873 _('[-r REV]'),
3873 helpcategory=command.CATEGORY_MAINTENANCE,
3874 helpcategory=command.CATEGORY_MAINTENANCE,
3874 intents={INTENT_READONLY})
3875 intents={INTENT_READONLY})
3875 def manifest(ui, repo, node=None, rev=None, **opts):
3876 def manifest(ui, repo, node=None, rev=None, **opts):
3876 """output the current or given revision of the project manifest
3877 """output the current or given revision of the project manifest
3877
3878
3878 Print a list of version controlled files for the given revision.
3879 Print a list of version controlled files for the given revision.
3879 If no revision is given, the first parent of the working directory
3880 If no revision is given, the first parent of the working directory
3880 is used, or the null revision if no revision is checked out.
3881 is used, or the null revision if no revision is checked out.
3881
3882
3882 With -v, print file permissions, symlink and executable bits.
3883 With -v, print file permissions, symlink and executable bits.
3883 With --debug, print file revision hashes.
3884 With --debug, print file revision hashes.
3884
3885
3885 If option --all is specified, the list of all files from all revisions
3886 If option --all is specified, the list of all files from all revisions
3886 is printed. This includes deleted and renamed files.
3887 is printed. This includes deleted and renamed files.
3887
3888
3888 Returns 0 on success.
3889 Returns 0 on success.
3889 """
3890 """
3890 opts = pycompat.byteskwargs(opts)
3891 opts = pycompat.byteskwargs(opts)
3891 fm = ui.formatter('manifest', opts)
3892 fm = ui.formatter('manifest', opts)
3892
3893
3893 if opts.get('all'):
3894 if opts.get('all'):
3894 if rev or node:
3895 if rev or node:
3895 raise error.Abort(_("can't specify a revision with --all"))
3896 raise error.Abort(_("can't specify a revision with --all"))
3896
3897
3897 res = set()
3898 res = set()
3898 for rev in repo:
3899 for rev in repo:
3899 ctx = repo[rev]
3900 ctx = repo[rev]
3900 res |= set(ctx.files())
3901 res |= set(ctx.files())
3901
3902
3902 ui.pager('manifest')
3903 ui.pager('manifest')
3903 for f in sorted(res):
3904 for f in sorted(res):
3904 fm.startitem()
3905 fm.startitem()
3905 fm.write("path", '%s\n', f)
3906 fm.write("path", '%s\n', f)
3906 fm.end()
3907 fm.end()
3907 return
3908 return
3908
3909
3909 if rev and node:
3910 if rev and node:
3910 raise error.Abort(_("please specify just one revision"))
3911 raise error.Abort(_("please specify just one revision"))
3911
3912
3912 if not node:
3913 if not node:
3913 node = rev
3914 node = rev
3914
3915
3915 char = {'l': '@', 'x': '*', '': '', 't': 'd'}
3916 char = {'l': '@', 'x': '*', '': '', 't': 'd'}
3916 mode = {'l': '644', 'x': '755', '': '644', 't': '755'}
3917 mode = {'l': '644', 'x': '755', '': '644', 't': '755'}
3917 if node:
3918 if node:
3918 repo = scmutil.unhidehashlikerevs(repo, [node], 'nowarn')
3919 repo = scmutil.unhidehashlikerevs(repo, [node], 'nowarn')
3919 ctx = scmutil.revsingle(repo, node)
3920 ctx = scmutil.revsingle(repo, node)
3920 mf = ctx.manifest()
3921 mf = ctx.manifest()
3921 ui.pager('manifest')
3922 ui.pager('manifest')
3922 for f in ctx:
3923 for f in ctx:
3923 fm.startitem()
3924 fm.startitem()
3924 fm.context(ctx=ctx)
3925 fm.context(ctx=ctx)
3925 fl = ctx[f].flags()
3926 fl = ctx[f].flags()
3926 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3927 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3927 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
3928 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
3928 fm.write('path', '%s\n', f)
3929 fm.write('path', '%s\n', f)
3929 fm.end()
3930 fm.end()
3930
3931
3931 @command('merge',
3932 @command('merge',
3932 [('f', 'force', None,
3933 [('f', 'force', None,
3933 _('force a merge including outstanding changes (DEPRECATED)')),
3934 _('force a merge including outstanding changes (DEPRECATED)')),
3934 ('r', 'rev', '', _('revision to merge'), _('REV')),
3935 ('r', 'rev', '', _('revision to merge'), _('REV')),
3935 ('P', 'preview', None,
3936 ('P', 'preview', None,
3936 _('review revisions to merge (no merge is performed)')),
3937 _('review revisions to merge (no merge is performed)')),
3937 ('', 'abort', None, _('abort the ongoing merge')),
3938 ('', 'abort', None, _('abort the ongoing merge')),
3938 ] + mergetoolopts,
3939 ] + mergetoolopts,
3939 _('[-P] [[-r] REV]'),
3940 _('[-P] [[-r] REV]'),
3940 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT, helpbasic=True)
3941 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT, helpbasic=True)
3941 def merge(ui, repo, node=None, **opts):
3942 def merge(ui, repo, node=None, **opts):
3942 """merge another revision into working directory
3943 """merge another revision into working directory
3943
3944
3944 The current working directory is updated with all changes made in
3945 The current working directory is updated with all changes made in
3945 the requested revision since the last common predecessor revision.
3946 the requested revision since the last common predecessor revision.
3946
3947
3947 Files that changed between either parent are marked as changed for
3948 Files that changed between either parent are marked as changed for
3948 the next commit and a commit must be performed before any further
3949 the next commit and a commit must be performed before any further
3949 updates to the repository are allowed. The next commit will have
3950 updates to the repository are allowed. The next commit will have
3950 two parents.
3951 two parents.
3951
3952
3952 ``--tool`` can be used to specify the merge tool used for file
3953 ``--tool`` can be used to specify the merge tool used for file
3953 merges. It overrides the HGMERGE environment variable and your
3954 merges. It overrides the HGMERGE environment variable and your
3954 configuration files. See :hg:`help merge-tools` for options.
3955 configuration files. See :hg:`help merge-tools` for options.
3955
3956
3956 If no revision is specified, the working directory's parent is a
3957 If no revision is specified, the working directory's parent is a
3957 head revision, and the current branch contains exactly one other
3958 head revision, and the current branch contains exactly one other
3958 head, the other head is merged with by default. Otherwise, an
3959 head, the other head is merged with by default. Otherwise, an
3959 explicit revision with which to merge with must be provided.
3960 explicit revision with which to merge with must be provided.
3960
3961
3961 See :hg:`help resolve` for information on handling file conflicts.
3962 See :hg:`help resolve` for information on handling file conflicts.
3962
3963
3963 To undo an uncommitted merge, use :hg:`merge --abort` which
3964 To undo an uncommitted merge, use :hg:`merge --abort` which
3964 will check out a clean copy of the original merge parent, losing
3965 will check out a clean copy of the original merge parent, losing
3965 all changes.
3966 all changes.
3966
3967
3967 Returns 0 on success, 1 if there are unresolved files.
3968 Returns 0 on success, 1 if there are unresolved files.
3968 """
3969 """
3969
3970
3970 opts = pycompat.byteskwargs(opts)
3971 opts = pycompat.byteskwargs(opts)
3971 abort = opts.get('abort')
3972 abort = opts.get('abort')
3972 if abort and repo.dirstate.p2() == nullid:
3973 if abort and repo.dirstate.p2() == nullid:
3973 cmdutil.wrongtooltocontinue(repo, _('merge'))
3974 cmdutil.wrongtooltocontinue(repo, _('merge'))
3974 if abort:
3975 if abort:
3975 if node:
3976 if node:
3976 raise error.Abort(_("cannot specify a node with --abort"))
3977 raise error.Abort(_("cannot specify a node with --abort"))
3977 if opts.get('rev'):
3978 if opts.get('rev'):
3978 raise error.Abort(_("cannot specify both --rev and --abort"))
3979 raise error.Abort(_("cannot specify both --rev and --abort"))
3979 if opts.get('preview'):
3980 if opts.get('preview'):
3980 raise error.Abort(_("cannot specify --preview with --abort"))
3981 raise error.Abort(_("cannot specify --preview with --abort"))
3981 if opts.get('rev') and node:
3982 if opts.get('rev') and node:
3982 raise error.Abort(_("please specify just one revision"))
3983 raise error.Abort(_("please specify just one revision"))
3983 if not node:
3984 if not node:
3984 node = opts.get('rev')
3985 node = opts.get('rev')
3985
3986
3986 if node:
3987 if node:
3987 node = scmutil.revsingle(repo, node).node()
3988 node = scmutil.revsingle(repo, node).node()
3988
3989
3989 if not node and not abort:
3990 if not node and not abort:
3990 node = repo[destutil.destmerge(repo)].node()
3991 node = repo[destutil.destmerge(repo)].node()
3991
3992
3992 if opts.get('preview'):
3993 if opts.get('preview'):
3993 # find nodes that are ancestors of p2 but not of p1
3994 # find nodes that are ancestors of p2 but not of p1
3994 p1 = repo.lookup('.')
3995 p1 = repo.lookup('.')
3995 p2 = node
3996 p2 = node
3996 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
3997 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
3997
3998
3998 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3999 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3999 for node in nodes:
4000 for node in nodes:
4000 displayer.show(repo[node])
4001 displayer.show(repo[node])
4001 displayer.close()
4002 displayer.close()
4002 return 0
4003 return 0
4003
4004
4004 # ui.forcemerge is an internal variable, do not document
4005 # ui.forcemerge is an internal variable, do not document
4005 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
4006 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
4006 with ui.configoverride(overrides, 'merge'):
4007 with ui.configoverride(overrides, 'merge'):
4007 force = opts.get('force')
4008 force = opts.get('force')
4008 labels = ['working copy', 'merge rev']
4009 labels = ['working copy', 'merge rev']
4009 return hg.merge(repo, node, force=force, mergeforce=force,
4010 return hg.merge(repo, node, force=force, mergeforce=force,
4010 labels=labels, abort=abort)
4011 labels=labels, abort=abort)
4011
4012
4012 @command('outgoing|out',
4013 @command('outgoing|out',
4013 [('f', 'force', None, _('run even when the destination is unrelated')),
4014 [('f', 'force', None, _('run even when the destination is unrelated')),
4014 ('r', 'rev', [],
4015 ('r', 'rev', [],
4015 _('a changeset intended to be included in the destination'), _('REV')),
4016 _('a changeset intended to be included in the destination'), _('REV')),
4016 ('n', 'newest-first', None, _('show newest record first')),
4017 ('n', 'newest-first', None, _('show newest record first')),
4017 ('B', 'bookmarks', False, _('compare bookmarks')),
4018 ('B', 'bookmarks', False, _('compare bookmarks')),
4018 ('b', 'branch', [], _('a specific branch you would like to push'),
4019 ('b', 'branch', [], _('a specific branch you would like to push'),
4019 _('BRANCH')),
4020 _('BRANCH')),
4020 ] + logopts + remoteopts + subrepoopts,
4021 ] + logopts + remoteopts + subrepoopts,
4021 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'),
4022 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'),
4022 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT)
4023 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT)
4023 def outgoing(ui, repo, dest=None, **opts):
4024 def outgoing(ui, repo, dest=None, **opts):
4024 """show changesets not found in the destination
4025 """show changesets not found in the destination
4025
4026
4026 Show changesets not found in the specified destination repository
4027 Show changesets not found in the specified destination repository
4027 or the default push location. These are the changesets that would
4028 or the default push location. These are the changesets that would
4028 be pushed if a push was requested.
4029 be pushed if a push was requested.
4029
4030
4030 See pull for details of valid destination formats.
4031 See pull for details of valid destination formats.
4031
4032
4032 .. container:: verbose
4033 .. container:: verbose
4033
4034
4034 With -B/--bookmarks, the result of bookmark comparison between
4035 With -B/--bookmarks, the result of bookmark comparison between
4035 local and remote repositories is displayed. With -v/--verbose,
4036 local and remote repositories is displayed. With -v/--verbose,
4036 status is also displayed for each bookmark like below::
4037 status is also displayed for each bookmark like below::
4037
4038
4038 BM1 01234567890a added
4039 BM1 01234567890a added
4039 BM2 deleted
4040 BM2 deleted
4040 BM3 234567890abc advanced
4041 BM3 234567890abc advanced
4041 BM4 34567890abcd diverged
4042 BM4 34567890abcd diverged
4042 BM5 4567890abcde changed
4043 BM5 4567890abcde changed
4043
4044
4044 The action taken when pushing depends on the
4045 The action taken when pushing depends on the
4045 status of each bookmark:
4046 status of each bookmark:
4046
4047
4047 :``added``: push with ``-B`` will create it
4048 :``added``: push with ``-B`` will create it
4048 :``deleted``: push with ``-B`` will delete it
4049 :``deleted``: push with ``-B`` will delete it
4049 :``advanced``: push will update it
4050 :``advanced``: push will update it
4050 :``diverged``: push with ``-B`` will update it
4051 :``diverged``: push with ``-B`` will update it
4051 :``changed``: push with ``-B`` will update it
4052 :``changed``: push with ``-B`` will update it
4052
4053
4053 From the point of view of pushing behavior, bookmarks
4054 From the point of view of pushing behavior, bookmarks
4054 existing only in the remote repository are treated as
4055 existing only in the remote repository are treated as
4055 ``deleted``, even if it is in fact added remotely.
4056 ``deleted``, even if it is in fact added remotely.
4056
4057
4057 Returns 0 if there are outgoing changes, 1 otherwise.
4058 Returns 0 if there are outgoing changes, 1 otherwise.
4058 """
4059 """
4059 # hg._outgoing() needs to re-resolve the path in order to handle #branch
4060 # hg._outgoing() needs to re-resolve the path in order to handle #branch
4060 # style URLs, so don't overwrite dest.
4061 # style URLs, so don't overwrite dest.
4061 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4062 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4062 if not path:
4063 if not path:
4063 raise error.Abort(_('default repository not configured!'),
4064 raise error.Abort(_('default repository not configured!'),
4064 hint=_("see 'hg help config.paths'"))
4065 hint=_("see 'hg help config.paths'"))
4065
4066
4066 opts = pycompat.byteskwargs(opts)
4067 opts = pycompat.byteskwargs(opts)
4067 if opts.get('graph'):
4068 if opts.get('graph'):
4068 logcmdutil.checkunsupportedgraphflags([], opts)
4069 logcmdutil.checkunsupportedgraphflags([], opts)
4069 o, other = hg._outgoing(ui, repo, dest, opts)
4070 o, other = hg._outgoing(ui, repo, dest, opts)
4070 if not o:
4071 if not o:
4071 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4072 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4072 return
4073 return
4073
4074
4074 revdag = logcmdutil.graphrevs(repo, o, opts)
4075 revdag = logcmdutil.graphrevs(repo, o, opts)
4075 ui.pager('outgoing')
4076 ui.pager('outgoing')
4076 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
4077 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
4077 logcmdutil.displaygraph(ui, repo, revdag, displayer,
4078 logcmdutil.displaygraph(ui, repo, revdag, displayer,
4078 graphmod.asciiedges)
4079 graphmod.asciiedges)
4079 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4080 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4080 return 0
4081 return 0
4081
4082
4082 if opts.get('bookmarks'):
4083 if opts.get('bookmarks'):
4083 dest = path.pushloc or path.loc
4084 dest = path.pushloc or path.loc
4084 other = hg.peer(repo, opts, dest)
4085 other = hg.peer(repo, opts, dest)
4085 if 'bookmarks' not in other.listkeys('namespaces'):
4086 if 'bookmarks' not in other.listkeys('namespaces'):
4086 ui.warn(_("remote doesn't support bookmarks\n"))
4087 ui.warn(_("remote doesn't support bookmarks\n"))
4087 return 0
4088 return 0
4088 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4089 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4089 ui.pager('outgoing')
4090 ui.pager('outgoing')
4090 return bookmarks.outgoing(ui, repo, other)
4091 return bookmarks.outgoing(ui, repo, other)
4091
4092
4092 repo._subtoppath = path.pushloc or path.loc
4093 repo._subtoppath = path.pushloc or path.loc
4093 try:
4094 try:
4094 return hg.outgoing(ui, repo, dest, opts)
4095 return hg.outgoing(ui, repo, dest, opts)
4095 finally:
4096 finally:
4096 del repo._subtoppath
4097 del repo._subtoppath
4097
4098
4098 @command('parents',
4099 @command('parents',
4099 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4100 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4100 ] + templateopts,
4101 ] + templateopts,
4101 _('[-r REV] [FILE]'),
4102 _('[-r REV] [FILE]'),
4102 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4103 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4103 inferrepo=True)
4104 inferrepo=True)
4104 def parents(ui, repo, file_=None, **opts):
4105 def parents(ui, repo, file_=None, **opts):
4105 """show the parents of the working directory or revision (DEPRECATED)
4106 """show the parents of the working directory or revision (DEPRECATED)
4106
4107
4107 Print the working directory's parent revisions. If a revision is
4108 Print the working directory's parent revisions. If a revision is
4108 given via -r/--rev, the parent of that revision will be printed.
4109 given via -r/--rev, the parent of that revision will be printed.
4109 If a file argument is given, the revision in which the file was
4110 If a file argument is given, the revision in which the file was
4110 last changed (before the working directory revision or the
4111 last changed (before the working directory revision or the
4111 argument to --rev if given) is printed.
4112 argument to --rev if given) is printed.
4112
4113
4113 This command is equivalent to::
4114 This command is equivalent to::
4114
4115
4115 hg log -r "p1()+p2()" or
4116 hg log -r "p1()+p2()" or
4116 hg log -r "p1(REV)+p2(REV)" or
4117 hg log -r "p1(REV)+p2(REV)" or
4117 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
4118 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
4118 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
4119 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
4119
4120
4120 See :hg:`summary` and :hg:`help revsets` for related information.
4121 See :hg:`summary` and :hg:`help revsets` for related information.
4121
4122
4122 Returns 0 on success.
4123 Returns 0 on success.
4123 """
4124 """
4124
4125
4125 opts = pycompat.byteskwargs(opts)
4126 opts = pycompat.byteskwargs(opts)
4126 rev = opts.get('rev')
4127 rev = opts.get('rev')
4127 if rev:
4128 if rev:
4128 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
4129 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
4129 ctx = scmutil.revsingle(repo, rev, None)
4130 ctx = scmutil.revsingle(repo, rev, None)
4130
4131
4131 if file_:
4132 if file_:
4132 m = scmutil.match(ctx, (file_,), opts)
4133 m = scmutil.match(ctx, (file_,), opts)
4133 if m.anypats() or len(m.files()) != 1:
4134 if m.anypats() or len(m.files()) != 1:
4134 raise error.Abort(_('can only specify an explicit filename'))
4135 raise error.Abort(_('can only specify an explicit filename'))
4135 file_ = m.files()[0]
4136 file_ = m.files()[0]
4136 filenodes = []
4137 filenodes = []
4137 for cp in ctx.parents():
4138 for cp in ctx.parents():
4138 if not cp:
4139 if not cp:
4139 continue
4140 continue
4140 try:
4141 try:
4141 filenodes.append(cp.filenode(file_))
4142 filenodes.append(cp.filenode(file_))
4142 except error.LookupError:
4143 except error.LookupError:
4143 pass
4144 pass
4144 if not filenodes:
4145 if not filenodes:
4145 raise error.Abort(_("'%s' not found in manifest!") % file_)
4146 raise error.Abort(_("'%s' not found in manifest!") % file_)
4146 p = []
4147 p = []
4147 for fn in filenodes:
4148 for fn in filenodes:
4148 fctx = repo.filectx(file_, fileid=fn)
4149 fctx = repo.filectx(file_, fileid=fn)
4149 p.append(fctx.node())
4150 p.append(fctx.node())
4150 else:
4151 else:
4151 p = [cp.node() for cp in ctx.parents()]
4152 p = [cp.node() for cp in ctx.parents()]
4152
4153
4153 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4154 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4154 for n in p:
4155 for n in p:
4155 if n != nullid:
4156 if n != nullid:
4156 displayer.show(repo[n])
4157 displayer.show(repo[n])
4157 displayer.close()
4158 displayer.close()
4158
4159
4159 @command('paths', formatteropts, _('[NAME]'),
4160 @command('paths', formatteropts, _('[NAME]'),
4160 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4161 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4161 optionalrepo=True, intents={INTENT_READONLY})
4162 optionalrepo=True, intents={INTENT_READONLY})
4162 def paths(ui, repo, search=None, **opts):
4163 def paths(ui, repo, search=None, **opts):
4163 """show aliases for remote repositories
4164 """show aliases for remote repositories
4164
4165
4165 Show definition of symbolic path name NAME. If no name is given,
4166 Show definition of symbolic path name NAME. If no name is given,
4166 show definition of all available names.
4167 show definition of all available names.
4167
4168
4168 Option -q/--quiet suppresses all output when searching for NAME
4169 Option -q/--quiet suppresses all output when searching for NAME
4169 and shows only the path names when listing all definitions.
4170 and shows only the path names when listing all definitions.
4170
4171
4171 Path names are defined in the [paths] section of your
4172 Path names are defined in the [paths] section of your
4172 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4173 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4173 repository, ``.hg/hgrc`` is used, too.
4174 repository, ``.hg/hgrc`` is used, too.
4174
4175
4175 The path names ``default`` and ``default-push`` have a special
4176 The path names ``default`` and ``default-push`` have a special
4176 meaning. When performing a push or pull operation, they are used
4177 meaning. When performing a push or pull operation, they are used
4177 as fallbacks if no location is specified on the command-line.
4178 as fallbacks if no location is specified on the command-line.
4178 When ``default-push`` is set, it will be used for push and
4179 When ``default-push`` is set, it will be used for push and
4179 ``default`` will be used for pull; otherwise ``default`` is used
4180 ``default`` will be used for pull; otherwise ``default`` is used
4180 as the fallback for both. When cloning a repository, the clone
4181 as the fallback for both. When cloning a repository, the clone
4181 source is written as ``default`` in ``.hg/hgrc``.
4182 source is written as ``default`` in ``.hg/hgrc``.
4182
4183
4183 .. note::
4184 .. note::
4184
4185
4185 ``default`` and ``default-push`` apply to all inbound (e.g.
4186 ``default`` and ``default-push`` apply to all inbound (e.g.
4186 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
4187 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
4187 and :hg:`bundle`) operations.
4188 and :hg:`bundle`) operations.
4188
4189
4189 See :hg:`help urls` for more information.
4190 See :hg:`help urls` for more information.
4190
4191
4191 .. container:: verbose
4192 .. container:: verbose
4192
4193
4193 Template:
4194 Template:
4194
4195
4195 The following keywords are supported. See also :hg:`help templates`.
4196 The following keywords are supported. See also :hg:`help templates`.
4196
4197
4197 :name: String. Symbolic name of the path alias.
4198 :name: String. Symbolic name of the path alias.
4198 :pushurl: String. URL for push operations.
4199 :pushurl: String. URL for push operations.
4199 :url: String. URL or directory path for the other operations.
4200 :url: String. URL or directory path for the other operations.
4200
4201
4201 Returns 0 on success.
4202 Returns 0 on success.
4202 """
4203 """
4203
4204
4204 opts = pycompat.byteskwargs(opts)
4205 opts = pycompat.byteskwargs(opts)
4205 ui.pager('paths')
4206 ui.pager('paths')
4206 if search:
4207 if search:
4207 pathitems = [(name, path) for name, path in ui.paths.iteritems()
4208 pathitems = [(name, path) for name, path in ui.paths.iteritems()
4208 if name == search]
4209 if name == search]
4209 else:
4210 else:
4210 pathitems = sorted(ui.paths.iteritems())
4211 pathitems = sorted(ui.paths.iteritems())
4211
4212
4212 fm = ui.formatter('paths', opts)
4213 fm = ui.formatter('paths', opts)
4213 if fm.isplain():
4214 if fm.isplain():
4214 hidepassword = util.hidepassword
4215 hidepassword = util.hidepassword
4215 else:
4216 else:
4216 hidepassword = bytes
4217 hidepassword = bytes
4217 if ui.quiet:
4218 if ui.quiet:
4218 namefmt = '%s\n'
4219 namefmt = '%s\n'
4219 else:
4220 else:
4220 namefmt = '%s = '
4221 namefmt = '%s = '
4221 showsubopts = not search and not ui.quiet
4222 showsubopts = not search and not ui.quiet
4222
4223
4223 for name, path in pathitems:
4224 for name, path in pathitems:
4224 fm.startitem()
4225 fm.startitem()
4225 fm.condwrite(not search, 'name', namefmt, name)
4226 fm.condwrite(not search, 'name', namefmt, name)
4226 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
4227 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
4227 for subopt, value in sorted(path.suboptions.items()):
4228 for subopt, value in sorted(path.suboptions.items()):
4228 assert subopt not in ('name', 'url')
4229 assert subopt not in ('name', 'url')
4229 if showsubopts:
4230 if showsubopts:
4230 fm.plain('%s:%s = ' % (name, subopt))
4231 fm.plain('%s:%s = ' % (name, subopt))
4231 fm.condwrite(showsubopts, subopt, '%s\n', value)
4232 fm.condwrite(showsubopts, subopt, '%s\n', value)
4232
4233
4233 fm.end()
4234 fm.end()
4234
4235
4235 if search and not pathitems:
4236 if search and not pathitems:
4236 if not ui.quiet:
4237 if not ui.quiet:
4237 ui.warn(_("not found!\n"))
4238 ui.warn(_("not found!\n"))
4238 return 1
4239 return 1
4239 else:
4240 else:
4240 return 0
4241 return 0
4241
4242
4242 @command('phase',
4243 @command('phase',
4243 [('p', 'public', False, _('set changeset phase to public')),
4244 [('p', 'public', False, _('set changeset phase to public')),
4244 ('d', 'draft', False, _('set changeset phase to draft')),
4245 ('d', 'draft', False, _('set changeset phase to draft')),
4245 ('s', 'secret', False, _('set changeset phase to secret')),
4246 ('s', 'secret', False, _('set changeset phase to secret')),
4246 ('f', 'force', False, _('allow to move boundary backward')),
4247 ('f', 'force', False, _('allow to move boundary backward')),
4247 ('r', 'rev', [], _('target revision'), _('REV')),
4248 ('r', 'rev', [], _('target revision'), _('REV')),
4248 ],
4249 ],
4249 _('[-p|-d|-s] [-f] [-r] [REV...]'),
4250 _('[-p|-d|-s] [-f] [-r] [REV...]'),
4250 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
4251 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
4251 def phase(ui, repo, *revs, **opts):
4252 def phase(ui, repo, *revs, **opts):
4252 """set or show the current phase name
4253 """set or show the current phase name
4253
4254
4254 With no argument, show the phase name of the current revision(s).
4255 With no argument, show the phase name of the current revision(s).
4255
4256
4256 With one of -p/--public, -d/--draft or -s/--secret, change the
4257 With one of -p/--public, -d/--draft or -s/--secret, change the
4257 phase value of the specified revisions.
4258 phase value of the specified revisions.
4258
4259
4259 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
4260 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
4260 lower phase to a higher phase. Phases are ordered as follows::
4261 lower phase to a higher phase. Phases are ordered as follows::
4261
4262
4262 public < draft < secret
4263 public < draft < secret
4263
4264
4264 Returns 0 on success, 1 if some phases could not be changed.
4265 Returns 0 on success, 1 if some phases could not be changed.
4265
4266
4266 (For more information about the phases concept, see :hg:`help phases`.)
4267 (For more information about the phases concept, see :hg:`help phases`.)
4267 """
4268 """
4268 opts = pycompat.byteskwargs(opts)
4269 opts = pycompat.byteskwargs(opts)
4269 # search for a unique phase argument
4270 # search for a unique phase argument
4270 targetphase = None
4271 targetphase = None
4271 for idx, name in enumerate(phases.cmdphasenames):
4272 for idx, name in enumerate(phases.cmdphasenames):
4272 if opts[name]:
4273 if opts[name]:
4273 if targetphase is not None:
4274 if targetphase is not None:
4274 raise error.Abort(_('only one phase can be specified'))
4275 raise error.Abort(_('only one phase can be specified'))
4275 targetphase = idx
4276 targetphase = idx
4276
4277
4277 # look for specified revision
4278 # look for specified revision
4278 revs = list(revs)
4279 revs = list(revs)
4279 revs.extend(opts['rev'])
4280 revs.extend(opts['rev'])
4280 if not revs:
4281 if not revs:
4281 # display both parents as the second parent phase can influence
4282 # display both parents as the second parent phase can influence
4282 # the phase of a merge commit
4283 # the phase of a merge commit
4283 revs = [c.rev() for c in repo[None].parents()]
4284 revs = [c.rev() for c in repo[None].parents()]
4284
4285
4285 revs = scmutil.revrange(repo, revs)
4286 revs = scmutil.revrange(repo, revs)
4286
4287
4287 ret = 0
4288 ret = 0
4288 if targetphase is None:
4289 if targetphase is None:
4289 # display
4290 # display
4290 for r in revs:
4291 for r in revs:
4291 ctx = repo[r]
4292 ctx = repo[r]
4292 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4293 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4293 else:
4294 else:
4294 with repo.lock(), repo.transaction("phase") as tr:
4295 with repo.lock(), repo.transaction("phase") as tr:
4295 # set phase
4296 # set phase
4296 if not revs:
4297 if not revs:
4297 raise error.Abort(_('empty revision set'))
4298 raise error.Abort(_('empty revision set'))
4298 nodes = [repo[r].node() for r in revs]
4299 nodes = [repo[r].node() for r in revs]
4299 # moving revision from public to draft may hide them
4300 # moving revision from public to draft may hide them
4300 # We have to check result on an unfiltered repository
4301 # We have to check result on an unfiltered repository
4301 unfi = repo.unfiltered()
4302 unfi = repo.unfiltered()
4302 getphase = unfi._phasecache.phase
4303 getphase = unfi._phasecache.phase
4303 olddata = [getphase(unfi, r) for r in unfi]
4304 olddata = [getphase(unfi, r) for r in unfi]
4304 phases.advanceboundary(repo, tr, targetphase, nodes)
4305 phases.advanceboundary(repo, tr, targetphase, nodes)
4305 if opts['force']:
4306 if opts['force']:
4306 phases.retractboundary(repo, tr, targetphase, nodes)
4307 phases.retractboundary(repo, tr, targetphase, nodes)
4307 getphase = unfi._phasecache.phase
4308 getphase = unfi._phasecache.phase
4308 newdata = [getphase(unfi, r) for r in unfi]
4309 newdata = [getphase(unfi, r) for r in unfi]
4309 changes = sum(newdata[r] != olddata[r] for r in unfi)
4310 changes = sum(newdata[r] != olddata[r] for r in unfi)
4310 cl = unfi.changelog
4311 cl = unfi.changelog
4311 rejected = [n for n in nodes
4312 rejected = [n for n in nodes
4312 if newdata[cl.rev(n)] < targetphase]
4313 if newdata[cl.rev(n)] < targetphase]
4313 if rejected:
4314 if rejected:
4314 ui.warn(_('cannot move %i changesets to a higher '
4315 ui.warn(_('cannot move %i changesets to a higher '
4315 'phase, use --force\n') % len(rejected))
4316 'phase, use --force\n') % len(rejected))
4316 ret = 1
4317 ret = 1
4317 if changes:
4318 if changes:
4318 msg = _('phase changed for %i changesets\n') % changes
4319 msg = _('phase changed for %i changesets\n') % changes
4319 if ret:
4320 if ret:
4320 ui.status(msg)
4321 ui.status(msg)
4321 else:
4322 else:
4322 ui.note(msg)
4323 ui.note(msg)
4323 else:
4324 else:
4324 ui.warn(_('no phases changed\n'))
4325 ui.warn(_('no phases changed\n'))
4325 return ret
4326 return ret
4326
4327
4327 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
4328 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
4328 """Run after a changegroup has been added via pull/unbundle
4329 """Run after a changegroup has been added via pull/unbundle
4329
4330
4330 This takes arguments below:
4331 This takes arguments below:
4331
4332
4332 :modheads: change of heads by pull/unbundle
4333 :modheads: change of heads by pull/unbundle
4333 :optupdate: updating working directory is needed or not
4334 :optupdate: updating working directory is needed or not
4334 :checkout: update destination revision (or None to default destination)
4335 :checkout: update destination revision (or None to default destination)
4335 :brev: a name, which might be a bookmark to be activated after updating
4336 :brev: a name, which might be a bookmark to be activated after updating
4336 """
4337 """
4337 if modheads == 0:
4338 if modheads == 0:
4338 return
4339 return
4339 if optupdate:
4340 if optupdate:
4340 try:
4341 try:
4341 return hg.updatetotally(ui, repo, checkout, brev)
4342 return hg.updatetotally(ui, repo, checkout, brev)
4342 except error.UpdateAbort as inst:
4343 except error.UpdateAbort as inst:
4343 msg = _("not updating: %s") % stringutil.forcebytestr(inst)
4344 msg = _("not updating: %s") % stringutil.forcebytestr(inst)
4344 hint = inst.hint
4345 hint = inst.hint
4345 raise error.UpdateAbort(msg, hint=hint)
4346 raise error.UpdateAbort(msg, hint=hint)
4346 if modheads > 1:
4347 if modheads > 1:
4347 currentbranchheads = len(repo.branchheads())
4348 currentbranchheads = len(repo.branchheads())
4348 if currentbranchheads == modheads:
4349 if currentbranchheads == modheads:
4349 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4350 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4350 elif currentbranchheads > 1:
4351 elif currentbranchheads > 1:
4351 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4352 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4352 "merge)\n"))
4353 "merge)\n"))
4353 else:
4354 else:
4354 ui.status(_("(run 'hg heads' to see heads)\n"))
4355 ui.status(_("(run 'hg heads' to see heads)\n"))
4355 elif not ui.configbool('commands', 'update.requiredest'):
4356 elif not ui.configbool('commands', 'update.requiredest'):
4356 ui.status(_("(run 'hg update' to get a working copy)\n"))
4357 ui.status(_("(run 'hg update' to get a working copy)\n"))
4357
4358
4358 @command('pull',
4359 @command('pull',
4359 [('u', 'update', None,
4360 [('u', 'update', None,
4360 _('update to new branch head if new descendants were pulled')),
4361 _('update to new branch head if new descendants were pulled')),
4361 ('f', 'force', None, _('run even when remote repository is unrelated')),
4362 ('f', 'force', None, _('run even when remote repository is unrelated')),
4362 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4363 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4363 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4364 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4364 ('b', 'branch', [], _('a specific branch you would like to pull'),
4365 ('b', 'branch', [], _('a specific branch you would like to pull'),
4365 _('BRANCH')),
4366 _('BRANCH')),
4366 ] + remoteopts,
4367 ] + remoteopts,
4367 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'),
4368 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'),
4368 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4369 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4369 helpbasic=True)
4370 helpbasic=True)
4370 def pull(ui, repo, source="default", **opts):
4371 def pull(ui, repo, source="default", **opts):
4371 """pull changes from the specified source
4372 """pull changes from the specified source
4372
4373
4373 Pull changes from a remote repository to a local one.
4374 Pull changes from a remote repository to a local one.
4374
4375
4375 This finds all changes from the repository at the specified path
4376 This finds all changes from the repository at the specified path
4376 or URL and adds them to a local repository (the current one unless
4377 or URL and adds them to a local repository (the current one unless
4377 -R is specified). By default, this does not update the copy of the
4378 -R is specified). By default, this does not update the copy of the
4378 project in the working directory.
4379 project in the working directory.
4379
4380
4380 When cloning from servers that support it, Mercurial may fetch
4381 When cloning from servers that support it, Mercurial may fetch
4381 pre-generated data. When this is done, hooks operating on incoming
4382 pre-generated data. When this is done, hooks operating on incoming
4382 changesets and changegroups may fire more than once, once for each
4383 changesets and changegroups may fire more than once, once for each
4383 pre-generated bundle and as well as for any additional remaining
4384 pre-generated bundle and as well as for any additional remaining
4384 data. See :hg:`help -e clonebundles` for more.
4385 data. See :hg:`help -e clonebundles` for more.
4385
4386
4386 Use :hg:`incoming` if you want to see what would have been added
4387 Use :hg:`incoming` if you want to see what would have been added
4387 by a pull at the time you issued this command. If you then decide
4388 by a pull at the time you issued this command. If you then decide
4388 to add those changes to the repository, you should use :hg:`pull
4389 to add those changes to the repository, you should use :hg:`pull
4389 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4390 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4390
4391
4391 If SOURCE is omitted, the 'default' path will be used.
4392 If SOURCE is omitted, the 'default' path will be used.
4392 See :hg:`help urls` for more information.
4393 See :hg:`help urls` for more information.
4393
4394
4394 Specifying bookmark as ``.`` is equivalent to specifying the active
4395 Specifying bookmark as ``.`` is equivalent to specifying the active
4395 bookmark's name.
4396 bookmark's name.
4396
4397
4397 Returns 0 on success, 1 if an update had unresolved files.
4398 Returns 0 on success, 1 if an update had unresolved files.
4398 """
4399 """
4399
4400
4400 opts = pycompat.byteskwargs(opts)
4401 opts = pycompat.byteskwargs(opts)
4401 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
4402 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
4402 msg = _('update destination required by configuration')
4403 msg = _('update destination required by configuration')
4403 hint = _('use hg pull followed by hg update DEST')
4404 hint = _('use hg pull followed by hg update DEST')
4404 raise error.Abort(msg, hint=hint)
4405 raise error.Abort(msg, hint=hint)
4405
4406
4406 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4407 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4407 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4408 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4408 other = hg.peer(repo, opts, source)
4409 other = hg.peer(repo, opts, source)
4409 try:
4410 try:
4410 revs, checkout = hg.addbranchrevs(repo, other, branches,
4411 revs, checkout = hg.addbranchrevs(repo, other, branches,
4411 opts.get('rev'))
4412 opts.get('rev'))
4412
4413
4413
4414
4414 pullopargs = {}
4415 pullopargs = {}
4415 if opts.get('bookmark'):
4416 if opts.get('bookmark'):
4416 if not revs:
4417 if not revs:
4417 revs = []
4418 revs = []
4418 # The list of bookmark used here is not the one used to actually
4419 # The list of bookmark used here is not the one used to actually
4419 # update the bookmark name. This can result in the revision pulled
4420 # update the bookmark name. This can result in the revision pulled
4420 # not ending up with the name of the bookmark because of a race
4421 # not ending up with the name of the bookmark because of a race
4421 # condition on the server. (See issue 4689 for details)
4422 # condition on the server. (See issue 4689 for details)
4422 remotebookmarks = other.listkeys('bookmarks')
4423 remotebookmarks = other.listkeys('bookmarks')
4423 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
4424 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
4424 pullopargs['remotebookmarks'] = remotebookmarks
4425 pullopargs['remotebookmarks'] = remotebookmarks
4425 for b in opts['bookmark']:
4426 for b in opts['bookmark']:
4426 b = repo._bookmarks.expandname(b)
4427 b = repo._bookmarks.expandname(b)
4427 if b not in remotebookmarks:
4428 if b not in remotebookmarks:
4428 raise error.Abort(_('remote bookmark %s not found!') % b)
4429 raise error.Abort(_('remote bookmark %s not found!') % b)
4429 revs.append(hex(remotebookmarks[b]))
4430 revs.append(hex(remotebookmarks[b]))
4430
4431
4431 if revs:
4432 if revs:
4432 try:
4433 try:
4433 # When 'rev' is a bookmark name, we cannot guarantee that it
4434 # When 'rev' is a bookmark name, we cannot guarantee that it
4434 # will be updated with that name because of a race condition
4435 # will be updated with that name because of a race condition
4435 # server side. (See issue 4689 for details)
4436 # server side. (See issue 4689 for details)
4436 oldrevs = revs
4437 oldrevs = revs
4437 revs = [] # actually, nodes
4438 revs = [] # actually, nodes
4438 for r in oldrevs:
4439 for r in oldrevs:
4439 with other.commandexecutor() as e:
4440 with other.commandexecutor() as e:
4440 node = e.callcommand('lookup', {'key': r}).result()
4441 node = e.callcommand('lookup', {'key': r}).result()
4441
4442
4442 revs.append(node)
4443 revs.append(node)
4443 if r == checkout:
4444 if r == checkout:
4444 checkout = node
4445 checkout = node
4445 except error.CapabilityError:
4446 except error.CapabilityError:
4446 err = _("other repository doesn't support revision lookup, "
4447 err = _("other repository doesn't support revision lookup, "
4447 "so a rev cannot be specified.")
4448 "so a rev cannot be specified.")
4448 raise error.Abort(err)
4449 raise error.Abort(err)
4449
4450
4450 wlock = util.nullcontextmanager()
4451 wlock = util.nullcontextmanager()
4451 if opts.get('update'):
4452 if opts.get('update'):
4452 wlock = repo.wlock()
4453 wlock = repo.wlock()
4453 with wlock:
4454 with wlock:
4454 pullopargs.update(opts.get('opargs', {}))
4455 pullopargs.update(opts.get('opargs', {}))
4455 modheads = exchange.pull(repo, other, heads=revs,
4456 modheads = exchange.pull(repo, other, heads=revs,
4456 force=opts.get('force'),
4457 force=opts.get('force'),
4457 bookmarks=opts.get('bookmark', ()),
4458 bookmarks=opts.get('bookmark', ()),
4458 opargs=pullopargs).cgresult
4459 opargs=pullopargs).cgresult
4459
4460
4460 # brev is a name, which might be a bookmark to be activated at
4461 # brev is a name, which might be a bookmark to be activated at
4461 # the end of the update. In other words, it is an explicit
4462 # the end of the update. In other words, it is an explicit
4462 # destination of the update
4463 # destination of the update
4463 brev = None
4464 brev = None
4464
4465
4465 if checkout:
4466 if checkout:
4466 checkout = repo.changelog.rev(checkout)
4467 checkout = repo.changelog.rev(checkout)
4467
4468
4468 # order below depends on implementation of
4469 # order below depends on implementation of
4469 # hg.addbranchrevs(). opts['bookmark'] is ignored,
4470 # hg.addbranchrevs(). opts['bookmark'] is ignored,
4470 # because 'checkout' is determined without it.
4471 # because 'checkout' is determined without it.
4471 if opts.get('rev'):
4472 if opts.get('rev'):
4472 brev = opts['rev'][0]
4473 brev = opts['rev'][0]
4473 elif opts.get('branch'):
4474 elif opts.get('branch'):
4474 brev = opts['branch'][0]
4475 brev = opts['branch'][0]
4475 else:
4476 else:
4476 brev = branches[0]
4477 brev = branches[0]
4477 repo._subtoppath = source
4478 repo._subtoppath = source
4478 try:
4479 try:
4479 ret = postincoming(ui, repo, modheads, opts.get('update'),
4480 ret = postincoming(ui, repo, modheads, opts.get('update'),
4480 checkout, brev)
4481 checkout, brev)
4481
4482
4482 finally:
4483 finally:
4483 del repo._subtoppath
4484 del repo._subtoppath
4484
4485
4485 finally:
4486 finally:
4486 other.close()
4487 other.close()
4487 return ret
4488 return ret
4488
4489
4489 @command('push',
4490 @command('push',
4490 [('f', 'force', None, _('force push')),
4491 [('f', 'force', None, _('force push')),
4491 ('r', 'rev', [],
4492 ('r', 'rev', [],
4492 _('a changeset intended to be included in the destination'),
4493 _('a changeset intended to be included in the destination'),
4493 _('REV')),
4494 _('REV')),
4494 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4495 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4495 ('b', 'branch', [],
4496 ('b', 'branch', [],
4496 _('a specific branch you would like to push'), _('BRANCH')),
4497 _('a specific branch you would like to push'), _('BRANCH')),
4497 ('', 'new-branch', False, _('allow pushing a new branch')),
4498 ('', 'new-branch', False, _('allow pushing a new branch')),
4498 ('', 'pushvars', [], _('variables that can be sent to server (ADVANCED)')),
4499 ('', 'pushvars', [], _('variables that can be sent to server (ADVANCED)')),
4499 ] + remoteopts,
4500 ] + remoteopts,
4500 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'),
4501 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'),
4501 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4502 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4502 helpbasic=True)
4503 helpbasic=True)
4503 def push(ui, repo, dest=None, **opts):
4504 def push(ui, repo, dest=None, **opts):
4504 """push changes to the specified destination
4505 """push changes to the specified destination
4505
4506
4506 Push changesets from the local repository to the specified
4507 Push changesets from the local repository to the specified
4507 destination.
4508 destination.
4508
4509
4509 This operation is symmetrical to pull: it is identical to a pull
4510 This operation is symmetrical to pull: it is identical to a pull
4510 in the destination repository from the current one.
4511 in the destination repository from the current one.
4511
4512
4512 By default, push will not allow creation of new heads at the
4513 By default, push will not allow creation of new heads at the
4513 destination, since multiple heads would make it unclear which head
4514 destination, since multiple heads would make it unclear which head
4514 to use. In this situation, it is recommended to pull and merge
4515 to use. In this situation, it is recommended to pull and merge
4515 before pushing.
4516 before pushing.
4516
4517
4517 Use --new-branch if you want to allow push to create a new named
4518 Use --new-branch if you want to allow push to create a new named
4518 branch that is not present at the destination. This allows you to
4519 branch that is not present at the destination. This allows you to
4519 only create a new branch without forcing other changes.
4520 only create a new branch without forcing other changes.
4520
4521
4521 .. note::
4522 .. note::
4522
4523
4523 Extra care should be taken with the -f/--force option,
4524 Extra care should be taken with the -f/--force option,
4524 which will push all new heads on all branches, an action which will
4525 which will push all new heads on all branches, an action which will
4525 almost always cause confusion for collaborators.
4526 almost always cause confusion for collaborators.
4526
4527
4527 If -r/--rev is used, the specified revision and all its ancestors
4528 If -r/--rev is used, the specified revision and all its ancestors
4528 will be pushed to the remote repository.
4529 will be pushed to the remote repository.
4529
4530
4530 If -B/--bookmark is used, the specified bookmarked revision, its
4531 If -B/--bookmark is used, the specified bookmarked revision, its
4531 ancestors, and the bookmark will be pushed to the remote
4532 ancestors, and the bookmark will be pushed to the remote
4532 repository. Specifying ``.`` is equivalent to specifying the active
4533 repository. Specifying ``.`` is equivalent to specifying the active
4533 bookmark's name.
4534 bookmark's name.
4534
4535
4535 Please see :hg:`help urls` for important details about ``ssh://``
4536 Please see :hg:`help urls` for important details about ``ssh://``
4536 URLs. If DESTINATION is omitted, a default path will be used.
4537 URLs. If DESTINATION is omitted, a default path will be used.
4537
4538
4538 .. container:: verbose
4539 .. container:: verbose
4539
4540
4540 The --pushvars option sends strings to the server that become
4541 The --pushvars option sends strings to the server that become
4541 environment variables prepended with ``HG_USERVAR_``. For example,
4542 environment variables prepended with ``HG_USERVAR_``. For example,
4542 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
4543 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
4543 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
4544 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
4544
4545
4545 pushvars can provide for user-overridable hooks as well as set debug
4546 pushvars can provide for user-overridable hooks as well as set debug
4546 levels. One example is having a hook that blocks commits containing
4547 levels. One example is having a hook that blocks commits containing
4547 conflict markers, but enables the user to override the hook if the file
4548 conflict markers, but enables the user to override the hook if the file
4548 is using conflict markers for testing purposes or the file format has
4549 is using conflict markers for testing purposes or the file format has
4549 strings that look like conflict markers.
4550 strings that look like conflict markers.
4550
4551
4551 By default, servers will ignore `--pushvars`. To enable it add the
4552 By default, servers will ignore `--pushvars`. To enable it add the
4552 following to your configuration file::
4553 following to your configuration file::
4553
4554
4554 [push]
4555 [push]
4555 pushvars.server = true
4556 pushvars.server = true
4556
4557
4557 Returns 0 if push was successful, 1 if nothing to push.
4558 Returns 0 if push was successful, 1 if nothing to push.
4558 """
4559 """
4559
4560
4560 opts = pycompat.byteskwargs(opts)
4561 opts = pycompat.byteskwargs(opts)
4561 if opts.get('bookmark'):
4562 if opts.get('bookmark'):
4562 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4563 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4563 for b in opts['bookmark']:
4564 for b in opts['bookmark']:
4564 # translate -B options to -r so changesets get pushed
4565 # translate -B options to -r so changesets get pushed
4565 b = repo._bookmarks.expandname(b)
4566 b = repo._bookmarks.expandname(b)
4566 if b in repo._bookmarks:
4567 if b in repo._bookmarks:
4567 opts.setdefault('rev', []).append(b)
4568 opts.setdefault('rev', []).append(b)
4568 else:
4569 else:
4569 # if we try to push a deleted bookmark, translate it to null
4570 # if we try to push a deleted bookmark, translate it to null
4570 # this lets simultaneous -r, -b options continue working
4571 # this lets simultaneous -r, -b options continue working
4571 opts.setdefault('rev', []).append("null")
4572 opts.setdefault('rev', []).append("null")
4572
4573
4573 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4574 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4574 if not path:
4575 if not path:
4575 raise error.Abort(_('default repository not configured!'),
4576 raise error.Abort(_('default repository not configured!'),
4576 hint=_("see 'hg help config.paths'"))
4577 hint=_("see 'hg help config.paths'"))
4577 dest = path.pushloc or path.loc
4578 dest = path.pushloc or path.loc
4578 branches = (path.branch, opts.get('branch') or [])
4579 branches = (path.branch, opts.get('branch') or [])
4579 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4580 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4580 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4581 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4581 other = hg.peer(repo, opts, dest)
4582 other = hg.peer(repo, opts, dest)
4582
4583
4583 if revs:
4584 if revs:
4584 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
4585 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
4585 if not revs:
4586 if not revs:
4586 raise error.Abort(_("specified revisions evaluate to an empty set"),
4587 raise error.Abort(_("specified revisions evaluate to an empty set"),
4587 hint=_("use different revision arguments"))
4588 hint=_("use different revision arguments"))
4588 elif path.pushrev:
4589 elif path.pushrev:
4589 # It doesn't make any sense to specify ancestor revisions. So limit
4590 # It doesn't make any sense to specify ancestor revisions. So limit
4590 # to DAG heads to make discovery simpler.
4591 # to DAG heads to make discovery simpler.
4591 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4592 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4592 revs = scmutil.revrange(repo, [expr])
4593 revs = scmutil.revrange(repo, [expr])
4593 revs = [repo[rev].node() for rev in revs]
4594 revs = [repo[rev].node() for rev in revs]
4594 if not revs:
4595 if not revs:
4595 raise error.Abort(_('default push revset for path evaluates to an '
4596 raise error.Abort(_('default push revset for path evaluates to an '
4596 'empty set'))
4597 'empty set'))
4597
4598
4598 repo._subtoppath = dest
4599 repo._subtoppath = dest
4599 try:
4600 try:
4600 # push subrepos depth-first for coherent ordering
4601 # push subrepos depth-first for coherent ordering
4601 c = repo['.']
4602 c = repo['.']
4602 subs = c.substate # only repos that are committed
4603 subs = c.substate # only repos that are committed
4603 for s in sorted(subs):
4604 for s in sorted(subs):
4604 result = c.sub(s).push(opts)
4605 result = c.sub(s).push(opts)
4605 if result == 0:
4606 if result == 0:
4606 return not result
4607 return not result
4607 finally:
4608 finally:
4608 del repo._subtoppath
4609 del repo._subtoppath
4609
4610
4610 opargs = dict(opts.get('opargs', {})) # copy opargs since we may mutate it
4611 opargs = dict(opts.get('opargs', {})) # copy opargs since we may mutate it
4611 opargs.setdefault('pushvars', []).extend(opts.get('pushvars', []))
4612 opargs.setdefault('pushvars', []).extend(opts.get('pushvars', []))
4612
4613
4613 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4614 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4614 newbranch=opts.get('new_branch'),
4615 newbranch=opts.get('new_branch'),
4615 bookmarks=opts.get('bookmark', ()),
4616 bookmarks=opts.get('bookmark', ()),
4616 opargs=opargs)
4617 opargs=opargs)
4617
4618
4618 result = not pushop.cgresult
4619 result = not pushop.cgresult
4619
4620
4620 if pushop.bkresult is not None:
4621 if pushop.bkresult is not None:
4621 if pushop.bkresult == 2:
4622 if pushop.bkresult == 2:
4622 result = 2
4623 result = 2
4623 elif not result and pushop.bkresult:
4624 elif not result and pushop.bkresult:
4624 result = 2
4625 result = 2
4625
4626
4626 return result
4627 return result
4627
4628
4628 @command('recover', [], helpcategory=command.CATEGORY_MAINTENANCE)
4629 @command('recover', [], helpcategory=command.CATEGORY_MAINTENANCE)
4629 def recover(ui, repo):
4630 def recover(ui, repo):
4630 """roll back an interrupted transaction
4631 """roll back an interrupted transaction
4631
4632
4632 Recover from an interrupted commit or pull.
4633 Recover from an interrupted commit or pull.
4633
4634
4634 This command tries to fix the repository status after an
4635 This command tries to fix the repository status after an
4635 interrupted operation. It should only be necessary when Mercurial
4636 interrupted operation. It should only be necessary when Mercurial
4636 suggests it.
4637 suggests it.
4637
4638
4638 Returns 0 if successful, 1 if nothing to recover or verify fails.
4639 Returns 0 if successful, 1 if nothing to recover or verify fails.
4639 """
4640 """
4640 if repo.recover():
4641 if repo.recover():
4641 return hg.verify(repo)
4642 return hg.verify(repo)
4642 return 1
4643 return 1
4643
4644
4644 @command('remove|rm',
4645 @command('remove|rm',
4645 [('A', 'after', None, _('record delete for missing files')),
4646 [('A', 'after', None, _('record delete for missing files')),
4646 ('f', 'force', None,
4647 ('f', 'force', None,
4647 _('forget added files, delete modified files')),
4648 _('forget added files, delete modified files')),
4648 ] + subrepoopts + walkopts + dryrunopts,
4649 ] + subrepoopts + walkopts + dryrunopts,
4649 _('[OPTION]... FILE...'),
4650 _('[OPTION]... FILE...'),
4650 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4651 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4651 helpbasic=True, inferrepo=True)
4652 helpbasic=True, inferrepo=True)
4652 def remove(ui, repo, *pats, **opts):
4653 def remove(ui, repo, *pats, **opts):
4653 """remove the specified files on the next commit
4654 """remove the specified files on the next commit
4654
4655
4655 Schedule the indicated files for removal from the current branch.
4656 Schedule the indicated files for removal from the current branch.
4656
4657
4657 This command schedules the files to be removed at the next commit.
4658 This command schedules the files to be removed at the next commit.
4658 To undo a remove before that, see :hg:`revert`. To undo added
4659 To undo a remove before that, see :hg:`revert`. To undo added
4659 files, see :hg:`forget`.
4660 files, see :hg:`forget`.
4660
4661
4661 .. container:: verbose
4662 .. container:: verbose
4662
4663
4663 -A/--after can be used to remove only files that have already
4664 -A/--after can be used to remove only files that have already
4664 been deleted, -f/--force can be used to force deletion, and -Af
4665 been deleted, -f/--force can be used to force deletion, and -Af
4665 can be used to remove files from the next revision without
4666 can be used to remove files from the next revision without
4666 deleting them from the working directory.
4667 deleting them from the working directory.
4667
4668
4668 The following table details the behavior of remove for different
4669 The following table details the behavior of remove for different
4669 file states (columns) and option combinations (rows). The file
4670 file states (columns) and option combinations (rows). The file
4670 states are Added [A], Clean [C], Modified [M] and Missing [!]
4671 states are Added [A], Clean [C], Modified [M] and Missing [!]
4671 (as reported by :hg:`status`). The actions are Warn, Remove
4672 (as reported by :hg:`status`). The actions are Warn, Remove
4672 (from branch) and Delete (from disk):
4673 (from branch) and Delete (from disk):
4673
4674
4674 ========= == == == ==
4675 ========= == == == ==
4675 opt/state A C M !
4676 opt/state A C M !
4676 ========= == == == ==
4677 ========= == == == ==
4677 none W RD W R
4678 none W RD W R
4678 -f R RD RD R
4679 -f R RD RD R
4679 -A W W W R
4680 -A W W W R
4680 -Af R R R R
4681 -Af R R R R
4681 ========= == == == ==
4682 ========= == == == ==
4682
4683
4683 .. note::
4684 .. note::
4684
4685
4685 :hg:`remove` never deletes files in Added [A] state from the
4686 :hg:`remove` never deletes files in Added [A] state from the
4686 working directory, not even if ``--force`` is specified.
4687 working directory, not even if ``--force`` is specified.
4687
4688
4688 Returns 0 on success, 1 if any warnings encountered.
4689 Returns 0 on success, 1 if any warnings encountered.
4689 """
4690 """
4690
4691
4691 opts = pycompat.byteskwargs(opts)
4692 opts = pycompat.byteskwargs(opts)
4692 after, force = opts.get('after'), opts.get('force')
4693 after, force = opts.get('after'), opts.get('force')
4693 dryrun = opts.get('dry_run')
4694 dryrun = opts.get('dry_run')
4694 if not pats and not after:
4695 if not pats and not after:
4695 raise error.Abort(_('no files specified'))
4696 raise error.Abort(_('no files specified'))
4696
4697
4697 m = scmutil.match(repo[None], pats, opts)
4698 m = scmutil.match(repo[None], pats, opts)
4698 subrepos = opts.get('subrepos')
4699 subrepos = opts.get('subrepos')
4699 return cmdutil.remove(ui, repo, m, "", after, force, subrepos,
4700 return cmdutil.remove(ui, repo, m, "", after, force, subrepos,
4700 dryrun=dryrun)
4701 dryrun=dryrun)
4701
4702
4702 @command('rename|move|mv',
4703 @command('rename|move|mv',
4703 [('A', 'after', None, _('record a rename that has already occurred')),
4704 [('A', 'after', None, _('record a rename that has already occurred')),
4704 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4705 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4705 ] + walkopts + dryrunopts,
4706 ] + walkopts + dryrunopts,
4706 _('[OPTION]... SOURCE... DEST'),
4707 _('[OPTION]... SOURCE... DEST'),
4707 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
4708 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
4708 def rename(ui, repo, *pats, **opts):
4709 def rename(ui, repo, *pats, **opts):
4709 """rename files; equivalent of copy + remove
4710 """rename files; equivalent of copy + remove
4710
4711
4711 Mark dest as copies of sources; mark sources for deletion. If dest
4712 Mark dest as copies of sources; mark sources for deletion. If dest
4712 is a directory, copies are put in that directory. If dest is a
4713 is a directory, copies are put in that directory. If dest is a
4713 file, there can only be one source.
4714 file, there can only be one source.
4714
4715
4715 By default, this command copies the contents of files as they
4716 By default, this command copies the contents of files as they
4716 exist in the working directory. If invoked with -A/--after, the
4717 exist in the working directory. If invoked with -A/--after, the
4717 operation is recorded, but no copying is performed.
4718 operation is recorded, but no copying is performed.
4718
4719
4719 This command takes effect at the next commit. To undo a rename
4720 This command takes effect at the next commit. To undo a rename
4720 before that, see :hg:`revert`.
4721 before that, see :hg:`revert`.
4721
4722
4722 Returns 0 on success, 1 if errors are encountered.
4723 Returns 0 on success, 1 if errors are encountered.
4723 """
4724 """
4724 opts = pycompat.byteskwargs(opts)
4725 opts = pycompat.byteskwargs(opts)
4725 with repo.wlock(False):
4726 with repo.wlock(False):
4726 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4727 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4727
4728
4728 @command('resolve',
4729 @command('resolve',
4729 [('a', 'all', None, _('select all unresolved files')),
4730 [('a', 'all', None, _('select all unresolved files')),
4730 ('l', 'list', None, _('list state of files needing merge')),
4731 ('l', 'list', None, _('list state of files needing merge')),
4731 ('m', 'mark', None, _('mark files as resolved')),
4732 ('m', 'mark', None, _('mark files as resolved')),
4732 ('u', 'unmark', None, _('mark files as unresolved')),
4733 ('u', 'unmark', None, _('mark files as unresolved')),
4733 ('n', 'no-status', None, _('hide status prefix')),
4734 ('n', 'no-status', None, _('hide status prefix')),
4734 ('', 're-merge', None, _('re-merge files'))]
4735 ('', 're-merge', None, _('re-merge files'))]
4735 + mergetoolopts + walkopts + formatteropts,
4736 + mergetoolopts + walkopts + formatteropts,
4736 _('[OPTION]... [FILE]...'),
4737 _('[OPTION]... [FILE]...'),
4737 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4738 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4738 inferrepo=True)
4739 inferrepo=True)
4739 def resolve(ui, repo, *pats, **opts):
4740 def resolve(ui, repo, *pats, **opts):
4740 """redo merges or set/view the merge status of files
4741 """redo merges or set/view the merge status of files
4741
4742
4742 Merges with unresolved conflicts are often the result of
4743 Merges with unresolved conflicts are often the result of
4743 non-interactive merging using the ``internal:merge`` configuration
4744 non-interactive merging using the ``internal:merge`` configuration
4744 setting, or a command-line merge tool like ``diff3``. The resolve
4745 setting, or a command-line merge tool like ``diff3``. The resolve
4745 command is used to manage the files involved in a merge, after
4746 command is used to manage the files involved in a merge, after
4746 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4747 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4747 working directory must have two parents). See :hg:`help
4748 working directory must have two parents). See :hg:`help
4748 merge-tools` for information on configuring merge tools.
4749 merge-tools` for information on configuring merge tools.
4749
4750
4750 The resolve command can be used in the following ways:
4751 The resolve command can be used in the following ways:
4751
4752
4752 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
4753 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
4753 the specified files, discarding any previous merge attempts. Re-merging
4754 the specified files, discarding any previous merge attempts. Re-merging
4754 is not performed for files already marked as resolved. Use ``--all/-a``
4755 is not performed for files already marked as resolved. Use ``--all/-a``
4755 to select all unresolved files. ``--tool`` can be used to specify
4756 to select all unresolved files. ``--tool`` can be used to specify
4756 the merge tool used for the given files. It overrides the HGMERGE
4757 the merge tool used for the given files. It overrides the HGMERGE
4757 environment variable and your configuration files. Previous file
4758 environment variable and your configuration files. Previous file
4758 contents are saved with a ``.orig`` suffix.
4759 contents are saved with a ``.orig`` suffix.
4759
4760
4760 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4761 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4761 (e.g. after having manually fixed-up the files). The default is
4762 (e.g. after having manually fixed-up the files). The default is
4762 to mark all unresolved files.
4763 to mark all unresolved files.
4763
4764
4764 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4765 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4765 default is to mark all resolved files.
4766 default is to mark all resolved files.
4766
4767
4767 - :hg:`resolve -l`: list files which had or still have conflicts.
4768 - :hg:`resolve -l`: list files which had or still have conflicts.
4768 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4769 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4769 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4770 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4770 the list. See :hg:`help filesets` for details.
4771 the list. See :hg:`help filesets` for details.
4771
4772
4772 .. note::
4773 .. note::
4773
4774
4774 Mercurial will not let you commit files with unresolved merge
4775 Mercurial will not let you commit files with unresolved merge
4775 conflicts. You must use :hg:`resolve -m ...` before you can
4776 conflicts. You must use :hg:`resolve -m ...` before you can
4776 commit after a conflicting merge.
4777 commit after a conflicting merge.
4777
4778
4778 .. container:: verbose
4779 .. container:: verbose
4779
4780
4780 Template:
4781 Template:
4781
4782
4782 The following keywords are supported in addition to the common template
4783 The following keywords are supported in addition to the common template
4783 keywords and functions. See also :hg:`help templates`.
4784 keywords and functions. See also :hg:`help templates`.
4784
4785
4785 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
4786 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
4786 :path: String. Repository-absolute path of the file.
4787 :path: String. Repository-absolute path of the file.
4787
4788
4788 Returns 0 on success, 1 if any files fail a resolve attempt.
4789 Returns 0 on success, 1 if any files fail a resolve attempt.
4789 """
4790 """
4790
4791
4791 opts = pycompat.byteskwargs(opts)
4792 opts = pycompat.byteskwargs(opts)
4792 confirm = ui.configbool('commands', 'resolve.confirm')
4793 confirm = ui.configbool('commands', 'resolve.confirm')
4793 flaglist = 'all mark unmark list no_status re_merge'.split()
4794 flaglist = 'all mark unmark list no_status re_merge'.split()
4794 all, mark, unmark, show, nostatus, remerge = \
4795 all, mark, unmark, show, nostatus, remerge = \
4795 [opts.get(o) for o in flaglist]
4796 [opts.get(o) for o in flaglist]
4796
4797
4797 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
4798 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
4798 if actioncount > 1:
4799 if actioncount > 1:
4799 raise error.Abort(_("too many actions specified"))
4800 raise error.Abort(_("too many actions specified"))
4800 elif (actioncount == 0
4801 elif (actioncount == 0
4801 and ui.configbool('commands', 'resolve.explicit-re-merge')):
4802 and ui.configbool('commands', 'resolve.explicit-re-merge')):
4802 hint = _('use --mark, --unmark, --list or --re-merge')
4803 hint = _('use --mark, --unmark, --list or --re-merge')
4803 raise error.Abort(_('no action specified'), hint=hint)
4804 raise error.Abort(_('no action specified'), hint=hint)
4804 if pats and all:
4805 if pats and all:
4805 raise error.Abort(_("can't specify --all and patterns"))
4806 raise error.Abort(_("can't specify --all and patterns"))
4806 if not (all or pats or show or mark or unmark):
4807 if not (all or pats or show or mark or unmark):
4807 raise error.Abort(_('no files or directories specified'),
4808 raise error.Abort(_('no files or directories specified'),
4808 hint=('use --all to re-merge all unresolved files'))
4809 hint=('use --all to re-merge all unresolved files'))
4809
4810
4810 if confirm:
4811 if confirm:
4811 if all:
4812 if all:
4812 if ui.promptchoice(_(b're-merge all unresolved files (yn)?'
4813 if ui.promptchoice(_(b're-merge all unresolved files (yn)?'
4813 b'$$ &Yes $$ &No')):
4814 b'$$ &Yes $$ &No')):
4814 raise error.Abort(_('user quit'))
4815 raise error.Abort(_('user quit'))
4815 if mark and not pats:
4816 if mark and not pats:
4816 if ui.promptchoice(_(b'mark all unresolved files as resolved (yn)?'
4817 if ui.promptchoice(_(b'mark all unresolved files as resolved (yn)?'
4817 b'$$ &Yes $$ &No')):
4818 b'$$ &Yes $$ &No')):
4818 raise error.Abort(_('user quit'))
4819 raise error.Abort(_('user quit'))
4819 if unmark and not pats:
4820 if unmark and not pats:
4820 if ui.promptchoice(_(b'mark all resolved files as unresolved (yn)?'
4821 if ui.promptchoice(_(b'mark all resolved files as unresolved (yn)?'
4821 b'$$ &Yes $$ &No')):
4822 b'$$ &Yes $$ &No')):
4822 raise error.Abort(_('user quit'))
4823 raise error.Abort(_('user quit'))
4823
4824
4824 if show:
4825 if show:
4825 ui.pager('resolve')
4826 ui.pager('resolve')
4826 fm = ui.formatter('resolve', opts)
4827 fm = ui.formatter('resolve', opts)
4827 ms = mergemod.mergestate.read(repo)
4828 ms = mergemod.mergestate.read(repo)
4828 wctx = repo[None]
4829 wctx = repo[None]
4829 m = scmutil.match(wctx, pats, opts)
4830 m = scmutil.match(wctx, pats, opts)
4830
4831
4831 # Labels and keys based on merge state. Unresolved path conflicts show
4832 # Labels and keys based on merge state. Unresolved path conflicts show
4832 # as 'P'. Resolved path conflicts show as 'R', the same as normal
4833 # as 'P'. Resolved path conflicts show as 'R', the same as normal
4833 # resolved conflicts.
4834 # resolved conflicts.
4834 mergestateinfo = {
4835 mergestateinfo = {
4835 mergemod.MERGE_RECORD_UNRESOLVED: ('resolve.unresolved', 'U'),
4836 mergemod.MERGE_RECORD_UNRESOLVED: ('resolve.unresolved', 'U'),
4836 mergemod.MERGE_RECORD_RESOLVED: ('resolve.resolved', 'R'),
4837 mergemod.MERGE_RECORD_RESOLVED: ('resolve.resolved', 'R'),
4837 mergemod.MERGE_RECORD_UNRESOLVED_PATH: ('resolve.unresolved', 'P'),
4838 mergemod.MERGE_RECORD_UNRESOLVED_PATH: ('resolve.unresolved', 'P'),
4838 mergemod.MERGE_RECORD_RESOLVED_PATH: ('resolve.resolved', 'R'),
4839 mergemod.MERGE_RECORD_RESOLVED_PATH: ('resolve.resolved', 'R'),
4839 mergemod.MERGE_RECORD_DRIVER_RESOLVED: ('resolve.driverresolved',
4840 mergemod.MERGE_RECORD_DRIVER_RESOLVED: ('resolve.driverresolved',
4840 'D'),
4841 'D'),
4841 }
4842 }
4842
4843
4843 for f in ms:
4844 for f in ms:
4844 if not m(f):
4845 if not m(f):
4845 continue
4846 continue
4846
4847
4847 label, key = mergestateinfo[ms[f]]
4848 label, key = mergestateinfo[ms[f]]
4848 fm.startitem()
4849 fm.startitem()
4849 fm.context(ctx=wctx)
4850 fm.context(ctx=wctx)
4850 fm.condwrite(not nostatus, 'mergestatus', '%s ', key, label=label)
4851 fm.condwrite(not nostatus, 'mergestatus', '%s ', key, label=label)
4851 fm.write('path', '%s\n', f, label=label)
4852 fm.write('path', '%s\n', f, label=label)
4852 fm.end()
4853 fm.end()
4853 return 0
4854 return 0
4854
4855
4855 with repo.wlock():
4856 with repo.wlock():
4856 ms = mergemod.mergestate.read(repo)
4857 ms = mergemod.mergestate.read(repo)
4857
4858
4858 if not (ms.active() or repo.dirstate.p2() != nullid):
4859 if not (ms.active() or repo.dirstate.p2() != nullid):
4859 raise error.Abort(
4860 raise error.Abort(
4860 _('resolve command not applicable when not merging'))
4861 _('resolve command not applicable when not merging'))
4861
4862
4862 wctx = repo[None]
4863 wctx = repo[None]
4863
4864
4864 if (ms.mergedriver
4865 if (ms.mergedriver
4865 and ms.mdstate() == mergemod.MERGE_DRIVER_STATE_UNMARKED):
4866 and ms.mdstate() == mergemod.MERGE_DRIVER_STATE_UNMARKED):
4866 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4867 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4867 ms.commit()
4868 ms.commit()
4868 # allow mark and unmark to go through
4869 # allow mark and unmark to go through
4869 if not mark and not unmark and not proceed:
4870 if not mark and not unmark and not proceed:
4870 return 1
4871 return 1
4871
4872
4872 m = scmutil.match(wctx, pats, opts)
4873 m = scmutil.match(wctx, pats, opts)
4873 ret = 0
4874 ret = 0
4874 didwork = False
4875 didwork = False
4875 runconclude = False
4876 runconclude = False
4876
4877
4877 tocomplete = []
4878 tocomplete = []
4878 hasconflictmarkers = []
4879 hasconflictmarkers = []
4879 if mark:
4880 if mark:
4880 markcheck = ui.config('commands', 'resolve.mark-check')
4881 markcheck = ui.config('commands', 'resolve.mark-check')
4881 if markcheck not in ['warn', 'abort']:
4882 if markcheck not in ['warn', 'abort']:
4882 # Treat all invalid / unrecognized values as 'none'.
4883 # Treat all invalid / unrecognized values as 'none'.
4883 markcheck = False
4884 markcheck = False
4884 for f in ms:
4885 for f in ms:
4885 if not m(f):
4886 if not m(f):
4886 continue
4887 continue
4887
4888
4888 didwork = True
4889 didwork = True
4889
4890
4890 # don't let driver-resolved files be marked, and run the conclude
4891 # don't let driver-resolved files be marked, and run the conclude
4891 # step if asked to resolve
4892 # step if asked to resolve
4892 if ms[f] == mergemod.MERGE_RECORD_DRIVER_RESOLVED:
4893 if ms[f] == mergemod.MERGE_RECORD_DRIVER_RESOLVED:
4893 exact = m.exact(f)
4894 exact = m.exact(f)
4894 if mark:
4895 if mark:
4895 if exact:
4896 if exact:
4896 ui.warn(_('not marking %s as it is driver-resolved\n')
4897 ui.warn(_('not marking %s as it is driver-resolved\n')
4897 % f)
4898 % f)
4898 elif unmark:
4899 elif unmark:
4899 if exact:
4900 if exact:
4900 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4901 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4901 % f)
4902 % f)
4902 else:
4903 else:
4903 runconclude = True
4904 runconclude = True
4904 continue
4905 continue
4905
4906
4906 # path conflicts must be resolved manually
4907 # path conflicts must be resolved manually
4907 if ms[f] in (mergemod.MERGE_RECORD_UNRESOLVED_PATH,
4908 if ms[f] in (mergemod.MERGE_RECORD_UNRESOLVED_PATH,
4908 mergemod.MERGE_RECORD_RESOLVED_PATH):
4909 mergemod.MERGE_RECORD_RESOLVED_PATH):
4909 if mark:
4910 if mark:
4910 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED_PATH)
4911 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED_PATH)
4911 elif unmark:
4912 elif unmark:
4912 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED_PATH)
4913 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED_PATH)
4913 elif ms[f] == mergemod.MERGE_RECORD_UNRESOLVED_PATH:
4914 elif ms[f] == mergemod.MERGE_RECORD_UNRESOLVED_PATH:
4914 ui.warn(_('%s: path conflict must be resolved manually\n')
4915 ui.warn(_('%s: path conflict must be resolved manually\n')
4915 % f)
4916 % f)
4916 continue
4917 continue
4917
4918
4918 if mark:
4919 if mark:
4919 if markcheck:
4920 if markcheck:
4920 with repo.wvfs(f) as fobj:
4921 with repo.wvfs(f) as fobj:
4921 fdata = fobj.read()
4922 fdata = fobj.read()
4922 if filemerge.hasconflictmarkers(fdata) and \
4923 if filemerge.hasconflictmarkers(fdata) and \
4923 ms[f] != mergemod.MERGE_RECORD_RESOLVED:
4924 ms[f] != mergemod.MERGE_RECORD_RESOLVED:
4924 hasconflictmarkers.append(f)
4925 hasconflictmarkers.append(f)
4925 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED)
4926 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED)
4926 elif unmark:
4927 elif unmark:
4927 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED)
4928 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED)
4928 else:
4929 else:
4929 # backup pre-resolve (merge uses .orig for its own purposes)
4930 # backup pre-resolve (merge uses .orig for its own purposes)
4930 a = repo.wjoin(f)
4931 a = repo.wjoin(f)
4931 try:
4932 try:
4932 util.copyfile(a, a + ".resolve")
4933 util.copyfile(a, a + ".resolve")
4933 except (IOError, OSError) as inst:
4934 except (IOError, OSError) as inst:
4934 if inst.errno != errno.ENOENT:
4935 if inst.errno != errno.ENOENT:
4935 raise
4936 raise
4936
4937
4937 try:
4938 try:
4938 # preresolve file
4939 # preresolve file
4939 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
4940 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
4940 with ui.configoverride(overrides, 'resolve'):
4941 with ui.configoverride(overrides, 'resolve'):
4941 complete, r = ms.preresolve(f, wctx)
4942 complete, r = ms.preresolve(f, wctx)
4942 if not complete:
4943 if not complete:
4943 tocomplete.append(f)
4944 tocomplete.append(f)
4944 elif r:
4945 elif r:
4945 ret = 1
4946 ret = 1
4946 finally:
4947 finally:
4947 ms.commit()
4948 ms.commit()
4948
4949
4949 # replace filemerge's .orig file with our resolve file, but only
4950 # replace filemerge's .orig file with our resolve file, but only
4950 # for merges that are complete
4951 # for merges that are complete
4951 if complete:
4952 if complete:
4952 try:
4953 try:
4953 util.rename(a + ".resolve",
4954 util.rename(a + ".resolve",
4954 scmutil.origpath(ui, repo, a))
4955 scmutil.origpath(ui, repo, a))
4955 except OSError as inst:
4956 except OSError as inst:
4956 if inst.errno != errno.ENOENT:
4957 if inst.errno != errno.ENOENT:
4957 raise
4958 raise
4958
4959
4959 if hasconflictmarkers:
4960 if hasconflictmarkers:
4960 ui.warn(_('warning: the following files still have conflict '
4961 ui.warn(_('warning: the following files still have conflict '
4961 'markers:\n ') + '\n '.join(hasconflictmarkers) + '\n')
4962 'markers:\n ') + '\n '.join(hasconflictmarkers) + '\n')
4962 if markcheck == 'abort' and not all and not pats:
4963 if markcheck == 'abort' and not all and not pats:
4963 raise error.Abort(_('conflict markers detected'),
4964 raise error.Abort(_('conflict markers detected'),
4964 hint=_('use --all to mark anyway'))
4965 hint=_('use --all to mark anyway'))
4965
4966
4966 for f in tocomplete:
4967 for f in tocomplete:
4967 try:
4968 try:
4968 # resolve file
4969 # resolve file
4969 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
4970 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
4970 with ui.configoverride(overrides, 'resolve'):
4971 with ui.configoverride(overrides, 'resolve'):
4971 r = ms.resolve(f, wctx)
4972 r = ms.resolve(f, wctx)
4972 if r:
4973 if r:
4973 ret = 1
4974 ret = 1
4974 finally:
4975 finally:
4975 ms.commit()
4976 ms.commit()
4976
4977
4977 # replace filemerge's .orig file with our resolve file
4978 # replace filemerge's .orig file with our resolve file
4978 a = repo.wjoin(f)
4979 a = repo.wjoin(f)
4979 try:
4980 try:
4980 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
4981 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
4981 except OSError as inst:
4982 except OSError as inst:
4982 if inst.errno != errno.ENOENT:
4983 if inst.errno != errno.ENOENT:
4983 raise
4984 raise
4984
4985
4985 ms.commit()
4986 ms.commit()
4986 ms.recordactions()
4987 ms.recordactions()
4987
4988
4988 if not didwork and pats:
4989 if not didwork and pats:
4989 hint = None
4990 hint = None
4990 if not any([p for p in pats if p.find(':') >= 0]):
4991 if not any([p for p in pats if p.find(':') >= 0]):
4991 pats = ['path:%s' % p for p in pats]
4992 pats = ['path:%s' % p for p in pats]
4992 m = scmutil.match(wctx, pats, opts)
4993 m = scmutil.match(wctx, pats, opts)
4993 for f in ms:
4994 for f in ms:
4994 if not m(f):
4995 if not m(f):
4995 continue
4996 continue
4996 def flag(o):
4997 def flag(o):
4997 if o == 're_merge':
4998 if o == 're_merge':
4998 return '--re-merge '
4999 return '--re-merge '
4999 return '-%s ' % o[0:1]
5000 return '-%s ' % o[0:1]
5000 flags = ''.join([flag(o) for o in flaglist if opts.get(o)])
5001 flags = ''.join([flag(o) for o in flaglist if opts.get(o)])
5001 hint = _("(try: hg resolve %s%s)\n") % (
5002 hint = _("(try: hg resolve %s%s)\n") % (
5002 flags,
5003 flags,
5003 ' '.join(pats))
5004 ' '.join(pats))
5004 break
5005 break
5005 ui.warn(_("arguments do not match paths that need resolving\n"))
5006 ui.warn(_("arguments do not match paths that need resolving\n"))
5006 if hint:
5007 if hint:
5007 ui.warn(hint)
5008 ui.warn(hint)
5008 elif ms.mergedriver and ms.mdstate() != 's':
5009 elif ms.mergedriver and ms.mdstate() != 's':
5009 # run conclude step when either a driver-resolved file is requested
5010 # run conclude step when either a driver-resolved file is requested
5010 # or there are no driver-resolved files
5011 # or there are no driver-resolved files
5011 # we can't use 'ret' to determine whether any files are unresolved
5012 # we can't use 'ret' to determine whether any files are unresolved
5012 # because we might not have tried to resolve some
5013 # because we might not have tried to resolve some
5013 if ((runconclude or not list(ms.driverresolved()))
5014 if ((runconclude or not list(ms.driverresolved()))
5014 and not list(ms.unresolved())):
5015 and not list(ms.unresolved())):
5015 proceed = mergemod.driverconclude(repo, ms, wctx)
5016 proceed = mergemod.driverconclude(repo, ms, wctx)
5016 ms.commit()
5017 ms.commit()
5017 if not proceed:
5018 if not proceed:
5018 return 1
5019 return 1
5019
5020
5020 # Nudge users into finishing an unfinished operation
5021 # Nudge users into finishing an unfinished operation
5021 unresolvedf = list(ms.unresolved())
5022 unresolvedf = list(ms.unresolved())
5022 driverresolvedf = list(ms.driverresolved())
5023 driverresolvedf = list(ms.driverresolved())
5023 if not unresolvedf and not driverresolvedf:
5024 if not unresolvedf and not driverresolvedf:
5024 ui.status(_('(no more unresolved files)\n'))
5025 ui.status(_('(no more unresolved files)\n'))
5025 cmdutil.checkafterresolved(repo)
5026 cmdutil.checkafterresolved(repo)
5026 elif not unresolvedf:
5027 elif not unresolvedf:
5027 ui.status(_('(no more unresolved files -- '
5028 ui.status(_('(no more unresolved files -- '
5028 'run "hg resolve --all" to conclude)\n'))
5029 'run "hg resolve --all" to conclude)\n'))
5029
5030
5030 return ret
5031 return ret
5031
5032
5032 @command('revert',
5033 @command('revert',
5033 [('a', 'all', None, _('revert all changes when no arguments given')),
5034 [('a', 'all', None, _('revert all changes when no arguments given')),
5034 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5035 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5035 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
5036 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
5036 ('C', 'no-backup', None, _('do not save backup copies of files')),
5037 ('C', 'no-backup', None, _('do not save backup copies of files')),
5037 ('i', 'interactive', None, _('interactively select the changes')),
5038 ('i', 'interactive', None, _('interactively select the changes')),
5038 ] + walkopts + dryrunopts,
5039 ] + walkopts + dryrunopts,
5039 _('[OPTION]... [-r REV] [NAME]...'),
5040 _('[OPTION]... [-r REV] [NAME]...'),
5040 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
5041 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
5041 def revert(ui, repo, *pats, **opts):
5042 def revert(ui, repo, *pats, **opts):
5042 """restore files to their checkout state
5043 """restore files to their checkout state
5043
5044
5044 .. note::
5045 .. note::
5045
5046
5046 To check out earlier revisions, you should use :hg:`update REV`.
5047 To check out earlier revisions, you should use :hg:`update REV`.
5047 To cancel an uncommitted merge (and lose your changes),
5048 To cancel an uncommitted merge (and lose your changes),
5048 use :hg:`merge --abort`.
5049 use :hg:`merge --abort`.
5049
5050
5050 With no revision specified, revert the specified files or directories
5051 With no revision specified, revert the specified files or directories
5051 to the contents they had in the parent of the working directory.
5052 to the contents they had in the parent of the working directory.
5052 This restores the contents of files to an unmodified
5053 This restores the contents of files to an unmodified
5053 state and unschedules adds, removes, copies, and renames. If the
5054 state and unschedules adds, removes, copies, and renames. If the
5054 working directory has two parents, you must explicitly specify a
5055 working directory has two parents, you must explicitly specify a
5055 revision.
5056 revision.
5056
5057
5057 Using the -r/--rev or -d/--date options, revert the given files or
5058 Using the -r/--rev or -d/--date options, revert the given files or
5058 directories to their states as of a specific revision. Because
5059 directories to their states as of a specific revision. Because
5059 revert does not change the working directory parents, this will
5060 revert does not change the working directory parents, this will
5060 cause these files to appear modified. This can be helpful to "back
5061 cause these files to appear modified. This can be helpful to "back
5061 out" some or all of an earlier change. See :hg:`backout` for a
5062 out" some or all of an earlier change. See :hg:`backout` for a
5062 related method.
5063 related method.
5063
5064
5064 Modified files are saved with a .orig suffix before reverting.
5065 Modified files are saved with a .orig suffix before reverting.
5065 To disable these backups, use --no-backup. It is possible to store
5066 To disable these backups, use --no-backup. It is possible to store
5066 the backup files in a custom directory relative to the root of the
5067 the backup files in a custom directory relative to the root of the
5067 repository by setting the ``ui.origbackuppath`` configuration
5068 repository by setting the ``ui.origbackuppath`` configuration
5068 option.
5069 option.
5069
5070
5070 See :hg:`help dates` for a list of formats valid for -d/--date.
5071 See :hg:`help dates` for a list of formats valid for -d/--date.
5071
5072
5072 See :hg:`help backout` for a way to reverse the effect of an
5073 See :hg:`help backout` for a way to reverse the effect of an
5073 earlier changeset.
5074 earlier changeset.
5074
5075
5075 Returns 0 on success.
5076 Returns 0 on success.
5076 """
5077 """
5077
5078
5078 opts = pycompat.byteskwargs(opts)
5079 opts = pycompat.byteskwargs(opts)
5079 if opts.get("date"):
5080 if opts.get("date"):
5080 if opts.get("rev"):
5081 if opts.get("rev"):
5081 raise error.Abort(_("you can't specify a revision and a date"))
5082 raise error.Abort(_("you can't specify a revision and a date"))
5082 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5083 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5083
5084
5084 parent, p2 = repo.dirstate.parents()
5085 parent, p2 = repo.dirstate.parents()
5085 if not opts.get('rev') and p2 != nullid:
5086 if not opts.get('rev') and p2 != nullid:
5086 # revert after merge is a trap for new users (issue2915)
5087 # revert after merge is a trap for new users (issue2915)
5087 raise error.Abort(_('uncommitted merge with no revision specified'),
5088 raise error.Abort(_('uncommitted merge with no revision specified'),
5088 hint=_("use 'hg update' or see 'hg help revert'"))
5089 hint=_("use 'hg update' or see 'hg help revert'"))
5089
5090
5090 rev = opts.get('rev')
5091 rev = opts.get('rev')
5091 if rev:
5092 if rev:
5092 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
5093 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
5093 ctx = scmutil.revsingle(repo, rev)
5094 ctx = scmutil.revsingle(repo, rev)
5094
5095
5095 if (not (pats or opts.get('include') or opts.get('exclude') or
5096 if (not (pats or opts.get('include') or opts.get('exclude') or
5096 opts.get('all') or opts.get('interactive'))):
5097 opts.get('all') or opts.get('interactive'))):
5097 msg = _("no files or directories specified")
5098 msg = _("no files or directories specified")
5098 if p2 != nullid:
5099 if p2 != nullid:
5099 hint = _("uncommitted merge, use --all to discard all changes,"
5100 hint = _("uncommitted merge, use --all to discard all changes,"
5100 " or 'hg update -C .' to abort the merge")
5101 " or 'hg update -C .' to abort the merge")
5101 raise error.Abort(msg, hint=hint)
5102 raise error.Abort(msg, hint=hint)
5102 dirty = any(repo.status())
5103 dirty = any(repo.status())
5103 node = ctx.node()
5104 node = ctx.node()
5104 if node != parent:
5105 if node != parent:
5105 if dirty:
5106 if dirty:
5106 hint = _("uncommitted changes, use --all to discard all"
5107 hint = _("uncommitted changes, use --all to discard all"
5107 " changes, or 'hg update %d' to update") % ctx.rev()
5108 " changes, or 'hg update %d' to update") % ctx.rev()
5108 else:
5109 else:
5109 hint = _("use --all to revert all files,"
5110 hint = _("use --all to revert all files,"
5110 " or 'hg update %d' to update") % ctx.rev()
5111 " or 'hg update %d' to update") % ctx.rev()
5111 elif dirty:
5112 elif dirty:
5112 hint = _("uncommitted changes, use --all to discard all changes")
5113 hint = _("uncommitted changes, use --all to discard all changes")
5113 else:
5114 else:
5114 hint = _("use --all to revert all files")
5115 hint = _("use --all to revert all files")
5115 raise error.Abort(msg, hint=hint)
5116 raise error.Abort(msg, hint=hint)
5116
5117
5117 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats,
5118 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats,
5118 **pycompat.strkwargs(opts))
5119 **pycompat.strkwargs(opts))
5119
5120
5120 @command(
5121 @command(
5121 'rollback',
5122 'rollback',
5122 dryrunopts + [('f', 'force', False, _('ignore safety measures'))],
5123 dryrunopts + [('f', 'force', False, _('ignore safety measures'))],
5123 helpcategory=command.CATEGORY_MAINTENANCE)
5124 helpcategory=command.CATEGORY_MAINTENANCE)
5124 def rollback(ui, repo, **opts):
5125 def rollback(ui, repo, **opts):
5125 """roll back the last transaction (DANGEROUS) (DEPRECATED)
5126 """roll back the last transaction (DANGEROUS) (DEPRECATED)
5126
5127
5127 Please use :hg:`commit --amend` instead of rollback to correct
5128 Please use :hg:`commit --amend` instead of rollback to correct
5128 mistakes in the last commit.
5129 mistakes in the last commit.
5129
5130
5130 This command should be used with care. There is only one level of
5131 This command should be used with care. There is only one level of
5131 rollback, and there is no way to undo a rollback. It will also
5132 rollback, and there is no way to undo a rollback. It will also
5132 restore the dirstate at the time of the last transaction, losing
5133 restore the dirstate at the time of the last transaction, losing
5133 any dirstate changes since that time. This command does not alter
5134 any dirstate changes since that time. This command does not alter
5134 the working directory.
5135 the working directory.
5135
5136
5136 Transactions are used to encapsulate the effects of all commands
5137 Transactions are used to encapsulate the effects of all commands
5137 that create new changesets or propagate existing changesets into a
5138 that create new changesets or propagate existing changesets into a
5138 repository.
5139 repository.
5139
5140
5140 .. container:: verbose
5141 .. container:: verbose
5141
5142
5142 For example, the following commands are transactional, and their
5143 For example, the following commands are transactional, and their
5143 effects can be rolled back:
5144 effects can be rolled back:
5144
5145
5145 - commit
5146 - commit
5146 - import
5147 - import
5147 - pull
5148 - pull
5148 - push (with this repository as the destination)
5149 - push (with this repository as the destination)
5149 - unbundle
5150 - unbundle
5150
5151
5151 To avoid permanent data loss, rollback will refuse to rollback a
5152 To avoid permanent data loss, rollback will refuse to rollback a
5152 commit transaction if it isn't checked out. Use --force to
5153 commit transaction if it isn't checked out. Use --force to
5153 override this protection.
5154 override this protection.
5154
5155
5155 The rollback command can be entirely disabled by setting the
5156 The rollback command can be entirely disabled by setting the
5156 ``ui.rollback`` configuration setting to false. If you're here
5157 ``ui.rollback`` configuration setting to false. If you're here
5157 because you want to use rollback and it's disabled, you can
5158 because you want to use rollback and it's disabled, you can
5158 re-enable the command by setting ``ui.rollback`` to true.
5159 re-enable the command by setting ``ui.rollback`` to true.
5159
5160
5160 This command is not intended for use on public repositories. Once
5161 This command is not intended for use on public repositories. Once
5161 changes are visible for pull by other users, rolling a transaction
5162 changes are visible for pull by other users, rolling a transaction
5162 back locally is ineffective (someone else may already have pulled
5163 back locally is ineffective (someone else may already have pulled
5163 the changes). Furthermore, a race is possible with readers of the
5164 the changes). Furthermore, a race is possible with readers of the
5164 repository; for example an in-progress pull from the repository
5165 repository; for example an in-progress pull from the repository
5165 may fail if a rollback is performed.
5166 may fail if a rollback is performed.
5166
5167
5167 Returns 0 on success, 1 if no rollback data is available.
5168 Returns 0 on success, 1 if no rollback data is available.
5168 """
5169 """
5169 if not ui.configbool('ui', 'rollback'):
5170 if not ui.configbool('ui', 'rollback'):
5170 raise error.Abort(_('rollback is disabled because it is unsafe'),
5171 raise error.Abort(_('rollback is disabled because it is unsafe'),
5171 hint=('see `hg help -v rollback` for information'))
5172 hint=('see `hg help -v rollback` for information'))
5172 return repo.rollback(dryrun=opts.get(r'dry_run'),
5173 return repo.rollback(dryrun=opts.get(r'dry_run'),
5173 force=opts.get(r'force'))
5174 force=opts.get(r'force'))
5174
5175
5175 @command(
5176 @command(
5176 'root', [], intents={INTENT_READONLY},
5177 'root', [], intents={INTENT_READONLY},
5177 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
5178 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
5178 def root(ui, repo):
5179 def root(ui, repo):
5179 """print the root (top) of the current working directory
5180 """print the root (top) of the current working directory
5180
5181
5181 Print the root directory of the current repository.
5182 Print the root directory of the current repository.
5182
5183
5183 Returns 0 on success.
5184 Returns 0 on success.
5184 """
5185 """
5185 ui.write(repo.root + "\n")
5186 ui.write(repo.root + "\n")
5186
5187
5187 @command('serve',
5188 @command('serve',
5188 [('A', 'accesslog', '', _('name of access log file to write to'),
5189 [('A', 'accesslog', '', _('name of access log file to write to'),
5189 _('FILE')),
5190 _('FILE')),
5190 ('d', 'daemon', None, _('run server in background')),
5191 ('d', 'daemon', None, _('run server in background')),
5191 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
5192 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
5192 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5193 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5193 # use string type, then we can check if something was passed
5194 # use string type, then we can check if something was passed
5194 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5195 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5195 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5196 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5196 _('ADDR')),
5197 _('ADDR')),
5197 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5198 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5198 _('PREFIX')),
5199 _('PREFIX')),
5199 ('n', 'name', '',
5200 ('n', 'name', '',
5200 _('name to show in web pages (default: working directory)'), _('NAME')),
5201 _('name to show in web pages (default: working directory)'), _('NAME')),
5201 ('', 'web-conf', '',
5202 ('', 'web-conf', '',
5202 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
5203 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
5203 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5204 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5204 _('FILE')),
5205 _('FILE')),
5205 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5206 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5206 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
5207 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
5207 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
5208 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
5208 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5209 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5209 ('', 'style', '', _('template style to use'), _('STYLE')),
5210 ('', 'style', '', _('template style to use'), _('STYLE')),
5210 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5211 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5211 ('', 'certificate', '', _('SSL certificate file'), _('FILE')),
5212 ('', 'certificate', '', _('SSL certificate file'), _('FILE')),
5212 ('', 'print-url', None, _('start and print only the URL'))]
5213 ('', 'print-url', None, _('start and print only the URL'))]
5213 + subrepoopts,
5214 + subrepoopts,
5214 _('[OPTION]...'),
5215 _('[OPTION]...'),
5215 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5216 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5216 helpbasic=True, optionalrepo=True)
5217 helpbasic=True, optionalrepo=True)
5217 def serve(ui, repo, **opts):
5218 def serve(ui, repo, **opts):
5218 """start stand-alone webserver
5219 """start stand-alone webserver
5219
5220
5220 Start a local HTTP repository browser and pull server. You can use
5221 Start a local HTTP repository browser and pull server. You can use
5221 this for ad-hoc sharing and browsing of repositories. It is
5222 this for ad-hoc sharing and browsing of repositories. It is
5222 recommended to use a real web server to serve a repository for
5223 recommended to use a real web server to serve a repository for
5223 longer periods of time.
5224 longer periods of time.
5224
5225
5225 Please note that the server does not implement access control.
5226 Please note that the server does not implement access control.
5226 This means that, by default, anybody can read from the server and
5227 This means that, by default, anybody can read from the server and
5227 nobody can write to it by default. Set the ``web.allow-push``
5228 nobody can write to it by default. Set the ``web.allow-push``
5228 option to ``*`` to allow everybody to push to the server. You
5229 option to ``*`` to allow everybody to push to the server. You
5229 should use a real web server if you need to authenticate users.
5230 should use a real web server if you need to authenticate users.
5230
5231
5231 By default, the server logs accesses to stdout and errors to
5232 By default, the server logs accesses to stdout and errors to
5232 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5233 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5233 files.
5234 files.
5234
5235
5235 To have the server choose a free port number to listen on, specify
5236 To have the server choose a free port number to listen on, specify
5236 a port number of 0; in this case, the server will print the port
5237 a port number of 0; in this case, the server will print the port
5237 number it uses.
5238 number it uses.
5238
5239
5239 Returns 0 on success.
5240 Returns 0 on success.
5240 """
5241 """
5241
5242
5242 opts = pycompat.byteskwargs(opts)
5243 opts = pycompat.byteskwargs(opts)
5243 if opts["stdio"] and opts["cmdserver"]:
5244 if opts["stdio"] and opts["cmdserver"]:
5244 raise error.Abort(_("cannot use --stdio with --cmdserver"))
5245 raise error.Abort(_("cannot use --stdio with --cmdserver"))
5245 if opts["print_url"] and ui.verbose:
5246 if opts["print_url"] and ui.verbose:
5246 raise error.Abort(_("cannot use --print-url with --verbose"))
5247 raise error.Abort(_("cannot use --print-url with --verbose"))
5247
5248
5248 if opts["stdio"]:
5249 if opts["stdio"]:
5249 if repo is None:
5250 if repo is None:
5250 raise error.RepoError(_("there is no Mercurial repository here"
5251 raise error.RepoError(_("there is no Mercurial repository here"
5251 " (.hg not found)"))
5252 " (.hg not found)"))
5252 s = wireprotoserver.sshserver(ui, repo)
5253 s = wireprotoserver.sshserver(ui, repo)
5253 s.serve_forever()
5254 s.serve_forever()
5254
5255
5255 service = server.createservice(ui, repo, opts)
5256 service = server.createservice(ui, repo, opts)
5256 return server.runservice(opts, initfn=service.init, runfn=service.run)
5257 return server.runservice(opts, initfn=service.init, runfn=service.run)
5257
5258
5258 _NOTTERSE = 'nothing'
5259 _NOTTERSE = 'nothing'
5259
5260
5260 @command('status|st',
5261 @command('status|st',
5261 [('A', 'all', None, _('show status of all files')),
5262 [('A', 'all', None, _('show status of all files')),
5262 ('m', 'modified', None, _('show only modified files')),
5263 ('m', 'modified', None, _('show only modified files')),
5263 ('a', 'added', None, _('show only added files')),
5264 ('a', 'added', None, _('show only added files')),
5264 ('r', 'removed', None, _('show only removed files')),
5265 ('r', 'removed', None, _('show only removed files')),
5265 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5266 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5266 ('c', 'clean', None, _('show only files without changes')),
5267 ('c', 'clean', None, _('show only files without changes')),
5267 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5268 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5268 ('i', 'ignored', None, _('show only ignored files')),
5269 ('i', 'ignored', None, _('show only ignored files')),
5269 ('n', 'no-status', None, _('hide status prefix')),
5270 ('n', 'no-status', None, _('hide status prefix')),
5270 ('t', 'terse', _NOTTERSE, _('show the terse output (EXPERIMENTAL)')),
5271 ('t', 'terse', _NOTTERSE, _('show the terse output (EXPERIMENTAL)')),
5271 ('C', 'copies', None, _('show source of copied files')),
5272 ('C', 'copies', None, _('show source of copied files')),
5272 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5273 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5273 ('', 'rev', [], _('show difference from revision'), _('REV')),
5274 ('', 'rev', [], _('show difference from revision'), _('REV')),
5274 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5275 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5275 ] + walkopts + subrepoopts + formatteropts,
5276 ] + walkopts + subrepoopts + formatteropts,
5276 _('[OPTION]... [FILE]...'),
5277 _('[OPTION]... [FILE]...'),
5277 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5278 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5278 helpbasic=True, inferrepo=True,
5279 helpbasic=True, inferrepo=True,
5279 intents={INTENT_READONLY})
5280 intents={INTENT_READONLY})
5280 def status(ui, repo, *pats, **opts):
5281 def status(ui, repo, *pats, **opts):
5281 """show changed files in the working directory
5282 """show changed files in the working directory
5282
5283
5283 Show status of files in the repository. If names are given, only
5284 Show status of files in the repository. If names are given, only
5284 files that match are shown. Files that are clean or ignored or
5285 files that match are shown. Files that are clean or ignored or
5285 the source of a copy/move operation, are not listed unless
5286 the source of a copy/move operation, are not listed unless
5286 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5287 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5287 Unless options described with "show only ..." are given, the
5288 Unless options described with "show only ..." are given, the
5288 options -mardu are used.
5289 options -mardu are used.
5289
5290
5290 Option -q/--quiet hides untracked (unknown and ignored) files
5291 Option -q/--quiet hides untracked (unknown and ignored) files
5291 unless explicitly requested with -u/--unknown or -i/--ignored.
5292 unless explicitly requested with -u/--unknown or -i/--ignored.
5292
5293
5293 .. note::
5294 .. note::
5294
5295
5295 :hg:`status` may appear to disagree with diff if permissions have
5296 :hg:`status` may appear to disagree with diff if permissions have
5296 changed or a merge has occurred. The standard diff format does
5297 changed or a merge has occurred. The standard diff format does
5297 not report permission changes and diff only reports changes
5298 not report permission changes and diff only reports changes
5298 relative to one merge parent.
5299 relative to one merge parent.
5299
5300
5300 If one revision is given, it is used as the base revision.
5301 If one revision is given, it is used as the base revision.
5301 If two revisions are given, the differences between them are
5302 If two revisions are given, the differences between them are
5302 shown. The --change option can also be used as a shortcut to list
5303 shown. The --change option can also be used as a shortcut to list
5303 the changed files of a revision from its first parent.
5304 the changed files of a revision from its first parent.
5304
5305
5305 The codes used to show the status of files are::
5306 The codes used to show the status of files are::
5306
5307
5307 M = modified
5308 M = modified
5308 A = added
5309 A = added
5309 R = removed
5310 R = removed
5310 C = clean
5311 C = clean
5311 ! = missing (deleted by non-hg command, but still tracked)
5312 ! = missing (deleted by non-hg command, but still tracked)
5312 ? = not tracked
5313 ? = not tracked
5313 I = ignored
5314 I = ignored
5314 = origin of the previous file (with --copies)
5315 = origin of the previous file (with --copies)
5315
5316
5316 .. container:: verbose
5317 .. container:: verbose
5317
5318
5318 The -t/--terse option abbreviates the output by showing only the directory
5319 The -t/--terse option abbreviates the output by showing only the directory
5319 name if all the files in it share the same status. The option takes an
5320 name if all the files in it share the same status. The option takes an
5320 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
5321 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
5321 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
5322 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
5322 for 'ignored' and 'c' for clean.
5323 for 'ignored' and 'c' for clean.
5323
5324
5324 It abbreviates only those statuses which are passed. Note that clean and
5325 It abbreviates only those statuses which are passed. Note that clean and
5325 ignored files are not displayed with '--terse ic' unless the -c/--clean
5326 ignored files are not displayed with '--terse ic' unless the -c/--clean
5326 and -i/--ignored options are also used.
5327 and -i/--ignored options are also used.
5327
5328
5328 The -v/--verbose option shows information when the repository is in an
5329 The -v/--verbose option shows information when the repository is in an
5329 unfinished merge, shelve, rebase state etc. You can have this behavior
5330 unfinished merge, shelve, rebase state etc. You can have this behavior
5330 turned on by default by enabling the ``commands.status.verbose`` option.
5331 turned on by default by enabling the ``commands.status.verbose`` option.
5331
5332
5332 You can skip displaying some of these states by setting
5333 You can skip displaying some of these states by setting
5333 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
5334 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
5334 'histedit', 'merge', 'rebase', or 'unshelve'.
5335 'histedit', 'merge', 'rebase', or 'unshelve'.
5335
5336
5336 Template:
5337 Template:
5337
5338
5338 The following keywords are supported in addition to the common template
5339 The following keywords are supported in addition to the common template
5339 keywords and functions. See also :hg:`help templates`.
5340 keywords and functions. See also :hg:`help templates`.
5340
5341
5341 :path: String. Repository-absolute path of the file.
5342 :path: String. Repository-absolute path of the file.
5342 :source: String. Repository-absolute path of the file originated from.
5343 :source: String. Repository-absolute path of the file originated from.
5343 Available if ``--copies`` is specified.
5344 Available if ``--copies`` is specified.
5344 :status: String. Character denoting file's status.
5345 :status: String. Character denoting file's status.
5345
5346
5346 Examples:
5347 Examples:
5347
5348
5348 - show changes in the working directory relative to a
5349 - show changes in the working directory relative to a
5349 changeset::
5350 changeset::
5350
5351
5351 hg status --rev 9353
5352 hg status --rev 9353
5352
5353
5353 - show changes in the working directory relative to the
5354 - show changes in the working directory relative to the
5354 current directory (see :hg:`help patterns` for more information)::
5355 current directory (see :hg:`help patterns` for more information)::
5355
5356
5356 hg status re:
5357 hg status re:
5357
5358
5358 - show all changes including copies in an existing changeset::
5359 - show all changes including copies in an existing changeset::
5359
5360
5360 hg status --copies --change 9353
5361 hg status --copies --change 9353
5361
5362
5362 - get a NUL separated list of added files, suitable for xargs::
5363 - get a NUL separated list of added files, suitable for xargs::
5363
5364
5364 hg status -an0
5365 hg status -an0
5365
5366
5366 - show more information about the repository status, abbreviating
5367 - show more information about the repository status, abbreviating
5367 added, removed, modified, deleted, and untracked paths::
5368 added, removed, modified, deleted, and untracked paths::
5368
5369
5369 hg status -v -t mardu
5370 hg status -v -t mardu
5370
5371
5371 Returns 0 on success.
5372 Returns 0 on success.
5372
5373
5373 """
5374 """
5374
5375
5375 opts = pycompat.byteskwargs(opts)
5376 opts = pycompat.byteskwargs(opts)
5376 revs = opts.get('rev')
5377 revs = opts.get('rev')
5377 change = opts.get('change')
5378 change = opts.get('change')
5378 terse = opts.get('terse')
5379 terse = opts.get('terse')
5379 if terse is _NOTTERSE:
5380 if terse is _NOTTERSE:
5380 if revs:
5381 if revs:
5381 terse = ''
5382 terse = ''
5382 else:
5383 else:
5383 terse = ui.config('commands', 'status.terse')
5384 terse = ui.config('commands', 'status.terse')
5384
5385
5385 if revs and change:
5386 if revs and change:
5386 msg = _('cannot specify --rev and --change at the same time')
5387 msg = _('cannot specify --rev and --change at the same time')
5387 raise error.Abort(msg)
5388 raise error.Abort(msg)
5388 elif revs and terse:
5389 elif revs and terse:
5389 msg = _('cannot use --terse with --rev')
5390 msg = _('cannot use --terse with --rev')
5390 raise error.Abort(msg)
5391 raise error.Abort(msg)
5391 elif change:
5392 elif change:
5392 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
5393 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
5393 ctx2 = scmutil.revsingle(repo, change, None)
5394 ctx2 = scmutil.revsingle(repo, change, None)
5394 ctx1 = ctx2.p1()
5395 ctx1 = ctx2.p1()
5395 else:
5396 else:
5396 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
5397 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
5397 ctx1, ctx2 = scmutil.revpair(repo, revs)
5398 ctx1, ctx2 = scmutil.revpair(repo, revs)
5398
5399
5399 if pats or ui.configbool('commands', 'status.relative'):
5400 if pats or ui.configbool('commands', 'status.relative'):
5400 cwd = repo.getcwd()
5401 cwd = repo.getcwd()
5401 else:
5402 else:
5402 cwd = ''
5403 cwd = ''
5403
5404
5404 if opts.get('print0'):
5405 if opts.get('print0'):
5405 end = '\0'
5406 end = '\0'
5406 else:
5407 else:
5407 end = '\n'
5408 end = '\n'
5408 copy = {}
5409 copy = {}
5409 states = 'modified added removed deleted unknown ignored clean'.split()
5410 states = 'modified added removed deleted unknown ignored clean'.split()
5410 show = [k for k in states if opts.get(k)]
5411 show = [k for k in states if opts.get(k)]
5411 if opts.get('all'):
5412 if opts.get('all'):
5412 show += ui.quiet and (states[:4] + ['clean']) or states
5413 show += ui.quiet and (states[:4] + ['clean']) or states
5413
5414
5414 if not show:
5415 if not show:
5415 if ui.quiet:
5416 if ui.quiet:
5416 show = states[:4]
5417 show = states[:4]
5417 else:
5418 else:
5418 show = states[:5]
5419 show = states[:5]
5419
5420
5420 m = scmutil.match(ctx2, pats, opts)
5421 m = scmutil.match(ctx2, pats, opts)
5421 if terse:
5422 if terse:
5422 # we need to compute clean and unknown to terse
5423 # we need to compute clean and unknown to terse
5423 stat = repo.status(ctx1.node(), ctx2.node(), m,
5424 stat = repo.status(ctx1.node(), ctx2.node(), m,
5424 'ignored' in show or 'i' in terse,
5425 'ignored' in show or 'i' in terse,
5425 clean=True, unknown=True,
5426 clean=True, unknown=True,
5426 listsubrepos=opts.get('subrepos'))
5427 listsubrepos=opts.get('subrepos'))
5427
5428
5428 stat = cmdutil.tersedir(stat, terse)
5429 stat = cmdutil.tersedir(stat, terse)
5429 else:
5430 else:
5430 stat = repo.status(ctx1.node(), ctx2.node(), m,
5431 stat = repo.status(ctx1.node(), ctx2.node(), m,
5431 'ignored' in show, 'clean' in show,
5432 'ignored' in show, 'clean' in show,
5432 'unknown' in show, opts.get('subrepos'))
5433 'unknown' in show, opts.get('subrepos'))
5433
5434
5434 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
5435 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
5435
5436
5436 if (opts.get('all') or opts.get('copies')
5437 if (opts.get('all') or opts.get('copies')
5437 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
5438 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
5438 copy = copies.pathcopies(ctx1, ctx2, m)
5439 copy = copies.pathcopies(ctx1, ctx2, m)
5439
5440
5440 ui.pager('status')
5441 ui.pager('status')
5441 fm = ui.formatter('status', opts)
5442 fm = ui.formatter('status', opts)
5442 fmt = '%s' + end
5443 fmt = '%s' + end
5443 showchar = not opts.get('no_status')
5444 showchar = not opts.get('no_status')
5444
5445
5445 for state, char, files in changestates:
5446 for state, char, files in changestates:
5446 if state in show:
5447 if state in show:
5447 label = 'status.' + state
5448 label = 'status.' + state
5448 for f in files:
5449 for f in files:
5449 fm.startitem()
5450 fm.startitem()
5450 fm.context(ctx=ctx2)
5451 fm.context(ctx=ctx2)
5451 fm.data(path=f)
5452 fm.data(path=f)
5452 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5453 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5453 fm.plain(fmt % repo.pathto(f, cwd), label=label)
5454 fm.plain(fmt % repo.pathto(f, cwd), label=label)
5454 if f in copy:
5455 if f in copy:
5455 fm.data(source=copy[f])
5456 fm.data(source=copy[f])
5456 fm.plain((' %s' + end) % repo.pathto(copy[f], cwd),
5457 fm.plain((' %s' + end) % repo.pathto(copy[f], cwd),
5457 label='status.copied')
5458 label='status.copied')
5458
5459
5459 if ((ui.verbose or ui.configbool('commands', 'status.verbose'))
5460 if ((ui.verbose or ui.configbool('commands', 'status.verbose'))
5460 and not ui.plain()):
5461 and not ui.plain()):
5461 cmdutil.morestatus(repo, fm)
5462 cmdutil.morestatus(repo, fm)
5462 fm.end()
5463 fm.end()
5463
5464
5464 @command('summary|sum',
5465 @command('summary|sum',
5465 [('', 'remote', None, _('check for push and pull'))],
5466 [('', 'remote', None, _('check for push and pull'))],
5466 '[--remote]',
5467 '[--remote]',
5467 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5468 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5468 helpbasic=True,
5469 helpbasic=True,
5469 intents={INTENT_READONLY})
5470 intents={INTENT_READONLY})
5470 def summary(ui, repo, **opts):
5471 def summary(ui, repo, **opts):
5471 """summarize working directory state
5472 """summarize working directory state
5472
5473
5473 This generates a brief summary of the working directory state,
5474 This generates a brief summary of the working directory state,
5474 including parents, branch, commit status, phase and available updates.
5475 including parents, branch, commit status, phase and available updates.
5475
5476
5476 With the --remote option, this will check the default paths for
5477 With the --remote option, this will check the default paths for
5477 incoming and outgoing changes. This can be time-consuming.
5478 incoming and outgoing changes. This can be time-consuming.
5478
5479
5479 Returns 0 on success.
5480 Returns 0 on success.
5480 """
5481 """
5481
5482
5482 opts = pycompat.byteskwargs(opts)
5483 opts = pycompat.byteskwargs(opts)
5483 ui.pager('summary')
5484 ui.pager('summary')
5484 ctx = repo[None]
5485 ctx = repo[None]
5485 parents = ctx.parents()
5486 parents = ctx.parents()
5486 pnode = parents[0].node()
5487 pnode = parents[0].node()
5487 marks = []
5488 marks = []
5488
5489
5489 ms = None
5490 ms = None
5490 try:
5491 try:
5491 ms = mergemod.mergestate.read(repo)
5492 ms = mergemod.mergestate.read(repo)
5492 except error.UnsupportedMergeRecords as e:
5493 except error.UnsupportedMergeRecords as e:
5493 s = ' '.join(e.recordtypes)
5494 s = ' '.join(e.recordtypes)
5494 ui.warn(
5495 ui.warn(
5495 _('warning: merge state has unsupported record types: %s\n') % s)
5496 _('warning: merge state has unsupported record types: %s\n') % s)
5496 unresolved = []
5497 unresolved = []
5497 else:
5498 else:
5498 unresolved = list(ms.unresolved())
5499 unresolved = list(ms.unresolved())
5499
5500
5500 for p in parents:
5501 for p in parents:
5501 # label with log.changeset (instead of log.parent) since this
5502 # label with log.changeset (instead of log.parent) since this
5502 # shows a working directory parent *changeset*:
5503 # shows a working directory parent *changeset*:
5503 # i18n: column positioning for "hg summary"
5504 # i18n: column positioning for "hg summary"
5504 ui.write(_('parent: %d:%s ') % (p.rev(), p),
5505 ui.write(_('parent: %d:%s ') % (p.rev(), p),
5505 label=logcmdutil.changesetlabels(p))
5506 label=logcmdutil.changesetlabels(p))
5506 ui.write(' '.join(p.tags()), label='log.tag')
5507 ui.write(' '.join(p.tags()), label='log.tag')
5507 if p.bookmarks():
5508 if p.bookmarks():
5508 marks.extend(p.bookmarks())
5509 marks.extend(p.bookmarks())
5509 if p.rev() == -1:
5510 if p.rev() == -1:
5510 if not len(repo):
5511 if not len(repo):
5511 ui.write(_(' (empty repository)'))
5512 ui.write(_(' (empty repository)'))
5512 else:
5513 else:
5513 ui.write(_(' (no revision checked out)'))
5514 ui.write(_(' (no revision checked out)'))
5514 if p.obsolete():
5515 if p.obsolete():
5515 ui.write(_(' (obsolete)'))
5516 ui.write(_(' (obsolete)'))
5516 if p.isunstable():
5517 if p.isunstable():
5517 instabilities = (ui.label(instability, 'trouble.%s' % instability)
5518 instabilities = (ui.label(instability, 'trouble.%s' % instability)
5518 for instability in p.instabilities())
5519 for instability in p.instabilities())
5519 ui.write(' ('
5520 ui.write(' ('
5520 + ', '.join(instabilities)
5521 + ', '.join(instabilities)
5521 + ')')
5522 + ')')
5522 ui.write('\n')
5523 ui.write('\n')
5523 if p.description():
5524 if p.description():
5524 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5525 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5525 label='log.summary')
5526 label='log.summary')
5526
5527
5527 branch = ctx.branch()
5528 branch = ctx.branch()
5528 bheads = repo.branchheads(branch)
5529 bheads = repo.branchheads(branch)
5529 # i18n: column positioning for "hg summary"
5530 # i18n: column positioning for "hg summary"
5530 m = _('branch: %s\n') % branch
5531 m = _('branch: %s\n') % branch
5531 if branch != 'default':
5532 if branch != 'default':
5532 ui.write(m, label='log.branch')
5533 ui.write(m, label='log.branch')
5533 else:
5534 else:
5534 ui.status(m, label='log.branch')
5535 ui.status(m, label='log.branch')
5535
5536
5536 if marks:
5537 if marks:
5537 active = repo._activebookmark
5538 active = repo._activebookmark
5538 # i18n: column positioning for "hg summary"
5539 # i18n: column positioning for "hg summary"
5539 ui.write(_('bookmarks:'), label='log.bookmark')
5540 ui.write(_('bookmarks:'), label='log.bookmark')
5540 if active is not None:
5541 if active is not None:
5541 if active in marks:
5542 if active in marks:
5542 ui.write(' *' + active, label=bookmarks.activebookmarklabel)
5543 ui.write(' *' + active, label=bookmarks.activebookmarklabel)
5543 marks.remove(active)
5544 marks.remove(active)
5544 else:
5545 else:
5545 ui.write(' [%s]' % active, label=bookmarks.activebookmarklabel)
5546 ui.write(' [%s]' % active, label=bookmarks.activebookmarklabel)
5546 for m in marks:
5547 for m in marks:
5547 ui.write(' ' + m, label='log.bookmark')
5548 ui.write(' ' + m, label='log.bookmark')
5548 ui.write('\n', label='log.bookmark')
5549 ui.write('\n', label='log.bookmark')
5549
5550
5550 status = repo.status(unknown=True)
5551 status = repo.status(unknown=True)
5551
5552
5552 c = repo.dirstate.copies()
5553 c = repo.dirstate.copies()
5553 copied, renamed = [], []
5554 copied, renamed = [], []
5554 for d, s in c.iteritems():
5555 for d, s in c.iteritems():
5555 if s in status.removed:
5556 if s in status.removed:
5556 status.removed.remove(s)
5557 status.removed.remove(s)
5557 renamed.append(d)
5558 renamed.append(d)
5558 else:
5559 else:
5559 copied.append(d)
5560 copied.append(d)
5560 if d in status.added:
5561 if d in status.added:
5561 status.added.remove(d)
5562 status.added.remove(d)
5562
5563
5563 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5564 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5564
5565
5565 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5566 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5566 (ui.label(_('%d added'), 'status.added'), status.added),
5567 (ui.label(_('%d added'), 'status.added'), status.added),
5567 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5568 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5568 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5569 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5569 (ui.label(_('%d copied'), 'status.copied'), copied),
5570 (ui.label(_('%d copied'), 'status.copied'), copied),
5570 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5571 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5571 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5572 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5572 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5573 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5573 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5574 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5574 t = []
5575 t = []
5575 for l, s in labels:
5576 for l, s in labels:
5576 if s:
5577 if s:
5577 t.append(l % len(s))
5578 t.append(l % len(s))
5578
5579
5579 t = ', '.join(t)
5580 t = ', '.join(t)
5580 cleanworkdir = False
5581 cleanworkdir = False
5581
5582
5582 if repo.vfs.exists('graftstate'):
5583 if repo.vfs.exists('graftstate'):
5583 t += _(' (graft in progress)')
5584 t += _(' (graft in progress)')
5584 if repo.vfs.exists('updatestate'):
5585 if repo.vfs.exists('updatestate'):
5585 t += _(' (interrupted update)')
5586 t += _(' (interrupted update)')
5586 elif len(parents) > 1:
5587 elif len(parents) > 1:
5587 t += _(' (merge)')
5588 t += _(' (merge)')
5588 elif branch != parents[0].branch():
5589 elif branch != parents[0].branch():
5589 t += _(' (new branch)')
5590 t += _(' (new branch)')
5590 elif (parents[0].closesbranch() and
5591 elif (parents[0].closesbranch() and
5591 pnode in repo.branchheads(branch, closed=True)):
5592 pnode in repo.branchheads(branch, closed=True)):
5592 t += _(' (head closed)')
5593 t += _(' (head closed)')
5593 elif not (status.modified or status.added or status.removed or renamed or
5594 elif not (status.modified or status.added or status.removed or renamed or
5594 copied or subs):
5595 copied or subs):
5595 t += _(' (clean)')
5596 t += _(' (clean)')
5596 cleanworkdir = True
5597 cleanworkdir = True
5597 elif pnode not in bheads:
5598 elif pnode not in bheads:
5598 t += _(' (new branch head)')
5599 t += _(' (new branch head)')
5599
5600
5600 if parents:
5601 if parents:
5601 pendingphase = max(p.phase() for p in parents)
5602 pendingphase = max(p.phase() for p in parents)
5602 else:
5603 else:
5603 pendingphase = phases.public
5604 pendingphase = phases.public
5604
5605
5605 if pendingphase > phases.newcommitphase(ui):
5606 if pendingphase > phases.newcommitphase(ui):
5606 t += ' (%s)' % phases.phasenames[pendingphase]
5607 t += ' (%s)' % phases.phasenames[pendingphase]
5607
5608
5608 if cleanworkdir:
5609 if cleanworkdir:
5609 # i18n: column positioning for "hg summary"
5610 # i18n: column positioning for "hg summary"
5610 ui.status(_('commit: %s\n') % t.strip())
5611 ui.status(_('commit: %s\n') % t.strip())
5611 else:
5612 else:
5612 # i18n: column positioning for "hg summary"
5613 # i18n: column positioning for "hg summary"
5613 ui.write(_('commit: %s\n') % t.strip())
5614 ui.write(_('commit: %s\n') % t.strip())
5614
5615
5615 # all ancestors of branch heads - all ancestors of parent = new csets
5616 # all ancestors of branch heads - all ancestors of parent = new csets
5616 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5617 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5617 bheads))
5618 bheads))
5618
5619
5619 if new == 0:
5620 if new == 0:
5620 # i18n: column positioning for "hg summary"
5621 # i18n: column positioning for "hg summary"
5621 ui.status(_('update: (current)\n'))
5622 ui.status(_('update: (current)\n'))
5622 elif pnode not in bheads:
5623 elif pnode not in bheads:
5623 # i18n: column positioning for "hg summary"
5624 # i18n: column positioning for "hg summary"
5624 ui.write(_('update: %d new changesets (update)\n') % new)
5625 ui.write(_('update: %d new changesets (update)\n') % new)
5625 else:
5626 else:
5626 # i18n: column positioning for "hg summary"
5627 # i18n: column positioning for "hg summary"
5627 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5628 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5628 (new, len(bheads)))
5629 (new, len(bheads)))
5629
5630
5630 t = []
5631 t = []
5631 draft = len(repo.revs('draft()'))
5632 draft = len(repo.revs('draft()'))
5632 if draft:
5633 if draft:
5633 t.append(_('%d draft') % draft)
5634 t.append(_('%d draft') % draft)
5634 secret = len(repo.revs('secret()'))
5635 secret = len(repo.revs('secret()'))
5635 if secret:
5636 if secret:
5636 t.append(_('%d secret') % secret)
5637 t.append(_('%d secret') % secret)
5637
5638
5638 if draft or secret:
5639 if draft or secret:
5639 ui.status(_('phases: %s\n') % ', '.join(t))
5640 ui.status(_('phases: %s\n') % ', '.join(t))
5640
5641
5641 if obsolete.isenabled(repo, obsolete.createmarkersopt):
5642 if obsolete.isenabled(repo, obsolete.createmarkersopt):
5642 for trouble in ("orphan", "contentdivergent", "phasedivergent"):
5643 for trouble in ("orphan", "contentdivergent", "phasedivergent"):
5643 numtrouble = len(repo.revs(trouble + "()"))
5644 numtrouble = len(repo.revs(trouble + "()"))
5644 # We write all the possibilities to ease translation
5645 # We write all the possibilities to ease translation
5645 troublemsg = {
5646 troublemsg = {
5646 "orphan": _("orphan: %d changesets"),
5647 "orphan": _("orphan: %d changesets"),
5647 "contentdivergent": _("content-divergent: %d changesets"),
5648 "contentdivergent": _("content-divergent: %d changesets"),
5648 "phasedivergent": _("phase-divergent: %d changesets"),
5649 "phasedivergent": _("phase-divergent: %d changesets"),
5649 }
5650 }
5650 if numtrouble > 0:
5651 if numtrouble > 0:
5651 ui.status(troublemsg[trouble] % numtrouble + "\n")
5652 ui.status(troublemsg[trouble] % numtrouble + "\n")
5652
5653
5653 cmdutil.summaryhooks(ui, repo)
5654 cmdutil.summaryhooks(ui, repo)
5654
5655
5655 if opts.get('remote'):
5656 if opts.get('remote'):
5656 needsincoming, needsoutgoing = True, True
5657 needsincoming, needsoutgoing = True, True
5657 else:
5658 else:
5658 needsincoming, needsoutgoing = False, False
5659 needsincoming, needsoutgoing = False, False
5659 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5660 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5660 if i:
5661 if i:
5661 needsincoming = True
5662 needsincoming = True
5662 if o:
5663 if o:
5663 needsoutgoing = True
5664 needsoutgoing = True
5664 if not needsincoming and not needsoutgoing:
5665 if not needsincoming and not needsoutgoing:
5665 return
5666 return
5666
5667
5667 def getincoming():
5668 def getincoming():
5668 source, branches = hg.parseurl(ui.expandpath('default'))
5669 source, branches = hg.parseurl(ui.expandpath('default'))
5669 sbranch = branches[0]
5670 sbranch = branches[0]
5670 try:
5671 try:
5671 other = hg.peer(repo, {}, source)
5672 other = hg.peer(repo, {}, source)
5672 except error.RepoError:
5673 except error.RepoError:
5673 if opts.get('remote'):
5674 if opts.get('remote'):
5674 raise
5675 raise
5675 return source, sbranch, None, None, None
5676 return source, sbranch, None, None, None
5676 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5677 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5677 if revs:
5678 if revs:
5678 revs = [other.lookup(rev) for rev in revs]
5679 revs = [other.lookup(rev) for rev in revs]
5679 ui.debug('comparing with %s\n' % util.hidepassword(source))
5680 ui.debug('comparing with %s\n' % util.hidepassword(source))
5680 repo.ui.pushbuffer()
5681 repo.ui.pushbuffer()
5681 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5682 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5682 repo.ui.popbuffer()
5683 repo.ui.popbuffer()
5683 return source, sbranch, other, commoninc, commoninc[1]
5684 return source, sbranch, other, commoninc, commoninc[1]
5684
5685
5685 if needsincoming:
5686 if needsincoming:
5686 source, sbranch, sother, commoninc, incoming = getincoming()
5687 source, sbranch, sother, commoninc, incoming = getincoming()
5687 else:
5688 else:
5688 source = sbranch = sother = commoninc = incoming = None
5689 source = sbranch = sother = commoninc = incoming = None
5689
5690
5690 def getoutgoing():
5691 def getoutgoing():
5691 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5692 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5692 dbranch = branches[0]
5693 dbranch = branches[0]
5693 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5694 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5694 if source != dest:
5695 if source != dest:
5695 try:
5696 try:
5696 dother = hg.peer(repo, {}, dest)
5697 dother = hg.peer(repo, {}, dest)
5697 except error.RepoError:
5698 except error.RepoError:
5698 if opts.get('remote'):
5699 if opts.get('remote'):
5699 raise
5700 raise
5700 return dest, dbranch, None, None
5701 return dest, dbranch, None, None
5701 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5702 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5702 elif sother is None:
5703 elif sother is None:
5703 # there is no explicit destination peer, but source one is invalid
5704 # there is no explicit destination peer, but source one is invalid
5704 return dest, dbranch, None, None
5705 return dest, dbranch, None, None
5705 else:
5706 else:
5706 dother = sother
5707 dother = sother
5707 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5708 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5708 common = None
5709 common = None
5709 else:
5710 else:
5710 common = commoninc
5711 common = commoninc
5711 if revs:
5712 if revs:
5712 revs = [repo.lookup(rev) for rev in revs]
5713 revs = [repo.lookup(rev) for rev in revs]
5713 repo.ui.pushbuffer()
5714 repo.ui.pushbuffer()
5714 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5715 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5715 commoninc=common)
5716 commoninc=common)
5716 repo.ui.popbuffer()
5717 repo.ui.popbuffer()
5717 return dest, dbranch, dother, outgoing
5718 return dest, dbranch, dother, outgoing
5718
5719
5719 if needsoutgoing:
5720 if needsoutgoing:
5720 dest, dbranch, dother, outgoing = getoutgoing()
5721 dest, dbranch, dother, outgoing = getoutgoing()
5721 else:
5722 else:
5722 dest = dbranch = dother = outgoing = None
5723 dest = dbranch = dother = outgoing = None
5723
5724
5724 if opts.get('remote'):
5725 if opts.get('remote'):
5725 t = []
5726 t = []
5726 if incoming:
5727 if incoming:
5727 t.append(_('1 or more incoming'))
5728 t.append(_('1 or more incoming'))
5728 o = outgoing.missing
5729 o = outgoing.missing
5729 if o:
5730 if o:
5730 t.append(_('%d outgoing') % len(o))
5731 t.append(_('%d outgoing') % len(o))
5731 other = dother or sother
5732 other = dother or sother
5732 if 'bookmarks' in other.listkeys('namespaces'):
5733 if 'bookmarks' in other.listkeys('namespaces'):
5733 counts = bookmarks.summary(repo, other)
5734 counts = bookmarks.summary(repo, other)
5734 if counts[0] > 0:
5735 if counts[0] > 0:
5735 t.append(_('%d incoming bookmarks') % counts[0])
5736 t.append(_('%d incoming bookmarks') % counts[0])
5736 if counts[1] > 0:
5737 if counts[1] > 0:
5737 t.append(_('%d outgoing bookmarks') % counts[1])
5738 t.append(_('%d outgoing bookmarks') % counts[1])
5738
5739
5739 if t:
5740 if t:
5740 # i18n: column positioning for "hg summary"
5741 # i18n: column positioning for "hg summary"
5741 ui.write(_('remote: %s\n') % (', '.join(t)))
5742 ui.write(_('remote: %s\n') % (', '.join(t)))
5742 else:
5743 else:
5743 # i18n: column positioning for "hg summary"
5744 # i18n: column positioning for "hg summary"
5744 ui.status(_('remote: (synced)\n'))
5745 ui.status(_('remote: (synced)\n'))
5745
5746
5746 cmdutil.summaryremotehooks(ui, repo, opts,
5747 cmdutil.summaryremotehooks(ui, repo, opts,
5747 ((source, sbranch, sother, commoninc),
5748 ((source, sbranch, sother, commoninc),
5748 (dest, dbranch, dother, outgoing)))
5749 (dest, dbranch, dother, outgoing)))
5749
5750
5750 @command('tag',
5751 @command('tag',
5751 [('f', 'force', None, _('force tag')),
5752 [('f', 'force', None, _('force tag')),
5752 ('l', 'local', None, _('make the tag local')),
5753 ('l', 'local', None, _('make the tag local')),
5753 ('r', 'rev', '', _('revision to tag'), _('REV')),
5754 ('r', 'rev', '', _('revision to tag'), _('REV')),
5754 ('', 'remove', None, _('remove a tag')),
5755 ('', 'remove', None, _('remove a tag')),
5755 # -l/--local is already there, commitopts cannot be used
5756 # -l/--local is already there, commitopts cannot be used
5756 ('e', 'edit', None, _('invoke editor on commit messages')),
5757 ('e', 'edit', None, _('invoke editor on commit messages')),
5757 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5758 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5758 ] + commitopts2,
5759 ] + commitopts2,
5759 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
5760 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
5760 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
5761 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
5761 def tag(ui, repo, name1, *names, **opts):
5762 def tag(ui, repo, name1, *names, **opts):
5762 """add one or more tags for the current or given revision
5763 """add one or more tags for the current or given revision
5763
5764
5764 Name a particular revision using <name>.
5765 Name a particular revision using <name>.
5765
5766
5766 Tags are used to name particular revisions of the repository and are
5767 Tags are used to name particular revisions of the repository and are
5767 very useful to compare different revisions, to go back to significant
5768 very useful to compare different revisions, to go back to significant
5768 earlier versions or to mark branch points as releases, etc. Changing
5769 earlier versions or to mark branch points as releases, etc. Changing
5769 an existing tag is normally disallowed; use -f/--force to override.
5770 an existing tag is normally disallowed; use -f/--force to override.
5770
5771
5771 If no revision is given, the parent of the working directory is
5772 If no revision is given, the parent of the working directory is
5772 used.
5773 used.
5773
5774
5774 To facilitate version control, distribution, and merging of tags,
5775 To facilitate version control, distribution, and merging of tags,
5775 they are stored as a file named ".hgtags" which is managed similarly
5776 they are stored as a file named ".hgtags" which is managed similarly
5776 to other project files and can be hand-edited if necessary. This
5777 to other project files and can be hand-edited if necessary. This
5777 also means that tagging creates a new commit. The file
5778 also means that tagging creates a new commit. The file
5778 ".hg/localtags" is used for local tags (not shared among
5779 ".hg/localtags" is used for local tags (not shared among
5779 repositories).
5780 repositories).
5780
5781
5781 Tag commits are usually made at the head of a branch. If the parent
5782 Tag commits are usually made at the head of a branch. If the parent
5782 of the working directory is not a branch head, :hg:`tag` aborts; use
5783 of the working directory is not a branch head, :hg:`tag` aborts; use
5783 -f/--force to force the tag commit to be based on a non-head
5784 -f/--force to force the tag commit to be based on a non-head
5784 changeset.
5785 changeset.
5785
5786
5786 See :hg:`help dates` for a list of formats valid for -d/--date.
5787 See :hg:`help dates` for a list of formats valid for -d/--date.
5787
5788
5788 Since tag names have priority over branch names during revision
5789 Since tag names have priority over branch names during revision
5789 lookup, using an existing branch name as a tag name is discouraged.
5790 lookup, using an existing branch name as a tag name is discouraged.
5790
5791
5791 Returns 0 on success.
5792 Returns 0 on success.
5792 """
5793 """
5793 opts = pycompat.byteskwargs(opts)
5794 opts = pycompat.byteskwargs(opts)
5794 with repo.wlock(), repo.lock():
5795 with repo.wlock(), repo.lock():
5795 rev_ = "."
5796 rev_ = "."
5796 names = [t.strip() for t in (name1,) + names]
5797 names = [t.strip() for t in (name1,) + names]
5797 if len(names) != len(set(names)):
5798 if len(names) != len(set(names)):
5798 raise error.Abort(_('tag names must be unique'))
5799 raise error.Abort(_('tag names must be unique'))
5799 for n in names:
5800 for n in names:
5800 scmutil.checknewlabel(repo, n, 'tag')
5801 scmutil.checknewlabel(repo, n, 'tag')
5801 if not n:
5802 if not n:
5802 raise error.Abort(_('tag names cannot consist entirely of '
5803 raise error.Abort(_('tag names cannot consist entirely of '
5803 'whitespace'))
5804 'whitespace'))
5804 if opts.get('rev') and opts.get('remove'):
5805 if opts.get('rev') and opts.get('remove'):
5805 raise error.Abort(_("--rev and --remove are incompatible"))
5806 raise error.Abort(_("--rev and --remove are incompatible"))
5806 if opts.get('rev'):
5807 if opts.get('rev'):
5807 rev_ = opts['rev']
5808 rev_ = opts['rev']
5808 message = opts.get('message')
5809 message = opts.get('message')
5809 if opts.get('remove'):
5810 if opts.get('remove'):
5810 if opts.get('local'):
5811 if opts.get('local'):
5811 expectedtype = 'local'
5812 expectedtype = 'local'
5812 else:
5813 else:
5813 expectedtype = 'global'
5814 expectedtype = 'global'
5814
5815
5815 for n in names:
5816 for n in names:
5816 if not repo.tagtype(n):
5817 if not repo.tagtype(n):
5817 raise error.Abort(_("tag '%s' does not exist") % n)
5818 raise error.Abort(_("tag '%s' does not exist") % n)
5818 if repo.tagtype(n) != expectedtype:
5819 if repo.tagtype(n) != expectedtype:
5819 if expectedtype == 'global':
5820 if expectedtype == 'global':
5820 raise error.Abort(_("tag '%s' is not a global tag") % n)
5821 raise error.Abort(_("tag '%s' is not a global tag") % n)
5821 else:
5822 else:
5822 raise error.Abort(_("tag '%s' is not a local tag") % n)
5823 raise error.Abort(_("tag '%s' is not a local tag") % n)
5823 rev_ = 'null'
5824 rev_ = 'null'
5824 if not message:
5825 if not message:
5825 # we don't translate commit messages
5826 # we don't translate commit messages
5826 message = 'Removed tag %s' % ', '.join(names)
5827 message = 'Removed tag %s' % ', '.join(names)
5827 elif not opts.get('force'):
5828 elif not opts.get('force'):
5828 for n in names:
5829 for n in names:
5829 if n in repo.tags():
5830 if n in repo.tags():
5830 raise error.Abort(_("tag '%s' already exists "
5831 raise error.Abort(_("tag '%s' already exists "
5831 "(use -f to force)") % n)
5832 "(use -f to force)") % n)
5832 if not opts.get('local'):
5833 if not opts.get('local'):
5833 p1, p2 = repo.dirstate.parents()
5834 p1, p2 = repo.dirstate.parents()
5834 if p2 != nullid:
5835 if p2 != nullid:
5835 raise error.Abort(_('uncommitted merge'))
5836 raise error.Abort(_('uncommitted merge'))
5836 bheads = repo.branchheads()
5837 bheads = repo.branchheads()
5837 if not opts.get('force') and bheads and p1 not in bheads:
5838 if not opts.get('force') and bheads and p1 not in bheads:
5838 raise error.Abort(_('working directory is not at a branch head '
5839 raise error.Abort(_('working directory is not at a branch head '
5839 '(use -f to force)'))
5840 '(use -f to force)'))
5840 node = scmutil.revsingle(repo, rev_).node()
5841 node = scmutil.revsingle(repo, rev_).node()
5841
5842
5842 if not message:
5843 if not message:
5843 # we don't translate commit messages
5844 # we don't translate commit messages
5844 message = ('Added tag %s for changeset %s' %
5845 message = ('Added tag %s for changeset %s' %
5845 (', '.join(names), short(node)))
5846 (', '.join(names), short(node)))
5846
5847
5847 date = opts.get('date')
5848 date = opts.get('date')
5848 if date:
5849 if date:
5849 date = dateutil.parsedate(date)
5850 date = dateutil.parsedate(date)
5850
5851
5851 if opts.get('remove'):
5852 if opts.get('remove'):
5852 editform = 'tag.remove'
5853 editform = 'tag.remove'
5853 else:
5854 else:
5854 editform = 'tag.add'
5855 editform = 'tag.add'
5855 editor = cmdutil.getcommiteditor(editform=editform,
5856 editor = cmdutil.getcommiteditor(editform=editform,
5856 **pycompat.strkwargs(opts))
5857 **pycompat.strkwargs(opts))
5857
5858
5858 # don't allow tagging the null rev
5859 # don't allow tagging the null rev
5859 if (not opts.get('remove') and
5860 if (not opts.get('remove') and
5860 scmutil.revsingle(repo, rev_).rev() == nullrev):
5861 scmutil.revsingle(repo, rev_).rev() == nullrev):
5861 raise error.Abort(_("cannot tag null revision"))
5862 raise error.Abort(_("cannot tag null revision"))
5862
5863
5863 tagsmod.tag(repo, names, node, message, opts.get('local'),
5864 tagsmod.tag(repo, names, node, message, opts.get('local'),
5864 opts.get('user'), date, editor=editor)
5865 opts.get('user'), date, editor=editor)
5865
5866
5866 @command(
5867 @command(
5867 'tags', formatteropts, '',
5868 'tags', formatteropts, '',
5868 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5869 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5869 intents={INTENT_READONLY})
5870 intents={INTENT_READONLY})
5870 def tags(ui, repo, **opts):
5871 def tags(ui, repo, **opts):
5871 """list repository tags
5872 """list repository tags
5872
5873
5873 This lists both regular and local tags. When the -v/--verbose
5874 This lists both regular and local tags. When the -v/--verbose
5874 switch is used, a third column "local" is printed for local tags.
5875 switch is used, a third column "local" is printed for local tags.
5875 When the -q/--quiet switch is used, only the tag name is printed.
5876 When the -q/--quiet switch is used, only the tag name is printed.
5876
5877
5877 .. container:: verbose
5878 .. container:: verbose
5878
5879
5879 Template:
5880 Template:
5880
5881
5881 The following keywords are supported in addition to the common template
5882 The following keywords are supported in addition to the common template
5882 keywords and functions such as ``{tag}``. See also
5883 keywords and functions such as ``{tag}``. See also
5883 :hg:`help templates`.
5884 :hg:`help templates`.
5884
5885
5885 :type: String. ``local`` for local tags.
5886 :type: String. ``local`` for local tags.
5886
5887
5887 Returns 0 on success.
5888 Returns 0 on success.
5888 """
5889 """
5889
5890
5890 opts = pycompat.byteskwargs(opts)
5891 opts = pycompat.byteskwargs(opts)
5891 ui.pager('tags')
5892 ui.pager('tags')
5892 fm = ui.formatter('tags', opts)
5893 fm = ui.formatter('tags', opts)
5893 hexfunc = fm.hexfunc
5894 hexfunc = fm.hexfunc
5894 tagtype = ""
5895 tagtype = ""
5895
5896
5896 for t, n in reversed(repo.tagslist()):
5897 for t, n in reversed(repo.tagslist()):
5897 hn = hexfunc(n)
5898 hn = hexfunc(n)
5898 label = 'tags.normal'
5899 label = 'tags.normal'
5899 tagtype = ''
5900 tagtype = ''
5900 if repo.tagtype(t) == 'local':
5901 if repo.tagtype(t) == 'local':
5901 label = 'tags.local'
5902 label = 'tags.local'
5902 tagtype = 'local'
5903 tagtype = 'local'
5903
5904
5904 fm.startitem()
5905 fm.startitem()
5905 fm.context(repo=repo)
5906 fm.context(repo=repo)
5906 fm.write('tag', '%s', t, label=label)
5907 fm.write('tag', '%s', t, label=label)
5907 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5908 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5908 fm.condwrite(not ui.quiet, 'rev node', fmt,
5909 fm.condwrite(not ui.quiet, 'rev node', fmt,
5909 repo.changelog.rev(n), hn, label=label)
5910 repo.changelog.rev(n), hn, label=label)
5910 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5911 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5911 tagtype, label=label)
5912 tagtype, label=label)
5912 fm.plain('\n')
5913 fm.plain('\n')
5913 fm.end()
5914 fm.end()
5914
5915
5915 @command('tip',
5916 @command('tip',
5916 [('p', 'patch', None, _('show patch')),
5917 [('p', 'patch', None, _('show patch')),
5917 ('g', 'git', None, _('use git extended diff format')),
5918 ('g', 'git', None, _('use git extended diff format')),
5918 ] + templateopts,
5919 ] + templateopts,
5919 _('[-p] [-g]'),
5920 _('[-p] [-g]'),
5920 helpcategory=command.CATEGORY_CHANGE_NAVIGATION)
5921 helpcategory=command.CATEGORY_CHANGE_NAVIGATION)
5921 def tip(ui, repo, **opts):
5922 def tip(ui, repo, **opts):
5922 """show the tip revision (DEPRECATED)
5923 """show the tip revision (DEPRECATED)
5923
5924
5924 The tip revision (usually just called the tip) is the changeset
5925 The tip revision (usually just called the tip) is the changeset
5925 most recently added to the repository (and therefore the most
5926 most recently added to the repository (and therefore the most
5926 recently changed head).
5927 recently changed head).
5927
5928
5928 If you have just made a commit, that commit will be the tip. If
5929 If you have just made a commit, that commit will be the tip. If
5929 you have just pulled changes from another repository, the tip of
5930 you have just pulled changes from another repository, the tip of
5930 that repository becomes the current tip. The "tip" tag is special
5931 that repository becomes the current tip. The "tip" tag is special
5931 and cannot be renamed or assigned to a different changeset.
5932 and cannot be renamed or assigned to a different changeset.
5932
5933
5933 This command is deprecated, please use :hg:`heads` instead.
5934 This command is deprecated, please use :hg:`heads` instead.
5934
5935
5935 Returns 0 on success.
5936 Returns 0 on success.
5936 """
5937 """
5937 opts = pycompat.byteskwargs(opts)
5938 opts = pycompat.byteskwargs(opts)
5938 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5939 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5939 displayer.show(repo['tip'])
5940 displayer.show(repo['tip'])
5940 displayer.close()
5941 displayer.close()
5941
5942
5942 @command('unbundle',
5943 @command('unbundle',
5943 [('u', 'update', None,
5944 [('u', 'update', None,
5944 _('update to new branch head if changesets were unbundled'))],
5945 _('update to new branch head if changesets were unbundled'))],
5945 _('[-u] FILE...'),
5946 _('[-u] FILE...'),
5946 helpcategory=command.CATEGORY_IMPORT_EXPORT)
5947 helpcategory=command.CATEGORY_IMPORT_EXPORT)
5947 def unbundle(ui, repo, fname1, *fnames, **opts):
5948 def unbundle(ui, repo, fname1, *fnames, **opts):
5948 """apply one or more bundle files
5949 """apply one or more bundle files
5949
5950
5950 Apply one or more bundle files generated by :hg:`bundle`.
5951 Apply one or more bundle files generated by :hg:`bundle`.
5951
5952
5952 Returns 0 on success, 1 if an update has unresolved files.
5953 Returns 0 on success, 1 if an update has unresolved files.
5953 """
5954 """
5954 fnames = (fname1,) + fnames
5955 fnames = (fname1,) + fnames
5955
5956
5956 with repo.lock():
5957 with repo.lock():
5957 for fname in fnames:
5958 for fname in fnames:
5958 f = hg.openpath(ui, fname)
5959 f = hg.openpath(ui, fname)
5959 gen = exchange.readbundle(ui, f, fname)
5960 gen = exchange.readbundle(ui, f, fname)
5960 if isinstance(gen, streamclone.streamcloneapplier):
5961 if isinstance(gen, streamclone.streamcloneapplier):
5961 raise error.Abort(
5962 raise error.Abort(
5962 _('packed bundles cannot be applied with '
5963 _('packed bundles cannot be applied with '
5963 '"hg unbundle"'),
5964 '"hg unbundle"'),
5964 hint=_('use "hg debugapplystreamclonebundle"'))
5965 hint=_('use "hg debugapplystreamclonebundle"'))
5965 url = 'bundle:' + fname
5966 url = 'bundle:' + fname
5966 try:
5967 try:
5967 txnname = 'unbundle'
5968 txnname = 'unbundle'
5968 if not isinstance(gen, bundle2.unbundle20):
5969 if not isinstance(gen, bundle2.unbundle20):
5969 txnname = 'unbundle\n%s' % util.hidepassword(url)
5970 txnname = 'unbundle\n%s' % util.hidepassword(url)
5970 with repo.transaction(txnname) as tr:
5971 with repo.transaction(txnname) as tr:
5971 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
5972 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
5972 url=url)
5973 url=url)
5973 except error.BundleUnknownFeatureError as exc:
5974 except error.BundleUnknownFeatureError as exc:
5974 raise error.Abort(
5975 raise error.Abort(
5975 _('%s: unknown bundle feature, %s') % (fname, exc),
5976 _('%s: unknown bundle feature, %s') % (fname, exc),
5976 hint=_("see https://mercurial-scm.org/"
5977 hint=_("see https://mercurial-scm.org/"
5977 "wiki/BundleFeature for more "
5978 "wiki/BundleFeature for more "
5978 "information"))
5979 "information"))
5979 modheads = bundle2.combinechangegroupresults(op)
5980 modheads = bundle2.combinechangegroupresults(op)
5980
5981
5981 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
5982 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
5982
5983
5983 @command('update|up|checkout|co',
5984 @command('update|up|checkout|co',
5984 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5985 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5985 ('c', 'check', None, _('require clean working directory')),
5986 ('c', 'check', None, _('require clean working directory')),
5986 ('m', 'merge', None, _('merge uncommitted changes')),
5987 ('m', 'merge', None, _('merge uncommitted changes')),
5987 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5988 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5988 ('r', 'rev', '', _('revision'), _('REV'))
5989 ('r', 'rev', '', _('revision'), _('REV'))
5989 ] + mergetoolopts,
5990 ] + mergetoolopts,
5990 _('[-C|-c|-m] [-d DATE] [[-r] REV]'),
5991 _('[-C|-c|-m] [-d DATE] [[-r] REV]'),
5991 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5992 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5992 helpbasic=True)
5993 helpbasic=True)
5993 def update(ui, repo, node=None, **opts):
5994 def update(ui, repo, node=None, **opts):
5994 """update working directory (or switch revisions)
5995 """update working directory (or switch revisions)
5995
5996
5996 Update the repository's working directory to the specified
5997 Update the repository's working directory to the specified
5997 changeset. If no changeset is specified, update to the tip of the
5998 changeset. If no changeset is specified, update to the tip of the
5998 current named branch and move the active bookmark (see :hg:`help
5999 current named branch and move the active bookmark (see :hg:`help
5999 bookmarks`).
6000 bookmarks`).
6000
6001
6001 Update sets the working directory's parent revision to the specified
6002 Update sets the working directory's parent revision to the specified
6002 changeset (see :hg:`help parents`).
6003 changeset (see :hg:`help parents`).
6003
6004
6004 If the changeset is not a descendant or ancestor of the working
6005 If the changeset is not a descendant or ancestor of the working
6005 directory's parent and there are uncommitted changes, the update is
6006 directory's parent and there are uncommitted changes, the update is
6006 aborted. With the -c/--check option, the working directory is checked
6007 aborted. With the -c/--check option, the working directory is checked
6007 for uncommitted changes; if none are found, the working directory is
6008 for uncommitted changes; if none are found, the working directory is
6008 updated to the specified changeset.
6009 updated to the specified changeset.
6009
6010
6010 .. container:: verbose
6011 .. container:: verbose
6011
6012
6012 The -C/--clean, -c/--check, and -m/--merge options control what
6013 The -C/--clean, -c/--check, and -m/--merge options control what
6013 happens if the working directory contains uncommitted changes.
6014 happens if the working directory contains uncommitted changes.
6014 At most of one of them can be specified.
6015 At most of one of them can be specified.
6015
6016
6016 1. If no option is specified, and if
6017 1. If no option is specified, and if
6017 the requested changeset is an ancestor or descendant of
6018 the requested changeset is an ancestor or descendant of
6018 the working directory's parent, the uncommitted changes
6019 the working directory's parent, the uncommitted changes
6019 are merged into the requested changeset and the merged
6020 are merged into the requested changeset and the merged
6020 result is left uncommitted. If the requested changeset is
6021 result is left uncommitted. If the requested changeset is
6021 not an ancestor or descendant (that is, it is on another
6022 not an ancestor or descendant (that is, it is on another
6022 branch), the update is aborted and the uncommitted changes
6023 branch), the update is aborted and the uncommitted changes
6023 are preserved.
6024 are preserved.
6024
6025
6025 2. With the -m/--merge option, the update is allowed even if the
6026 2. With the -m/--merge option, the update is allowed even if the
6026 requested changeset is not an ancestor or descendant of
6027 requested changeset is not an ancestor or descendant of
6027 the working directory's parent.
6028 the working directory's parent.
6028
6029
6029 3. With the -c/--check option, the update is aborted and the
6030 3. With the -c/--check option, the update is aborted and the
6030 uncommitted changes are preserved.
6031 uncommitted changes are preserved.
6031
6032
6032 4. With the -C/--clean option, uncommitted changes are discarded and
6033 4. With the -C/--clean option, uncommitted changes are discarded and
6033 the working directory is updated to the requested changeset.
6034 the working directory is updated to the requested changeset.
6034
6035
6035 To cancel an uncommitted merge (and lose your changes), use
6036 To cancel an uncommitted merge (and lose your changes), use
6036 :hg:`merge --abort`.
6037 :hg:`merge --abort`.
6037
6038
6038 Use null as the changeset to remove the working directory (like
6039 Use null as the changeset to remove the working directory (like
6039 :hg:`clone -U`).
6040 :hg:`clone -U`).
6040
6041
6041 If you want to revert just one file to an older revision, use
6042 If you want to revert just one file to an older revision, use
6042 :hg:`revert [-r REV] NAME`.
6043 :hg:`revert [-r REV] NAME`.
6043
6044
6044 See :hg:`help dates` for a list of formats valid for -d/--date.
6045 See :hg:`help dates` for a list of formats valid for -d/--date.
6045
6046
6046 Returns 0 on success, 1 if there are unresolved files.
6047 Returns 0 on success, 1 if there are unresolved files.
6047 """
6048 """
6048 rev = opts.get(r'rev')
6049 rev = opts.get(r'rev')
6049 date = opts.get(r'date')
6050 date = opts.get(r'date')
6050 clean = opts.get(r'clean')
6051 clean = opts.get(r'clean')
6051 check = opts.get(r'check')
6052 check = opts.get(r'check')
6052 merge = opts.get(r'merge')
6053 merge = opts.get(r'merge')
6053 if rev and node:
6054 if rev and node:
6054 raise error.Abort(_("please specify just one revision"))
6055 raise error.Abort(_("please specify just one revision"))
6055
6056
6056 if ui.configbool('commands', 'update.requiredest'):
6057 if ui.configbool('commands', 'update.requiredest'):
6057 if not node and not rev and not date:
6058 if not node and not rev and not date:
6058 raise error.Abort(_('you must specify a destination'),
6059 raise error.Abort(_('you must specify a destination'),
6059 hint=_('for example: hg update ".::"'))
6060 hint=_('for example: hg update ".::"'))
6060
6061
6061 if rev is None or rev == '':
6062 if rev is None or rev == '':
6062 rev = node
6063 rev = node
6063
6064
6064 if date and rev is not None:
6065 if date and rev is not None:
6065 raise error.Abort(_("you can't specify a revision and a date"))
6066 raise error.Abort(_("you can't specify a revision and a date"))
6066
6067
6067 if len([x for x in (clean, check, merge) if x]) > 1:
6068 if len([x for x in (clean, check, merge) if x]) > 1:
6068 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
6069 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
6069 "or -m/--merge"))
6070 "or -m/--merge"))
6070
6071
6071 updatecheck = None
6072 updatecheck = None
6072 if check:
6073 if check:
6073 updatecheck = 'abort'
6074 updatecheck = 'abort'
6074 elif merge:
6075 elif merge:
6075 updatecheck = 'none'
6076 updatecheck = 'none'
6076
6077
6077 with repo.wlock():
6078 with repo.wlock():
6078 cmdutil.clearunfinished(repo)
6079 cmdutil.clearunfinished(repo)
6079
6080
6080 if date:
6081 if date:
6081 rev = cmdutil.finddate(ui, repo, date)
6082 rev = cmdutil.finddate(ui, repo, date)
6082
6083
6083 # if we defined a bookmark, we have to remember the original name
6084 # if we defined a bookmark, we have to remember the original name
6084 brev = rev
6085 brev = rev
6085 if rev:
6086 if rev:
6086 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
6087 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
6087 ctx = scmutil.revsingle(repo, rev, rev)
6088 ctx = scmutil.revsingle(repo, rev, rev)
6088 rev = ctx.rev()
6089 rev = ctx.rev()
6089 hidden = ctx.hidden()
6090 hidden = ctx.hidden()
6090 overrides = {('ui', 'forcemerge'): opts.get(r'tool', '')}
6091 overrides = {('ui', 'forcemerge'): opts.get(r'tool', '')}
6091 with ui.configoverride(overrides, 'update'):
6092 with ui.configoverride(overrides, 'update'):
6092 ret = hg.updatetotally(ui, repo, rev, brev, clean=clean,
6093 ret = hg.updatetotally(ui, repo, rev, brev, clean=clean,
6093 updatecheck=updatecheck)
6094 updatecheck=updatecheck)
6094 if hidden:
6095 if hidden:
6095 ctxstr = ctx.hex()[:12]
6096 ctxstr = ctx.hex()[:12]
6096 ui.warn(_("updated to hidden changeset %s\n") % ctxstr)
6097 ui.warn(_("updated to hidden changeset %s\n") % ctxstr)
6097
6098
6098 if ctx.obsolete():
6099 if ctx.obsolete():
6099 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
6100 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
6100 ui.warn("(%s)\n" % obsfatemsg)
6101 ui.warn("(%s)\n" % obsfatemsg)
6101 return ret
6102 return ret
6102
6103
6103 @command('verify', [], helpcategory=command.CATEGORY_MAINTENANCE)
6104 @command('verify', [], helpcategory=command.CATEGORY_MAINTENANCE)
6104 def verify(ui, repo):
6105 def verify(ui, repo):
6105 """verify the integrity of the repository
6106 """verify the integrity of the repository
6106
6107
6107 Verify the integrity of the current repository.
6108 Verify the integrity of the current repository.
6108
6109
6109 This will perform an extensive check of the repository's
6110 This will perform an extensive check of the repository's
6110 integrity, validating the hashes and checksums of each entry in
6111 integrity, validating the hashes and checksums of each entry in
6111 the changelog, manifest, and tracked files, as well as the
6112 the changelog, manifest, and tracked files, as well as the
6112 integrity of their crosslinks and indices.
6113 integrity of their crosslinks and indices.
6113
6114
6114 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
6115 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
6115 for more information about recovery from corruption of the
6116 for more information about recovery from corruption of the
6116 repository.
6117 repository.
6117
6118
6118 Returns 0 on success, 1 if errors are encountered.
6119 Returns 0 on success, 1 if errors are encountered.
6119 """
6120 """
6120 return hg.verify(repo)
6121 return hg.verify(repo)
6121
6122
6122 @command(
6123 @command(
6123 'version', [] + formatteropts, helpcategory=command.CATEGORY_HELP,
6124 'version', [] + formatteropts, helpcategory=command.CATEGORY_HELP,
6124 norepo=True, intents={INTENT_READONLY})
6125 norepo=True, intents={INTENT_READONLY})
6125 def version_(ui, **opts):
6126 def version_(ui, **opts):
6126 """output version and copyright information
6127 """output version and copyright information
6127
6128
6128 .. container:: verbose
6129 .. container:: verbose
6129
6130
6130 Template:
6131 Template:
6131
6132
6132 The following keywords are supported. See also :hg:`help templates`.
6133 The following keywords are supported. See also :hg:`help templates`.
6133
6134
6134 :extensions: List of extensions.
6135 :extensions: List of extensions.
6135 :ver: String. Version number.
6136 :ver: String. Version number.
6136
6137
6137 And each entry of ``{extensions}`` provides the following sub-keywords
6138 And each entry of ``{extensions}`` provides the following sub-keywords
6138 in addition to ``{ver}``.
6139 in addition to ``{ver}``.
6139
6140
6140 :bundled: Boolean. True if included in the release.
6141 :bundled: Boolean. True if included in the release.
6141 :name: String. Extension name.
6142 :name: String. Extension name.
6142 """
6143 """
6143 opts = pycompat.byteskwargs(opts)
6144 opts = pycompat.byteskwargs(opts)
6144 if ui.verbose:
6145 if ui.verbose:
6145 ui.pager('version')
6146 ui.pager('version')
6146 fm = ui.formatter("version", opts)
6147 fm = ui.formatter("version", opts)
6147 fm.startitem()
6148 fm.startitem()
6148 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
6149 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
6149 util.version())
6150 util.version())
6150 license = _(
6151 license = _(
6151 "(see https://mercurial-scm.org for more information)\n"
6152 "(see https://mercurial-scm.org for more information)\n"
6152 "\nCopyright (C) 2005-2018 Matt Mackall and others\n"
6153 "\nCopyright (C) 2005-2018 Matt Mackall and others\n"
6153 "This is free software; see the source for copying conditions. "
6154 "This is free software; see the source for copying conditions. "
6154 "There is NO\nwarranty; "
6155 "There is NO\nwarranty; "
6155 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
6156 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
6156 )
6157 )
6157 if not ui.quiet:
6158 if not ui.quiet:
6158 fm.plain(license)
6159 fm.plain(license)
6159
6160
6160 if ui.verbose:
6161 if ui.verbose:
6161 fm.plain(_("\nEnabled extensions:\n\n"))
6162 fm.plain(_("\nEnabled extensions:\n\n"))
6162 # format names and versions into columns
6163 # format names and versions into columns
6163 names = []
6164 names = []
6164 vers = []
6165 vers = []
6165 isinternals = []
6166 isinternals = []
6166 for name, module in extensions.extensions():
6167 for name, module in extensions.extensions():
6167 names.append(name)
6168 names.append(name)
6168 vers.append(extensions.moduleversion(module) or None)
6169 vers.append(extensions.moduleversion(module) or None)
6169 isinternals.append(extensions.ismoduleinternal(module))
6170 isinternals.append(extensions.ismoduleinternal(module))
6170 fn = fm.nested("extensions", tmpl='{name}\n')
6171 fn = fm.nested("extensions", tmpl='{name}\n')
6171 if names:
6172 if names:
6172 namefmt = " %%-%ds " % max(len(n) for n in names)
6173 namefmt = " %%-%ds " % max(len(n) for n in names)
6173 places = [_("external"), _("internal")]
6174 places = [_("external"), _("internal")]
6174 for n, v, p in zip(names, vers, isinternals):
6175 for n, v, p in zip(names, vers, isinternals):
6175 fn.startitem()
6176 fn.startitem()
6176 fn.condwrite(ui.verbose, "name", namefmt, n)
6177 fn.condwrite(ui.verbose, "name", namefmt, n)
6177 if ui.verbose:
6178 if ui.verbose:
6178 fn.plain("%s " % places[p])
6179 fn.plain("%s " % places[p])
6179 fn.data(bundled=p)
6180 fn.data(bundled=p)
6180 fn.condwrite(ui.verbose and v, "ver", "%s", v)
6181 fn.condwrite(ui.verbose and v, "ver", "%s", v)
6181 if ui.verbose:
6182 if ui.verbose:
6182 fn.plain("\n")
6183 fn.plain("\n")
6183 fn.end()
6184 fn.end()
6184 fm.end()
6185 fm.end()
6185
6186
6186 def loadcmdtable(ui, name, cmdtable):
6187 def loadcmdtable(ui, name, cmdtable):
6187 """Load command functions from specified cmdtable
6188 """Load command functions from specified cmdtable
6188 """
6189 """
6189 cmdtable = cmdtable.copy()
6190 cmdtable = cmdtable.copy()
6190 for cmd in list(cmdtable):
6191 for cmd in list(cmdtable):
6191 if not cmd.startswith('^'):
6192 if not cmd.startswith('^'):
6192 continue
6193 continue
6193 ui.deprecwarn("old-style command registration '%s' in extension '%s'"
6194 ui.deprecwarn("old-style command registration '%s' in extension '%s'"
6194 % (cmd, name), '4.8')
6195 % (cmd, name), '4.8')
6195 entry = cmdtable.pop(cmd)
6196 entry = cmdtable.pop(cmd)
6196 entry[0].helpbasic = True
6197 entry[0].helpbasic = True
6197 cmdtable[cmd[1:]] = entry
6198 cmdtable[cmd[1:]] = entry
6198
6199
6199 overrides = [cmd for cmd in cmdtable if cmd in table]
6200 overrides = [cmd for cmd in cmdtable if cmd in table]
6200 if overrides:
6201 if overrides:
6201 ui.warn(_("extension '%s' overrides commands: %s\n")
6202 ui.warn(_("extension '%s' overrides commands: %s\n")
6202 % (name, " ".join(overrides)))
6203 % (name, " ".join(overrides)))
6203 table.update(cmdtable)
6204 table.update(cmdtable)
@@ -1,684 +1,699 b''
1 # posix.py - Posix utility function implementations for Mercurial
1 # posix.py - Posix utility function implementations for Mercurial
2 #
2 #
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
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 __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import errno
10 import errno
11 import fcntl
11 import fcntl
12 import getpass
12 import getpass
13 import grp
13 import grp
14 import os
14 import os
15 import pwd
15 import pwd
16 import re
16 import re
17 import select
17 import select
18 import stat
18 import stat
19 import sys
19 import sys
20 import tempfile
20 import tempfile
21 import unicodedata
21 import unicodedata
22
22
23 from .i18n import _
23 from .i18n import _
24 from . import (
24 from . import (
25 encoding,
25 encoding,
26 error,
26 error,
27 policy,
27 policy,
28 pycompat,
28 pycompat,
29 )
29 )
30
30
31 osutil = policy.importmod(r'osutil')
31 osutil = policy.importmod(r'osutil')
32
32
33 posixfile = open
33 posixfile = open
34 normpath = os.path.normpath
34 normpath = os.path.normpath
35 samestat = os.path.samestat
35 samestat = os.path.samestat
36 try:
36 try:
37 oslink = os.link
37 oslink = os.link
38 except AttributeError:
38 except AttributeError:
39 # Some platforms build Python without os.link on systems that are
39 # Some platforms build Python without os.link on systems that are
40 # vaguely unix-like but don't have hardlink support. For those
40 # vaguely unix-like but don't have hardlink support. For those
41 # poor souls, just say we tried and that it failed so we fall back
41 # poor souls, just say we tried and that it failed so we fall back
42 # to copies.
42 # to copies.
43 def oslink(src, dst):
43 def oslink(src, dst):
44 raise OSError(errno.EINVAL,
44 raise OSError(errno.EINVAL,
45 'hardlinks not supported: %s to %s' % (src, dst))
45 'hardlinks not supported: %s to %s' % (src, dst))
46 readlink = os.readlink
46 readlink = os.readlink
47 unlink = os.unlink
47 unlink = os.unlink
48 rename = os.rename
48 rename = os.rename
49 removedirs = os.removedirs
49 removedirs = os.removedirs
50 expandglobs = False
50 expandglobs = False
51
51
52 umask = os.umask(0)
52 umask = os.umask(0)
53 os.umask(umask)
53 os.umask(umask)
54
54
55 def split(p):
55 def split(p):
56 '''Same as posixpath.split, but faster
56 '''Same as posixpath.split, but faster
57
57
58 >>> import posixpath
58 >>> import posixpath
59 >>> for f in [b'/absolute/path/to/file',
59 >>> for f in [b'/absolute/path/to/file',
60 ... b'relative/path/to/file',
60 ... b'relative/path/to/file',
61 ... b'file_alone',
61 ... b'file_alone',
62 ... b'path/to/directory/',
62 ... b'path/to/directory/',
63 ... b'/multiple/path//separators',
63 ... b'/multiple/path//separators',
64 ... b'/file_at_root',
64 ... b'/file_at_root',
65 ... b'///multiple_leading_separators_at_root',
65 ... b'///multiple_leading_separators_at_root',
66 ... b'']:
66 ... b'']:
67 ... assert split(f) == posixpath.split(f), f
67 ... assert split(f) == posixpath.split(f), f
68 '''
68 '''
69 ht = p.rsplit('/', 1)
69 ht = p.rsplit('/', 1)
70 if len(ht) == 1:
70 if len(ht) == 1:
71 return '', p
71 return '', p
72 nh = ht[0].rstrip('/')
72 nh = ht[0].rstrip('/')
73 if nh:
73 if nh:
74 return nh, ht[1]
74 return nh, ht[1]
75 return ht[0] + '/', ht[1]
75 return ht[0] + '/', ht[1]
76
76
77 def openhardlinks():
77 def openhardlinks():
78 '''return true if it is safe to hold open file handles to hardlinks'''
78 '''return true if it is safe to hold open file handles to hardlinks'''
79 return True
79 return True
80
80
81 def nlinks(name):
81 def nlinks(name):
82 '''return number of hardlinks for the given file'''
82 '''return number of hardlinks for the given file'''
83 return os.lstat(name).st_nlink
83 return os.lstat(name).st_nlink
84
84
85 def parsepatchoutput(output_line):
85 def parsepatchoutput(output_line):
86 """parses the output produced by patch and returns the filename"""
86 """parses the output produced by patch and returns the filename"""
87 pf = output_line[14:]
87 pf = output_line[14:]
88 if pycompat.sysplatform == 'OpenVMS':
88 if pycompat.sysplatform == 'OpenVMS':
89 if pf[0] == '`':
89 if pf[0] == '`':
90 pf = pf[1:-1] # Remove the quotes
90 pf = pf[1:-1] # Remove the quotes
91 else:
91 else:
92 if pf.startswith("'") and pf.endswith("'") and " " in pf:
92 if pf.startswith("'") and pf.endswith("'") and " " in pf:
93 pf = pf[1:-1] # Remove the quotes
93 pf = pf[1:-1] # Remove the quotes
94 return pf
94 return pf
95
95
96 def sshargs(sshcmd, host, user, port):
96 def sshargs(sshcmd, host, user, port):
97 '''Build argument list for ssh'''
97 '''Build argument list for ssh'''
98 args = user and ("%s@%s" % (user, host)) or host
98 args = user and ("%s@%s" % (user, host)) or host
99 if '-' in args[:1]:
99 if '-' in args[:1]:
100 raise error.Abort(
100 raise error.Abort(
101 _('illegal ssh hostname or username starting with -: %s') % args)
101 _('illegal ssh hostname or username starting with -: %s') % args)
102 args = shellquote(args)
102 args = shellquote(args)
103 if port:
103 if port:
104 args = '-p %s %s' % (shellquote(port), args)
104 args = '-p %s %s' % (shellquote(port), args)
105 return args
105 return args
106
106
107 def isexec(f):
107 def isexec(f):
108 """check whether a file is executable"""
108 """check whether a file is executable"""
109 return (os.lstat(f).st_mode & 0o100 != 0)
109 return (os.lstat(f).st_mode & 0o100 != 0)
110
110
111 def setflags(f, l, x):
111 def setflags(f, l, x):
112 st = os.lstat(f)
112 st = os.lstat(f)
113 s = st.st_mode
113 s = st.st_mode
114 if l:
114 if l:
115 if not stat.S_ISLNK(s):
115 if not stat.S_ISLNK(s):
116 # switch file to link
116 # switch file to link
117 fp = open(f, 'rb')
117 fp = open(f, 'rb')
118 data = fp.read()
118 data = fp.read()
119 fp.close()
119 fp.close()
120 unlink(f)
120 unlink(f)
121 try:
121 try:
122 os.symlink(data, f)
122 os.symlink(data, f)
123 except OSError:
123 except OSError:
124 # failed to make a link, rewrite file
124 # failed to make a link, rewrite file
125 fp = open(f, "wb")
125 fp = open(f, "wb")
126 fp.write(data)
126 fp.write(data)
127 fp.close()
127 fp.close()
128 # no chmod needed at this point
128 # no chmod needed at this point
129 return
129 return
130 if stat.S_ISLNK(s):
130 if stat.S_ISLNK(s):
131 # switch link to file
131 # switch link to file
132 data = os.readlink(f)
132 data = os.readlink(f)
133 unlink(f)
133 unlink(f)
134 fp = open(f, "wb")
134 fp = open(f, "wb")
135 fp.write(data)
135 fp.write(data)
136 fp.close()
136 fp.close()
137 s = 0o666 & ~umask # avoid restatting for chmod
137 s = 0o666 & ~umask # avoid restatting for chmod
138
138
139 sx = s & 0o100
139 sx = s & 0o100
140 if st.st_nlink > 1 and bool(x) != bool(sx):
140 if st.st_nlink > 1 and bool(x) != bool(sx):
141 # the file is a hardlink, break it
141 # the file is a hardlink, break it
142 with open(f, "rb") as fp:
142 with open(f, "rb") as fp:
143 data = fp.read()
143 data = fp.read()
144 unlink(f)
144 unlink(f)
145 with open(f, "wb") as fp:
145 with open(f, "wb") as fp:
146 fp.write(data)
146 fp.write(data)
147
147
148 if x and not sx:
148 if x and not sx:
149 # Turn on +x for every +r bit when making a file executable
149 # Turn on +x for every +r bit when making a file executable
150 # and obey umask.
150 # and obey umask.
151 os.chmod(f, s | (s & 0o444) >> 2 & ~umask)
151 os.chmod(f, s | (s & 0o444) >> 2 & ~umask)
152 elif not x and sx:
152 elif not x and sx:
153 # Turn off all +x bits
153 # Turn off all +x bits
154 os.chmod(f, s & 0o666)
154 os.chmod(f, s & 0o666)
155
155
156 def copymode(src, dst, mode=None):
156 def copymode(src, dst, mode=None):
157 '''Copy the file mode from the file at path src to dst.
157 '''Copy the file mode from the file at path src to dst.
158 If src doesn't exist, we're using mode instead. If mode is None, we're
158 If src doesn't exist, we're using mode instead. If mode is None, we're
159 using umask.'''
159 using umask.'''
160 try:
160 try:
161 st_mode = os.lstat(src).st_mode & 0o777
161 st_mode = os.lstat(src).st_mode & 0o777
162 except OSError as inst:
162 except OSError as inst:
163 if inst.errno != errno.ENOENT:
163 if inst.errno != errno.ENOENT:
164 raise
164 raise
165 st_mode = mode
165 st_mode = mode
166 if st_mode is None:
166 if st_mode is None:
167 st_mode = ~umask
167 st_mode = ~umask
168 st_mode &= 0o666
168 st_mode &= 0o666
169 os.chmod(dst, st_mode)
169 os.chmod(dst, st_mode)
170
170
171 def checkexec(path):
171 def checkexec(path):
172 """
172 """
173 Check whether the given path is on a filesystem with UNIX-like exec flags
173 Check whether the given path is on a filesystem with UNIX-like exec flags
174
174
175 Requires a directory (like /foo/.hg)
175 Requires a directory (like /foo/.hg)
176 """
176 """
177
177
178 # VFAT on some Linux versions can flip mode but it doesn't persist
178 # VFAT on some Linux versions can flip mode but it doesn't persist
179 # a FS remount. Frequently we can detect it if files are created
179 # a FS remount. Frequently we can detect it if files are created
180 # with exec bit on.
180 # with exec bit on.
181
181
182 try:
182 try:
183 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
183 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
184 cachedir = os.path.join(path, '.hg', 'cache')
184 basedir = os.path.join(path, '.hg')
185 cachedir = os.path.join(basedir, 'cache')
186 storedir = os.path.join(basedir, 'store')
187 if not os.path.exists(cachedir):
188 try:
189 # we want to create the 'cache' directory, not the '.hg' one.
190 # Automatically creating '.hg' directory could silently spawn
191 # invalid Mercurial repositories. That seems like a bad idea.
192 os.mkdir(cachedir)
193 if os.path.exists(storedir):
194 copymode(storedir, cachedir)
195 else:
196 copymode(basedir, cachedir)
197 except (IOError, OSError):
198 # we other fallback logic triggers
199 pass
185 if os.path.isdir(cachedir):
200 if os.path.isdir(cachedir):
186 checkisexec = os.path.join(cachedir, 'checkisexec')
201 checkisexec = os.path.join(cachedir, 'checkisexec')
187 checknoexec = os.path.join(cachedir, 'checknoexec')
202 checknoexec = os.path.join(cachedir, 'checknoexec')
188
203
189 try:
204 try:
190 m = os.stat(checkisexec).st_mode
205 m = os.stat(checkisexec).st_mode
191 except OSError as e:
206 except OSError as e:
192 if e.errno != errno.ENOENT:
207 if e.errno != errno.ENOENT:
193 raise
208 raise
194 # checkisexec does not exist - fall through ...
209 # checkisexec does not exist - fall through ...
195 else:
210 else:
196 # checkisexec exists, check if it actually is exec
211 # checkisexec exists, check if it actually is exec
197 if m & EXECFLAGS != 0:
212 if m & EXECFLAGS != 0:
198 # ensure checkisexec exists, check it isn't exec
213 # ensure checkisexec exists, check it isn't exec
199 try:
214 try:
200 m = os.stat(checknoexec).st_mode
215 m = os.stat(checknoexec).st_mode
201 except OSError as e:
216 except OSError as e:
202 if e.errno != errno.ENOENT:
217 if e.errno != errno.ENOENT:
203 raise
218 raise
204 open(checknoexec, 'w').close() # might fail
219 open(checknoexec, 'w').close() # might fail
205 m = os.stat(checknoexec).st_mode
220 m = os.stat(checknoexec).st_mode
206 if m & EXECFLAGS == 0:
221 if m & EXECFLAGS == 0:
207 # check-exec is exec and check-no-exec is not exec
222 # check-exec is exec and check-no-exec is not exec
208 return True
223 return True
209 # checknoexec exists but is exec - delete it
224 # checknoexec exists but is exec - delete it
210 unlink(checknoexec)
225 unlink(checknoexec)
211 # checkisexec exists but is not exec - delete it
226 # checkisexec exists but is not exec - delete it
212 unlink(checkisexec)
227 unlink(checkisexec)
213
228
214 # check using one file, leave it as checkisexec
229 # check using one file, leave it as checkisexec
215 checkdir = cachedir
230 checkdir = cachedir
216 else:
231 else:
217 # check directly in path and don't leave checkisexec behind
232 # check directly in path and don't leave checkisexec behind
218 checkdir = path
233 checkdir = path
219 checkisexec = None
234 checkisexec = None
220 fh, fn = pycompat.mkstemp(dir=checkdir, prefix='hg-checkexec-')
235 fh, fn = pycompat.mkstemp(dir=checkdir, prefix='hg-checkexec-')
221 try:
236 try:
222 os.close(fh)
237 os.close(fh)
223 m = os.stat(fn).st_mode
238 m = os.stat(fn).st_mode
224 if m & EXECFLAGS == 0:
239 if m & EXECFLAGS == 0:
225 os.chmod(fn, m & 0o777 | EXECFLAGS)
240 os.chmod(fn, m & 0o777 | EXECFLAGS)
226 if os.stat(fn).st_mode & EXECFLAGS != 0:
241 if os.stat(fn).st_mode & EXECFLAGS != 0:
227 if checkisexec is not None:
242 if checkisexec is not None:
228 os.rename(fn, checkisexec)
243 os.rename(fn, checkisexec)
229 fn = None
244 fn = None
230 return True
245 return True
231 finally:
246 finally:
232 if fn is not None:
247 if fn is not None:
233 unlink(fn)
248 unlink(fn)
234 except (IOError, OSError):
249 except (IOError, OSError):
235 # we don't care, the user probably won't be able to commit anyway
250 # we don't care, the user probably won't be able to commit anyway
236 return False
251 return False
237
252
238 def checklink(path):
253 def checklink(path):
239 """check whether the given path is on a symlink-capable filesystem"""
254 """check whether the given path is on a symlink-capable filesystem"""
240 # mktemp is not racy because symlink creation will fail if the
255 # mktemp is not racy because symlink creation will fail if the
241 # file already exists
256 # file already exists
242 while True:
257 while True:
243 cachedir = os.path.join(path, '.hg', 'cache')
258 cachedir = os.path.join(path, '.hg', 'cache')
244 checklink = os.path.join(cachedir, 'checklink')
259 checklink = os.path.join(cachedir, 'checklink')
245 # try fast path, read only
260 # try fast path, read only
246 if os.path.islink(checklink):
261 if os.path.islink(checklink):
247 return True
262 return True
248 if os.path.isdir(cachedir):
263 if os.path.isdir(cachedir):
249 checkdir = cachedir
264 checkdir = cachedir
250 else:
265 else:
251 checkdir = path
266 checkdir = path
252 cachedir = None
267 cachedir = None
253 name = tempfile.mktemp(dir=pycompat.fsdecode(checkdir),
268 name = tempfile.mktemp(dir=pycompat.fsdecode(checkdir),
254 prefix=r'checklink-')
269 prefix=r'checklink-')
255 name = pycompat.fsencode(name)
270 name = pycompat.fsencode(name)
256 try:
271 try:
257 fd = None
272 fd = None
258 if cachedir is None:
273 if cachedir is None:
259 fd = pycompat.namedtempfile(dir=checkdir,
274 fd = pycompat.namedtempfile(dir=checkdir,
260 prefix='hg-checklink-')
275 prefix='hg-checklink-')
261 target = os.path.basename(fd.name)
276 target = os.path.basename(fd.name)
262 else:
277 else:
263 # create a fixed file to link to; doesn't matter if it
278 # create a fixed file to link to; doesn't matter if it
264 # already exists.
279 # already exists.
265 target = 'checklink-target'
280 target = 'checklink-target'
266 try:
281 try:
267 fullpath = os.path.join(cachedir, target)
282 fullpath = os.path.join(cachedir, target)
268 open(fullpath, 'w').close()
283 open(fullpath, 'w').close()
269 except IOError as inst:
284 except IOError as inst:
270 if inst[0] == errno.EACCES:
285 if inst[0] == errno.EACCES:
271 # If we can't write to cachedir, just pretend
286 # If we can't write to cachedir, just pretend
272 # that the fs is readonly and by association
287 # that the fs is readonly and by association
273 # that the fs won't support symlinks. This
288 # that the fs won't support symlinks. This
274 # seems like the least dangerous way to avoid
289 # seems like the least dangerous way to avoid
275 # data loss.
290 # data loss.
276 return False
291 return False
277 raise
292 raise
278 try:
293 try:
279 os.symlink(target, name)
294 os.symlink(target, name)
280 if cachedir is None:
295 if cachedir is None:
281 unlink(name)
296 unlink(name)
282 else:
297 else:
283 try:
298 try:
284 os.rename(name, checklink)
299 os.rename(name, checklink)
285 except OSError:
300 except OSError:
286 unlink(name)
301 unlink(name)
287 return True
302 return True
288 except OSError as inst:
303 except OSError as inst:
289 # link creation might race, try again
304 # link creation might race, try again
290 if inst.errno == errno.EEXIST:
305 if inst.errno == errno.EEXIST:
291 continue
306 continue
292 raise
307 raise
293 finally:
308 finally:
294 if fd is not None:
309 if fd is not None:
295 fd.close()
310 fd.close()
296 except AttributeError:
311 except AttributeError:
297 return False
312 return False
298 except OSError as inst:
313 except OSError as inst:
299 # sshfs might report failure while successfully creating the link
314 # sshfs might report failure while successfully creating the link
300 if inst.errno == errno.EIO and os.path.exists(name):
315 if inst.errno == errno.EIO and os.path.exists(name):
301 unlink(name)
316 unlink(name)
302 return False
317 return False
303
318
304 def checkosfilename(path):
319 def checkosfilename(path):
305 '''Check that the base-relative path is a valid filename on this platform.
320 '''Check that the base-relative path is a valid filename on this platform.
306 Returns None if the path is ok, or a UI string describing the problem.'''
321 Returns None if the path is ok, or a UI string describing the problem.'''
307 return None # on posix platforms, every path is ok
322 return None # on posix platforms, every path is ok
308
323
309 def getfsmountpoint(dirpath):
324 def getfsmountpoint(dirpath):
310 '''Get the filesystem mount point from a directory (best-effort)
325 '''Get the filesystem mount point from a directory (best-effort)
311
326
312 Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc.
327 Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc.
313 '''
328 '''
314 return getattr(osutil, 'getfsmountpoint', lambda x: None)(dirpath)
329 return getattr(osutil, 'getfsmountpoint', lambda x: None)(dirpath)
315
330
316 def getfstype(dirpath):
331 def getfstype(dirpath):
317 '''Get the filesystem type name from a directory (best-effort)
332 '''Get the filesystem type name from a directory (best-effort)
318
333
319 Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc.
334 Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc.
320 '''
335 '''
321 return getattr(osutil, 'getfstype', lambda x: None)(dirpath)
336 return getattr(osutil, 'getfstype', lambda x: None)(dirpath)
322
337
323 def setbinary(fd):
338 def setbinary(fd):
324 pass
339 pass
325
340
326 def pconvert(path):
341 def pconvert(path):
327 return path
342 return path
328
343
329 def localpath(path):
344 def localpath(path):
330 return path
345 return path
331
346
332 def samefile(fpath1, fpath2):
347 def samefile(fpath1, fpath2):
333 """Returns whether path1 and path2 refer to the same file. This is only
348 """Returns whether path1 and path2 refer to the same file. This is only
334 guaranteed to work for files, not directories."""
349 guaranteed to work for files, not directories."""
335 return os.path.samefile(fpath1, fpath2)
350 return os.path.samefile(fpath1, fpath2)
336
351
337 def samedevice(fpath1, fpath2):
352 def samedevice(fpath1, fpath2):
338 """Returns whether fpath1 and fpath2 are on the same device. This is only
353 """Returns whether fpath1 and fpath2 are on the same device. This is only
339 guaranteed to work for files, not directories."""
354 guaranteed to work for files, not directories."""
340 st1 = os.lstat(fpath1)
355 st1 = os.lstat(fpath1)
341 st2 = os.lstat(fpath2)
356 st2 = os.lstat(fpath2)
342 return st1.st_dev == st2.st_dev
357 return st1.st_dev == st2.st_dev
343
358
344 # os.path.normcase is a no-op, which doesn't help us on non-native filesystems
359 # os.path.normcase is a no-op, which doesn't help us on non-native filesystems
345 def normcase(path):
360 def normcase(path):
346 return path.lower()
361 return path.lower()
347
362
348 # what normcase does to ASCII strings
363 # what normcase does to ASCII strings
349 normcasespec = encoding.normcasespecs.lower
364 normcasespec = encoding.normcasespecs.lower
350 # fallback normcase function for non-ASCII strings
365 # fallback normcase function for non-ASCII strings
351 normcasefallback = normcase
366 normcasefallback = normcase
352
367
353 if pycompat.isdarwin:
368 if pycompat.isdarwin:
354
369
355 def normcase(path):
370 def normcase(path):
356 '''
371 '''
357 Normalize a filename for OS X-compatible comparison:
372 Normalize a filename for OS X-compatible comparison:
358 - escape-encode invalid characters
373 - escape-encode invalid characters
359 - decompose to NFD
374 - decompose to NFD
360 - lowercase
375 - lowercase
361 - omit ignored characters [200c-200f, 202a-202e, 206a-206f,feff]
376 - omit ignored characters [200c-200f, 202a-202e, 206a-206f,feff]
362
377
363 >>> normcase(b'UPPER')
378 >>> normcase(b'UPPER')
364 'upper'
379 'upper'
365 >>> normcase(b'Caf\\xc3\\xa9')
380 >>> normcase(b'Caf\\xc3\\xa9')
366 'cafe\\xcc\\x81'
381 'cafe\\xcc\\x81'
367 >>> normcase(b'\\xc3\\x89')
382 >>> normcase(b'\\xc3\\x89')
368 'e\\xcc\\x81'
383 'e\\xcc\\x81'
369 >>> normcase(b'\\xb8\\xca\\xc3\\xca\\xbe\\xc8.JPG') # issue3918
384 >>> normcase(b'\\xb8\\xca\\xc3\\xca\\xbe\\xc8.JPG') # issue3918
370 '%b8%ca%c3\\xca\\xbe%c8.jpg'
385 '%b8%ca%c3\\xca\\xbe%c8.jpg'
371 '''
386 '''
372
387
373 try:
388 try:
374 return encoding.asciilower(path) # exception for non-ASCII
389 return encoding.asciilower(path) # exception for non-ASCII
375 except UnicodeDecodeError:
390 except UnicodeDecodeError:
376 return normcasefallback(path)
391 return normcasefallback(path)
377
392
378 normcasespec = encoding.normcasespecs.lower
393 normcasespec = encoding.normcasespecs.lower
379
394
380 def normcasefallback(path):
395 def normcasefallback(path):
381 try:
396 try:
382 u = path.decode('utf-8')
397 u = path.decode('utf-8')
383 except UnicodeDecodeError:
398 except UnicodeDecodeError:
384 # OS X percent-encodes any bytes that aren't valid utf-8
399 # OS X percent-encodes any bytes that aren't valid utf-8
385 s = ''
400 s = ''
386 pos = 0
401 pos = 0
387 l = len(path)
402 l = len(path)
388 while pos < l:
403 while pos < l:
389 try:
404 try:
390 c = encoding.getutf8char(path, pos)
405 c = encoding.getutf8char(path, pos)
391 pos += len(c)
406 pos += len(c)
392 except ValueError:
407 except ValueError:
393 c = '%%%02X' % ord(path[pos:pos + 1])
408 c = '%%%02X' % ord(path[pos:pos + 1])
394 pos += 1
409 pos += 1
395 s += c
410 s += c
396
411
397 u = s.decode('utf-8')
412 u = s.decode('utf-8')
398
413
399 # Decompose then lowercase (HFS+ technote specifies lower)
414 # Decompose then lowercase (HFS+ technote specifies lower)
400 enc = unicodedata.normalize(r'NFD', u).lower().encode('utf-8')
415 enc = unicodedata.normalize(r'NFD', u).lower().encode('utf-8')
401 # drop HFS+ ignored characters
416 # drop HFS+ ignored characters
402 return encoding.hfsignoreclean(enc)
417 return encoding.hfsignoreclean(enc)
403
418
404 if pycompat.sysplatform == 'cygwin':
419 if pycompat.sysplatform == 'cygwin':
405 # workaround for cygwin, in which mount point part of path is
420 # workaround for cygwin, in which mount point part of path is
406 # treated as case sensitive, even though underlying NTFS is case
421 # treated as case sensitive, even though underlying NTFS is case
407 # insensitive.
422 # insensitive.
408
423
409 # default mount points
424 # default mount points
410 cygwinmountpoints = sorted([
425 cygwinmountpoints = sorted([
411 "/usr/bin",
426 "/usr/bin",
412 "/usr/lib",
427 "/usr/lib",
413 "/cygdrive",
428 "/cygdrive",
414 ], reverse=True)
429 ], reverse=True)
415
430
416 # use upper-ing as normcase as same as NTFS workaround
431 # use upper-ing as normcase as same as NTFS workaround
417 def normcase(path):
432 def normcase(path):
418 pathlen = len(path)
433 pathlen = len(path)
419 if (pathlen == 0) or (path[0] != pycompat.ossep):
434 if (pathlen == 0) or (path[0] != pycompat.ossep):
420 # treat as relative
435 # treat as relative
421 return encoding.upper(path)
436 return encoding.upper(path)
422
437
423 # to preserve case of mountpoint part
438 # to preserve case of mountpoint part
424 for mp in cygwinmountpoints:
439 for mp in cygwinmountpoints:
425 if not path.startswith(mp):
440 if not path.startswith(mp):
426 continue
441 continue
427
442
428 mplen = len(mp)
443 mplen = len(mp)
429 if mplen == pathlen: # mount point itself
444 if mplen == pathlen: # mount point itself
430 return mp
445 return mp
431 if path[mplen] == pycompat.ossep:
446 if path[mplen] == pycompat.ossep:
432 return mp + encoding.upper(path[mplen:])
447 return mp + encoding.upper(path[mplen:])
433
448
434 return encoding.upper(path)
449 return encoding.upper(path)
435
450
436 normcasespec = encoding.normcasespecs.other
451 normcasespec = encoding.normcasespecs.other
437 normcasefallback = normcase
452 normcasefallback = normcase
438
453
439 # Cygwin translates native ACLs to POSIX permissions,
454 # Cygwin translates native ACLs to POSIX permissions,
440 # but these translations are not supported by native
455 # but these translations are not supported by native
441 # tools, so the exec bit tends to be set erroneously.
456 # tools, so the exec bit tends to be set erroneously.
442 # Therefore, disable executable bit access on Cygwin.
457 # Therefore, disable executable bit access on Cygwin.
443 def checkexec(path):
458 def checkexec(path):
444 return False
459 return False
445
460
446 # Similarly, Cygwin's symlink emulation is likely to create
461 # Similarly, Cygwin's symlink emulation is likely to create
447 # problems when Mercurial is used from both Cygwin and native
462 # problems when Mercurial is used from both Cygwin and native
448 # Windows, with other native tools, or on shared volumes
463 # Windows, with other native tools, or on shared volumes
449 def checklink(path):
464 def checklink(path):
450 return False
465 return False
451
466
452 _needsshellquote = None
467 _needsshellquote = None
453 def shellquote(s):
468 def shellquote(s):
454 if pycompat.sysplatform == 'OpenVMS':
469 if pycompat.sysplatform == 'OpenVMS':
455 return '"%s"' % s
470 return '"%s"' % s
456 global _needsshellquote
471 global _needsshellquote
457 if _needsshellquote is None:
472 if _needsshellquote is None:
458 _needsshellquote = re.compile(br'[^a-zA-Z0-9._/+-]').search
473 _needsshellquote = re.compile(br'[^a-zA-Z0-9._/+-]').search
459 if s and not _needsshellquote(s):
474 if s and not _needsshellquote(s):
460 # "s" shouldn't have to be quoted
475 # "s" shouldn't have to be quoted
461 return s
476 return s
462 else:
477 else:
463 return "'%s'" % s.replace("'", "'\\''")
478 return "'%s'" % s.replace("'", "'\\''")
464
479
465 def shellsplit(s):
480 def shellsplit(s):
466 """Parse a command string in POSIX shell way (best-effort)"""
481 """Parse a command string in POSIX shell way (best-effort)"""
467 return pycompat.shlexsplit(s, posix=True)
482 return pycompat.shlexsplit(s, posix=True)
468
483
469 def quotecommand(cmd):
484 def quotecommand(cmd):
470 return cmd
485 return cmd
471
486
472 def testpid(pid):
487 def testpid(pid):
473 '''return False if pid dead, True if running or not sure'''
488 '''return False if pid dead, True if running or not sure'''
474 if pycompat.sysplatform == 'OpenVMS':
489 if pycompat.sysplatform == 'OpenVMS':
475 return True
490 return True
476 try:
491 try:
477 os.kill(pid, 0)
492 os.kill(pid, 0)
478 return True
493 return True
479 except OSError as inst:
494 except OSError as inst:
480 return inst.errno != errno.ESRCH
495 return inst.errno != errno.ESRCH
481
496
482 def isowner(st):
497 def isowner(st):
483 """Return True if the stat object st is from the current user."""
498 """Return True if the stat object st is from the current user."""
484 return st.st_uid == os.getuid()
499 return st.st_uid == os.getuid()
485
500
486 def findexe(command):
501 def findexe(command):
487 '''Find executable for command searching like which does.
502 '''Find executable for command searching like which does.
488 If command is a basename then PATH is searched for command.
503 If command is a basename then PATH is searched for command.
489 PATH isn't searched if command is an absolute or relative path.
504 PATH isn't searched if command is an absolute or relative path.
490 If command isn't found None is returned.'''
505 If command isn't found None is returned.'''
491 if pycompat.sysplatform == 'OpenVMS':
506 if pycompat.sysplatform == 'OpenVMS':
492 return command
507 return command
493
508
494 def findexisting(executable):
509 def findexisting(executable):
495 'Will return executable if existing file'
510 'Will return executable if existing file'
496 if os.path.isfile(executable) and os.access(executable, os.X_OK):
511 if os.path.isfile(executable) and os.access(executable, os.X_OK):
497 return executable
512 return executable
498 return None
513 return None
499
514
500 if pycompat.ossep in command:
515 if pycompat.ossep in command:
501 return findexisting(command)
516 return findexisting(command)
502
517
503 if pycompat.sysplatform == 'plan9':
518 if pycompat.sysplatform == 'plan9':
504 return findexisting(os.path.join('/bin', command))
519 return findexisting(os.path.join('/bin', command))
505
520
506 for path in encoding.environ.get('PATH', '').split(pycompat.ospathsep):
521 for path in encoding.environ.get('PATH', '').split(pycompat.ospathsep):
507 executable = findexisting(os.path.join(path, command))
522 executable = findexisting(os.path.join(path, command))
508 if executable is not None:
523 if executable is not None:
509 return executable
524 return executable
510 return None
525 return None
511
526
512 def setsignalhandler():
527 def setsignalhandler():
513 pass
528 pass
514
529
515 _wantedkinds = {stat.S_IFREG, stat.S_IFLNK}
530 _wantedkinds = {stat.S_IFREG, stat.S_IFLNK}
516
531
517 def statfiles(files):
532 def statfiles(files):
518 '''Stat each file in files. Yield each stat, or None if a file does not
533 '''Stat each file in files. Yield each stat, or None if a file does not
519 exist or has a type we don't care about.'''
534 exist or has a type we don't care about.'''
520 lstat = os.lstat
535 lstat = os.lstat
521 getkind = stat.S_IFMT
536 getkind = stat.S_IFMT
522 for nf in files:
537 for nf in files:
523 try:
538 try:
524 st = lstat(nf)
539 st = lstat(nf)
525 if getkind(st.st_mode) not in _wantedkinds:
540 if getkind(st.st_mode) not in _wantedkinds:
526 st = None
541 st = None
527 except OSError as err:
542 except OSError as err:
528 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
543 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
529 raise
544 raise
530 st = None
545 st = None
531 yield st
546 yield st
532
547
533 def getuser():
548 def getuser():
534 '''return name of current user'''
549 '''return name of current user'''
535 return pycompat.fsencode(getpass.getuser())
550 return pycompat.fsencode(getpass.getuser())
536
551
537 def username(uid=None):
552 def username(uid=None):
538 """Return the name of the user with the given uid.
553 """Return the name of the user with the given uid.
539
554
540 If uid is None, return the name of the current user."""
555 If uid is None, return the name of the current user."""
541
556
542 if uid is None:
557 if uid is None:
543 uid = os.getuid()
558 uid = os.getuid()
544 try:
559 try:
545 return pycompat.fsencode(pwd.getpwuid(uid)[0])
560 return pycompat.fsencode(pwd.getpwuid(uid)[0])
546 except KeyError:
561 except KeyError:
547 return b'%d' % uid
562 return b'%d' % uid
548
563
549 def groupname(gid=None):
564 def groupname(gid=None):
550 """Return the name of the group with the given gid.
565 """Return the name of the group with the given gid.
551
566
552 If gid is None, return the name of the current group."""
567 If gid is None, return the name of the current group."""
553
568
554 if gid is None:
569 if gid is None:
555 gid = os.getgid()
570 gid = os.getgid()
556 try:
571 try:
557 return grp.getgrgid(gid)[0]
572 return grp.getgrgid(gid)[0]
558 except KeyError:
573 except KeyError:
559 return str(gid)
574 return str(gid)
560
575
561 def groupmembers(name):
576 def groupmembers(name):
562 """Return the list of members of the group with the given
577 """Return the list of members of the group with the given
563 name, KeyError if the group does not exist.
578 name, KeyError if the group does not exist.
564 """
579 """
565 return list(grp.getgrnam(name).gr_mem)
580 return list(grp.getgrnam(name).gr_mem)
566
581
567 def spawndetached(args):
582 def spawndetached(args):
568 return os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
583 return os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
569 args[0], args)
584 args[0], args)
570
585
571 def gethgcmd():
586 def gethgcmd():
572 return sys.argv[:1]
587 return sys.argv[:1]
573
588
574 def makedir(path, notindexed):
589 def makedir(path, notindexed):
575 os.mkdir(path)
590 os.mkdir(path)
576
591
577 def lookupreg(key, name=None, scope=None):
592 def lookupreg(key, name=None, scope=None):
578 return None
593 return None
579
594
580 def hidewindow():
595 def hidewindow():
581 """Hide current shell window.
596 """Hide current shell window.
582
597
583 Used to hide the window opened when starting asynchronous
598 Used to hide the window opened when starting asynchronous
584 child process under Windows, unneeded on other systems.
599 child process under Windows, unneeded on other systems.
585 """
600 """
586 pass
601 pass
587
602
588 class cachestat(object):
603 class cachestat(object):
589 def __init__(self, path):
604 def __init__(self, path):
590 self.stat = os.stat(path)
605 self.stat = os.stat(path)
591
606
592 def cacheable(self):
607 def cacheable(self):
593 return bool(self.stat.st_ino)
608 return bool(self.stat.st_ino)
594
609
595 __hash__ = object.__hash__
610 __hash__ = object.__hash__
596
611
597 def __eq__(self, other):
612 def __eq__(self, other):
598 try:
613 try:
599 # Only dev, ino, size, mtime and atime are likely to change. Out
614 # Only dev, ino, size, mtime and atime are likely to change. Out
600 # of these, we shouldn't compare atime but should compare the
615 # of these, we shouldn't compare atime but should compare the
601 # rest. However, one of the other fields changing indicates
616 # rest. However, one of the other fields changing indicates
602 # something fishy going on, so return False if anything but atime
617 # something fishy going on, so return False if anything but atime
603 # changes.
618 # changes.
604 return (self.stat.st_mode == other.stat.st_mode and
619 return (self.stat.st_mode == other.stat.st_mode and
605 self.stat.st_ino == other.stat.st_ino and
620 self.stat.st_ino == other.stat.st_ino and
606 self.stat.st_dev == other.stat.st_dev and
621 self.stat.st_dev == other.stat.st_dev and
607 self.stat.st_nlink == other.stat.st_nlink and
622 self.stat.st_nlink == other.stat.st_nlink and
608 self.stat.st_uid == other.stat.st_uid and
623 self.stat.st_uid == other.stat.st_uid and
609 self.stat.st_gid == other.stat.st_gid and
624 self.stat.st_gid == other.stat.st_gid and
610 self.stat.st_size == other.stat.st_size and
625 self.stat.st_size == other.stat.st_size and
611 self.stat[stat.ST_MTIME] == other.stat[stat.ST_MTIME] and
626 self.stat[stat.ST_MTIME] == other.stat[stat.ST_MTIME] and
612 self.stat[stat.ST_CTIME] == other.stat[stat.ST_CTIME])
627 self.stat[stat.ST_CTIME] == other.stat[stat.ST_CTIME])
613 except AttributeError:
628 except AttributeError:
614 return False
629 return False
615
630
616 def __ne__(self, other):
631 def __ne__(self, other):
617 return not self == other
632 return not self == other
618
633
619 def statislink(st):
634 def statislink(st):
620 '''check whether a stat result is a symlink'''
635 '''check whether a stat result is a symlink'''
621 return st and stat.S_ISLNK(st.st_mode)
636 return st and stat.S_ISLNK(st.st_mode)
622
637
623 def statisexec(st):
638 def statisexec(st):
624 '''check whether a stat result is an executable file'''
639 '''check whether a stat result is an executable file'''
625 return st and (st.st_mode & 0o100 != 0)
640 return st and (st.st_mode & 0o100 != 0)
626
641
627 def poll(fds):
642 def poll(fds):
628 """block until something happens on any file descriptor
643 """block until something happens on any file descriptor
629
644
630 This is a generic helper that will check for any activity
645 This is a generic helper that will check for any activity
631 (read, write. exception) and return the list of touched files.
646 (read, write. exception) and return the list of touched files.
632
647
633 In unsupported cases, it will raise a NotImplementedError"""
648 In unsupported cases, it will raise a NotImplementedError"""
634 try:
649 try:
635 while True:
650 while True:
636 try:
651 try:
637 res = select.select(fds, fds, fds)
652 res = select.select(fds, fds, fds)
638 break
653 break
639 except select.error as inst:
654 except select.error as inst:
640 if inst.args[0] == errno.EINTR:
655 if inst.args[0] == errno.EINTR:
641 continue
656 continue
642 raise
657 raise
643 except ValueError: # out of range file descriptor
658 except ValueError: # out of range file descriptor
644 raise NotImplementedError()
659 raise NotImplementedError()
645 return sorted(list(set(sum(res, []))))
660 return sorted(list(set(sum(res, []))))
646
661
647 def readpipe(pipe):
662 def readpipe(pipe):
648 """Read all available data from a pipe."""
663 """Read all available data from a pipe."""
649 # We can't fstat() a pipe because Linux will always report 0.
664 # We can't fstat() a pipe because Linux will always report 0.
650 # So, we set the pipe to non-blocking mode and read everything
665 # So, we set the pipe to non-blocking mode and read everything
651 # that's available.
666 # that's available.
652 flags = fcntl.fcntl(pipe, fcntl.F_GETFL)
667 flags = fcntl.fcntl(pipe, fcntl.F_GETFL)
653 flags |= os.O_NONBLOCK
668 flags |= os.O_NONBLOCK
654 oldflags = fcntl.fcntl(pipe, fcntl.F_SETFL, flags)
669 oldflags = fcntl.fcntl(pipe, fcntl.F_SETFL, flags)
655
670
656 try:
671 try:
657 chunks = []
672 chunks = []
658 while True:
673 while True:
659 try:
674 try:
660 s = pipe.read()
675 s = pipe.read()
661 if not s:
676 if not s:
662 break
677 break
663 chunks.append(s)
678 chunks.append(s)
664 except IOError:
679 except IOError:
665 break
680 break
666
681
667 return ''.join(chunks)
682 return ''.join(chunks)
668 finally:
683 finally:
669 fcntl.fcntl(pipe, fcntl.F_SETFL, oldflags)
684 fcntl.fcntl(pipe, fcntl.F_SETFL, oldflags)
670
685
671 def bindunixsocket(sock, path):
686 def bindunixsocket(sock, path):
672 """Bind the UNIX domain socket to the specified path"""
687 """Bind the UNIX domain socket to the specified path"""
673 # use relative path instead of full path at bind() if possible, since
688 # use relative path instead of full path at bind() if possible, since
674 # AF_UNIX path has very small length limit (107 chars) on common
689 # AF_UNIX path has very small length limit (107 chars) on common
675 # platforms (see sys/un.h)
690 # platforms (see sys/un.h)
676 dirname, basename = os.path.split(path)
691 dirname, basename = os.path.split(path)
677 bakwdfd = None
692 bakwdfd = None
678 if dirname:
693 if dirname:
679 bakwdfd = os.open('.', os.O_DIRECTORY)
694 bakwdfd = os.open('.', os.O_DIRECTORY)
680 os.chdir(dirname)
695 os.chdir(dirname)
681 sock.bind(basename)
696 sock.bind(basename)
682 if bakwdfd:
697 if bakwdfd:
683 os.fchdir(bakwdfd)
698 os.fchdir(bakwdfd)
684 os.close(bakwdfd)
699 os.close(bakwdfd)
@@ -1,1826 +1,1830 b''
1 # subrepo.py - sub-repository classes and factory
1 # subrepo.py - sub-repository classes and factory
2 #
2 #
3 # Copyright 2009-2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2009-2010 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 __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import copy
10 import copy
11 import errno
11 import errno
12 import hashlib
12 import hashlib
13 import os
13 import os
14 import posixpath
14 import posixpath
15 import re
15 import re
16 import stat
16 import stat
17 import subprocess
17 import subprocess
18 import sys
18 import sys
19 import tarfile
19 import tarfile
20 import xml.dom.minidom
20 import xml.dom.minidom
21
21
22 from .i18n import _
22 from .i18n import _
23 from . import (
23 from . import (
24 cmdutil,
24 cmdutil,
25 encoding,
25 encoding,
26 error,
26 error,
27 exchange,
27 exchange,
28 logcmdutil,
28 logcmdutil,
29 match as matchmod,
29 match as matchmod,
30 node,
30 node,
31 pathutil,
31 pathutil,
32 phases,
32 phases,
33 pycompat,
33 pycompat,
34 scmutil,
34 scmutil,
35 subrepoutil,
35 subrepoutil,
36 util,
36 util,
37 vfs as vfsmod,
37 vfs as vfsmod,
38 )
38 )
39 from .utils import (
39 from .utils import (
40 dateutil,
40 dateutil,
41 procutil,
41 procutil,
42 stringutil,
42 stringutil,
43 )
43 )
44
44
45 hg = None
45 hg = None
46 reporelpath = subrepoutil.reporelpath
46 reporelpath = subrepoutil.reporelpath
47 subrelpath = subrepoutil.subrelpath
47 subrelpath = subrepoutil.subrelpath
48 _abssource = subrepoutil._abssource
48 _abssource = subrepoutil._abssource
49 propertycache = util.propertycache
49 propertycache = util.propertycache
50
50
51 def _expandedabspath(path):
51 def _expandedabspath(path):
52 '''
52 '''
53 get a path or url and if it is a path expand it and return an absolute path
53 get a path or url and if it is a path expand it and return an absolute path
54 '''
54 '''
55 expandedpath = util.urllocalpath(util.expandpath(path))
55 expandedpath = util.urllocalpath(util.expandpath(path))
56 u = util.url(expandedpath)
56 u = util.url(expandedpath)
57 if not u.scheme:
57 if not u.scheme:
58 path = util.normpath(os.path.abspath(u.path))
58 path = util.normpath(os.path.abspath(u.path))
59 return path
59 return path
60
60
61 def _getstorehashcachename(remotepath):
61 def _getstorehashcachename(remotepath):
62 '''get a unique filename for the store hash cache of a remote repository'''
62 '''get a unique filename for the store hash cache of a remote repository'''
63 return node.hex(hashlib.sha1(_expandedabspath(remotepath)).digest())[0:12]
63 return node.hex(hashlib.sha1(_expandedabspath(remotepath)).digest())[0:12]
64
64
65 class SubrepoAbort(error.Abort):
65 class SubrepoAbort(error.Abort):
66 """Exception class used to avoid handling a subrepo error more than once"""
66 """Exception class used to avoid handling a subrepo error more than once"""
67 def __init__(self, *args, **kw):
67 def __init__(self, *args, **kw):
68 self.subrepo = kw.pop(r'subrepo', None)
68 self.subrepo = kw.pop(r'subrepo', None)
69 self.cause = kw.pop(r'cause', None)
69 self.cause = kw.pop(r'cause', None)
70 error.Abort.__init__(self, *args, **kw)
70 error.Abort.__init__(self, *args, **kw)
71
71
72 def annotatesubrepoerror(func):
72 def annotatesubrepoerror(func):
73 def decoratedmethod(self, *args, **kargs):
73 def decoratedmethod(self, *args, **kargs):
74 try:
74 try:
75 res = func(self, *args, **kargs)
75 res = func(self, *args, **kargs)
76 except SubrepoAbort as ex:
76 except SubrepoAbort as ex:
77 # This exception has already been handled
77 # This exception has already been handled
78 raise ex
78 raise ex
79 except error.Abort as ex:
79 except error.Abort as ex:
80 subrepo = subrelpath(self)
80 subrepo = subrelpath(self)
81 errormsg = (stringutil.forcebytestr(ex) + ' '
81 errormsg = (stringutil.forcebytestr(ex) + ' '
82 + _('(in subrepository "%s")') % subrepo)
82 + _('(in subrepository "%s")') % subrepo)
83 # avoid handling this exception by raising a SubrepoAbort exception
83 # avoid handling this exception by raising a SubrepoAbort exception
84 raise SubrepoAbort(errormsg, hint=ex.hint, subrepo=subrepo,
84 raise SubrepoAbort(errormsg, hint=ex.hint, subrepo=subrepo,
85 cause=sys.exc_info())
85 cause=sys.exc_info())
86 return res
86 return res
87 return decoratedmethod
87 return decoratedmethod
88
88
89 def _updateprompt(ui, sub, dirty, local, remote):
89 def _updateprompt(ui, sub, dirty, local, remote):
90 if dirty:
90 if dirty:
91 msg = (_(' subrepository sources for %s differ\n'
91 msg = (_(' subrepository sources for %s differ\n'
92 'use (l)ocal source (%s) or (r)emote source (%s)?'
92 'use (l)ocal source (%s) or (r)emote source (%s)?'
93 '$$ &Local $$ &Remote')
93 '$$ &Local $$ &Remote')
94 % (subrelpath(sub), local, remote))
94 % (subrelpath(sub), local, remote))
95 else:
95 else:
96 msg = (_(' subrepository sources for %s differ (in checked out '
96 msg = (_(' subrepository sources for %s differ (in checked out '
97 'version)\n'
97 'version)\n'
98 'use (l)ocal source (%s) or (r)emote source (%s)?'
98 'use (l)ocal source (%s) or (r)emote source (%s)?'
99 '$$ &Local $$ &Remote')
99 '$$ &Local $$ &Remote')
100 % (subrelpath(sub), local, remote))
100 % (subrelpath(sub), local, remote))
101 return ui.promptchoice(msg, 0)
101 return ui.promptchoice(msg, 0)
102
102
103 def _sanitize(ui, vfs, ignore):
103 def _sanitize(ui, vfs, ignore):
104 for dirname, dirs, names in vfs.walk():
104 for dirname, dirs, names in vfs.walk():
105 for i, d in enumerate(dirs):
105 for i, d in enumerate(dirs):
106 if d.lower() == ignore:
106 if d.lower() == ignore:
107 del dirs[i]
107 del dirs[i]
108 break
108 break
109 if vfs.basename(dirname).lower() != '.hg':
109 if vfs.basename(dirname).lower() != '.hg':
110 continue
110 continue
111 for f in names:
111 for f in names:
112 if f.lower() == 'hgrc':
112 if f.lower() == 'hgrc':
113 ui.warn(_("warning: removing potentially hostile 'hgrc' "
113 ui.warn(_("warning: removing potentially hostile 'hgrc' "
114 "in '%s'\n") % vfs.join(dirname))
114 "in '%s'\n") % vfs.join(dirname))
115 vfs.unlink(vfs.reljoin(dirname, f))
115 vfs.unlink(vfs.reljoin(dirname, f))
116
116
117 def _auditsubrepopath(repo, path):
117 def _auditsubrepopath(repo, path):
118 # auditor doesn't check if the path itself is a symlink
118 # auditor doesn't check if the path itself is a symlink
119 pathutil.pathauditor(repo.root)(path)
119 pathutil.pathauditor(repo.root)(path)
120 if repo.wvfs.islink(path):
120 if repo.wvfs.islink(path):
121 raise error.Abort(_("subrepo '%s' traverses symbolic link") % path)
121 raise error.Abort(_("subrepo '%s' traverses symbolic link") % path)
122
122
123 SUBREPO_ALLOWED_DEFAULTS = {
123 SUBREPO_ALLOWED_DEFAULTS = {
124 'hg': True,
124 'hg': True,
125 'git': False,
125 'git': False,
126 'svn': False,
126 'svn': False,
127 }
127 }
128
128
129 def _checktype(ui, kind):
129 def _checktype(ui, kind):
130 # subrepos.allowed is a master kill switch. If disabled, subrepos are
130 # subrepos.allowed is a master kill switch. If disabled, subrepos are
131 # disabled period.
131 # disabled period.
132 if not ui.configbool('subrepos', 'allowed', True):
132 if not ui.configbool('subrepos', 'allowed', True):
133 raise error.Abort(_('subrepos not enabled'),
133 raise error.Abort(_('subrepos not enabled'),
134 hint=_("see 'hg help config.subrepos' for details"))
134 hint=_("see 'hg help config.subrepos' for details"))
135
135
136 default = SUBREPO_ALLOWED_DEFAULTS.get(kind, False)
136 default = SUBREPO_ALLOWED_DEFAULTS.get(kind, False)
137 if not ui.configbool('subrepos', '%s:allowed' % kind, default):
137 if not ui.configbool('subrepos', '%s:allowed' % kind, default):
138 raise error.Abort(_('%s subrepos not allowed') % kind,
138 raise error.Abort(_('%s subrepos not allowed') % kind,
139 hint=_("see 'hg help config.subrepos' for details"))
139 hint=_("see 'hg help config.subrepos' for details"))
140
140
141 if kind not in types:
141 if kind not in types:
142 raise error.Abort(_('unknown subrepo type %s') % kind)
142 raise error.Abort(_('unknown subrepo type %s') % kind)
143
143
144 def subrepo(ctx, path, allowwdir=False, allowcreate=True):
144 def subrepo(ctx, path, allowwdir=False, allowcreate=True):
145 """return instance of the right subrepo class for subrepo in path"""
145 """return instance of the right subrepo class for subrepo in path"""
146 # subrepo inherently violates our import layering rules
146 # subrepo inherently violates our import layering rules
147 # because it wants to make repo objects from deep inside the stack
147 # because it wants to make repo objects from deep inside the stack
148 # so we manually delay the circular imports to not break
148 # so we manually delay the circular imports to not break
149 # scripts that don't use our demand-loading
149 # scripts that don't use our demand-loading
150 global hg
150 global hg
151 from . import hg as h
151 from . import hg as h
152 hg = h
152 hg = h
153
153
154 repo = ctx.repo()
154 repo = ctx.repo()
155 _auditsubrepopath(repo, path)
155 _auditsubrepopath(repo, path)
156 state = ctx.substate[path]
156 state = ctx.substate[path]
157 _checktype(repo.ui, state[2])
157 _checktype(repo.ui, state[2])
158 if allowwdir:
158 if allowwdir:
159 state = (state[0], ctx.subrev(path), state[2])
159 state = (state[0], ctx.subrev(path), state[2])
160 return types[state[2]](ctx, path, state[:2], allowcreate)
160 return types[state[2]](ctx, path, state[:2], allowcreate)
161
161
162 def nullsubrepo(ctx, path, pctx):
162 def nullsubrepo(ctx, path, pctx):
163 """return an empty subrepo in pctx for the extant subrepo in ctx"""
163 """return an empty subrepo in pctx for the extant subrepo in ctx"""
164 # subrepo inherently violates our import layering rules
164 # subrepo inherently violates our import layering rules
165 # because it wants to make repo objects from deep inside the stack
165 # because it wants to make repo objects from deep inside the stack
166 # so we manually delay the circular imports to not break
166 # so we manually delay the circular imports to not break
167 # scripts that don't use our demand-loading
167 # scripts that don't use our demand-loading
168 global hg
168 global hg
169 from . import hg as h
169 from . import hg as h
170 hg = h
170 hg = h
171
171
172 repo = ctx.repo()
172 repo = ctx.repo()
173 _auditsubrepopath(repo, path)
173 _auditsubrepopath(repo, path)
174 state = ctx.substate[path]
174 state = ctx.substate[path]
175 _checktype(repo.ui, state[2])
175 _checktype(repo.ui, state[2])
176 subrev = ''
176 subrev = ''
177 if state[2] == 'hg':
177 if state[2] == 'hg':
178 subrev = "0" * 40
178 subrev = "0" * 40
179 return types[state[2]](pctx, path, (state[0], subrev), True)
179 return types[state[2]](pctx, path, (state[0], subrev), True)
180
180
181 # subrepo classes need to implement the following abstract class:
181 # subrepo classes need to implement the following abstract class:
182
182
183 class abstractsubrepo(object):
183 class abstractsubrepo(object):
184
184
185 def __init__(self, ctx, path):
185 def __init__(self, ctx, path):
186 """Initialize abstractsubrepo part
186 """Initialize abstractsubrepo part
187
187
188 ``ctx`` is the context referring this subrepository in the
188 ``ctx`` is the context referring this subrepository in the
189 parent repository.
189 parent repository.
190
190
191 ``path`` is the path to this subrepository as seen from
191 ``path`` is the path to this subrepository as seen from
192 innermost repository.
192 innermost repository.
193 """
193 """
194 self.ui = ctx.repo().ui
194 self.ui = ctx.repo().ui
195 self._ctx = ctx
195 self._ctx = ctx
196 self._path = path
196 self._path = path
197
197
198 def addwebdirpath(self, serverpath, webconf):
198 def addwebdirpath(self, serverpath, webconf):
199 """Add the hgwebdir entries for this subrepo, and any of its subrepos.
199 """Add the hgwebdir entries for this subrepo, and any of its subrepos.
200
200
201 ``serverpath`` is the path component of the URL for this repo.
201 ``serverpath`` is the path component of the URL for this repo.
202
202
203 ``webconf`` is the dictionary of hgwebdir entries.
203 ``webconf`` is the dictionary of hgwebdir entries.
204 """
204 """
205 pass
205 pass
206
206
207 def storeclean(self, path):
207 def storeclean(self, path):
208 """
208 """
209 returns true if the repository has not changed since it was last
209 returns true if the repository has not changed since it was last
210 cloned from or pushed to a given repository.
210 cloned from or pushed to a given repository.
211 """
211 """
212 return False
212 return False
213
213
214 def dirty(self, ignoreupdate=False, missing=False):
214 def dirty(self, ignoreupdate=False, missing=False):
215 """returns true if the dirstate of the subrepo is dirty or does not
215 """returns true if the dirstate of the subrepo is dirty or does not
216 match current stored state. If ignoreupdate is true, only check
216 match current stored state. If ignoreupdate is true, only check
217 whether the subrepo has uncommitted changes in its dirstate. If missing
217 whether the subrepo has uncommitted changes in its dirstate. If missing
218 is true, check for deleted files.
218 is true, check for deleted files.
219 """
219 """
220 raise NotImplementedError
220 raise NotImplementedError
221
221
222 def dirtyreason(self, ignoreupdate=False, missing=False):
222 def dirtyreason(self, ignoreupdate=False, missing=False):
223 """return reason string if it is ``dirty()``
223 """return reason string if it is ``dirty()``
224
224
225 Returned string should have enough information for the message
225 Returned string should have enough information for the message
226 of exception.
226 of exception.
227
227
228 This returns None, otherwise.
228 This returns None, otherwise.
229 """
229 """
230 if self.dirty(ignoreupdate=ignoreupdate, missing=missing):
230 if self.dirty(ignoreupdate=ignoreupdate, missing=missing):
231 return _('uncommitted changes in subrepository "%s"'
231 return _('uncommitted changes in subrepository "%s"'
232 ) % subrelpath(self)
232 ) % subrelpath(self)
233
233
234 def bailifchanged(self, ignoreupdate=False, hint=None):
234 def bailifchanged(self, ignoreupdate=False, hint=None):
235 """raise Abort if subrepository is ``dirty()``
235 """raise Abort if subrepository is ``dirty()``
236 """
236 """
237 dirtyreason = self.dirtyreason(ignoreupdate=ignoreupdate,
237 dirtyreason = self.dirtyreason(ignoreupdate=ignoreupdate,
238 missing=True)
238 missing=True)
239 if dirtyreason:
239 if dirtyreason:
240 raise error.Abort(dirtyreason, hint=hint)
240 raise error.Abort(dirtyreason, hint=hint)
241
241
242 def basestate(self):
242 def basestate(self):
243 """current working directory base state, disregarding .hgsubstate
243 """current working directory base state, disregarding .hgsubstate
244 state and working directory modifications"""
244 state and working directory modifications"""
245 raise NotImplementedError
245 raise NotImplementedError
246
246
247 def checknested(self, path):
247 def checknested(self, path):
248 """check if path is a subrepository within this repository"""
248 """check if path is a subrepository within this repository"""
249 return False
249 return False
250
250
251 def commit(self, text, user, date):
251 def commit(self, text, user, date):
252 """commit the current changes to the subrepo with the given
252 """commit the current changes to the subrepo with the given
253 log message. Use given user and date if possible. Return the
253 log message. Use given user and date if possible. Return the
254 new state of the subrepo.
254 new state of the subrepo.
255 """
255 """
256 raise NotImplementedError
256 raise NotImplementedError
257
257
258 def phase(self, state):
258 def phase(self, state):
259 """returns phase of specified state in the subrepository.
259 """returns phase of specified state in the subrepository.
260 """
260 """
261 return phases.public
261 return phases.public
262
262
263 def remove(self):
263 def remove(self):
264 """remove the subrepo
264 """remove the subrepo
265
265
266 (should verify the dirstate is not dirty first)
266 (should verify the dirstate is not dirty first)
267 """
267 """
268 raise NotImplementedError
268 raise NotImplementedError
269
269
270 def get(self, state, overwrite=False):
270 def get(self, state, overwrite=False):
271 """run whatever commands are needed to put the subrepo into
271 """run whatever commands are needed to put the subrepo into
272 this state
272 this state
273 """
273 """
274 raise NotImplementedError
274 raise NotImplementedError
275
275
276 def merge(self, state):
276 def merge(self, state):
277 """merge currently-saved state with the new state."""
277 """merge currently-saved state with the new state."""
278 raise NotImplementedError
278 raise NotImplementedError
279
279
280 def push(self, opts):
280 def push(self, opts):
281 """perform whatever action is analogous to 'hg push'
281 """perform whatever action is analogous to 'hg push'
282
282
283 This may be a no-op on some systems.
283 This may be a no-op on some systems.
284 """
284 """
285 raise NotImplementedError
285 raise NotImplementedError
286
286
287 def add(self, ui, match, prefix, explicitonly, **opts):
287 def add(self, ui, match, prefix, explicitonly, **opts):
288 return []
288 return []
289
289
290 def addremove(self, matcher, prefix, opts):
290 def addremove(self, matcher, prefix, opts):
291 self.ui.warn("%s: %s" % (prefix, _("addremove is not supported")))
291 self.ui.warn("%s: %s" % (prefix, _("addremove is not supported")))
292 return 1
292 return 1
293
293
294 def cat(self, match, fm, fntemplate, prefix, **opts):
294 def cat(self, match, fm, fntemplate, prefix, **opts):
295 return 1
295 return 1
296
296
297 def status(self, rev2, **opts):
297 def status(self, rev2, **opts):
298 return scmutil.status([], [], [], [], [], [], [])
298 return scmutil.status([], [], [], [], [], [], [])
299
299
300 def diff(self, ui, diffopts, node2, match, prefix, **opts):
300 def diff(self, ui, diffopts, node2, match, prefix, **opts):
301 pass
301 pass
302
302
303 def outgoing(self, ui, dest, opts):
303 def outgoing(self, ui, dest, opts):
304 return 1
304 return 1
305
305
306 def incoming(self, ui, source, opts):
306 def incoming(self, ui, source, opts):
307 return 1
307 return 1
308
308
309 def files(self):
309 def files(self):
310 """return filename iterator"""
310 """return filename iterator"""
311 raise NotImplementedError
311 raise NotImplementedError
312
312
313 def filedata(self, name, decode):
313 def filedata(self, name, decode):
314 """return file data, optionally passed through repo decoders"""
314 """return file data, optionally passed through repo decoders"""
315 raise NotImplementedError
315 raise NotImplementedError
316
316
317 def fileflags(self, name):
317 def fileflags(self, name):
318 """return file flags"""
318 """return file flags"""
319 return ''
319 return ''
320
320
321 def matchfileset(self, expr, badfn=None):
321 def matchfileset(self, expr, badfn=None):
322 """Resolve the fileset expression for this repo"""
322 """Resolve the fileset expression for this repo"""
323 return matchmod.nevermatcher(self.wvfs.base, '', badfn=badfn)
323 return matchmod.nevermatcher(self.wvfs.base, '', badfn=badfn)
324
324
325 def printfiles(self, ui, m, fm, fmt, subrepos):
325 def printfiles(self, ui, m, fm, fmt, subrepos):
326 """handle the files command for this subrepo"""
326 """handle the files command for this subrepo"""
327 return 1
327 return 1
328
328
329 def archive(self, archiver, prefix, match=None, decode=True):
329 def archive(self, archiver, prefix, match=None, decode=True):
330 if match is not None:
330 if match is not None:
331 files = [f for f in self.files() if match(f)]
331 files = [f for f in self.files() if match(f)]
332 else:
332 else:
333 files = self.files()
333 files = self.files()
334 total = len(files)
334 total = len(files)
335 relpath = subrelpath(self)
335 relpath = subrelpath(self)
336 progress = self.ui.makeprogress(_('archiving (%s)') % relpath,
336 progress = self.ui.makeprogress(_('archiving (%s)') % relpath,
337 unit=_('files'), total=total)
337 unit=_('files'), total=total)
338 progress.update(0)
338 progress.update(0)
339 for name in files:
339 for name in files:
340 flags = self.fileflags(name)
340 flags = self.fileflags(name)
341 mode = 'x' in flags and 0o755 or 0o644
341 mode = 'x' in flags and 0o755 or 0o644
342 symlink = 'l' in flags
342 symlink = 'l' in flags
343 archiver.addfile(prefix + self._path + '/' + name,
343 archiver.addfile(prefix + self._path + '/' + name,
344 mode, symlink, self.filedata(name, decode))
344 mode, symlink, self.filedata(name, decode))
345 progress.increment()
345 progress.increment()
346 progress.complete()
346 progress.complete()
347 return total
347 return total
348
348
349 def walk(self, match):
349 def walk(self, match):
350 '''
350 '''
351 walk recursively through the directory tree, finding all files
351 walk recursively through the directory tree, finding all files
352 matched by the match function
352 matched by the match function
353 '''
353 '''
354
354
355 def forget(self, match, prefix, dryrun, interactive):
355 def forget(self, match, prefix, dryrun, interactive):
356 return ([], [])
356 return ([], [])
357
357
358 def removefiles(self, matcher, prefix, after, force, subrepos,
358 def removefiles(self, matcher, prefix, after, force, subrepos,
359 dryrun, warnings):
359 dryrun, warnings):
360 """remove the matched files from the subrepository and the filesystem,
360 """remove the matched files from the subrepository and the filesystem,
361 possibly by force and/or after the file has been removed from the
361 possibly by force and/or after the file has been removed from the
362 filesystem. Return 0 on success, 1 on any warning.
362 filesystem. Return 0 on success, 1 on any warning.
363 """
363 """
364 warnings.append(_("warning: removefiles not implemented (%s)")
364 warnings.append(_("warning: removefiles not implemented (%s)")
365 % self._path)
365 % self._path)
366 return 1
366 return 1
367
367
368 def revert(self, substate, *pats, **opts):
368 def revert(self, substate, *pats, **opts):
369 self.ui.warn(_('%s: reverting %s subrepos is unsupported\n') \
369 self.ui.warn(_('%s: reverting %s subrepos is unsupported\n') \
370 % (substate[0], substate[2]))
370 % (substate[0], substate[2]))
371 return []
371 return []
372
372
373 def shortid(self, revid):
373 def shortid(self, revid):
374 return revid
374 return revid
375
375
376 def unshare(self):
376 def unshare(self):
377 '''
377 '''
378 convert this repository from shared to normal storage.
378 convert this repository from shared to normal storage.
379 '''
379 '''
380
380
381 def verify(self):
381 def verify(self):
382 '''verify the integrity of the repository. Return 0 on success or
382 '''verify the integrity of the repository. Return 0 on success or
383 warning, 1 on any error.
383 warning, 1 on any error.
384 '''
384 '''
385 return 0
385 return 0
386
386
387 @propertycache
387 @propertycache
388 def wvfs(self):
388 def wvfs(self):
389 """return vfs to access the working directory of this subrepository
389 """return vfs to access the working directory of this subrepository
390 """
390 """
391 return vfsmod.vfs(self._ctx.repo().wvfs.join(self._path))
391 return vfsmod.vfs(self._ctx.repo().wvfs.join(self._path))
392
392
393 @propertycache
393 @propertycache
394 def _relpath(self):
394 def _relpath(self):
395 """return path to this subrepository as seen from outermost repository
395 """return path to this subrepository as seen from outermost repository
396 """
396 """
397 return self.wvfs.reljoin(reporelpath(self._ctx.repo()), self._path)
397 return self.wvfs.reljoin(reporelpath(self._ctx.repo()), self._path)
398
398
399 class hgsubrepo(abstractsubrepo):
399 class hgsubrepo(abstractsubrepo):
400 def __init__(self, ctx, path, state, allowcreate):
400 def __init__(self, ctx, path, state, allowcreate):
401 super(hgsubrepo, self).__init__(ctx, path)
401 super(hgsubrepo, self).__init__(ctx, path)
402 self._state = state
402 self._state = state
403 r = ctx.repo()
403 r = ctx.repo()
404 root = r.wjoin(path)
404 root = r.wjoin(path)
405 create = allowcreate and not r.wvfs.exists('%s/.hg' % path)
405 create = allowcreate and not r.wvfs.exists('%s/.hg' % path)
406 self._repo = hg.repository(r.baseui, root, create=create)
406 self._repo = hg.repository(r.baseui, root, create=create)
407
407
408 # Propagate the parent's --hidden option
408 # Propagate the parent's --hidden option
409 if r is r.unfiltered():
409 if r is r.unfiltered():
410 self._repo = self._repo.unfiltered()
410 self._repo = self._repo.unfiltered()
411
411
412 self.ui = self._repo.ui
412 self.ui = self._repo.ui
413 for s, k in [('ui', 'commitsubrepos')]:
413 for s, k in [('ui', 'commitsubrepos')]:
414 v = r.ui.config(s, k)
414 v = r.ui.config(s, k)
415 if v:
415 if v:
416 self.ui.setconfig(s, k, v, 'subrepo')
416 self.ui.setconfig(s, k, v, 'subrepo')
417 # internal config: ui._usedassubrepo
417 # internal config: ui._usedassubrepo
418 self.ui.setconfig('ui', '_usedassubrepo', 'True', 'subrepo')
418 self.ui.setconfig('ui', '_usedassubrepo', 'True', 'subrepo')
419 self._initrepo(r, state[0], create)
419 self._initrepo(r, state[0], create)
420
420
421 @annotatesubrepoerror
421 @annotatesubrepoerror
422 def addwebdirpath(self, serverpath, webconf):
422 def addwebdirpath(self, serverpath, webconf):
423 cmdutil.addwebdirpath(self._repo, subrelpath(self), webconf)
423 cmdutil.addwebdirpath(self._repo, subrelpath(self), webconf)
424
424
425 def storeclean(self, path):
425 def storeclean(self, path):
426 with self._repo.lock():
426 with self._repo.lock():
427 return self._storeclean(path)
427 return self._storeclean(path)
428
428
429 def _storeclean(self, path):
429 def _storeclean(self, path):
430 clean = True
430 clean = True
431 itercache = self._calcstorehash(path)
431 itercache = self._calcstorehash(path)
432 for filehash in self._readstorehashcache(path):
432 for filehash in self._readstorehashcache(path):
433 if filehash != next(itercache, None):
433 if filehash != next(itercache, None):
434 clean = False
434 clean = False
435 break
435 break
436 if clean:
436 if clean:
437 # if not empty:
437 # if not empty:
438 # the cached and current pull states have a different size
438 # the cached and current pull states have a different size
439 clean = next(itercache, None) is None
439 clean = next(itercache, None) is None
440 return clean
440 return clean
441
441
442 def _calcstorehash(self, remotepath):
442 def _calcstorehash(self, remotepath):
443 '''calculate a unique "store hash"
443 '''calculate a unique "store hash"
444
444
445 This method is used to to detect when there are changes that may
445 This method is used to to detect when there are changes that may
446 require a push to a given remote path.'''
446 require a push to a given remote path.'''
447 # sort the files that will be hashed in increasing (likely) file size
447 # sort the files that will be hashed in increasing (likely) file size
448 filelist = ('bookmarks', 'store/phaseroots', 'store/00changelog.i')
448 filelist = ('bookmarks', 'store/phaseroots', 'store/00changelog.i')
449 yield '# %s\n' % _expandedabspath(remotepath)
449 yield '# %s\n' % _expandedabspath(remotepath)
450 vfs = self._repo.vfs
450 vfs = self._repo.vfs
451 for relname in filelist:
451 for relname in filelist:
452 filehash = node.hex(hashlib.sha1(vfs.tryread(relname)).digest())
452 filehash = node.hex(hashlib.sha1(vfs.tryread(relname)).digest())
453 yield '%s = %s\n' % (relname, filehash)
453 yield '%s = %s\n' % (relname, filehash)
454
454
455 @propertycache
455 @propertycache
456 def _cachestorehashvfs(self):
456 def _cachestorehashvfs(self):
457 return vfsmod.vfs(self._repo.vfs.join('cache/storehash'))
457 return vfsmod.vfs(self._repo.vfs.join('cache/storehash'))
458
458
459 def _readstorehashcache(self, remotepath):
459 def _readstorehashcache(self, remotepath):
460 '''read the store hash cache for a given remote repository'''
460 '''read the store hash cache for a given remote repository'''
461 cachefile = _getstorehashcachename(remotepath)
461 cachefile = _getstorehashcachename(remotepath)
462 return self._cachestorehashvfs.tryreadlines(cachefile, 'r')
462 return self._cachestorehashvfs.tryreadlines(cachefile, 'r')
463
463
464 def _cachestorehash(self, remotepath):
464 def _cachestorehash(self, remotepath):
465 '''cache the current store hash
465 '''cache the current store hash
466
466
467 Each remote repo requires its own store hash cache, because a subrepo
467 Each remote repo requires its own store hash cache, because a subrepo
468 store may be "clean" versus a given remote repo, but not versus another
468 store may be "clean" versus a given remote repo, but not versus another
469 '''
469 '''
470 cachefile = _getstorehashcachename(remotepath)
470 cachefile = _getstorehashcachename(remotepath)
471 with self._repo.lock():
471 with self._repo.lock():
472 storehash = list(self._calcstorehash(remotepath))
472 storehash = list(self._calcstorehash(remotepath))
473 vfs = self._cachestorehashvfs
473 vfs = self._cachestorehashvfs
474 vfs.writelines(cachefile, storehash, mode='wb', notindexed=True)
474 vfs.writelines(cachefile, storehash, mode='wb', notindexed=True)
475
475
476 def _getctx(self):
476 def _getctx(self):
477 '''fetch the context for this subrepo revision, possibly a workingctx
477 '''fetch the context for this subrepo revision, possibly a workingctx
478 '''
478 '''
479 if self._ctx.rev() is None:
479 if self._ctx.rev() is None:
480 return self._repo[None] # workingctx if parent is workingctx
480 return self._repo[None] # workingctx if parent is workingctx
481 else:
481 else:
482 rev = self._state[1]
482 rev = self._state[1]
483 return self._repo[rev]
483 return self._repo[rev]
484
484
485 @annotatesubrepoerror
485 @annotatesubrepoerror
486 def _initrepo(self, parentrepo, source, create):
486 def _initrepo(self, parentrepo, source, create):
487 self._repo._subparent = parentrepo
487 self._repo._subparent = parentrepo
488 self._repo._subsource = source
488 self._repo._subsource = source
489
489
490 if create:
490 if create:
491 lines = ['[paths]\n']
491 lines = ['[paths]\n']
492
492
493 def addpathconfig(key, value):
493 def addpathconfig(key, value):
494 if value:
494 if value:
495 lines.append('%s = %s\n' % (key, value))
495 lines.append('%s = %s\n' % (key, value))
496 self.ui.setconfig('paths', key, value, 'subrepo')
496 self.ui.setconfig('paths', key, value, 'subrepo')
497
497
498 defpath = _abssource(self._repo, abort=False)
498 defpath = _abssource(self._repo, abort=False)
499 defpushpath = _abssource(self._repo, True, abort=False)
499 defpushpath = _abssource(self._repo, True, abort=False)
500 addpathconfig('default', defpath)
500 addpathconfig('default', defpath)
501 if defpath != defpushpath:
501 if defpath != defpushpath:
502 addpathconfig('default-push', defpushpath)
502 addpathconfig('default-push', defpushpath)
503
503
504 self._repo.vfs.write('hgrc', util.tonativeeol(''.join(lines)))
504 self._repo.vfs.write('hgrc', util.tonativeeol(''.join(lines)))
505
505
506 @annotatesubrepoerror
506 @annotatesubrepoerror
507 def add(self, ui, match, prefix, explicitonly, **opts):
507 def add(self, ui, match, prefix, explicitonly, **opts):
508 return cmdutil.add(ui, self._repo, match,
508 return cmdutil.add(ui, self._repo, match,
509 self.wvfs.reljoin(prefix, self._path),
509 self.wvfs.reljoin(prefix, self._path),
510 explicitonly, **opts)
510 explicitonly, **opts)
511
511
512 @annotatesubrepoerror
512 @annotatesubrepoerror
513 def addremove(self, m, prefix, opts):
513 def addremove(self, m, prefix, opts):
514 # In the same way as sub directories are processed, once in a subrepo,
514 # In the same way as sub directories are processed, once in a subrepo,
515 # always entry any of its subrepos. Don't corrupt the options that will
515 # always entry any of its subrepos. Don't corrupt the options that will
516 # be used to process sibling subrepos however.
516 # be used to process sibling subrepos however.
517 opts = copy.copy(opts)
517 opts = copy.copy(opts)
518 opts['subrepos'] = True
518 opts['subrepos'] = True
519 return scmutil.addremove(self._repo, m,
519 return scmutil.addremove(self._repo, m,
520 self.wvfs.reljoin(prefix, self._path), opts)
520 self.wvfs.reljoin(prefix, self._path), opts)
521
521
522 @annotatesubrepoerror
522 @annotatesubrepoerror
523 def cat(self, match, fm, fntemplate, prefix, **opts):
523 def cat(self, match, fm, fntemplate, prefix, **opts):
524 rev = self._state[1]
524 rev = self._state[1]
525 ctx = self._repo[rev]
525 ctx = self._repo[rev]
526 return cmdutil.cat(self.ui, self._repo, ctx, match, fm, fntemplate,
526 return cmdutil.cat(self.ui, self._repo, ctx, match, fm, fntemplate,
527 prefix, **opts)
527 prefix, **opts)
528
528
529 @annotatesubrepoerror
529 @annotatesubrepoerror
530 def status(self, rev2, **opts):
530 def status(self, rev2, **opts):
531 try:
531 try:
532 rev1 = self._state[1]
532 rev1 = self._state[1]
533 ctx1 = self._repo[rev1]
533 ctx1 = self._repo[rev1]
534 ctx2 = self._repo[rev2]
534 ctx2 = self._repo[rev2]
535 return self._repo.status(ctx1, ctx2, **opts)
535 return self._repo.status(ctx1, ctx2, **opts)
536 except error.RepoLookupError as inst:
536 except error.RepoLookupError as inst:
537 self.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
537 self.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
538 % (inst, subrelpath(self)))
538 % (inst, subrelpath(self)))
539 return scmutil.status([], [], [], [], [], [], [])
539 return scmutil.status([], [], [], [], [], [], [])
540
540
541 @annotatesubrepoerror
541 @annotatesubrepoerror
542 def diff(self, ui, diffopts, node2, match, prefix, **opts):
542 def diff(self, ui, diffopts, node2, match, prefix, **opts):
543 try:
543 try:
544 node1 = node.bin(self._state[1])
544 node1 = node.bin(self._state[1])
545 # We currently expect node2 to come from substate and be
545 # We currently expect node2 to come from substate and be
546 # in hex format
546 # in hex format
547 if node2 is not None:
547 if node2 is not None:
548 node2 = node.bin(node2)
548 node2 = node.bin(node2)
549 logcmdutil.diffordiffstat(ui, self._repo, diffopts,
549 logcmdutil.diffordiffstat(ui, self._repo, diffopts,
550 node1, node2, match,
550 node1, node2, match,
551 prefix=posixpath.join(prefix, self._path),
551 prefix=posixpath.join(prefix, self._path),
552 listsubrepos=True, **opts)
552 listsubrepos=True, **opts)
553 except error.RepoLookupError as inst:
553 except error.RepoLookupError as inst:
554 self.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
554 self.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
555 % (inst, subrelpath(self)))
555 % (inst, subrelpath(self)))
556
556
557 @annotatesubrepoerror
557 @annotatesubrepoerror
558 def archive(self, archiver, prefix, match=None, decode=True):
558 def archive(self, archiver, prefix, match=None, decode=True):
559 self._get(self._state + ('hg',))
559 self._get(self._state + ('hg',))
560 files = self.files()
560 files = self.files()
561 if match:
561 if match:
562 files = [f for f in files if match(f)]
562 files = [f for f in files if match(f)]
563 rev = self._state[1]
563 rev = self._state[1]
564 ctx = self._repo[rev]
564 ctx = self._repo[rev]
565 scmutil.prefetchfiles(self._repo, [ctx.rev()],
565 scmutil.prefetchfiles(self._repo, [ctx.rev()],
566 scmutil.matchfiles(self._repo, files))
566 scmutil.matchfiles(self._repo, files))
567 total = abstractsubrepo.archive(self, archiver, prefix, match)
567 total = abstractsubrepo.archive(self, archiver, prefix, match)
568 for subpath in ctx.substate:
568 for subpath in ctx.substate:
569 s = subrepo(ctx, subpath, True)
569 s = subrepo(ctx, subpath, True)
570 submatch = matchmod.subdirmatcher(subpath, match)
570 submatch = matchmod.subdirmatcher(subpath, match)
571 total += s.archive(archiver, prefix + self._path + '/', submatch,
571 total += s.archive(archiver, prefix + self._path + '/', submatch,
572 decode)
572 decode)
573 return total
573 return total
574
574
575 @annotatesubrepoerror
575 @annotatesubrepoerror
576 def dirty(self, ignoreupdate=False, missing=False):
576 def dirty(self, ignoreupdate=False, missing=False):
577 r = self._state[1]
577 r = self._state[1]
578 if r == '' and not ignoreupdate: # no state recorded
578 if r == '' and not ignoreupdate: # no state recorded
579 return True
579 return True
580 w = self._repo[None]
580 w = self._repo[None]
581 if r != w.p1().hex() and not ignoreupdate:
581 if r != w.p1().hex() and not ignoreupdate:
582 # different version checked out
582 # different version checked out
583 return True
583 return True
584 return w.dirty(missing=missing) # working directory changed
584 return w.dirty(missing=missing) # working directory changed
585
585
586 def basestate(self):
586 def basestate(self):
587 return self._repo['.'].hex()
587 return self._repo['.'].hex()
588
588
589 def checknested(self, path):
589 def checknested(self, path):
590 return self._repo._checknested(self._repo.wjoin(path))
590 return self._repo._checknested(self._repo.wjoin(path))
591
591
592 @annotatesubrepoerror
592 @annotatesubrepoerror
593 def commit(self, text, user, date):
593 def commit(self, text, user, date):
594 # don't bother committing in the subrepo if it's only been
594 # don't bother committing in the subrepo if it's only been
595 # updated
595 # updated
596 if not self.dirty(True):
596 if not self.dirty(True):
597 return self._repo['.'].hex()
597 return self._repo['.'].hex()
598 self.ui.debug("committing subrepo %s\n" % subrelpath(self))
598 self.ui.debug("committing subrepo %s\n" % subrelpath(self))
599 n = self._repo.commit(text, user, date)
599 n = self._repo.commit(text, user, date)
600 if not n:
600 if not n:
601 return self._repo['.'].hex() # different version checked out
601 return self._repo['.'].hex() # different version checked out
602 return node.hex(n)
602 return node.hex(n)
603
603
604 @annotatesubrepoerror
604 @annotatesubrepoerror
605 def phase(self, state):
605 def phase(self, state):
606 return self._repo[state or '.'].phase()
606 return self._repo[state or '.'].phase()
607
607
608 @annotatesubrepoerror
608 @annotatesubrepoerror
609 def remove(self):
609 def remove(self):
610 # we can't fully delete the repository as it may contain
610 # we can't fully delete the repository as it may contain
611 # local-only history
611 # local-only history
612 self.ui.note(_('removing subrepo %s\n') % subrelpath(self))
612 self.ui.note(_('removing subrepo %s\n') % subrelpath(self))
613 hg.clean(self._repo, node.nullid, False)
613 hg.clean(self._repo, node.nullid, False)
614
614
615 def _get(self, state):
615 def _get(self, state):
616 source, revision, kind = state
616 source, revision, kind = state
617 parentrepo = self._repo._subparent
617 parentrepo = self._repo._subparent
618
618
619 if revision in self._repo.unfiltered():
619 if revision in self._repo.unfiltered():
620 # Allow shared subrepos tracked at null to setup the sharedpath
620 # Allow shared subrepos tracked at null to setup the sharedpath
621 if len(self._repo) != 0 or not parentrepo.shared():
621 if len(self._repo) != 0 or not parentrepo.shared():
622 return True
622 return True
623 self._repo._subsource = source
623 self._repo._subsource = source
624 srcurl = _abssource(self._repo)
624 srcurl = _abssource(self._repo)
625 other = hg.peer(self._repo, {}, srcurl)
625
626 # Defer creating the peer until after the status message is logged, in
627 # case there are network problems.
628 getpeer = lambda: hg.peer(self._repo, {}, srcurl)
629
626 if len(self._repo) == 0:
630 if len(self._repo) == 0:
627 # use self._repo.vfs instead of self.wvfs to remove .hg only
631 # use self._repo.vfs instead of self.wvfs to remove .hg only
628 self._repo.vfs.rmtree()
632 self._repo.vfs.rmtree()
629
633
630 # A remote subrepo could be shared if there is a local copy
634 # A remote subrepo could be shared if there is a local copy
631 # relative to the parent's share source. But clone pooling doesn't
635 # relative to the parent's share source. But clone pooling doesn't
632 # assemble the repos in a tree, so that can't be consistently done.
636 # assemble the repos in a tree, so that can't be consistently done.
633 # A simpler option is for the user to configure clone pooling, and
637 # A simpler option is for the user to configure clone pooling, and
634 # work with that.
638 # work with that.
635 if parentrepo.shared() and hg.islocal(srcurl):
639 if parentrepo.shared() and hg.islocal(srcurl):
636 self.ui.status(_('sharing subrepo %s from %s\n')
640 self.ui.status(_('sharing subrepo %s from %s\n')
637 % (subrelpath(self), srcurl))
641 % (subrelpath(self), srcurl))
638 shared = hg.share(self._repo._subparent.baseui,
642 shared = hg.share(self._repo._subparent.baseui,
639 other, self._repo.root,
643 getpeer(), self._repo.root,
640 update=False, bookmarks=False)
644 update=False, bookmarks=False)
641 self._repo = shared.local()
645 self._repo = shared.local()
642 else:
646 else:
643 # TODO: find a common place for this and this code in the
647 # TODO: find a common place for this and this code in the
644 # share.py wrap of the clone command.
648 # share.py wrap of the clone command.
645 if parentrepo.shared():
649 if parentrepo.shared():
646 pool = self.ui.config('share', 'pool')
650 pool = self.ui.config('share', 'pool')
647 if pool:
651 if pool:
648 pool = util.expandpath(pool)
652 pool = util.expandpath(pool)
649
653
650 shareopts = {
654 shareopts = {
651 'pool': pool,
655 'pool': pool,
652 'mode': self.ui.config('share', 'poolnaming'),
656 'mode': self.ui.config('share', 'poolnaming'),
653 }
657 }
654 else:
658 else:
655 shareopts = {}
659 shareopts = {}
656
660
657 self.ui.status(_('cloning subrepo %s from %s\n')
661 self.ui.status(_('cloning subrepo %s from %s\n')
658 % (subrelpath(self), util.hidepassword(srcurl)))
662 % (subrelpath(self), util.hidepassword(srcurl)))
659 other, cloned = hg.clone(self._repo._subparent.baseui, {},
663 other, cloned = hg.clone(self._repo._subparent.baseui, {},
660 other, self._repo.root,
664 getpeer(), self._repo.root,
661 update=False, shareopts=shareopts)
665 update=False, shareopts=shareopts)
662 self._repo = cloned.local()
666 self._repo = cloned.local()
663 self._initrepo(parentrepo, source, create=True)
667 self._initrepo(parentrepo, source, create=True)
664 self._cachestorehash(srcurl)
668 self._cachestorehash(srcurl)
665 else:
669 else:
666 self.ui.status(_('pulling subrepo %s from %s\n')
670 self.ui.status(_('pulling subrepo %s from %s\n')
667 % (subrelpath(self), util.hidepassword(srcurl)))
671 % (subrelpath(self), util.hidepassword(srcurl)))
668 cleansub = self.storeclean(srcurl)
672 cleansub = self.storeclean(srcurl)
669 exchange.pull(self._repo, other)
673 exchange.pull(self._repo, getpeer())
670 if cleansub:
674 if cleansub:
671 # keep the repo clean after pull
675 # keep the repo clean after pull
672 self._cachestorehash(srcurl)
676 self._cachestorehash(srcurl)
673 return False
677 return False
674
678
675 @annotatesubrepoerror
679 @annotatesubrepoerror
676 def get(self, state, overwrite=False):
680 def get(self, state, overwrite=False):
677 inrepo = self._get(state)
681 inrepo = self._get(state)
678 source, revision, kind = state
682 source, revision, kind = state
679 repo = self._repo
683 repo = self._repo
680 repo.ui.debug("getting subrepo %s\n" % self._path)
684 repo.ui.debug("getting subrepo %s\n" % self._path)
681 if inrepo:
685 if inrepo:
682 urepo = repo.unfiltered()
686 urepo = repo.unfiltered()
683 ctx = urepo[revision]
687 ctx = urepo[revision]
684 if ctx.hidden():
688 if ctx.hidden():
685 urepo.ui.warn(
689 urepo.ui.warn(
686 _('revision %s in subrepository "%s" is hidden\n') \
690 _('revision %s in subrepository "%s" is hidden\n') \
687 % (revision[0:12], self._path))
691 % (revision[0:12], self._path))
688 repo = urepo
692 repo = urepo
689 hg.updaterepo(repo, revision, overwrite)
693 hg.updaterepo(repo, revision, overwrite)
690
694
691 @annotatesubrepoerror
695 @annotatesubrepoerror
692 def merge(self, state):
696 def merge(self, state):
693 self._get(state)
697 self._get(state)
694 cur = self._repo['.']
698 cur = self._repo['.']
695 dst = self._repo[state[1]]
699 dst = self._repo[state[1]]
696 anc = dst.ancestor(cur)
700 anc = dst.ancestor(cur)
697
701
698 def mergefunc():
702 def mergefunc():
699 if anc == cur and dst.branch() == cur.branch():
703 if anc == cur and dst.branch() == cur.branch():
700 self.ui.debug('updating subrepository "%s"\n'
704 self.ui.debug('updating subrepository "%s"\n'
701 % subrelpath(self))
705 % subrelpath(self))
702 hg.update(self._repo, state[1])
706 hg.update(self._repo, state[1])
703 elif anc == dst:
707 elif anc == dst:
704 self.ui.debug('skipping subrepository "%s"\n'
708 self.ui.debug('skipping subrepository "%s"\n'
705 % subrelpath(self))
709 % subrelpath(self))
706 else:
710 else:
707 self.ui.debug('merging subrepository "%s"\n' % subrelpath(self))
711 self.ui.debug('merging subrepository "%s"\n' % subrelpath(self))
708 hg.merge(self._repo, state[1], remind=False)
712 hg.merge(self._repo, state[1], remind=False)
709
713
710 wctx = self._repo[None]
714 wctx = self._repo[None]
711 if self.dirty():
715 if self.dirty():
712 if anc != dst:
716 if anc != dst:
713 if _updateprompt(self.ui, self, wctx.dirty(), cur, dst):
717 if _updateprompt(self.ui, self, wctx.dirty(), cur, dst):
714 mergefunc()
718 mergefunc()
715 else:
719 else:
716 mergefunc()
720 mergefunc()
717 else:
721 else:
718 mergefunc()
722 mergefunc()
719
723
720 @annotatesubrepoerror
724 @annotatesubrepoerror
721 def push(self, opts):
725 def push(self, opts):
722 force = opts.get('force')
726 force = opts.get('force')
723 newbranch = opts.get('new_branch')
727 newbranch = opts.get('new_branch')
724 ssh = opts.get('ssh')
728 ssh = opts.get('ssh')
725
729
726 # push subrepos depth-first for coherent ordering
730 # push subrepos depth-first for coherent ordering
727 c = self._repo['.']
731 c = self._repo['.']
728 subs = c.substate # only repos that are committed
732 subs = c.substate # only repos that are committed
729 for s in sorted(subs):
733 for s in sorted(subs):
730 if c.sub(s).push(opts) == 0:
734 if c.sub(s).push(opts) == 0:
731 return False
735 return False
732
736
733 dsturl = _abssource(self._repo, True)
737 dsturl = _abssource(self._repo, True)
734 if not force:
738 if not force:
735 if self.storeclean(dsturl):
739 if self.storeclean(dsturl):
736 self.ui.status(
740 self.ui.status(
737 _('no changes made to subrepo %s since last push to %s\n')
741 _('no changes made to subrepo %s since last push to %s\n')
738 % (subrelpath(self), util.hidepassword(dsturl)))
742 % (subrelpath(self), util.hidepassword(dsturl)))
739 return None
743 return None
740 self.ui.status(_('pushing subrepo %s to %s\n') %
744 self.ui.status(_('pushing subrepo %s to %s\n') %
741 (subrelpath(self), util.hidepassword(dsturl)))
745 (subrelpath(self), util.hidepassword(dsturl)))
742 other = hg.peer(self._repo, {'ssh': ssh}, dsturl)
746 other = hg.peer(self._repo, {'ssh': ssh}, dsturl)
743 res = exchange.push(self._repo, other, force, newbranch=newbranch)
747 res = exchange.push(self._repo, other, force, newbranch=newbranch)
744
748
745 # the repo is now clean
749 # the repo is now clean
746 self._cachestorehash(dsturl)
750 self._cachestorehash(dsturl)
747 return res.cgresult
751 return res.cgresult
748
752
749 @annotatesubrepoerror
753 @annotatesubrepoerror
750 def outgoing(self, ui, dest, opts):
754 def outgoing(self, ui, dest, opts):
751 if 'rev' in opts or 'branch' in opts:
755 if 'rev' in opts or 'branch' in opts:
752 opts = copy.copy(opts)
756 opts = copy.copy(opts)
753 opts.pop('rev', None)
757 opts.pop('rev', None)
754 opts.pop('branch', None)
758 opts.pop('branch', None)
755 return hg.outgoing(ui, self._repo, _abssource(self._repo, True), opts)
759 return hg.outgoing(ui, self._repo, _abssource(self._repo, True), opts)
756
760
757 @annotatesubrepoerror
761 @annotatesubrepoerror
758 def incoming(self, ui, source, opts):
762 def incoming(self, ui, source, opts):
759 if 'rev' in opts or 'branch' in opts:
763 if 'rev' in opts or 'branch' in opts:
760 opts = copy.copy(opts)
764 opts = copy.copy(opts)
761 opts.pop('rev', None)
765 opts.pop('rev', None)
762 opts.pop('branch', None)
766 opts.pop('branch', None)
763 return hg.incoming(ui, self._repo, _abssource(self._repo, False), opts)
767 return hg.incoming(ui, self._repo, _abssource(self._repo, False), opts)
764
768
765 @annotatesubrepoerror
769 @annotatesubrepoerror
766 def files(self):
770 def files(self):
767 rev = self._state[1]
771 rev = self._state[1]
768 ctx = self._repo[rev]
772 ctx = self._repo[rev]
769 return ctx.manifest().keys()
773 return ctx.manifest().keys()
770
774
771 def filedata(self, name, decode):
775 def filedata(self, name, decode):
772 rev = self._state[1]
776 rev = self._state[1]
773 data = self._repo[rev][name].data()
777 data = self._repo[rev][name].data()
774 if decode:
778 if decode:
775 data = self._repo.wwritedata(name, data)
779 data = self._repo.wwritedata(name, data)
776 return data
780 return data
777
781
778 def fileflags(self, name):
782 def fileflags(self, name):
779 rev = self._state[1]
783 rev = self._state[1]
780 ctx = self._repo[rev]
784 ctx = self._repo[rev]
781 return ctx.flags(name)
785 return ctx.flags(name)
782
786
783 @annotatesubrepoerror
787 @annotatesubrepoerror
784 def printfiles(self, ui, m, fm, fmt, subrepos):
788 def printfiles(self, ui, m, fm, fmt, subrepos):
785 # If the parent context is a workingctx, use the workingctx here for
789 # If the parent context is a workingctx, use the workingctx here for
786 # consistency.
790 # consistency.
787 if self._ctx.rev() is None:
791 if self._ctx.rev() is None:
788 ctx = self._repo[None]
792 ctx = self._repo[None]
789 else:
793 else:
790 rev = self._state[1]
794 rev = self._state[1]
791 ctx = self._repo[rev]
795 ctx = self._repo[rev]
792 return cmdutil.files(ui, ctx, m, fm, fmt, subrepos)
796 return cmdutil.files(ui, ctx, m, fm, fmt, subrepos)
793
797
794 @annotatesubrepoerror
798 @annotatesubrepoerror
795 def matchfileset(self, expr, badfn=None):
799 def matchfileset(self, expr, badfn=None):
796 repo = self._repo
800 repo = self._repo
797 if self._ctx.rev() is None:
801 if self._ctx.rev() is None:
798 ctx = repo[None]
802 ctx = repo[None]
799 else:
803 else:
800 rev = self._state[1]
804 rev = self._state[1]
801 ctx = repo[rev]
805 ctx = repo[rev]
802
806
803 matchers = [ctx.matchfileset(expr, badfn=badfn)]
807 matchers = [ctx.matchfileset(expr, badfn=badfn)]
804
808
805 for subpath in ctx.substate:
809 for subpath in ctx.substate:
806 sub = ctx.sub(subpath)
810 sub = ctx.sub(subpath)
807
811
808 try:
812 try:
809 sm = sub.matchfileset(expr, badfn=badfn)
813 sm = sub.matchfileset(expr, badfn=badfn)
810 pm = matchmod.prefixdirmatcher(repo.root, repo.getcwd(),
814 pm = matchmod.prefixdirmatcher(repo.root, repo.getcwd(),
811 subpath, sm, badfn=badfn)
815 subpath, sm, badfn=badfn)
812 matchers.append(pm)
816 matchers.append(pm)
813 except error.LookupError:
817 except error.LookupError:
814 self.ui.status(_("skipping missing subrepository: %s\n")
818 self.ui.status(_("skipping missing subrepository: %s\n")
815 % self.wvfs.reljoin(reporelpath(self), subpath))
819 % self.wvfs.reljoin(reporelpath(self), subpath))
816 if len(matchers) == 1:
820 if len(matchers) == 1:
817 return matchers[0]
821 return matchers[0]
818 return matchmod.unionmatcher(matchers)
822 return matchmod.unionmatcher(matchers)
819
823
820 def walk(self, match):
824 def walk(self, match):
821 ctx = self._repo[None]
825 ctx = self._repo[None]
822 return ctx.walk(match)
826 return ctx.walk(match)
823
827
824 @annotatesubrepoerror
828 @annotatesubrepoerror
825 def forget(self, match, prefix, dryrun, interactive):
829 def forget(self, match, prefix, dryrun, interactive):
826 return cmdutil.forget(self.ui, self._repo, match,
830 return cmdutil.forget(self.ui, self._repo, match,
827 self.wvfs.reljoin(prefix, self._path),
831 self.wvfs.reljoin(prefix, self._path),
828 True, dryrun=dryrun, interactive=interactive)
832 True, dryrun=dryrun, interactive=interactive)
829
833
830 @annotatesubrepoerror
834 @annotatesubrepoerror
831 def removefiles(self, matcher, prefix, after, force, subrepos,
835 def removefiles(self, matcher, prefix, after, force, subrepos,
832 dryrun, warnings):
836 dryrun, warnings):
833 return cmdutil.remove(self.ui, self._repo, matcher,
837 return cmdutil.remove(self.ui, self._repo, matcher,
834 self.wvfs.reljoin(prefix, self._path),
838 self.wvfs.reljoin(prefix, self._path),
835 after, force, subrepos, dryrun)
839 after, force, subrepos, dryrun)
836
840
837 @annotatesubrepoerror
841 @annotatesubrepoerror
838 def revert(self, substate, *pats, **opts):
842 def revert(self, substate, *pats, **opts):
839 # reverting a subrepo is a 2 step process:
843 # reverting a subrepo is a 2 step process:
840 # 1. if the no_backup is not set, revert all modified
844 # 1. if the no_backup is not set, revert all modified
841 # files inside the subrepo
845 # files inside the subrepo
842 # 2. update the subrepo to the revision specified in
846 # 2. update the subrepo to the revision specified in
843 # the corresponding substate dictionary
847 # the corresponding substate dictionary
844 self.ui.status(_('reverting subrepo %s\n') % substate[0])
848 self.ui.status(_('reverting subrepo %s\n') % substate[0])
845 if not opts.get(r'no_backup'):
849 if not opts.get(r'no_backup'):
846 # Revert all files on the subrepo, creating backups
850 # Revert all files on the subrepo, creating backups
847 # Note that this will not recursively revert subrepos
851 # Note that this will not recursively revert subrepos
848 # We could do it if there was a set:subrepos() predicate
852 # We could do it if there was a set:subrepos() predicate
849 opts = opts.copy()
853 opts = opts.copy()
850 opts[r'date'] = None
854 opts[r'date'] = None
851 opts[r'rev'] = substate[1]
855 opts[r'rev'] = substate[1]
852
856
853 self.filerevert(*pats, **opts)
857 self.filerevert(*pats, **opts)
854
858
855 # Update the repo to the revision specified in the given substate
859 # Update the repo to the revision specified in the given substate
856 if not opts.get(r'dry_run'):
860 if not opts.get(r'dry_run'):
857 self.get(substate, overwrite=True)
861 self.get(substate, overwrite=True)
858
862
859 def filerevert(self, *pats, **opts):
863 def filerevert(self, *pats, **opts):
860 ctx = self._repo[opts[r'rev']]
864 ctx = self._repo[opts[r'rev']]
861 parents = self._repo.dirstate.parents()
865 parents = self._repo.dirstate.parents()
862 if opts.get(r'all'):
866 if opts.get(r'all'):
863 pats = ['set:modified()']
867 pats = ['set:modified()']
864 else:
868 else:
865 pats = []
869 pats = []
866 cmdutil.revert(self.ui, self._repo, ctx, parents, *pats, **opts)
870 cmdutil.revert(self.ui, self._repo, ctx, parents, *pats, **opts)
867
871
868 def shortid(self, revid):
872 def shortid(self, revid):
869 return revid[:12]
873 return revid[:12]
870
874
871 @annotatesubrepoerror
875 @annotatesubrepoerror
872 def unshare(self):
876 def unshare(self):
873 # subrepo inherently violates our import layering rules
877 # subrepo inherently violates our import layering rules
874 # because it wants to make repo objects from deep inside the stack
878 # because it wants to make repo objects from deep inside the stack
875 # so we manually delay the circular imports to not break
879 # so we manually delay the circular imports to not break
876 # scripts that don't use our demand-loading
880 # scripts that don't use our demand-loading
877 global hg
881 global hg
878 from . import hg as h
882 from . import hg as h
879 hg = h
883 hg = h
880
884
881 # Nothing prevents a user from sharing in a repo, and then making that a
885 # Nothing prevents a user from sharing in a repo, and then making that a
882 # subrepo. Alternately, the previous unshare attempt may have failed
886 # subrepo. Alternately, the previous unshare attempt may have failed
883 # part way through. So recurse whether or not this layer is shared.
887 # part way through. So recurse whether or not this layer is shared.
884 if self._repo.shared():
888 if self._repo.shared():
885 self.ui.status(_("unsharing subrepo '%s'\n") % self._relpath)
889 self.ui.status(_("unsharing subrepo '%s'\n") % self._relpath)
886
890
887 hg.unshare(self.ui, self._repo)
891 hg.unshare(self.ui, self._repo)
888
892
889 def verify(self):
893 def verify(self):
890 try:
894 try:
891 rev = self._state[1]
895 rev = self._state[1]
892 ctx = self._repo.unfiltered()[rev]
896 ctx = self._repo.unfiltered()[rev]
893 if ctx.hidden():
897 if ctx.hidden():
894 # Since hidden revisions aren't pushed/pulled, it seems worth an
898 # Since hidden revisions aren't pushed/pulled, it seems worth an
895 # explicit warning.
899 # explicit warning.
896 ui = self._repo.ui
900 ui = self._repo.ui
897 ui.warn(_("subrepo '%s' is hidden in revision %s\n") %
901 ui.warn(_("subrepo '%s' is hidden in revision %s\n") %
898 (self._relpath, node.short(self._ctx.node())))
902 (self._relpath, node.short(self._ctx.node())))
899 return 0
903 return 0
900 except error.RepoLookupError:
904 except error.RepoLookupError:
901 # A missing subrepo revision may be a case of needing to pull it, so
905 # A missing subrepo revision may be a case of needing to pull it, so
902 # don't treat this as an error.
906 # don't treat this as an error.
903 self._repo.ui.warn(_("subrepo '%s' not found in revision %s\n") %
907 self._repo.ui.warn(_("subrepo '%s' not found in revision %s\n") %
904 (self._relpath, node.short(self._ctx.node())))
908 (self._relpath, node.short(self._ctx.node())))
905 return 0
909 return 0
906
910
907 @propertycache
911 @propertycache
908 def wvfs(self):
912 def wvfs(self):
909 """return own wvfs for efficiency and consistency
913 """return own wvfs for efficiency and consistency
910 """
914 """
911 return self._repo.wvfs
915 return self._repo.wvfs
912
916
913 @propertycache
917 @propertycache
914 def _relpath(self):
918 def _relpath(self):
915 """return path to this subrepository as seen from outermost repository
919 """return path to this subrepository as seen from outermost repository
916 """
920 """
917 # Keep consistent dir separators by avoiding vfs.join(self._path)
921 # Keep consistent dir separators by avoiding vfs.join(self._path)
918 return reporelpath(self._repo)
922 return reporelpath(self._repo)
919
923
920 class svnsubrepo(abstractsubrepo):
924 class svnsubrepo(abstractsubrepo):
921 def __init__(self, ctx, path, state, allowcreate):
925 def __init__(self, ctx, path, state, allowcreate):
922 super(svnsubrepo, self).__init__(ctx, path)
926 super(svnsubrepo, self).__init__(ctx, path)
923 self._state = state
927 self._state = state
924 self._exe = procutil.findexe('svn')
928 self._exe = procutil.findexe('svn')
925 if not self._exe:
929 if not self._exe:
926 raise error.Abort(_("'svn' executable not found for subrepo '%s'")
930 raise error.Abort(_("'svn' executable not found for subrepo '%s'")
927 % self._path)
931 % self._path)
928
932
929 def _svncommand(self, commands, filename='', failok=False):
933 def _svncommand(self, commands, filename='', failok=False):
930 cmd = [self._exe]
934 cmd = [self._exe]
931 extrakw = {}
935 extrakw = {}
932 if not self.ui.interactive():
936 if not self.ui.interactive():
933 # Making stdin be a pipe should prevent svn from behaving
937 # Making stdin be a pipe should prevent svn from behaving
934 # interactively even if we can't pass --non-interactive.
938 # interactively even if we can't pass --non-interactive.
935 extrakw[r'stdin'] = subprocess.PIPE
939 extrakw[r'stdin'] = subprocess.PIPE
936 # Starting in svn 1.5 --non-interactive is a global flag
940 # Starting in svn 1.5 --non-interactive is a global flag
937 # instead of being per-command, but we need to support 1.4 so
941 # instead of being per-command, but we need to support 1.4 so
938 # we have to be intelligent about what commands take
942 # we have to be intelligent about what commands take
939 # --non-interactive.
943 # --non-interactive.
940 if commands[0] in ('update', 'checkout', 'commit'):
944 if commands[0] in ('update', 'checkout', 'commit'):
941 cmd.append('--non-interactive')
945 cmd.append('--non-interactive')
942 cmd.extend(commands)
946 cmd.extend(commands)
943 if filename is not None:
947 if filename is not None:
944 path = self.wvfs.reljoin(self._ctx.repo().origroot,
948 path = self.wvfs.reljoin(self._ctx.repo().origroot,
945 self._path, filename)
949 self._path, filename)
946 cmd.append(path)
950 cmd.append(path)
947 env = dict(encoding.environ)
951 env = dict(encoding.environ)
948 # Avoid localized output, preserve current locale for everything else.
952 # Avoid localized output, preserve current locale for everything else.
949 lc_all = env.get('LC_ALL')
953 lc_all = env.get('LC_ALL')
950 if lc_all:
954 if lc_all:
951 env['LANG'] = lc_all
955 env['LANG'] = lc_all
952 del env['LC_ALL']
956 del env['LC_ALL']
953 env['LC_MESSAGES'] = 'C'
957 env['LC_MESSAGES'] = 'C'
954 p = subprocess.Popen(pycompat.rapply(procutil.tonativestr, cmd),
958 p = subprocess.Popen(pycompat.rapply(procutil.tonativestr, cmd),
955 bufsize=-1, close_fds=procutil.closefds,
959 bufsize=-1, close_fds=procutil.closefds,
956 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
960 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
957 universal_newlines=True,
961 universal_newlines=True,
958 env=procutil.tonativeenv(env), **extrakw)
962 env=procutil.tonativeenv(env), **extrakw)
959 stdout, stderr = p.communicate()
963 stdout, stderr = p.communicate()
960 stderr = stderr.strip()
964 stderr = stderr.strip()
961 if not failok:
965 if not failok:
962 if p.returncode:
966 if p.returncode:
963 raise error.Abort(stderr or 'exited with code %d'
967 raise error.Abort(stderr or 'exited with code %d'
964 % p.returncode)
968 % p.returncode)
965 if stderr:
969 if stderr:
966 self.ui.warn(stderr + '\n')
970 self.ui.warn(stderr + '\n')
967 return stdout, stderr
971 return stdout, stderr
968
972
969 @propertycache
973 @propertycache
970 def _svnversion(self):
974 def _svnversion(self):
971 output, err = self._svncommand(['--version', '--quiet'], filename=None)
975 output, err = self._svncommand(['--version', '--quiet'], filename=None)
972 m = re.search(br'^(\d+)\.(\d+)', output)
976 m = re.search(br'^(\d+)\.(\d+)', output)
973 if not m:
977 if not m:
974 raise error.Abort(_('cannot retrieve svn tool version'))
978 raise error.Abort(_('cannot retrieve svn tool version'))
975 return (int(m.group(1)), int(m.group(2)))
979 return (int(m.group(1)), int(m.group(2)))
976
980
977 def _svnmissing(self):
981 def _svnmissing(self):
978 return not self.wvfs.exists('.svn')
982 return not self.wvfs.exists('.svn')
979
983
980 def _wcrevs(self):
984 def _wcrevs(self):
981 # Get the working directory revision as well as the last
985 # Get the working directory revision as well as the last
982 # commit revision so we can compare the subrepo state with
986 # commit revision so we can compare the subrepo state with
983 # both. We used to store the working directory one.
987 # both. We used to store the working directory one.
984 output, err = self._svncommand(['info', '--xml'])
988 output, err = self._svncommand(['info', '--xml'])
985 doc = xml.dom.minidom.parseString(output)
989 doc = xml.dom.minidom.parseString(output)
986 entries = doc.getElementsByTagName('entry')
990 entries = doc.getElementsByTagName('entry')
987 lastrev, rev = '0', '0'
991 lastrev, rev = '0', '0'
988 if entries:
992 if entries:
989 rev = str(entries[0].getAttribute('revision')) or '0'
993 rev = str(entries[0].getAttribute('revision')) or '0'
990 commits = entries[0].getElementsByTagName('commit')
994 commits = entries[0].getElementsByTagName('commit')
991 if commits:
995 if commits:
992 lastrev = str(commits[0].getAttribute('revision')) or '0'
996 lastrev = str(commits[0].getAttribute('revision')) or '0'
993 return (lastrev, rev)
997 return (lastrev, rev)
994
998
995 def _wcrev(self):
999 def _wcrev(self):
996 return self._wcrevs()[0]
1000 return self._wcrevs()[0]
997
1001
998 def _wcchanged(self):
1002 def _wcchanged(self):
999 """Return (changes, extchanges, missing) where changes is True
1003 """Return (changes, extchanges, missing) where changes is True
1000 if the working directory was changed, extchanges is
1004 if the working directory was changed, extchanges is
1001 True if any of these changes concern an external entry and missing
1005 True if any of these changes concern an external entry and missing
1002 is True if any change is a missing entry.
1006 is True if any change is a missing entry.
1003 """
1007 """
1004 output, err = self._svncommand(['status', '--xml'])
1008 output, err = self._svncommand(['status', '--xml'])
1005 externals, changes, missing = [], [], []
1009 externals, changes, missing = [], [], []
1006 doc = xml.dom.minidom.parseString(output)
1010 doc = xml.dom.minidom.parseString(output)
1007 for e in doc.getElementsByTagName('entry'):
1011 for e in doc.getElementsByTagName('entry'):
1008 s = e.getElementsByTagName('wc-status')
1012 s = e.getElementsByTagName('wc-status')
1009 if not s:
1013 if not s:
1010 continue
1014 continue
1011 item = s[0].getAttribute('item')
1015 item = s[0].getAttribute('item')
1012 props = s[0].getAttribute('props')
1016 props = s[0].getAttribute('props')
1013 path = e.getAttribute('path')
1017 path = e.getAttribute('path')
1014 if item == 'external':
1018 if item == 'external':
1015 externals.append(path)
1019 externals.append(path)
1016 elif item == 'missing':
1020 elif item == 'missing':
1017 missing.append(path)
1021 missing.append(path)
1018 if (item not in ('', 'normal', 'unversioned', 'external')
1022 if (item not in ('', 'normal', 'unversioned', 'external')
1019 or props not in ('', 'none', 'normal')):
1023 or props not in ('', 'none', 'normal')):
1020 changes.append(path)
1024 changes.append(path)
1021 for path in changes:
1025 for path in changes:
1022 for ext in externals:
1026 for ext in externals:
1023 if path == ext or path.startswith(ext + pycompat.ossep):
1027 if path == ext or path.startswith(ext + pycompat.ossep):
1024 return True, True, bool(missing)
1028 return True, True, bool(missing)
1025 return bool(changes), False, bool(missing)
1029 return bool(changes), False, bool(missing)
1026
1030
1027 @annotatesubrepoerror
1031 @annotatesubrepoerror
1028 def dirty(self, ignoreupdate=False, missing=False):
1032 def dirty(self, ignoreupdate=False, missing=False):
1029 if self._svnmissing():
1033 if self._svnmissing():
1030 return self._state[1] != ''
1034 return self._state[1] != ''
1031 wcchanged = self._wcchanged()
1035 wcchanged = self._wcchanged()
1032 changed = wcchanged[0] or (missing and wcchanged[2])
1036 changed = wcchanged[0] or (missing and wcchanged[2])
1033 if not changed:
1037 if not changed:
1034 if self._state[1] in self._wcrevs() or ignoreupdate:
1038 if self._state[1] in self._wcrevs() or ignoreupdate:
1035 return False
1039 return False
1036 return True
1040 return True
1037
1041
1038 def basestate(self):
1042 def basestate(self):
1039 lastrev, rev = self._wcrevs()
1043 lastrev, rev = self._wcrevs()
1040 if lastrev != rev:
1044 if lastrev != rev:
1041 # Last committed rev is not the same than rev. We would
1045 # Last committed rev is not the same than rev. We would
1042 # like to take lastrev but we do not know if the subrepo
1046 # like to take lastrev but we do not know if the subrepo
1043 # URL exists at lastrev. Test it and fallback to rev it
1047 # URL exists at lastrev. Test it and fallback to rev it
1044 # is not there.
1048 # is not there.
1045 try:
1049 try:
1046 self._svncommand(['list', '%s@%s' % (self._state[0], lastrev)])
1050 self._svncommand(['list', '%s@%s' % (self._state[0], lastrev)])
1047 return lastrev
1051 return lastrev
1048 except error.Abort:
1052 except error.Abort:
1049 pass
1053 pass
1050 return rev
1054 return rev
1051
1055
1052 @annotatesubrepoerror
1056 @annotatesubrepoerror
1053 def commit(self, text, user, date):
1057 def commit(self, text, user, date):
1054 # user and date are out of our hands since svn is centralized
1058 # user and date are out of our hands since svn is centralized
1055 changed, extchanged, missing = self._wcchanged()
1059 changed, extchanged, missing = self._wcchanged()
1056 if not changed:
1060 if not changed:
1057 return self.basestate()
1061 return self.basestate()
1058 if extchanged:
1062 if extchanged:
1059 # Do not try to commit externals
1063 # Do not try to commit externals
1060 raise error.Abort(_('cannot commit svn externals'))
1064 raise error.Abort(_('cannot commit svn externals'))
1061 if missing:
1065 if missing:
1062 # svn can commit with missing entries but aborting like hg
1066 # svn can commit with missing entries but aborting like hg
1063 # seems a better approach.
1067 # seems a better approach.
1064 raise error.Abort(_('cannot commit missing svn entries'))
1068 raise error.Abort(_('cannot commit missing svn entries'))
1065 commitinfo, err = self._svncommand(['commit', '-m', text])
1069 commitinfo, err = self._svncommand(['commit', '-m', text])
1066 self.ui.status(commitinfo)
1070 self.ui.status(commitinfo)
1067 newrev = re.search('Committed revision ([0-9]+).', commitinfo)
1071 newrev = re.search('Committed revision ([0-9]+).', commitinfo)
1068 if not newrev:
1072 if not newrev:
1069 if not commitinfo.strip():
1073 if not commitinfo.strip():
1070 # Sometimes, our definition of "changed" differs from
1074 # Sometimes, our definition of "changed" differs from
1071 # svn one. For instance, svn ignores missing files
1075 # svn one. For instance, svn ignores missing files
1072 # when committing. If there are only missing files, no
1076 # when committing. If there are only missing files, no
1073 # commit is made, no output and no error code.
1077 # commit is made, no output and no error code.
1074 raise error.Abort(_('failed to commit svn changes'))
1078 raise error.Abort(_('failed to commit svn changes'))
1075 raise error.Abort(commitinfo.splitlines()[-1])
1079 raise error.Abort(commitinfo.splitlines()[-1])
1076 newrev = newrev.groups()[0]
1080 newrev = newrev.groups()[0]
1077 self.ui.status(self._svncommand(['update', '-r', newrev])[0])
1081 self.ui.status(self._svncommand(['update', '-r', newrev])[0])
1078 return newrev
1082 return newrev
1079
1083
1080 @annotatesubrepoerror
1084 @annotatesubrepoerror
1081 def remove(self):
1085 def remove(self):
1082 if self.dirty():
1086 if self.dirty():
1083 self.ui.warn(_('not removing repo %s because '
1087 self.ui.warn(_('not removing repo %s because '
1084 'it has changes.\n') % self._path)
1088 'it has changes.\n') % self._path)
1085 return
1089 return
1086 self.ui.note(_('removing subrepo %s\n') % self._path)
1090 self.ui.note(_('removing subrepo %s\n') % self._path)
1087
1091
1088 self.wvfs.rmtree(forcibly=True)
1092 self.wvfs.rmtree(forcibly=True)
1089 try:
1093 try:
1090 pwvfs = self._ctx.repo().wvfs
1094 pwvfs = self._ctx.repo().wvfs
1091 pwvfs.removedirs(pwvfs.dirname(self._path))
1095 pwvfs.removedirs(pwvfs.dirname(self._path))
1092 except OSError:
1096 except OSError:
1093 pass
1097 pass
1094
1098
1095 @annotatesubrepoerror
1099 @annotatesubrepoerror
1096 def get(self, state, overwrite=False):
1100 def get(self, state, overwrite=False):
1097 if overwrite:
1101 if overwrite:
1098 self._svncommand(['revert', '--recursive'])
1102 self._svncommand(['revert', '--recursive'])
1099 args = ['checkout']
1103 args = ['checkout']
1100 if self._svnversion >= (1, 5):
1104 if self._svnversion >= (1, 5):
1101 args.append('--force')
1105 args.append('--force')
1102 # The revision must be specified at the end of the URL to properly
1106 # The revision must be specified at the end of the URL to properly
1103 # update to a directory which has since been deleted and recreated.
1107 # update to a directory which has since been deleted and recreated.
1104 args.append('%s@%s' % (state[0], state[1]))
1108 args.append('%s@%s' % (state[0], state[1]))
1105
1109
1106 # SEC: check that the ssh url is safe
1110 # SEC: check that the ssh url is safe
1107 util.checksafessh(state[0])
1111 util.checksafessh(state[0])
1108
1112
1109 status, err = self._svncommand(args, failok=True)
1113 status, err = self._svncommand(args, failok=True)
1110 _sanitize(self.ui, self.wvfs, '.svn')
1114 _sanitize(self.ui, self.wvfs, '.svn')
1111 if not re.search('Checked out revision [0-9]+.', status):
1115 if not re.search('Checked out revision [0-9]+.', status):
1112 if ('is already a working copy for a different URL' in err
1116 if ('is already a working copy for a different URL' in err
1113 and (self._wcchanged()[:2] == (False, False))):
1117 and (self._wcchanged()[:2] == (False, False))):
1114 # obstructed but clean working copy, so just blow it away.
1118 # obstructed but clean working copy, so just blow it away.
1115 self.remove()
1119 self.remove()
1116 self.get(state, overwrite=False)
1120 self.get(state, overwrite=False)
1117 return
1121 return
1118 raise error.Abort((status or err).splitlines()[-1])
1122 raise error.Abort((status or err).splitlines()[-1])
1119 self.ui.status(status)
1123 self.ui.status(status)
1120
1124
1121 @annotatesubrepoerror
1125 @annotatesubrepoerror
1122 def merge(self, state):
1126 def merge(self, state):
1123 old = self._state[1]
1127 old = self._state[1]
1124 new = state[1]
1128 new = state[1]
1125 wcrev = self._wcrev()
1129 wcrev = self._wcrev()
1126 if new != wcrev:
1130 if new != wcrev:
1127 dirty = old == wcrev or self._wcchanged()[0]
1131 dirty = old == wcrev or self._wcchanged()[0]
1128 if _updateprompt(self.ui, self, dirty, wcrev, new):
1132 if _updateprompt(self.ui, self, dirty, wcrev, new):
1129 self.get(state, False)
1133 self.get(state, False)
1130
1134
1131 def push(self, opts):
1135 def push(self, opts):
1132 # push is a no-op for SVN
1136 # push is a no-op for SVN
1133 return True
1137 return True
1134
1138
1135 @annotatesubrepoerror
1139 @annotatesubrepoerror
1136 def files(self):
1140 def files(self):
1137 output = self._svncommand(['list', '--recursive', '--xml'])[0]
1141 output = self._svncommand(['list', '--recursive', '--xml'])[0]
1138 doc = xml.dom.minidom.parseString(output)
1142 doc = xml.dom.minidom.parseString(output)
1139 paths = []
1143 paths = []
1140 for e in doc.getElementsByTagName('entry'):
1144 for e in doc.getElementsByTagName('entry'):
1141 kind = pycompat.bytestr(e.getAttribute('kind'))
1145 kind = pycompat.bytestr(e.getAttribute('kind'))
1142 if kind != 'file':
1146 if kind != 'file':
1143 continue
1147 continue
1144 name = ''.join(c.data for c
1148 name = ''.join(c.data for c
1145 in e.getElementsByTagName('name')[0].childNodes
1149 in e.getElementsByTagName('name')[0].childNodes
1146 if c.nodeType == c.TEXT_NODE)
1150 if c.nodeType == c.TEXT_NODE)
1147 paths.append(name.encode('utf-8'))
1151 paths.append(name.encode('utf-8'))
1148 return paths
1152 return paths
1149
1153
1150 def filedata(self, name, decode):
1154 def filedata(self, name, decode):
1151 return self._svncommand(['cat'], name)[0]
1155 return self._svncommand(['cat'], name)[0]
1152
1156
1153
1157
1154 class gitsubrepo(abstractsubrepo):
1158 class gitsubrepo(abstractsubrepo):
1155 def __init__(self, ctx, path, state, allowcreate):
1159 def __init__(self, ctx, path, state, allowcreate):
1156 super(gitsubrepo, self).__init__(ctx, path)
1160 super(gitsubrepo, self).__init__(ctx, path)
1157 self._state = state
1161 self._state = state
1158 self._abspath = ctx.repo().wjoin(path)
1162 self._abspath = ctx.repo().wjoin(path)
1159 self._subparent = ctx.repo()
1163 self._subparent = ctx.repo()
1160 self._ensuregit()
1164 self._ensuregit()
1161
1165
1162 def _ensuregit(self):
1166 def _ensuregit(self):
1163 try:
1167 try:
1164 self._gitexecutable = 'git'
1168 self._gitexecutable = 'git'
1165 out, err = self._gitnodir(['--version'])
1169 out, err = self._gitnodir(['--version'])
1166 except OSError as e:
1170 except OSError as e:
1167 genericerror = _("error executing git for subrepo '%s': %s")
1171 genericerror = _("error executing git for subrepo '%s': %s")
1168 notfoundhint = _("check git is installed and in your PATH")
1172 notfoundhint = _("check git is installed and in your PATH")
1169 if e.errno != errno.ENOENT:
1173 if e.errno != errno.ENOENT:
1170 raise error.Abort(genericerror % (
1174 raise error.Abort(genericerror % (
1171 self._path, encoding.strtolocal(e.strerror)))
1175 self._path, encoding.strtolocal(e.strerror)))
1172 elif pycompat.iswindows:
1176 elif pycompat.iswindows:
1173 try:
1177 try:
1174 self._gitexecutable = 'git.cmd'
1178 self._gitexecutable = 'git.cmd'
1175 out, err = self._gitnodir(['--version'])
1179 out, err = self._gitnodir(['--version'])
1176 except OSError as e2:
1180 except OSError as e2:
1177 if e2.errno == errno.ENOENT:
1181 if e2.errno == errno.ENOENT:
1178 raise error.Abort(_("couldn't find 'git' or 'git.cmd'"
1182 raise error.Abort(_("couldn't find 'git' or 'git.cmd'"
1179 " for subrepo '%s'") % self._path,
1183 " for subrepo '%s'") % self._path,
1180 hint=notfoundhint)
1184 hint=notfoundhint)
1181 else:
1185 else:
1182 raise error.Abort(genericerror % (self._path,
1186 raise error.Abort(genericerror % (self._path,
1183 encoding.strtolocal(e2.strerror)))
1187 encoding.strtolocal(e2.strerror)))
1184 else:
1188 else:
1185 raise error.Abort(_("couldn't find git for subrepo '%s'")
1189 raise error.Abort(_("couldn't find git for subrepo '%s'")
1186 % self._path, hint=notfoundhint)
1190 % self._path, hint=notfoundhint)
1187 versionstatus = self._checkversion(out)
1191 versionstatus = self._checkversion(out)
1188 if versionstatus == 'unknown':
1192 if versionstatus == 'unknown':
1189 self.ui.warn(_('cannot retrieve git version\n'))
1193 self.ui.warn(_('cannot retrieve git version\n'))
1190 elif versionstatus == 'abort':
1194 elif versionstatus == 'abort':
1191 raise error.Abort(_('git subrepo requires at least 1.6.0 or later'))
1195 raise error.Abort(_('git subrepo requires at least 1.6.0 or later'))
1192 elif versionstatus == 'warning':
1196 elif versionstatus == 'warning':
1193 self.ui.warn(_('git subrepo requires at least 1.6.0 or later\n'))
1197 self.ui.warn(_('git subrepo requires at least 1.6.0 or later\n'))
1194
1198
1195 @staticmethod
1199 @staticmethod
1196 def _gitversion(out):
1200 def _gitversion(out):
1197 m = re.search(br'^git version (\d+)\.(\d+)\.(\d+)', out)
1201 m = re.search(br'^git version (\d+)\.(\d+)\.(\d+)', out)
1198 if m:
1202 if m:
1199 return (int(m.group(1)), int(m.group(2)), int(m.group(3)))
1203 return (int(m.group(1)), int(m.group(2)), int(m.group(3)))
1200
1204
1201 m = re.search(br'^git version (\d+)\.(\d+)', out)
1205 m = re.search(br'^git version (\d+)\.(\d+)', out)
1202 if m:
1206 if m:
1203 return (int(m.group(1)), int(m.group(2)), 0)
1207 return (int(m.group(1)), int(m.group(2)), 0)
1204
1208
1205 return -1
1209 return -1
1206
1210
1207 @staticmethod
1211 @staticmethod
1208 def _checkversion(out):
1212 def _checkversion(out):
1209 '''ensure git version is new enough
1213 '''ensure git version is new enough
1210
1214
1211 >>> _checkversion = gitsubrepo._checkversion
1215 >>> _checkversion = gitsubrepo._checkversion
1212 >>> _checkversion(b'git version 1.6.0')
1216 >>> _checkversion(b'git version 1.6.0')
1213 'ok'
1217 'ok'
1214 >>> _checkversion(b'git version 1.8.5')
1218 >>> _checkversion(b'git version 1.8.5')
1215 'ok'
1219 'ok'
1216 >>> _checkversion(b'git version 1.4.0')
1220 >>> _checkversion(b'git version 1.4.0')
1217 'abort'
1221 'abort'
1218 >>> _checkversion(b'git version 1.5.0')
1222 >>> _checkversion(b'git version 1.5.0')
1219 'warning'
1223 'warning'
1220 >>> _checkversion(b'git version 1.9-rc0')
1224 >>> _checkversion(b'git version 1.9-rc0')
1221 'ok'
1225 'ok'
1222 >>> _checkversion(b'git version 1.9.0.265.g81cdec2')
1226 >>> _checkversion(b'git version 1.9.0.265.g81cdec2')
1223 'ok'
1227 'ok'
1224 >>> _checkversion(b'git version 1.9.0.GIT')
1228 >>> _checkversion(b'git version 1.9.0.GIT')
1225 'ok'
1229 'ok'
1226 >>> _checkversion(b'git version 12345')
1230 >>> _checkversion(b'git version 12345')
1227 'unknown'
1231 'unknown'
1228 >>> _checkversion(b'no')
1232 >>> _checkversion(b'no')
1229 'unknown'
1233 'unknown'
1230 '''
1234 '''
1231 version = gitsubrepo._gitversion(out)
1235 version = gitsubrepo._gitversion(out)
1232 # git 1.4.0 can't work at all, but 1.5.X can in at least some cases,
1236 # git 1.4.0 can't work at all, but 1.5.X can in at least some cases,
1233 # despite the docstring comment. For now, error on 1.4.0, warn on
1237 # despite the docstring comment. For now, error on 1.4.0, warn on
1234 # 1.5.0 but attempt to continue.
1238 # 1.5.0 but attempt to continue.
1235 if version == -1:
1239 if version == -1:
1236 return 'unknown'
1240 return 'unknown'
1237 if version < (1, 5, 0):
1241 if version < (1, 5, 0):
1238 return 'abort'
1242 return 'abort'
1239 elif version < (1, 6, 0):
1243 elif version < (1, 6, 0):
1240 return 'warning'
1244 return 'warning'
1241 return 'ok'
1245 return 'ok'
1242
1246
1243 def _gitcommand(self, commands, env=None, stream=False):
1247 def _gitcommand(self, commands, env=None, stream=False):
1244 return self._gitdir(commands, env=env, stream=stream)[0]
1248 return self._gitdir(commands, env=env, stream=stream)[0]
1245
1249
1246 def _gitdir(self, commands, env=None, stream=False):
1250 def _gitdir(self, commands, env=None, stream=False):
1247 return self._gitnodir(commands, env=env, stream=stream,
1251 return self._gitnodir(commands, env=env, stream=stream,
1248 cwd=self._abspath)
1252 cwd=self._abspath)
1249
1253
1250 def _gitnodir(self, commands, env=None, stream=False, cwd=None):
1254 def _gitnodir(self, commands, env=None, stream=False, cwd=None):
1251 """Calls the git command
1255 """Calls the git command
1252
1256
1253 The methods tries to call the git command. versions prior to 1.6.0
1257 The methods tries to call the git command. versions prior to 1.6.0
1254 are not supported and very probably fail.
1258 are not supported and very probably fail.
1255 """
1259 """
1256 self.ui.debug('%s: git %s\n' % (self._relpath, ' '.join(commands)))
1260 self.ui.debug('%s: git %s\n' % (self._relpath, ' '.join(commands)))
1257 if env is None:
1261 if env is None:
1258 env = encoding.environ.copy()
1262 env = encoding.environ.copy()
1259 # disable localization for Git output (issue5176)
1263 # disable localization for Git output (issue5176)
1260 env['LC_ALL'] = 'C'
1264 env['LC_ALL'] = 'C'
1261 # fix for Git CVE-2015-7545
1265 # fix for Git CVE-2015-7545
1262 if 'GIT_ALLOW_PROTOCOL' not in env:
1266 if 'GIT_ALLOW_PROTOCOL' not in env:
1263 env['GIT_ALLOW_PROTOCOL'] = 'file:git:http:https:ssh'
1267 env['GIT_ALLOW_PROTOCOL'] = 'file:git:http:https:ssh'
1264 # unless ui.quiet is set, print git's stderr,
1268 # unless ui.quiet is set, print git's stderr,
1265 # which is mostly progress and useful info
1269 # which is mostly progress and useful info
1266 errpipe = None
1270 errpipe = None
1267 if self.ui.quiet:
1271 if self.ui.quiet:
1268 errpipe = open(os.devnull, 'w')
1272 errpipe = open(os.devnull, 'w')
1269 if self.ui._colormode and len(commands) and commands[0] == "diff":
1273 if self.ui._colormode and len(commands) and commands[0] == "diff":
1270 # insert the argument in the front,
1274 # insert the argument in the front,
1271 # the end of git diff arguments is used for paths
1275 # the end of git diff arguments is used for paths
1272 commands.insert(1, '--color')
1276 commands.insert(1, '--color')
1273 p = subprocess.Popen(pycompat.rapply(procutil.tonativestr,
1277 p = subprocess.Popen(pycompat.rapply(procutil.tonativestr,
1274 [self._gitexecutable] + commands),
1278 [self._gitexecutable] + commands),
1275 bufsize=-1,
1279 bufsize=-1,
1276 cwd=pycompat.rapply(procutil.tonativestr, cwd),
1280 cwd=pycompat.rapply(procutil.tonativestr, cwd),
1277 env=procutil.tonativeenv(env),
1281 env=procutil.tonativeenv(env),
1278 close_fds=procutil.closefds,
1282 close_fds=procutil.closefds,
1279 stdout=subprocess.PIPE, stderr=errpipe)
1283 stdout=subprocess.PIPE, stderr=errpipe)
1280 if stream:
1284 if stream:
1281 return p.stdout, None
1285 return p.stdout, None
1282
1286
1283 retdata = p.stdout.read().strip()
1287 retdata = p.stdout.read().strip()
1284 # wait for the child to exit to avoid race condition.
1288 # wait for the child to exit to avoid race condition.
1285 p.wait()
1289 p.wait()
1286
1290
1287 if p.returncode != 0 and p.returncode != 1:
1291 if p.returncode != 0 and p.returncode != 1:
1288 # there are certain error codes that are ok
1292 # there are certain error codes that are ok
1289 command = commands[0]
1293 command = commands[0]
1290 if command in ('cat-file', 'symbolic-ref'):
1294 if command in ('cat-file', 'symbolic-ref'):
1291 return retdata, p.returncode
1295 return retdata, p.returncode
1292 # for all others, abort
1296 # for all others, abort
1293 raise error.Abort(_('git %s error %d in %s') %
1297 raise error.Abort(_('git %s error %d in %s') %
1294 (command, p.returncode, self._relpath))
1298 (command, p.returncode, self._relpath))
1295
1299
1296 return retdata, p.returncode
1300 return retdata, p.returncode
1297
1301
1298 def _gitmissing(self):
1302 def _gitmissing(self):
1299 return not self.wvfs.exists('.git')
1303 return not self.wvfs.exists('.git')
1300
1304
1301 def _gitstate(self):
1305 def _gitstate(self):
1302 return self._gitcommand(['rev-parse', 'HEAD'])
1306 return self._gitcommand(['rev-parse', 'HEAD'])
1303
1307
1304 def _gitcurrentbranch(self):
1308 def _gitcurrentbranch(self):
1305 current, err = self._gitdir(['symbolic-ref', 'HEAD', '--quiet'])
1309 current, err = self._gitdir(['symbolic-ref', 'HEAD', '--quiet'])
1306 if err:
1310 if err:
1307 current = None
1311 current = None
1308 return current
1312 return current
1309
1313
1310 def _gitremote(self, remote):
1314 def _gitremote(self, remote):
1311 out = self._gitcommand(['remote', 'show', '-n', remote])
1315 out = self._gitcommand(['remote', 'show', '-n', remote])
1312 line = out.split('\n')[1]
1316 line = out.split('\n')[1]
1313 i = line.index('URL: ') + len('URL: ')
1317 i = line.index('URL: ') + len('URL: ')
1314 return line[i:]
1318 return line[i:]
1315
1319
1316 def _githavelocally(self, revision):
1320 def _githavelocally(self, revision):
1317 out, code = self._gitdir(['cat-file', '-e', revision])
1321 out, code = self._gitdir(['cat-file', '-e', revision])
1318 return code == 0
1322 return code == 0
1319
1323
1320 def _gitisancestor(self, r1, r2):
1324 def _gitisancestor(self, r1, r2):
1321 base = self._gitcommand(['merge-base', r1, r2])
1325 base = self._gitcommand(['merge-base', r1, r2])
1322 return base == r1
1326 return base == r1
1323
1327
1324 def _gitisbare(self):
1328 def _gitisbare(self):
1325 return self._gitcommand(['config', '--bool', 'core.bare']) == 'true'
1329 return self._gitcommand(['config', '--bool', 'core.bare']) == 'true'
1326
1330
1327 def _gitupdatestat(self):
1331 def _gitupdatestat(self):
1328 """This must be run before git diff-index.
1332 """This must be run before git diff-index.
1329 diff-index only looks at changes to file stat;
1333 diff-index only looks at changes to file stat;
1330 this command looks at file contents and updates the stat."""
1334 this command looks at file contents and updates the stat."""
1331 self._gitcommand(['update-index', '-q', '--refresh'])
1335 self._gitcommand(['update-index', '-q', '--refresh'])
1332
1336
1333 def _gitbranchmap(self):
1337 def _gitbranchmap(self):
1334 '''returns 2 things:
1338 '''returns 2 things:
1335 a map from git branch to revision
1339 a map from git branch to revision
1336 a map from revision to branches'''
1340 a map from revision to branches'''
1337 branch2rev = {}
1341 branch2rev = {}
1338 rev2branch = {}
1342 rev2branch = {}
1339
1343
1340 out = self._gitcommand(['for-each-ref', '--format',
1344 out = self._gitcommand(['for-each-ref', '--format',
1341 '%(objectname) %(refname)'])
1345 '%(objectname) %(refname)'])
1342 for line in out.split('\n'):
1346 for line in out.split('\n'):
1343 revision, ref = line.split(' ')
1347 revision, ref = line.split(' ')
1344 if (not ref.startswith('refs/heads/') and
1348 if (not ref.startswith('refs/heads/') and
1345 not ref.startswith('refs/remotes/')):
1349 not ref.startswith('refs/remotes/')):
1346 continue
1350 continue
1347 if ref.startswith('refs/remotes/') and ref.endswith('/HEAD'):
1351 if ref.startswith('refs/remotes/') and ref.endswith('/HEAD'):
1348 continue # ignore remote/HEAD redirects
1352 continue # ignore remote/HEAD redirects
1349 branch2rev[ref] = revision
1353 branch2rev[ref] = revision
1350 rev2branch.setdefault(revision, []).append(ref)
1354 rev2branch.setdefault(revision, []).append(ref)
1351 return branch2rev, rev2branch
1355 return branch2rev, rev2branch
1352
1356
1353 def _gittracking(self, branches):
1357 def _gittracking(self, branches):
1354 'return map of remote branch to local tracking branch'
1358 'return map of remote branch to local tracking branch'
1355 # assumes no more than one local tracking branch for each remote
1359 # assumes no more than one local tracking branch for each remote
1356 tracking = {}
1360 tracking = {}
1357 for b in branches:
1361 for b in branches:
1358 if b.startswith('refs/remotes/'):
1362 if b.startswith('refs/remotes/'):
1359 continue
1363 continue
1360 bname = b.split('/', 2)[2]
1364 bname = b.split('/', 2)[2]
1361 remote = self._gitcommand(['config', 'branch.%s.remote' % bname])
1365 remote = self._gitcommand(['config', 'branch.%s.remote' % bname])
1362 if remote:
1366 if remote:
1363 ref = self._gitcommand(['config', 'branch.%s.merge' % bname])
1367 ref = self._gitcommand(['config', 'branch.%s.merge' % bname])
1364 tracking['refs/remotes/%s/%s' %
1368 tracking['refs/remotes/%s/%s' %
1365 (remote, ref.split('/', 2)[2])] = b
1369 (remote, ref.split('/', 2)[2])] = b
1366 return tracking
1370 return tracking
1367
1371
1368 def _abssource(self, source):
1372 def _abssource(self, source):
1369 if '://' not in source:
1373 if '://' not in source:
1370 # recognize the scp syntax as an absolute source
1374 # recognize the scp syntax as an absolute source
1371 colon = source.find(':')
1375 colon = source.find(':')
1372 if colon != -1 and '/' not in source[:colon]:
1376 if colon != -1 and '/' not in source[:colon]:
1373 return source
1377 return source
1374 self._subsource = source
1378 self._subsource = source
1375 return _abssource(self)
1379 return _abssource(self)
1376
1380
1377 def _fetch(self, source, revision):
1381 def _fetch(self, source, revision):
1378 if self._gitmissing():
1382 if self._gitmissing():
1379 # SEC: check for safe ssh url
1383 # SEC: check for safe ssh url
1380 util.checksafessh(source)
1384 util.checksafessh(source)
1381
1385
1382 source = self._abssource(source)
1386 source = self._abssource(source)
1383 self.ui.status(_('cloning subrepo %s from %s\n') %
1387 self.ui.status(_('cloning subrepo %s from %s\n') %
1384 (self._relpath, source))
1388 (self._relpath, source))
1385 self._gitnodir(['clone', source, self._abspath])
1389 self._gitnodir(['clone', source, self._abspath])
1386 if self._githavelocally(revision):
1390 if self._githavelocally(revision):
1387 return
1391 return
1388 self.ui.status(_('pulling subrepo %s from %s\n') %
1392 self.ui.status(_('pulling subrepo %s from %s\n') %
1389 (self._relpath, self._gitremote('origin')))
1393 (self._relpath, self._gitremote('origin')))
1390 # try only origin: the originally cloned repo
1394 # try only origin: the originally cloned repo
1391 self._gitcommand(['fetch'])
1395 self._gitcommand(['fetch'])
1392 if not self._githavelocally(revision):
1396 if not self._githavelocally(revision):
1393 raise error.Abort(_('revision %s does not exist in subrepository '
1397 raise error.Abort(_('revision %s does not exist in subrepository '
1394 '"%s"\n') % (revision, self._relpath))
1398 '"%s"\n') % (revision, self._relpath))
1395
1399
1396 @annotatesubrepoerror
1400 @annotatesubrepoerror
1397 def dirty(self, ignoreupdate=False, missing=False):
1401 def dirty(self, ignoreupdate=False, missing=False):
1398 if self._gitmissing():
1402 if self._gitmissing():
1399 return self._state[1] != ''
1403 return self._state[1] != ''
1400 if self._gitisbare():
1404 if self._gitisbare():
1401 return True
1405 return True
1402 if not ignoreupdate and self._state[1] != self._gitstate():
1406 if not ignoreupdate and self._state[1] != self._gitstate():
1403 # different version checked out
1407 # different version checked out
1404 return True
1408 return True
1405 # check for staged changes or modified files; ignore untracked files
1409 # check for staged changes or modified files; ignore untracked files
1406 self._gitupdatestat()
1410 self._gitupdatestat()
1407 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
1411 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
1408 return code == 1
1412 return code == 1
1409
1413
1410 def basestate(self):
1414 def basestate(self):
1411 return self._gitstate()
1415 return self._gitstate()
1412
1416
1413 @annotatesubrepoerror
1417 @annotatesubrepoerror
1414 def get(self, state, overwrite=False):
1418 def get(self, state, overwrite=False):
1415 source, revision, kind = state
1419 source, revision, kind = state
1416 if not revision:
1420 if not revision:
1417 self.remove()
1421 self.remove()
1418 return
1422 return
1419 self._fetch(source, revision)
1423 self._fetch(source, revision)
1420 # if the repo was set to be bare, unbare it
1424 # if the repo was set to be bare, unbare it
1421 if self._gitisbare():
1425 if self._gitisbare():
1422 self._gitcommand(['config', 'core.bare', 'false'])
1426 self._gitcommand(['config', 'core.bare', 'false'])
1423 if self._gitstate() == revision:
1427 if self._gitstate() == revision:
1424 self._gitcommand(['reset', '--hard', 'HEAD'])
1428 self._gitcommand(['reset', '--hard', 'HEAD'])
1425 return
1429 return
1426 elif self._gitstate() == revision:
1430 elif self._gitstate() == revision:
1427 if overwrite:
1431 if overwrite:
1428 # first reset the index to unmark new files for commit, because
1432 # first reset the index to unmark new files for commit, because
1429 # reset --hard will otherwise throw away files added for commit,
1433 # reset --hard will otherwise throw away files added for commit,
1430 # not just unmark them.
1434 # not just unmark them.
1431 self._gitcommand(['reset', 'HEAD'])
1435 self._gitcommand(['reset', 'HEAD'])
1432 self._gitcommand(['reset', '--hard', 'HEAD'])
1436 self._gitcommand(['reset', '--hard', 'HEAD'])
1433 return
1437 return
1434 branch2rev, rev2branch = self._gitbranchmap()
1438 branch2rev, rev2branch = self._gitbranchmap()
1435
1439
1436 def checkout(args):
1440 def checkout(args):
1437 cmd = ['checkout']
1441 cmd = ['checkout']
1438 if overwrite:
1442 if overwrite:
1439 # first reset the index to unmark new files for commit, because
1443 # first reset the index to unmark new files for commit, because
1440 # the -f option will otherwise throw away files added for
1444 # the -f option will otherwise throw away files added for
1441 # commit, not just unmark them.
1445 # commit, not just unmark them.
1442 self._gitcommand(['reset', 'HEAD'])
1446 self._gitcommand(['reset', 'HEAD'])
1443 cmd.append('-f')
1447 cmd.append('-f')
1444 self._gitcommand(cmd + args)
1448 self._gitcommand(cmd + args)
1445 _sanitize(self.ui, self.wvfs, '.git')
1449 _sanitize(self.ui, self.wvfs, '.git')
1446
1450
1447 def rawcheckout():
1451 def rawcheckout():
1448 # no branch to checkout, check it out with no branch
1452 # no branch to checkout, check it out with no branch
1449 self.ui.warn(_('checking out detached HEAD in '
1453 self.ui.warn(_('checking out detached HEAD in '
1450 'subrepository "%s"\n') % self._relpath)
1454 'subrepository "%s"\n') % self._relpath)
1451 self.ui.warn(_('check out a git branch if you intend '
1455 self.ui.warn(_('check out a git branch if you intend '
1452 'to make changes\n'))
1456 'to make changes\n'))
1453 checkout(['-q', revision])
1457 checkout(['-q', revision])
1454
1458
1455 if revision not in rev2branch:
1459 if revision not in rev2branch:
1456 rawcheckout()
1460 rawcheckout()
1457 return
1461 return
1458 branches = rev2branch[revision]
1462 branches = rev2branch[revision]
1459 firstlocalbranch = None
1463 firstlocalbranch = None
1460 for b in branches:
1464 for b in branches:
1461 if b == 'refs/heads/master':
1465 if b == 'refs/heads/master':
1462 # master trumps all other branches
1466 # master trumps all other branches
1463 checkout(['refs/heads/master'])
1467 checkout(['refs/heads/master'])
1464 return
1468 return
1465 if not firstlocalbranch and not b.startswith('refs/remotes/'):
1469 if not firstlocalbranch and not b.startswith('refs/remotes/'):
1466 firstlocalbranch = b
1470 firstlocalbranch = b
1467 if firstlocalbranch:
1471 if firstlocalbranch:
1468 checkout([firstlocalbranch])
1472 checkout([firstlocalbranch])
1469 return
1473 return
1470
1474
1471 tracking = self._gittracking(branch2rev.keys())
1475 tracking = self._gittracking(branch2rev.keys())
1472 # choose a remote branch already tracked if possible
1476 # choose a remote branch already tracked if possible
1473 remote = branches[0]
1477 remote = branches[0]
1474 if remote not in tracking:
1478 if remote not in tracking:
1475 for b in branches:
1479 for b in branches:
1476 if b in tracking:
1480 if b in tracking:
1477 remote = b
1481 remote = b
1478 break
1482 break
1479
1483
1480 if remote not in tracking:
1484 if remote not in tracking:
1481 # create a new local tracking branch
1485 # create a new local tracking branch
1482 local = remote.split('/', 3)[3]
1486 local = remote.split('/', 3)[3]
1483 checkout(['-b', local, remote])
1487 checkout(['-b', local, remote])
1484 elif self._gitisancestor(branch2rev[tracking[remote]], remote):
1488 elif self._gitisancestor(branch2rev[tracking[remote]], remote):
1485 # When updating to a tracked remote branch,
1489 # When updating to a tracked remote branch,
1486 # if the local tracking branch is downstream of it,
1490 # if the local tracking branch is downstream of it,
1487 # a normal `git pull` would have performed a "fast-forward merge"
1491 # a normal `git pull` would have performed a "fast-forward merge"
1488 # which is equivalent to updating the local branch to the remote.
1492 # which is equivalent to updating the local branch to the remote.
1489 # Since we are only looking at branching at update, we need to
1493 # Since we are only looking at branching at update, we need to
1490 # detect this situation and perform this action lazily.
1494 # detect this situation and perform this action lazily.
1491 if tracking[remote] != self._gitcurrentbranch():
1495 if tracking[remote] != self._gitcurrentbranch():
1492 checkout([tracking[remote]])
1496 checkout([tracking[remote]])
1493 self._gitcommand(['merge', '--ff', remote])
1497 self._gitcommand(['merge', '--ff', remote])
1494 _sanitize(self.ui, self.wvfs, '.git')
1498 _sanitize(self.ui, self.wvfs, '.git')
1495 else:
1499 else:
1496 # a real merge would be required, just checkout the revision
1500 # a real merge would be required, just checkout the revision
1497 rawcheckout()
1501 rawcheckout()
1498
1502
1499 @annotatesubrepoerror
1503 @annotatesubrepoerror
1500 def commit(self, text, user, date):
1504 def commit(self, text, user, date):
1501 if self._gitmissing():
1505 if self._gitmissing():
1502 raise error.Abort(_("subrepo %s is missing") % self._relpath)
1506 raise error.Abort(_("subrepo %s is missing") % self._relpath)
1503 cmd = ['commit', '-a', '-m', text]
1507 cmd = ['commit', '-a', '-m', text]
1504 env = encoding.environ.copy()
1508 env = encoding.environ.copy()
1505 if user:
1509 if user:
1506 cmd += ['--author', user]
1510 cmd += ['--author', user]
1507 if date:
1511 if date:
1508 # git's date parser silently ignores when seconds < 1e9
1512 # git's date parser silently ignores when seconds < 1e9
1509 # convert to ISO8601
1513 # convert to ISO8601
1510 env['GIT_AUTHOR_DATE'] = dateutil.datestr(date,
1514 env['GIT_AUTHOR_DATE'] = dateutil.datestr(date,
1511 '%Y-%m-%dT%H:%M:%S %1%2')
1515 '%Y-%m-%dT%H:%M:%S %1%2')
1512 self._gitcommand(cmd, env=env)
1516 self._gitcommand(cmd, env=env)
1513 # make sure commit works otherwise HEAD might not exist under certain
1517 # make sure commit works otherwise HEAD might not exist under certain
1514 # circumstances
1518 # circumstances
1515 return self._gitstate()
1519 return self._gitstate()
1516
1520
1517 @annotatesubrepoerror
1521 @annotatesubrepoerror
1518 def merge(self, state):
1522 def merge(self, state):
1519 source, revision, kind = state
1523 source, revision, kind = state
1520 self._fetch(source, revision)
1524 self._fetch(source, revision)
1521 base = self._gitcommand(['merge-base', revision, self._state[1]])
1525 base = self._gitcommand(['merge-base', revision, self._state[1]])
1522 self._gitupdatestat()
1526 self._gitupdatestat()
1523 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
1527 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
1524
1528
1525 def mergefunc():
1529 def mergefunc():
1526 if base == revision:
1530 if base == revision:
1527 self.get(state) # fast forward merge
1531 self.get(state) # fast forward merge
1528 elif base != self._state[1]:
1532 elif base != self._state[1]:
1529 self._gitcommand(['merge', '--no-commit', revision])
1533 self._gitcommand(['merge', '--no-commit', revision])
1530 _sanitize(self.ui, self.wvfs, '.git')
1534 _sanitize(self.ui, self.wvfs, '.git')
1531
1535
1532 if self.dirty():
1536 if self.dirty():
1533 if self._gitstate() != revision:
1537 if self._gitstate() != revision:
1534 dirty = self._gitstate() == self._state[1] or code != 0
1538 dirty = self._gitstate() == self._state[1] or code != 0
1535 if _updateprompt(self.ui, self, dirty,
1539 if _updateprompt(self.ui, self, dirty,
1536 self._state[1][:7], revision[:7]):
1540 self._state[1][:7], revision[:7]):
1537 mergefunc()
1541 mergefunc()
1538 else:
1542 else:
1539 mergefunc()
1543 mergefunc()
1540
1544
1541 @annotatesubrepoerror
1545 @annotatesubrepoerror
1542 def push(self, opts):
1546 def push(self, opts):
1543 force = opts.get('force')
1547 force = opts.get('force')
1544
1548
1545 if not self._state[1]:
1549 if not self._state[1]:
1546 return True
1550 return True
1547 if self._gitmissing():
1551 if self._gitmissing():
1548 raise error.Abort(_("subrepo %s is missing") % self._relpath)
1552 raise error.Abort(_("subrepo %s is missing") % self._relpath)
1549 # if a branch in origin contains the revision, nothing to do
1553 # if a branch in origin contains the revision, nothing to do
1550 branch2rev, rev2branch = self._gitbranchmap()
1554 branch2rev, rev2branch = self._gitbranchmap()
1551 if self._state[1] in rev2branch:
1555 if self._state[1] in rev2branch:
1552 for b in rev2branch[self._state[1]]:
1556 for b in rev2branch[self._state[1]]:
1553 if b.startswith('refs/remotes/origin/'):
1557 if b.startswith('refs/remotes/origin/'):
1554 return True
1558 return True
1555 for b, revision in branch2rev.iteritems():
1559 for b, revision in branch2rev.iteritems():
1556 if b.startswith('refs/remotes/origin/'):
1560 if b.startswith('refs/remotes/origin/'):
1557 if self._gitisancestor(self._state[1], revision):
1561 if self._gitisancestor(self._state[1], revision):
1558 return True
1562 return True
1559 # otherwise, try to push the currently checked out branch
1563 # otherwise, try to push the currently checked out branch
1560 cmd = ['push']
1564 cmd = ['push']
1561 if force:
1565 if force:
1562 cmd.append('--force')
1566 cmd.append('--force')
1563
1567
1564 current = self._gitcurrentbranch()
1568 current = self._gitcurrentbranch()
1565 if current:
1569 if current:
1566 # determine if the current branch is even useful
1570 # determine if the current branch is even useful
1567 if not self._gitisancestor(self._state[1], current):
1571 if not self._gitisancestor(self._state[1], current):
1568 self.ui.warn(_('unrelated git branch checked out '
1572 self.ui.warn(_('unrelated git branch checked out '
1569 'in subrepository "%s"\n') % self._relpath)
1573 'in subrepository "%s"\n') % self._relpath)
1570 return False
1574 return False
1571 self.ui.status(_('pushing branch %s of subrepository "%s"\n') %
1575 self.ui.status(_('pushing branch %s of subrepository "%s"\n') %
1572 (current.split('/', 2)[2], self._relpath))
1576 (current.split('/', 2)[2], self._relpath))
1573 ret = self._gitdir(cmd + ['origin', current])
1577 ret = self._gitdir(cmd + ['origin', current])
1574 return ret[1] == 0
1578 return ret[1] == 0
1575 else:
1579 else:
1576 self.ui.warn(_('no branch checked out in subrepository "%s"\n'
1580 self.ui.warn(_('no branch checked out in subrepository "%s"\n'
1577 'cannot push revision %s\n') %
1581 'cannot push revision %s\n') %
1578 (self._relpath, self._state[1]))
1582 (self._relpath, self._state[1]))
1579 return False
1583 return False
1580
1584
1581 @annotatesubrepoerror
1585 @annotatesubrepoerror
1582 def add(self, ui, match, prefix, explicitonly, **opts):
1586 def add(self, ui, match, prefix, explicitonly, **opts):
1583 if self._gitmissing():
1587 if self._gitmissing():
1584 return []
1588 return []
1585
1589
1586 s = self.status(None, unknown=True, clean=True)
1590 s = self.status(None, unknown=True, clean=True)
1587
1591
1588 tracked = set()
1592 tracked = set()
1589 # dirstates 'amn' warn, 'r' is added again
1593 # dirstates 'amn' warn, 'r' is added again
1590 for l in (s.modified, s.added, s.deleted, s.clean):
1594 for l in (s.modified, s.added, s.deleted, s.clean):
1591 tracked.update(l)
1595 tracked.update(l)
1592
1596
1593 # Unknown files not of interest will be rejected by the matcher
1597 # Unknown files not of interest will be rejected by the matcher
1594 files = s.unknown
1598 files = s.unknown
1595 files.extend(match.files())
1599 files.extend(match.files())
1596
1600
1597 rejected = []
1601 rejected = []
1598
1602
1599 files = [f for f in sorted(set(files)) if match(f)]
1603 files = [f for f in sorted(set(files)) if match(f)]
1600 for f in files:
1604 for f in files:
1601 exact = match.exact(f)
1605 exact = match.exact(f)
1602 command = ["add"]
1606 command = ["add"]
1603 if exact:
1607 if exact:
1604 command.append("-f") #should be added, even if ignored
1608 command.append("-f") #should be added, even if ignored
1605 if ui.verbose or not exact:
1609 if ui.verbose or not exact:
1606 ui.status(_('adding %s\n') % match.rel(f))
1610 ui.status(_('adding %s\n') % match.rel(f))
1607
1611
1608 if f in tracked: # hg prints 'adding' even if already tracked
1612 if f in tracked: # hg prints 'adding' even if already tracked
1609 if exact:
1613 if exact:
1610 rejected.append(f)
1614 rejected.append(f)
1611 continue
1615 continue
1612 if not opts.get(r'dry_run'):
1616 if not opts.get(r'dry_run'):
1613 self._gitcommand(command + [f])
1617 self._gitcommand(command + [f])
1614
1618
1615 for f in rejected:
1619 for f in rejected:
1616 ui.warn(_("%s already tracked!\n") % match.abs(f))
1620 ui.warn(_("%s already tracked!\n") % match.abs(f))
1617
1621
1618 return rejected
1622 return rejected
1619
1623
1620 @annotatesubrepoerror
1624 @annotatesubrepoerror
1621 def remove(self):
1625 def remove(self):
1622 if self._gitmissing():
1626 if self._gitmissing():
1623 return
1627 return
1624 if self.dirty():
1628 if self.dirty():
1625 self.ui.warn(_('not removing repo %s because '
1629 self.ui.warn(_('not removing repo %s because '
1626 'it has changes.\n') % self._relpath)
1630 'it has changes.\n') % self._relpath)
1627 return
1631 return
1628 # we can't fully delete the repository as it may contain
1632 # we can't fully delete the repository as it may contain
1629 # local-only history
1633 # local-only history
1630 self.ui.note(_('removing subrepo %s\n') % self._relpath)
1634 self.ui.note(_('removing subrepo %s\n') % self._relpath)
1631 self._gitcommand(['config', 'core.bare', 'true'])
1635 self._gitcommand(['config', 'core.bare', 'true'])
1632 for f, kind in self.wvfs.readdir():
1636 for f, kind in self.wvfs.readdir():
1633 if f == '.git':
1637 if f == '.git':
1634 continue
1638 continue
1635 if kind == stat.S_IFDIR:
1639 if kind == stat.S_IFDIR:
1636 self.wvfs.rmtree(f)
1640 self.wvfs.rmtree(f)
1637 else:
1641 else:
1638 self.wvfs.unlink(f)
1642 self.wvfs.unlink(f)
1639
1643
1640 def archive(self, archiver, prefix, match=None, decode=True):
1644 def archive(self, archiver, prefix, match=None, decode=True):
1641 total = 0
1645 total = 0
1642 source, revision = self._state
1646 source, revision = self._state
1643 if not revision:
1647 if not revision:
1644 return total
1648 return total
1645 self._fetch(source, revision)
1649 self._fetch(source, revision)
1646
1650
1647 # Parse git's native archive command.
1651 # Parse git's native archive command.
1648 # This should be much faster than manually traversing the trees
1652 # This should be much faster than manually traversing the trees
1649 # and objects with many subprocess calls.
1653 # and objects with many subprocess calls.
1650 tarstream = self._gitcommand(['archive', revision], stream=True)
1654 tarstream = self._gitcommand(['archive', revision], stream=True)
1651 tar = tarfile.open(fileobj=tarstream, mode=r'r|')
1655 tar = tarfile.open(fileobj=tarstream, mode=r'r|')
1652 relpath = subrelpath(self)
1656 relpath = subrelpath(self)
1653 progress = self.ui.makeprogress(_('archiving (%s)') % relpath,
1657 progress = self.ui.makeprogress(_('archiving (%s)') % relpath,
1654 unit=_('files'))
1658 unit=_('files'))
1655 progress.update(0)
1659 progress.update(0)
1656 for info in tar:
1660 for info in tar:
1657 if info.isdir():
1661 if info.isdir():
1658 continue
1662 continue
1659 if match and not match(info.name):
1663 if match and not match(info.name):
1660 continue
1664 continue
1661 if info.issym():
1665 if info.issym():
1662 data = info.linkname
1666 data = info.linkname
1663 else:
1667 else:
1664 data = tar.extractfile(info).read()
1668 data = tar.extractfile(info).read()
1665 archiver.addfile(prefix + self._path + '/' + info.name,
1669 archiver.addfile(prefix + self._path + '/' + info.name,
1666 info.mode, info.issym(), data)
1670 info.mode, info.issym(), data)
1667 total += 1
1671 total += 1
1668 progress.increment()
1672 progress.increment()
1669 progress.complete()
1673 progress.complete()
1670 return total
1674 return total
1671
1675
1672
1676
1673 @annotatesubrepoerror
1677 @annotatesubrepoerror
1674 def cat(self, match, fm, fntemplate, prefix, **opts):
1678 def cat(self, match, fm, fntemplate, prefix, **opts):
1675 rev = self._state[1]
1679 rev = self._state[1]
1676 if match.anypats():
1680 if match.anypats():
1677 return 1 #No support for include/exclude yet
1681 return 1 #No support for include/exclude yet
1678
1682
1679 if not match.files():
1683 if not match.files():
1680 return 1
1684 return 1
1681
1685
1682 # TODO: add support for non-plain formatter (see cmdutil.cat())
1686 # TODO: add support for non-plain formatter (see cmdutil.cat())
1683 for f in match.files():
1687 for f in match.files():
1684 output = self._gitcommand(["show", "%s:%s" % (rev, f)])
1688 output = self._gitcommand(["show", "%s:%s" % (rev, f)])
1685 fp = cmdutil.makefileobj(self._ctx, fntemplate,
1689 fp = cmdutil.makefileobj(self._ctx, fntemplate,
1686 pathname=self.wvfs.reljoin(prefix, f))
1690 pathname=self.wvfs.reljoin(prefix, f))
1687 fp.write(output)
1691 fp.write(output)
1688 fp.close()
1692 fp.close()
1689 return 0
1693 return 0
1690
1694
1691
1695
1692 @annotatesubrepoerror
1696 @annotatesubrepoerror
1693 def status(self, rev2, **opts):
1697 def status(self, rev2, **opts):
1694 rev1 = self._state[1]
1698 rev1 = self._state[1]
1695 if self._gitmissing() or not rev1:
1699 if self._gitmissing() or not rev1:
1696 # if the repo is missing, return no results
1700 # if the repo is missing, return no results
1697 return scmutil.status([], [], [], [], [], [], [])
1701 return scmutil.status([], [], [], [], [], [], [])
1698 modified, added, removed = [], [], []
1702 modified, added, removed = [], [], []
1699 self._gitupdatestat()
1703 self._gitupdatestat()
1700 if rev2:
1704 if rev2:
1701 command = ['diff-tree', '--no-renames', '-r', rev1, rev2]
1705 command = ['diff-tree', '--no-renames', '-r', rev1, rev2]
1702 else:
1706 else:
1703 command = ['diff-index', '--no-renames', rev1]
1707 command = ['diff-index', '--no-renames', rev1]
1704 out = self._gitcommand(command)
1708 out = self._gitcommand(command)
1705 for line in out.split('\n'):
1709 for line in out.split('\n'):
1706 tab = line.find('\t')
1710 tab = line.find('\t')
1707 if tab == -1:
1711 if tab == -1:
1708 continue
1712 continue
1709 status, f = line[tab - 1:tab], line[tab + 1:]
1713 status, f = line[tab - 1:tab], line[tab + 1:]
1710 if status == 'M':
1714 if status == 'M':
1711 modified.append(f)
1715 modified.append(f)
1712 elif status == 'A':
1716 elif status == 'A':
1713 added.append(f)
1717 added.append(f)
1714 elif status == 'D':
1718 elif status == 'D':
1715 removed.append(f)
1719 removed.append(f)
1716
1720
1717 deleted, unknown, ignored, clean = [], [], [], []
1721 deleted, unknown, ignored, clean = [], [], [], []
1718
1722
1719 command = ['status', '--porcelain', '-z']
1723 command = ['status', '--porcelain', '-z']
1720 if opts.get(r'unknown'):
1724 if opts.get(r'unknown'):
1721 command += ['--untracked-files=all']
1725 command += ['--untracked-files=all']
1722 if opts.get(r'ignored'):
1726 if opts.get(r'ignored'):
1723 command += ['--ignored']
1727 command += ['--ignored']
1724 out = self._gitcommand(command)
1728 out = self._gitcommand(command)
1725
1729
1726 changedfiles = set()
1730 changedfiles = set()
1727 changedfiles.update(modified)
1731 changedfiles.update(modified)
1728 changedfiles.update(added)
1732 changedfiles.update(added)
1729 changedfiles.update(removed)
1733 changedfiles.update(removed)
1730 for line in out.split('\0'):
1734 for line in out.split('\0'):
1731 if not line:
1735 if not line:
1732 continue
1736 continue
1733 st = line[0:2]
1737 st = line[0:2]
1734 #moves and copies show 2 files on one line
1738 #moves and copies show 2 files on one line
1735 if line.find('\0') >= 0:
1739 if line.find('\0') >= 0:
1736 filename1, filename2 = line[3:].split('\0')
1740 filename1, filename2 = line[3:].split('\0')
1737 else:
1741 else:
1738 filename1 = line[3:]
1742 filename1 = line[3:]
1739 filename2 = None
1743 filename2 = None
1740
1744
1741 changedfiles.add(filename1)
1745 changedfiles.add(filename1)
1742 if filename2:
1746 if filename2:
1743 changedfiles.add(filename2)
1747 changedfiles.add(filename2)
1744
1748
1745 if st == '??':
1749 if st == '??':
1746 unknown.append(filename1)
1750 unknown.append(filename1)
1747 elif st == '!!':
1751 elif st == '!!':
1748 ignored.append(filename1)
1752 ignored.append(filename1)
1749
1753
1750 if opts.get(r'clean'):
1754 if opts.get(r'clean'):
1751 out = self._gitcommand(['ls-files'])
1755 out = self._gitcommand(['ls-files'])
1752 for f in out.split('\n'):
1756 for f in out.split('\n'):
1753 if not f in changedfiles:
1757 if not f in changedfiles:
1754 clean.append(f)
1758 clean.append(f)
1755
1759
1756 return scmutil.status(modified, added, removed, deleted,
1760 return scmutil.status(modified, added, removed, deleted,
1757 unknown, ignored, clean)
1761 unknown, ignored, clean)
1758
1762
1759 @annotatesubrepoerror
1763 @annotatesubrepoerror
1760 def diff(self, ui, diffopts, node2, match, prefix, **opts):
1764 def diff(self, ui, diffopts, node2, match, prefix, **opts):
1761 node1 = self._state[1]
1765 node1 = self._state[1]
1762 cmd = ['diff', '--no-renames']
1766 cmd = ['diff', '--no-renames']
1763 if opts[r'stat']:
1767 if opts[r'stat']:
1764 cmd.append('--stat')
1768 cmd.append('--stat')
1765 else:
1769 else:
1766 # for Git, this also implies '-p'
1770 # for Git, this also implies '-p'
1767 cmd.append('-U%d' % diffopts.context)
1771 cmd.append('-U%d' % diffopts.context)
1768
1772
1769 gitprefix = self.wvfs.reljoin(prefix, self._path)
1773 gitprefix = self.wvfs.reljoin(prefix, self._path)
1770
1774
1771 if diffopts.noprefix:
1775 if diffopts.noprefix:
1772 cmd.extend(['--src-prefix=%s/' % gitprefix,
1776 cmd.extend(['--src-prefix=%s/' % gitprefix,
1773 '--dst-prefix=%s/' % gitprefix])
1777 '--dst-prefix=%s/' % gitprefix])
1774 else:
1778 else:
1775 cmd.extend(['--src-prefix=a/%s/' % gitprefix,
1779 cmd.extend(['--src-prefix=a/%s/' % gitprefix,
1776 '--dst-prefix=b/%s/' % gitprefix])
1780 '--dst-prefix=b/%s/' % gitprefix])
1777
1781
1778 if diffopts.ignorews:
1782 if diffopts.ignorews:
1779 cmd.append('--ignore-all-space')
1783 cmd.append('--ignore-all-space')
1780 if diffopts.ignorewsamount:
1784 if diffopts.ignorewsamount:
1781 cmd.append('--ignore-space-change')
1785 cmd.append('--ignore-space-change')
1782 if self._gitversion(self._gitcommand(['--version'])) >= (1, 8, 4) \
1786 if self._gitversion(self._gitcommand(['--version'])) >= (1, 8, 4) \
1783 and diffopts.ignoreblanklines:
1787 and diffopts.ignoreblanklines:
1784 cmd.append('--ignore-blank-lines')
1788 cmd.append('--ignore-blank-lines')
1785
1789
1786 cmd.append(node1)
1790 cmd.append(node1)
1787 if node2:
1791 if node2:
1788 cmd.append(node2)
1792 cmd.append(node2)
1789
1793
1790 output = ""
1794 output = ""
1791 if match.always():
1795 if match.always():
1792 output += self._gitcommand(cmd) + '\n'
1796 output += self._gitcommand(cmd) + '\n'
1793 else:
1797 else:
1794 st = self.status(node2)[:3]
1798 st = self.status(node2)[:3]
1795 files = [f for sublist in st for f in sublist]
1799 files = [f for sublist in st for f in sublist]
1796 for f in files:
1800 for f in files:
1797 if match(f):
1801 if match(f):
1798 output += self._gitcommand(cmd + ['--', f]) + '\n'
1802 output += self._gitcommand(cmd + ['--', f]) + '\n'
1799
1803
1800 if output.strip():
1804 if output.strip():
1801 ui.write(output)
1805 ui.write(output)
1802
1806
1803 @annotatesubrepoerror
1807 @annotatesubrepoerror
1804 def revert(self, substate, *pats, **opts):
1808 def revert(self, substate, *pats, **opts):
1805 self.ui.status(_('reverting subrepo %s\n') % substate[0])
1809 self.ui.status(_('reverting subrepo %s\n') % substate[0])
1806 if not opts.get(r'no_backup'):
1810 if not opts.get(r'no_backup'):
1807 status = self.status(None)
1811 status = self.status(None)
1808 names = status.modified
1812 names = status.modified
1809 for name in names:
1813 for name in names:
1810 bakname = scmutil.origpath(self.ui, self._subparent, name)
1814 bakname = scmutil.origpath(self.ui, self._subparent, name)
1811 self.ui.note(_('saving current version of %s as %s\n') %
1815 self.ui.note(_('saving current version of %s as %s\n') %
1812 (name, bakname))
1816 (name, bakname))
1813 self.wvfs.rename(name, bakname)
1817 self.wvfs.rename(name, bakname)
1814
1818
1815 if not opts.get(r'dry_run'):
1819 if not opts.get(r'dry_run'):
1816 self.get(substate, overwrite=True)
1820 self.get(substate, overwrite=True)
1817 return []
1821 return []
1818
1822
1819 def shortid(self, revid):
1823 def shortid(self, revid):
1820 return revid[:7]
1824 return revid[:7]
1821
1825
1822 types = {
1826 types = {
1823 'hg': hgsubrepo,
1827 'hg': hgsubrepo,
1824 'svn': svnsubrepo,
1828 'svn': svnsubrepo,
1825 'git': gitsubrepo,
1829 'git': gitsubrepo,
1826 }
1830 }
@@ -1,510 +1,516 b''
1 #require repofncache
1 #require repofncache
2
2
3 Init repo1:
3 Init repo1:
4
4
5 $ hg init repo1
5 $ hg init repo1
6 $ cd repo1
6 $ cd repo1
7 $ echo "some text" > a
7 $ echo "some text" > a
8 $ hg add
8 $ hg add
9 adding a
9 adding a
10 $ hg ci -m first
10 $ hg ci -m first
11 $ cat .hg/store/fncache | sort
11 $ cat .hg/store/fncache | sort
12 data/a.i
12 data/a.i
13
13
14 Testing a.i/b:
14 Testing a.i/b:
15
15
16 $ mkdir a.i
16 $ mkdir a.i
17 $ echo "some other text" > a.i/b
17 $ echo "some other text" > a.i/b
18 $ hg add
18 $ hg add
19 adding a.i/b
19 adding a.i/b
20 $ hg ci -m second
20 $ hg ci -m second
21 $ cat .hg/store/fncache | sort
21 $ cat .hg/store/fncache | sort
22 data/a.i
22 data/a.i
23 data/a.i.hg/b.i
23 data/a.i.hg/b.i
24
24
25 Testing a.i.hg/c:
25 Testing a.i.hg/c:
26
26
27 $ mkdir a.i.hg
27 $ mkdir a.i.hg
28 $ echo "yet another text" > a.i.hg/c
28 $ echo "yet another text" > a.i.hg/c
29 $ hg add
29 $ hg add
30 adding a.i.hg/c
30 adding a.i.hg/c
31 $ hg ci -m third
31 $ hg ci -m third
32 $ cat .hg/store/fncache | sort
32 $ cat .hg/store/fncache | sort
33 data/a.i
33 data/a.i
34 data/a.i.hg.hg/c.i
34 data/a.i.hg.hg/c.i
35 data/a.i.hg/b.i
35 data/a.i.hg/b.i
36
36
37 Testing verify:
37 Testing verify:
38
38
39 $ hg verify
39 $ hg verify
40 checking changesets
40 checking changesets
41 checking manifests
41 checking manifests
42 crosschecking files in changesets and manifests
42 crosschecking files in changesets and manifests
43 checking files
43 checking files
44 checked 3 changesets with 3 changes to 3 files
44 checked 3 changesets with 3 changes to 3 files
45
45
46 $ rm .hg/store/fncache
46 $ rm .hg/store/fncache
47
47
48 $ hg verify
48 $ hg verify
49 checking changesets
49 checking changesets
50 checking manifests
50 checking manifests
51 crosschecking files in changesets and manifests
51 crosschecking files in changesets and manifests
52 checking files
52 checking files
53 warning: revlog 'data/a.i' not in fncache!
53 warning: revlog 'data/a.i' not in fncache!
54 warning: revlog 'data/a.i.hg/c.i' not in fncache!
54 warning: revlog 'data/a.i.hg/c.i' not in fncache!
55 warning: revlog 'data/a.i/b.i' not in fncache!
55 warning: revlog 'data/a.i/b.i' not in fncache!
56 checked 3 changesets with 3 changes to 3 files
56 checked 3 changesets with 3 changes to 3 files
57 3 warnings encountered!
57 3 warnings encountered!
58 hint: run "hg debugrebuildfncache" to recover from corrupt fncache
58 hint: run "hg debugrebuildfncache" to recover from corrupt fncache
59
59
60 Follow the hint to make sure it works
60 Follow the hint to make sure it works
61
61
62 $ hg debugrebuildfncache
62 $ hg debugrebuildfncache
63 adding data/a.i
63 adding data/a.i
64 adding data/a.i.hg/c.i
64 adding data/a.i.hg/c.i
65 adding data/a.i/b.i
65 adding data/a.i/b.i
66 3 items added, 0 removed from fncache
66 3 items added, 0 removed from fncache
67
67
68 $ hg verify
68 $ hg verify
69 checking changesets
69 checking changesets
70 checking manifests
70 checking manifests
71 crosschecking files in changesets and manifests
71 crosschecking files in changesets and manifests
72 checking files
72 checking files
73 checked 3 changesets with 3 changes to 3 files
73 checked 3 changesets with 3 changes to 3 files
74
74
75 $ cd ..
75 $ cd ..
76
76
77 Non store repo:
77 Non store repo:
78
78
79 $ hg --config format.usestore=False init foo
79 $ hg --config format.usestore=False init foo
80 $ cd foo
80 $ cd foo
81 $ mkdir tst.d
81 $ mkdir tst.d
82 $ echo foo > tst.d/foo
82 $ echo foo > tst.d/foo
83 $ hg ci -Amfoo
83 $ hg ci -Amfoo
84 adding tst.d/foo
84 adding tst.d/foo
85 $ find .hg | sort
85 $ find .hg | sort
86 .hg
86 .hg
87 .hg/00changelog.i
87 .hg/00changelog.i
88 .hg/00manifest.i
88 .hg/00manifest.i
89 .hg/cache
89 .hg/cache
90 .hg/cache/branch2-served
90 .hg/cache/branch2-served
91 .hg/cache/checkisexec (execbit !)
92 .hg/cache/checklink (symlink !)
93 .hg/cache/checklink-target (symlink !)
91 .hg/cache/manifestfulltextcache (reporevlogstore !)
94 .hg/cache/manifestfulltextcache (reporevlogstore !)
92 .hg/cache/rbc-names-v1
95 .hg/cache/rbc-names-v1
93 .hg/cache/rbc-revs-v1
96 .hg/cache/rbc-revs-v1
94 .hg/data
97 .hg/data
95 .hg/data/tst.d.hg
98 .hg/data/tst.d.hg
96 .hg/data/tst.d.hg/foo.i
99 .hg/data/tst.d.hg/foo.i
97 .hg/dirstate
100 .hg/dirstate
98 .hg/fsmonitor.state (fsmonitor !)
101 .hg/fsmonitor.state (fsmonitor !)
99 .hg/last-message.txt
102 .hg/last-message.txt
100 .hg/phaseroots
103 .hg/phaseroots
101 .hg/requires
104 .hg/requires
102 .hg/undo
105 .hg/undo
103 .hg/undo.backup.dirstate
106 .hg/undo.backup.dirstate
104 .hg/undo.backupfiles
107 .hg/undo.backupfiles
105 .hg/undo.bookmarks
108 .hg/undo.bookmarks
106 .hg/undo.branch
109 .hg/undo.branch
107 .hg/undo.desc
110 .hg/undo.desc
108 .hg/undo.dirstate
111 .hg/undo.dirstate
109 .hg/undo.phaseroots
112 .hg/undo.phaseroots
110 $ cd ..
113 $ cd ..
111
114
112 Non fncache repo:
115 Non fncache repo:
113
116
114 $ hg --config format.usefncache=False init bar
117 $ hg --config format.usefncache=False init bar
115 $ cd bar
118 $ cd bar
116 $ mkdir tst.d
119 $ mkdir tst.d
117 $ echo foo > tst.d/Foo
120 $ echo foo > tst.d/Foo
118 $ hg ci -Amfoo
121 $ hg ci -Amfoo
119 adding tst.d/Foo
122 adding tst.d/Foo
120 $ find .hg | sort
123 $ find .hg | sort
121 .hg
124 .hg
122 .hg/00changelog.i
125 .hg/00changelog.i
123 .hg/cache
126 .hg/cache
124 .hg/cache/branch2-served
127 .hg/cache/branch2-served
128 .hg/cache/checkisexec (execbit !)
129 .hg/cache/checklink (symlink !)
130 .hg/cache/checklink-target (symlink !)
125 .hg/cache/manifestfulltextcache (reporevlogstore !)
131 .hg/cache/manifestfulltextcache (reporevlogstore !)
126 .hg/cache/rbc-names-v1
132 .hg/cache/rbc-names-v1
127 .hg/cache/rbc-revs-v1
133 .hg/cache/rbc-revs-v1
128 .hg/dirstate
134 .hg/dirstate
129 .hg/fsmonitor.state (fsmonitor !)
135 .hg/fsmonitor.state (fsmonitor !)
130 .hg/last-message.txt
136 .hg/last-message.txt
131 .hg/requires
137 .hg/requires
132 .hg/store
138 .hg/store
133 .hg/store/00changelog.i
139 .hg/store/00changelog.i
134 .hg/store/00manifest.i
140 .hg/store/00manifest.i
135 .hg/store/data
141 .hg/store/data
136 .hg/store/data/tst.d.hg
142 .hg/store/data/tst.d.hg
137 .hg/store/data/tst.d.hg/_foo.i
143 .hg/store/data/tst.d.hg/_foo.i
138 .hg/store/phaseroots
144 .hg/store/phaseroots
139 .hg/store/undo
145 .hg/store/undo
140 .hg/store/undo.backupfiles
146 .hg/store/undo.backupfiles
141 .hg/store/undo.phaseroots
147 .hg/store/undo.phaseroots
142 .hg/undo.backup.dirstate
148 .hg/undo.backup.dirstate
143 .hg/undo.bookmarks
149 .hg/undo.bookmarks
144 .hg/undo.branch
150 .hg/undo.branch
145 .hg/undo.desc
151 .hg/undo.desc
146 .hg/undo.dirstate
152 .hg/undo.dirstate
147 $ cd ..
153 $ cd ..
148
154
149 Encoding of reserved / long paths in the store
155 Encoding of reserved / long paths in the store
150
156
151 $ hg init r2
157 $ hg init r2
152 $ cd r2
158 $ cd r2
153 $ cat <<EOF > .hg/hgrc
159 $ cat <<EOF > .hg/hgrc
154 > [ui]
160 > [ui]
155 > portablefilenames = ignore
161 > portablefilenames = ignore
156 > EOF
162 > EOF
157
163
158 $ hg import -q --bypass - <<EOF
164 $ hg import -q --bypass - <<EOF
159 > # HG changeset patch
165 > # HG changeset patch
160 > # User test
166 > # User test
161 > # Date 0 0
167 > # Date 0 0
162 > # Node ID 1c7a2f7cb77be1a0def34e4c7cabc562ad98fbd7
168 > # Node ID 1c7a2f7cb77be1a0def34e4c7cabc562ad98fbd7
163 > # Parent 0000000000000000000000000000000000000000
169 > # Parent 0000000000000000000000000000000000000000
164 > 1
170 > 1
165 >
171 >
166 > diff --git a/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3456789-12345-ABCDEFGHIJKLMNOPRSTUVWXYZ-abcdefghjiklmnopqrstuvwxyz b/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3456789-12345-ABCDEFGHIJKLMNOPRSTUVWXYZ-abcdefghjiklmnopqrstuvwxyz
172 > diff --git a/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3456789-12345-ABCDEFGHIJKLMNOPRSTUVWXYZ-abcdefghjiklmnopqrstuvwxyz b/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3456789-12345-ABCDEFGHIJKLMNOPRSTUVWXYZ-abcdefghjiklmnopqrstuvwxyz
167 > new file mode 100644
173 > new file mode 100644
168 > --- /dev/null
174 > --- /dev/null
169 > +++ b/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3456789-12345-ABCDEFGHIJKLMNOPRSTUVWXYZ-abcdefghjiklmnopqrstuvwxyz
175 > +++ b/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3456789-12345-ABCDEFGHIJKLMNOPRSTUVWXYZ-abcdefghjiklmnopqrstuvwxyz
170 > @@ -0,0 +1,1 @@
176 > @@ -0,0 +1,1 @@
171 > +foo
177 > +foo
172 > diff --git a/AUX/SECOND/X.PRN/FOURTH/FI:FTH/SIXTH/SEVENTH/EIGHTH/NINETH/TENTH/ELEVENTH/LOREMIPSUM.TXT b/AUX/SECOND/X.PRN/FOURTH/FI:FTH/SIXTH/SEVENTH/EIGHTH/NINETH/TENTH/ELEVENTH/LOREMIPSUM.TXT
178 > diff --git a/AUX/SECOND/X.PRN/FOURTH/FI:FTH/SIXTH/SEVENTH/EIGHTH/NINETH/TENTH/ELEVENTH/LOREMIPSUM.TXT b/AUX/SECOND/X.PRN/FOURTH/FI:FTH/SIXTH/SEVENTH/EIGHTH/NINETH/TENTH/ELEVENTH/LOREMIPSUM.TXT
173 > new file mode 100644
179 > new file mode 100644
174 > --- /dev/null
180 > --- /dev/null
175 > +++ b/AUX/SECOND/X.PRN/FOURTH/FI:FTH/SIXTH/SEVENTH/EIGHTH/NINETH/TENTH/ELEVENTH/LOREMIPSUM.TXT
181 > +++ b/AUX/SECOND/X.PRN/FOURTH/FI:FTH/SIXTH/SEVENTH/EIGHTH/NINETH/TENTH/ELEVENTH/LOREMIPSUM.TXT
176 > @@ -0,0 +1,1 @@
182 > @@ -0,0 +1,1 @@
177 > +foo
183 > +foo
178 > diff --git a/Project Planning/Resources/AnotherLongDirectoryName/Followedbyanother/AndAnother/AndThenAnExtremelyLongFileName.txt b/Project Planning/Resources/AnotherLongDirectoryName/Followedbyanother/AndAnother/AndThenAnExtremelyLongFileName.txt
184 > diff --git a/Project Planning/Resources/AnotherLongDirectoryName/Followedbyanother/AndAnother/AndThenAnExtremelyLongFileName.txt b/Project Planning/Resources/AnotherLongDirectoryName/Followedbyanother/AndAnother/AndThenAnExtremelyLongFileName.txt
179 > new file mode 100644
185 > new file mode 100644
180 > --- /dev/null
186 > --- /dev/null
181 > +++ b/Project Planning/Resources/AnotherLongDirectoryName/Followedbyanother/AndAnother/AndThenAnExtremelyLongFileName.txt
187 > +++ b/Project Planning/Resources/AnotherLongDirectoryName/Followedbyanother/AndAnother/AndThenAnExtremelyLongFileName.txt
182 > @@ -0,0 +1,1 @@
188 > @@ -0,0 +1,1 @@
183 > +foo
189 > +foo
184 > diff --git a/bla.aux/prn/PRN/lpt/com3/nul/coma/foo.NUL/normal.c b/bla.aux/prn/PRN/lpt/com3/nul/coma/foo.NUL/normal.c
190 > diff --git a/bla.aux/prn/PRN/lpt/com3/nul/coma/foo.NUL/normal.c b/bla.aux/prn/PRN/lpt/com3/nul/coma/foo.NUL/normal.c
185 > new file mode 100644
191 > new file mode 100644
186 > --- /dev/null
192 > --- /dev/null
187 > +++ b/bla.aux/prn/PRN/lpt/com3/nul/coma/foo.NUL/normal.c
193 > +++ b/bla.aux/prn/PRN/lpt/com3/nul/coma/foo.NUL/normal.c
188 > @@ -0,0 +1,1 @@
194 > @@ -0,0 +1,1 @@
189 > +foo
195 > +foo
190 > diff --git a/enterprise/openesbaddons/contrib-imola/corba-bc/netbeansplugin/wsdlExtension/src/main/java/META-INF/services/org.netbeans.modules.xml.wsdl.bindingsupport.spi.ExtensibilityElementTemplateProvider b/enterprise/openesbaddons/contrib-imola/corba-bc/netbeansplugin/wsdlExtension/src/main/java/META-INF/services/org.netbeans.modules.xml.wsdl.bindingsupport.spi.ExtensibilityElementTemplateProvider
196 > diff --git a/enterprise/openesbaddons/contrib-imola/corba-bc/netbeansplugin/wsdlExtension/src/main/java/META-INF/services/org.netbeans.modules.xml.wsdl.bindingsupport.spi.ExtensibilityElementTemplateProvider b/enterprise/openesbaddons/contrib-imola/corba-bc/netbeansplugin/wsdlExtension/src/main/java/META-INF/services/org.netbeans.modules.xml.wsdl.bindingsupport.spi.ExtensibilityElementTemplateProvider
191 > new file mode 100644
197 > new file mode 100644
192 > --- /dev/null
198 > --- /dev/null
193 > +++ b/enterprise/openesbaddons/contrib-imola/corba-bc/netbeansplugin/wsdlExtension/src/main/java/META-INF/services/org.netbeans.modules.xml.wsdl.bindingsupport.spi.ExtensibilityElementTemplateProvider
199 > +++ b/enterprise/openesbaddons/contrib-imola/corba-bc/netbeansplugin/wsdlExtension/src/main/java/META-INF/services/org.netbeans.modules.xml.wsdl.bindingsupport.spi.ExtensibilityElementTemplateProvider
194 > @@ -0,0 +1,1 @@
200 > @@ -0,0 +1,1 @@
195 > +foo
201 > +foo
196 > EOF
202 > EOF
197
203
198 $ find .hg/store -name *.i | sort
204 $ find .hg/store -name *.i | sort
199 .hg/store/00changelog.i
205 .hg/store/00changelog.i
200 .hg/store/00manifest.i
206 .hg/store/00manifest.i
201 .hg/store/data/bla.aux/pr~6e/_p_r_n/lpt/co~6d3/nu~6c/coma/foo._n_u_l/normal.c.i
207 .hg/store/data/bla.aux/pr~6e/_p_r_n/lpt/co~6d3/nu~6c/coma/foo._n_u_l/normal.c.i
202 .hg/store/dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxx168e07b38e65eff86ab579afaaa8e30bfbe0f35f.i
208 .hg/store/dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxx168e07b38e65eff86ab579afaaa8e30bfbe0f35f.i
203 .hg/store/dh/au~78/second/x.prn/fourth/fi~3afth/sixth/seventh/eighth/nineth/tenth/loremia20419e358ddff1bf8751e38288aff1d7c32ec05.i
209 .hg/store/dh/au~78/second/x.prn/fourth/fi~3afth/sixth/seventh/eighth/nineth/tenth/loremia20419e358ddff1bf8751e38288aff1d7c32ec05.i
204 .hg/store/dh/enterpri/openesba/contrib-/corba-bc/netbeans/wsdlexte/src/main/java/org.net7018f27961fdf338a598a40c4683429e7ffb9743.i
210 .hg/store/dh/enterpri/openesba/contrib-/corba-bc/netbeans/wsdlexte/src/main/java/org.net7018f27961fdf338a598a40c4683429e7ffb9743.i
205 .hg/store/dh/project_/resource/anotherl/followed/andanoth/andthenanextremelylongfilename0d8e1f4187c650e2f1fdca9fd90f786bc0976b6b.i
211 .hg/store/dh/project_/resource/anotherl/followed/andanoth/andthenanextremelylongfilename0d8e1f4187c650e2f1fdca9fd90f786bc0976b6b.i
206
212
207 $ cd ..
213 $ cd ..
208
214
209 Aborting lock does not prevent fncache writes
215 Aborting lock does not prevent fncache writes
210
216
211 $ cat > exceptionext.py <<EOF
217 $ cat > exceptionext.py <<EOF
212 > from __future__ import absolute_import
218 > from __future__ import absolute_import
213 > import os
219 > import os
214 > from mercurial import commands, error, extensions
220 > from mercurial import commands, error, extensions
215 >
221 >
216 > def lockexception(orig, vfs, lockname, wait, releasefn, *args, **kwargs):
222 > def lockexception(orig, vfs, lockname, wait, releasefn, *args, **kwargs):
217 > def releasewrap():
223 > def releasewrap():
218 > l.held = False # ensure __del__ is a noop
224 > l.held = False # ensure __del__ is a noop
219 > raise error.Abort("forced lock failure")
225 > raise error.Abort("forced lock failure")
220 > l = orig(vfs, lockname, wait, releasewrap, *args, **kwargs)
226 > l = orig(vfs, lockname, wait, releasewrap, *args, **kwargs)
221 > return l
227 > return l
222 >
228 >
223 > def reposetup(ui, repo):
229 > def reposetup(ui, repo):
224 > extensions.wrapfunction(repo, '_lock', lockexception)
230 > extensions.wrapfunction(repo, '_lock', lockexception)
225 >
231 >
226 > cmdtable = {}
232 > cmdtable = {}
227 >
233 >
228 > # wrap "commit" command to prevent wlock from being '__del__()'-ed
234 > # wrap "commit" command to prevent wlock from being '__del__()'-ed
229 > # at the end of dispatching (for intentional "forced lcok failure")
235 > # at the end of dispatching (for intentional "forced lcok failure")
230 > def commitwrap(orig, ui, repo, *pats, **opts):
236 > def commitwrap(orig, ui, repo, *pats, **opts):
231 > repo = repo.unfiltered() # to use replaced repo._lock certainly
237 > repo = repo.unfiltered() # to use replaced repo._lock certainly
232 > wlock = repo.wlock()
238 > wlock = repo.wlock()
233 > try:
239 > try:
234 > return orig(ui, repo, *pats, **opts)
240 > return orig(ui, repo, *pats, **opts)
235 > finally:
241 > finally:
236 > # multiple 'relase()' is needed for complete releasing wlock,
242 > # multiple 'relase()' is needed for complete releasing wlock,
237 > # because "forced" abort at last releasing store lock
243 > # because "forced" abort at last releasing store lock
238 > # prevents wlock from being released at same 'lockmod.release()'
244 > # prevents wlock from being released at same 'lockmod.release()'
239 > for i in range(wlock.held):
245 > for i in range(wlock.held):
240 > wlock.release()
246 > wlock.release()
241 >
247 >
242 > def extsetup(ui):
248 > def extsetup(ui):
243 > extensions.wrapcommand(commands.table, b"commit", commitwrap)
249 > extensions.wrapcommand(commands.table, b"commit", commitwrap)
244 > EOF
250 > EOF
245 $ extpath=`pwd`/exceptionext.py
251 $ extpath=`pwd`/exceptionext.py
246 $ hg init fncachetxn
252 $ hg init fncachetxn
247 $ cd fncachetxn
253 $ cd fncachetxn
248 $ printf "[extensions]\nexceptionext=$extpath\n" >> .hg/hgrc
254 $ printf "[extensions]\nexceptionext=$extpath\n" >> .hg/hgrc
249 $ touch y
255 $ touch y
250 $ hg ci -qAm y
256 $ hg ci -qAm y
251 abort: forced lock failure
257 abort: forced lock failure
252 [255]
258 [255]
253 $ cat .hg/store/fncache
259 $ cat .hg/store/fncache
254 data/y.i
260 data/y.i
255
261
256 Aborting transaction prevents fncache change
262 Aborting transaction prevents fncache change
257
263
258 $ cat > ../exceptionext.py <<EOF
264 $ cat > ../exceptionext.py <<EOF
259 > from __future__ import absolute_import
265 > from __future__ import absolute_import
260 > import os
266 > import os
261 > from mercurial import commands, error, extensions, localrepo
267 > from mercurial import commands, error, extensions, localrepo
262 >
268 >
263 > def wrapper(orig, self, *args, **kwargs):
269 > def wrapper(orig, self, *args, **kwargs):
264 > tr = orig(self, *args, **kwargs)
270 > tr = orig(self, *args, **kwargs)
265 > def fail(tr):
271 > def fail(tr):
266 > raise error.Abort(b"forced transaction failure")
272 > raise error.Abort(b"forced transaction failure")
267 > # zzz prefix to ensure it sorted after store.write
273 > # zzz prefix to ensure it sorted after store.write
268 > tr.addfinalize(b'zzz-forcefails', fail)
274 > tr.addfinalize(b'zzz-forcefails', fail)
269 > return tr
275 > return tr
270 >
276 >
271 > def uisetup(ui):
277 > def uisetup(ui):
272 > extensions.wrapfunction(
278 > extensions.wrapfunction(
273 > localrepo.localrepository, b'transaction', wrapper)
279 > localrepo.localrepository, b'transaction', wrapper)
274 >
280 >
275 > cmdtable = {}
281 > cmdtable = {}
276 >
282 >
277 > EOF
283 > EOF
278
284
279 Clean cached version
285 Clean cached version
280 $ rm -f "${extpath}c"
286 $ rm -f "${extpath}c"
281 $ rm -Rf "`dirname $extpath`/__pycache__"
287 $ rm -Rf "`dirname $extpath`/__pycache__"
282
288
283 $ touch z
289 $ touch z
284 $ hg ci -qAm z
290 $ hg ci -qAm z
285 transaction abort!
291 transaction abort!
286 rollback completed
292 rollback completed
287 abort: forced transaction failure
293 abort: forced transaction failure
288 [255]
294 [255]
289 $ cat .hg/store/fncache
295 $ cat .hg/store/fncache
290 data/y.i
296 data/y.i
291
297
292 Aborted transactions can be recovered later
298 Aborted transactions can be recovered later
293
299
294 $ cat > ../exceptionext.py <<EOF
300 $ cat > ../exceptionext.py <<EOF
295 > from __future__ import absolute_import
301 > from __future__ import absolute_import
296 > import os
302 > import os
297 > from mercurial import (
303 > from mercurial import (
298 > commands,
304 > commands,
299 > error,
305 > error,
300 > extensions,
306 > extensions,
301 > localrepo,
307 > localrepo,
302 > transaction,
308 > transaction,
303 > )
309 > )
304 >
310 >
305 > def trwrapper(orig, self, *args, **kwargs):
311 > def trwrapper(orig, self, *args, **kwargs):
306 > tr = orig(self, *args, **kwargs)
312 > tr = orig(self, *args, **kwargs)
307 > def fail(tr):
313 > def fail(tr):
308 > raise error.Abort(b"forced transaction failure")
314 > raise error.Abort(b"forced transaction failure")
309 > # zzz prefix to ensure it sorted after store.write
315 > # zzz prefix to ensure it sorted after store.write
310 > tr.addfinalize(b'zzz-forcefails', fail)
316 > tr.addfinalize(b'zzz-forcefails', fail)
311 > return tr
317 > return tr
312 >
318 >
313 > def abortwrapper(orig, self, *args, **kwargs):
319 > def abortwrapper(orig, self, *args, **kwargs):
314 > raise error.Abort(b"forced transaction failure")
320 > raise error.Abort(b"forced transaction failure")
315 >
321 >
316 > def uisetup(ui):
322 > def uisetup(ui):
317 > extensions.wrapfunction(localrepo.localrepository, 'transaction',
323 > extensions.wrapfunction(localrepo.localrepository, 'transaction',
318 > trwrapper)
324 > trwrapper)
319 > extensions.wrapfunction(transaction.transaction, '_abort',
325 > extensions.wrapfunction(transaction.transaction, '_abort',
320 > abortwrapper)
326 > abortwrapper)
321 >
327 >
322 > cmdtable = {}
328 > cmdtable = {}
323 >
329 >
324 > EOF
330 > EOF
325
331
326 Clean cached versions
332 Clean cached versions
327 $ rm -f "${extpath}c"
333 $ rm -f "${extpath}c"
328 $ rm -Rf "`dirname $extpath`/__pycache__"
334 $ rm -Rf "`dirname $extpath`/__pycache__"
329
335
330 $ hg up -q 1
336 $ hg up -q 1
331 $ touch z
337 $ touch z
332 $ hg ci -qAm z 2>/dev/null
338 $ hg ci -qAm z 2>/dev/null
333 [255]
339 [255]
334 $ cat .hg/store/fncache | sort
340 $ cat .hg/store/fncache | sort
335 data/y.i
341 data/y.i
336 data/z.i
342 data/z.i
337 $ hg recover
343 $ hg recover
338 rolling back interrupted transaction
344 rolling back interrupted transaction
339 checking changesets
345 checking changesets
340 checking manifests
346 checking manifests
341 crosschecking files in changesets and manifests
347 crosschecking files in changesets and manifests
342 checking files
348 checking files
343 checked 1 changesets with 1 changes to 1 files
349 checked 1 changesets with 1 changes to 1 files
344 $ cat .hg/store/fncache
350 $ cat .hg/store/fncache
345 data/y.i
351 data/y.i
346
352
347 $ cd ..
353 $ cd ..
348
354
349 debugrebuildfncache does nothing unless repo has fncache requirement
355 debugrebuildfncache does nothing unless repo has fncache requirement
350
356
351 $ hg --config format.usefncache=false init nofncache
357 $ hg --config format.usefncache=false init nofncache
352 $ cd nofncache
358 $ cd nofncache
353 $ hg debugrebuildfncache
359 $ hg debugrebuildfncache
354 (not rebuilding fncache because repository does not support fncache)
360 (not rebuilding fncache because repository does not support fncache)
355
361
356 $ cd ..
362 $ cd ..
357
363
358 debugrebuildfncache works on empty repository
364 debugrebuildfncache works on empty repository
359
365
360 $ hg init empty
366 $ hg init empty
361 $ cd empty
367 $ cd empty
362 $ hg debugrebuildfncache
368 $ hg debugrebuildfncache
363 fncache already up to date
369 fncache already up to date
364 $ cd ..
370 $ cd ..
365
371
366 debugrebuildfncache on an up to date repository no-ops
372 debugrebuildfncache on an up to date repository no-ops
367
373
368 $ hg init repo
374 $ hg init repo
369 $ cd repo
375 $ cd repo
370 $ echo initial > foo
376 $ echo initial > foo
371 $ echo initial > .bar
377 $ echo initial > .bar
372 $ hg commit -A -m initial
378 $ hg commit -A -m initial
373 adding .bar
379 adding .bar
374 adding foo
380 adding foo
375
381
376 $ cat .hg/store/fncache | sort
382 $ cat .hg/store/fncache | sort
377 data/.bar.i
383 data/.bar.i
378 data/foo.i
384 data/foo.i
379
385
380 $ hg debugrebuildfncache
386 $ hg debugrebuildfncache
381 fncache already up to date
387 fncache already up to date
382
388
383 debugrebuildfncache restores deleted fncache file
389 debugrebuildfncache restores deleted fncache file
384
390
385 $ rm -f .hg/store/fncache
391 $ rm -f .hg/store/fncache
386 $ hg debugrebuildfncache
392 $ hg debugrebuildfncache
387 adding data/.bar.i
393 adding data/.bar.i
388 adding data/foo.i
394 adding data/foo.i
389 2 items added, 0 removed from fncache
395 2 items added, 0 removed from fncache
390
396
391 $ cat .hg/store/fncache | sort
397 $ cat .hg/store/fncache | sort
392 data/.bar.i
398 data/.bar.i
393 data/foo.i
399 data/foo.i
394
400
395 Rebuild after rebuild should no-op
401 Rebuild after rebuild should no-op
396
402
397 $ hg debugrebuildfncache
403 $ hg debugrebuildfncache
398 fncache already up to date
404 fncache already up to date
399
405
400 A single missing file should get restored, an extra file should be removed
406 A single missing file should get restored, an extra file should be removed
401
407
402 $ cat > .hg/store/fncache << EOF
408 $ cat > .hg/store/fncache << EOF
403 > data/foo.i
409 > data/foo.i
404 > data/bad-entry.i
410 > data/bad-entry.i
405 > EOF
411 > EOF
406
412
407 $ hg debugrebuildfncache
413 $ hg debugrebuildfncache
408 removing data/bad-entry.i
414 removing data/bad-entry.i
409 adding data/.bar.i
415 adding data/.bar.i
410 1 items added, 1 removed from fncache
416 1 items added, 1 removed from fncache
411
417
412 $ cat .hg/store/fncache | sort
418 $ cat .hg/store/fncache | sort
413 data/.bar.i
419 data/.bar.i
414 data/foo.i
420 data/foo.i
415
421
416 $ cd ..
422 $ cd ..
417
423
418 Try a simple variation without dotencode to ensure fncache is ignorant of encoding
424 Try a simple variation without dotencode to ensure fncache is ignorant of encoding
419
425
420 $ hg --config format.dotencode=false init nodotencode
426 $ hg --config format.dotencode=false init nodotencode
421 $ cd nodotencode
427 $ cd nodotencode
422 $ echo initial > foo
428 $ echo initial > foo
423 $ echo initial > .bar
429 $ echo initial > .bar
424 $ hg commit -A -m initial
430 $ hg commit -A -m initial
425 adding .bar
431 adding .bar
426 adding foo
432 adding foo
427
433
428 $ cat .hg/store/fncache | sort
434 $ cat .hg/store/fncache | sort
429 data/.bar.i
435 data/.bar.i
430 data/foo.i
436 data/foo.i
431
437
432 $ rm .hg/store/fncache
438 $ rm .hg/store/fncache
433 $ hg debugrebuildfncache
439 $ hg debugrebuildfncache
434 adding data/.bar.i
440 adding data/.bar.i
435 adding data/foo.i
441 adding data/foo.i
436 2 items added, 0 removed from fncache
442 2 items added, 0 removed from fncache
437
443
438 $ cat .hg/store/fncache | sort
444 $ cat .hg/store/fncache | sort
439 data/.bar.i
445 data/.bar.i
440 data/foo.i
446 data/foo.i
441
447
442 $ cd ..
448 $ cd ..
443
449
444 In repositories that have accumulated a large number of files over time, the
450 In repositories that have accumulated a large number of files over time, the
445 fncache file is going to be large. If we possibly can avoid loading it, so much the better.
451 fncache file is going to be large. If we possibly can avoid loading it, so much the better.
446 The cache should not loaded when committing changes to existing files, or when unbundling
452 The cache should not loaded when committing changes to existing files, or when unbundling
447 changesets that only contain changes to existing files:
453 changesets that only contain changes to existing files:
448
454
449 $ cat > fncacheloadwarn.py << EOF
455 $ cat > fncacheloadwarn.py << EOF
450 > from __future__ import absolute_import
456 > from __future__ import absolute_import
451 > from mercurial import extensions, localrepo
457 > from mercurial import extensions, localrepo
452 >
458 >
453 > def extsetup(ui):
459 > def extsetup(ui):
454 > def wrapstore(orig, requirements, *args):
460 > def wrapstore(orig, requirements, *args):
455 > store = orig(requirements, *args)
461 > store = orig(requirements, *args)
456 > if b'store' in requirements and b'fncache' in requirements:
462 > if b'store' in requirements and b'fncache' in requirements:
457 > instrumentfncachestore(store, ui)
463 > instrumentfncachestore(store, ui)
458 > return store
464 > return store
459 > extensions.wrapfunction(localrepo, 'makestore', wrapstore)
465 > extensions.wrapfunction(localrepo, 'makestore', wrapstore)
460 >
466 >
461 > def instrumentfncachestore(fncachestore, ui):
467 > def instrumentfncachestore(fncachestore, ui):
462 > class instrumentedfncache(type(fncachestore.fncache)):
468 > class instrumentedfncache(type(fncachestore.fncache)):
463 > def _load(self):
469 > def _load(self):
464 > ui.warn(b'fncache load triggered!\n')
470 > ui.warn(b'fncache load triggered!\n')
465 > super(instrumentedfncache, self)._load()
471 > super(instrumentedfncache, self)._load()
466 > fncachestore.fncache.__class__ = instrumentedfncache
472 > fncachestore.fncache.__class__ = instrumentedfncache
467 > EOF
473 > EOF
468
474
469 $ fncachextpath=`pwd`/fncacheloadwarn.py
475 $ fncachextpath=`pwd`/fncacheloadwarn.py
470 $ hg init nofncacheload
476 $ hg init nofncacheload
471 $ cd nofncacheload
477 $ cd nofncacheload
472 $ printf "[extensions]\nfncacheloadwarn=$fncachextpath\n" >> .hg/hgrc
478 $ printf "[extensions]\nfncacheloadwarn=$fncachextpath\n" >> .hg/hgrc
473
479
474 A new file should trigger a load, as we'd want to update the fncache set in that case:
480 A new file should trigger a load, as we'd want to update the fncache set in that case:
475
481
476 $ touch foo
482 $ touch foo
477 $ hg ci -qAm foo
483 $ hg ci -qAm foo
478 fncache load triggered!
484 fncache load triggered!
479
485
480 But modifying that file should not:
486 But modifying that file should not:
481
487
482 $ echo bar >> foo
488 $ echo bar >> foo
483 $ hg ci -qm foo
489 $ hg ci -qm foo
484
490
485 If a transaction has been aborted, the zero-size truncated index file will
491 If a transaction has been aborted, the zero-size truncated index file will
486 not prevent the fncache from being loaded; rather than actually abort
492 not prevent the fncache from being loaded; rather than actually abort
487 a transaction, we simulate the situation by creating a zero-size index file:
493 a transaction, we simulate the situation by creating a zero-size index file:
488
494
489 $ touch .hg/store/data/bar.i
495 $ touch .hg/store/data/bar.i
490 $ touch bar
496 $ touch bar
491 $ hg ci -qAm bar
497 $ hg ci -qAm bar
492 fncache load triggered!
498 fncache load triggered!
493
499
494 Unbundling should follow the same rules; existing files should not cause a load:
500 Unbundling should follow the same rules; existing files should not cause a load:
495
501
496 $ hg clone -q . tobundle
502 $ hg clone -q . tobundle
497 $ echo 'new line' > tobundle/bar
503 $ echo 'new line' > tobundle/bar
498 $ hg -R tobundle ci -qm bar
504 $ hg -R tobundle ci -qm bar
499 $ hg -R tobundle bundle -q barupdated.hg
505 $ hg -R tobundle bundle -q barupdated.hg
500 $ hg unbundle -q barupdated.hg
506 $ hg unbundle -q barupdated.hg
501
507
502 but adding new files should:
508 but adding new files should:
503
509
504 $ touch tobundle/newfile
510 $ touch tobundle/newfile
505 $ hg -R tobundle ci -qAm newfile
511 $ hg -R tobundle ci -qAm newfile
506 $ hg -R tobundle bundle -q newfile.hg
512 $ hg -R tobundle bundle -q newfile.hg
507 $ hg unbundle -q newfile.hg
513 $ hg unbundle -q newfile.hg
508 fncache load triggered!
514 fncache load triggered!
509
515
510 $ cd ..
516 $ cd ..
@@ -1,2178 +1,2224 b''
1 $ cat >> $HGRCPATH <<EOF
1 $ cat >> $HGRCPATH <<EOF
2 > [extdiff]
2 > [extdiff]
3 > # for portability:
3 > # for portability:
4 > pdiff = sh "$RUNTESTDIR/pdiff"
4 > pdiff = sh "$RUNTESTDIR/pdiff"
5 > EOF
5 > EOF
6
6
7 Create a repo with some stuff in it:
7 Create a repo with some stuff in it:
8
8
9 $ hg init a
9 $ hg init a
10 $ cd a
10 $ cd a
11 $ echo a > a
11 $ echo a > a
12 $ echo a > d
12 $ echo a > d
13 $ echo a > e
13 $ echo a > e
14 $ hg ci -qAm0
14 $ hg ci -qAm0
15 $ echo b > a
15 $ echo b > a
16 $ hg ci -m1 -u bar
16 $ hg ci -m1 -u bar
17 $ hg mv a b
17 $ hg mv a b
18 $ hg ci -m2
18 $ hg ci -m2
19 $ hg cp b c
19 $ hg cp b c
20 $ hg ci -m3 -u baz
20 $ hg ci -m3 -u baz
21 $ echo b > d
21 $ echo b > d
22 $ echo f > e
22 $ echo f > e
23 $ hg ci -m4
23 $ hg ci -m4
24 $ hg up -q 3
24 $ hg up -q 3
25 $ echo b > e
25 $ echo b > e
26 $ hg branch -q stable
26 $ hg branch -q stable
27 $ hg ci -m5
27 $ hg ci -m5
28 $ hg merge -q default --tool internal:local # for conflicts in e, choose 5 and ignore 4
28 $ hg merge -q default --tool internal:local # for conflicts in e, choose 5 and ignore 4
29 $ hg branch -q default
29 $ hg branch -q default
30 $ hg ci -m6
30 $ hg ci -m6
31 $ hg phase --public 3
31 $ hg phase --public 3
32 $ hg phase --force --secret 6
32 $ hg phase --force --secret 6
33
33
34 $ hg log -G --template '{author}@{rev}.{phase}: {desc}\n'
34 $ hg log -G --template '{author}@{rev}.{phase}: {desc}\n'
35 @ test@6.secret: 6
35 @ test@6.secret: 6
36 |\
36 |\
37 | o test@5.draft: 5
37 | o test@5.draft: 5
38 | |
38 | |
39 o | test@4.draft: 4
39 o | test@4.draft: 4
40 |/
40 |/
41 o baz@3.public: 3
41 o baz@3.public: 3
42 |
42 |
43 o test@2.public: 2
43 o test@2.public: 2
44 |
44 |
45 o bar@1.public: 1
45 o bar@1.public: 1
46 |
46 |
47 o test@0.public: 0
47 o test@0.public: 0
48
48
49 Test --base for grafting the merge of 4 from the perspective of 5, thus only getting the change to d
49 Test --base for grafting the merge of 4 from the perspective of 5, thus only getting the change to d
50
50
51 $ hg up -cqr 3
51 $ hg up -cqr 3
52 $ hg graft -r 6 --base 5
52 $ hg graft -r 6 --base 5
53 grafting 6:25a2b029d3ae "6" (tip)
53 grafting 6:25a2b029d3ae "6" (tip)
54 merging e
54 merging e
55 $ hg st --change .
55 $ hg st --change .
56 M d
56 M d
57
57
58 $ hg -q strip . --config extensions.strip=
58 $ hg -q strip . --config extensions.strip=
59
59
60 Test --base for collapsing changesets 2 and 3, thus getting both b and c
60 Test --base for collapsing changesets 2 and 3, thus getting both b and c
61
61
62 $ hg up -cqr 0
62 $ hg up -cqr 0
63 $ hg graft -r 3 --base 1
63 $ hg graft -r 3 --base 1
64 grafting 3:4c60f11aa304 "3"
64 grafting 3:4c60f11aa304 "3"
65 merging a and b to b
65 merging a and b to b
66 merging a and c to c
66 merging a and c to c
67 $ hg st --change .
67 $ hg st --change .
68 A b
68 A b
69 A c
69 A c
70 R a
70 R a
71
71
72 $ hg -q strip . --config extensions.strip=
72 $ hg -q strip . --config extensions.strip=
73
73
74 Specifying child as --base revision fails safely (perhaps slightly confusing, but consistent)
74 Specifying child as --base revision fails safely (perhaps slightly confusing, but consistent)
75
75
76 $ hg graft -r 2 --base 3
76 $ hg graft -r 2 --base 3
77 grafting 2:5c095ad7e90f "2"
77 grafting 2:5c095ad7e90f "2"
78 note: graft of 2:5c095ad7e90f created no changes to commit
78 note: graft of 2:5c095ad7e90f created no changes to commit
79
79
80 Can't continue without starting:
80 Can't continue without starting:
81
81
82 $ hg -q up -cr tip
82 $ hg -q up -cr tip
83 $ hg rm -q e
83 $ hg rm -q e
84 $ hg graft --continue
84 $ hg graft --continue
85 abort: no graft in progress
85 abort: no graft in progress
86 [255]
86 [255]
87 $ hg revert -r . -q e
87 $ hg revert -r . -q e
88
88
89 Need to specify a rev:
89 Need to specify a rev:
90
90
91 $ hg graft
91 $ hg graft
92 abort: no revisions specified
92 abort: no revisions specified
93 [255]
93 [255]
94
94
95 Can't graft ancestor:
95 Can't graft ancestor:
96
96
97 $ hg graft 1 2
97 $ hg graft 1 2
98 skipping ancestor revision 1:5d205f8b35b6
98 skipping ancestor revision 1:5d205f8b35b6
99 skipping ancestor revision 2:5c095ad7e90f
99 skipping ancestor revision 2:5c095ad7e90f
100 [255]
100 [255]
101
101
102 Specify revisions with -r:
102 Specify revisions with -r:
103
103
104 $ hg graft -r 1 -r 2
104 $ hg graft -r 1 -r 2
105 skipping ancestor revision 1:5d205f8b35b6
105 skipping ancestor revision 1:5d205f8b35b6
106 skipping ancestor revision 2:5c095ad7e90f
106 skipping ancestor revision 2:5c095ad7e90f
107 [255]
107 [255]
108
108
109 $ hg graft -r 1 2
109 $ hg graft -r 1 2
110 warning: inconsistent use of --rev might give unexpected revision ordering!
110 warning: inconsistent use of --rev might give unexpected revision ordering!
111 skipping ancestor revision 2:5c095ad7e90f
111 skipping ancestor revision 2:5c095ad7e90f
112 skipping ancestor revision 1:5d205f8b35b6
112 skipping ancestor revision 1:5d205f8b35b6
113 [255]
113 [255]
114
114
115 Can't graft with dirty wd:
115 Can't graft with dirty wd:
116
116
117 $ hg up -q 0
117 $ hg up -q 0
118 $ echo foo > a
118 $ echo foo > a
119 $ hg graft 1
119 $ hg graft 1
120 abort: uncommitted changes
120 abort: uncommitted changes
121 [255]
121 [255]
122 $ hg revert a
122 $ hg revert a
123
123
124 Graft a rename:
124 Graft a rename:
125 (this also tests that editor is invoked if '--edit' is specified)
125 (this also tests that editor is invoked if '--edit' is specified)
126
126
127 $ hg status --rev "2^1" --rev 2
127 $ hg status --rev "2^1" --rev 2
128 A b
128 A b
129 R a
129 R a
130 $ HGEDITOR=cat hg graft 2 -u foo --edit
130 $ HGEDITOR=cat hg graft 2 -u foo --edit
131 grafting 2:5c095ad7e90f "2"
131 grafting 2:5c095ad7e90f "2"
132 merging a and b to b
132 merging a and b to b
133 2
133 2
134
134
135
135
136 HG: Enter commit message. Lines beginning with 'HG:' are removed.
136 HG: Enter commit message. Lines beginning with 'HG:' are removed.
137 HG: Leave message empty to abort commit.
137 HG: Leave message empty to abort commit.
138 HG: --
138 HG: --
139 HG: user: foo
139 HG: user: foo
140 HG: branch 'default'
140 HG: branch 'default'
141 HG: added b
141 HG: added b
142 HG: removed a
142 HG: removed a
143 $ hg export tip --git
143 $ hg export tip --git
144 # HG changeset patch
144 # HG changeset patch
145 # User foo
145 # User foo
146 # Date 0 0
146 # Date 0 0
147 # Thu Jan 01 00:00:00 1970 +0000
147 # Thu Jan 01 00:00:00 1970 +0000
148 # Node ID ef0ef43d49e79e81ddafdc7997401ba0041efc82
148 # Node ID ef0ef43d49e79e81ddafdc7997401ba0041efc82
149 # Parent 68795b066622ca79a25816a662041d8f78f3cd9e
149 # Parent 68795b066622ca79a25816a662041d8f78f3cd9e
150 2
150 2
151
151
152 diff --git a/a b/b
152 diff --git a/a b/b
153 rename from a
153 rename from a
154 rename to b
154 rename to b
155
155
156 Look for extra:source
156 Look for extra:source
157
157
158 $ hg log --debug -r tip
158 $ hg log --debug -r tip
159 changeset: 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82
159 changeset: 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82
160 tag: tip
160 tag: tip
161 phase: draft
161 phase: draft
162 parent: 0:68795b066622ca79a25816a662041d8f78f3cd9e
162 parent: 0:68795b066622ca79a25816a662041d8f78f3cd9e
163 parent: -1:0000000000000000000000000000000000000000
163 parent: -1:0000000000000000000000000000000000000000
164 manifest: 7:e59b6b228f9cbf9903d5e9abf996e083a1f533eb
164 manifest: 7:e59b6b228f9cbf9903d5e9abf996e083a1f533eb
165 user: foo
165 user: foo
166 date: Thu Jan 01 00:00:00 1970 +0000
166 date: Thu Jan 01 00:00:00 1970 +0000
167 files+: b
167 files+: b
168 files-: a
168 files-: a
169 extra: branch=default
169 extra: branch=default
170 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
170 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
171 description:
171 description:
172 2
172 2
173
173
174
174
175
175
176 Graft out of order, skipping a merge and a duplicate
176 Graft out of order, skipping a merge and a duplicate
177 (this also tests that editor is not invoked if '--edit' is not specified)
177 (this also tests that editor is not invoked if '--edit' is not specified)
178
178
179 $ hg graft 1 5 4 3 'merge()' 2 -n
179 $ hg graft 1 5 4 3 'merge()' 2 -n
180 skipping ungraftable merge revision 6
180 skipping ungraftable merge revision 6
181 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
181 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
182 grafting 1:5d205f8b35b6 "1"
182 grafting 1:5d205f8b35b6 "1"
183 grafting 5:97f8bfe72746 "5"
183 grafting 5:97f8bfe72746 "5"
184 grafting 4:9c233e8e184d "4"
184 grafting 4:9c233e8e184d "4"
185 grafting 3:4c60f11aa304 "3"
185 grafting 3:4c60f11aa304 "3"
186
186
187 $ HGEDITOR=cat hg graft 1 5 'merge()' 2 --debug
187 $ HGEDITOR=cat hg graft 1 5 'merge()' 2 --debug
188 skipping ungraftable merge revision 6
188 skipping ungraftable merge revision 6
189 scanning for duplicate grafts
189 scanning for duplicate grafts
190 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
190 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
191 grafting 1:5d205f8b35b6 "1"
191 grafting 1:5d205f8b35b6 "1"
192 searching for copies back to rev 1
192 searching for copies back to rev 1
193 unmatched files in local:
193 unmatched files in local:
194 b
194 b
195 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
195 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
196 src: 'a' -> dst: 'b' *
196 src: 'a' -> dst: 'b' *
197 checking for directory renames
197 checking for directory renames
198 resolving manifests
198 resolving manifests
199 branchmerge: True, force: True, partial: False
199 branchmerge: True, force: True, partial: False
200 ancestor: 68795b066622, local: ef0ef43d49e7+, remote: 5d205f8b35b6
200 ancestor: 68795b066622, local: ef0ef43d49e7+, remote: 5d205f8b35b6
201 preserving b for resolve of b
201 preserving b for resolve of b
202 starting 4 threads for background file closing (?)
202 starting 4 threads for background file closing (?)
203 b: local copied/moved from a -> m (premerge)
203 b: local copied/moved from a -> m (premerge)
204 picked tool ':merge' for b (binary False symlink False changedelete False)
204 picked tool ':merge' for b (binary False symlink False changedelete False)
205 merging b and a to b
205 merging b and a to b
206 my b@ef0ef43d49e7+ other a@5d205f8b35b6 ancestor a@68795b066622
206 my b@ef0ef43d49e7+ other a@5d205f8b35b6 ancestor a@68795b066622
207 premerge successful
207 premerge successful
208 committing files:
208 committing files:
209 b
209 b
210 committing manifest
210 committing manifest
211 committing changelog
211 committing changelog
212 updating the branch cache
212 updating the branch cache
213 grafting 5:97f8bfe72746 "5"
213 grafting 5:97f8bfe72746 "5"
214 searching for copies back to rev 1
214 searching for copies back to rev 1
215 unmatched files in other (from topological common ancestor):
215 unmatched files in other (from topological common ancestor):
216 c
216 c
217 resolving manifests
217 resolving manifests
218 branchmerge: True, force: True, partial: False
218 branchmerge: True, force: True, partial: False
219 ancestor: 4c60f11aa304, local: 6b9e5368ca4e+, remote: 97f8bfe72746
219 ancestor: 4c60f11aa304, local: 6b9e5368ca4e+, remote: 97f8bfe72746
220 e: remote is newer -> g
220 e: remote is newer -> g
221 getting e
221 getting e
222 committing files:
222 committing files:
223 e
223 e
224 committing manifest
224 committing manifest
225 committing changelog
225 committing changelog
226 updating the branch cache
226 updating the branch cache
227 $ HGEDITOR=cat hg graft 4 3 --log --debug
227 $ HGEDITOR=cat hg graft 4 3 --log --debug
228 scanning for duplicate grafts
228 scanning for duplicate grafts
229 grafting 4:9c233e8e184d "4"
229 grafting 4:9c233e8e184d "4"
230 searching for copies back to rev 1
230 searching for copies back to rev 1
231 unmatched files in other (from topological common ancestor):
231 unmatched files in other (from topological common ancestor):
232 c
232 c
233 resolving manifests
233 resolving manifests
234 branchmerge: True, force: True, partial: False
234 branchmerge: True, force: True, partial: False
235 ancestor: 4c60f11aa304, local: 1905859650ec+, remote: 9c233e8e184d
235 ancestor: 4c60f11aa304, local: 1905859650ec+, remote: 9c233e8e184d
236 preserving e for resolve of e
236 preserving e for resolve of e
237 d: remote is newer -> g
237 d: remote is newer -> g
238 getting d
238 getting d
239 e: versions differ -> m (premerge)
239 e: versions differ -> m (premerge)
240 picked tool ':merge' for e (binary False symlink False changedelete False)
240 picked tool ':merge' for e (binary False symlink False changedelete False)
241 merging e
241 merging e
242 my e@1905859650ec+ other e@9c233e8e184d ancestor e@4c60f11aa304
242 my e@1905859650ec+ other e@9c233e8e184d ancestor e@4c60f11aa304
243 e: versions differ -> m (merge)
243 e: versions differ -> m (merge)
244 picked tool ':merge' for e (binary False symlink False changedelete False)
244 picked tool ':merge' for e (binary False symlink False changedelete False)
245 my e@1905859650ec+ other e@9c233e8e184d ancestor e@4c60f11aa304
245 my e@1905859650ec+ other e@9c233e8e184d ancestor e@4c60f11aa304
246 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
246 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
247 abort: unresolved conflicts, can't continue
247 abort: unresolved conflicts, can't continue
248 (use 'hg resolve' and 'hg graft --continue')
248 (use 'hg resolve' and 'hg graft --continue')
249 [255]
249 [255]
250
250
251 Summary should mention graft:
251 Summary should mention graft:
252
252
253 $ hg summary |grep graft
253 $ hg summary |grep graft
254 commit: 2 modified, 2 unknown, 1 unresolved (graft in progress)
254 commit: 2 modified, 2 unknown, 1 unresolved (graft in progress)
255
255
256 Using status to get more context
256 Using status to get more context
257
257
258 $ hg status --verbose
258 $ hg status --verbose
259 M d
259 M d
260 M e
260 M e
261 ? a.orig
261 ? a.orig
262 ? e.orig
262 ? e.orig
263 # The repository is in an unfinished *graft* state.
263 # The repository is in an unfinished *graft* state.
264
264
265 # Unresolved merge conflicts:
265 # Unresolved merge conflicts:
266 #
266 #
267 # e
267 # e
268 #
268 #
269 # To mark files as resolved: hg resolve --mark FILE
269 # To mark files as resolved: hg resolve --mark FILE
270
270
271 # To continue: hg graft --continue
271 # To continue: hg graft --continue
272 # To abort: hg graft --abort
272 # To abort: hg graft --abort
273
273
274
274
275 Commit while interrupted should fail:
275 Commit while interrupted should fail:
276
276
277 $ hg ci -m 'commit interrupted graft'
277 $ hg ci -m 'commit interrupted graft'
278 abort: graft in progress
278 abort: graft in progress
279 (use 'hg graft --continue' or 'hg graft --stop' to stop)
279 (use 'hg graft --continue' or 'hg graft --stop' to stop)
280 [255]
280 [255]
281
281
282 Abort the graft and try committing:
282 Abort the graft and try committing:
283
283
284 $ hg up -C .
284 $ hg up -C .
285 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
285 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
286 $ echo c >> e
286 $ echo c >> e
287 $ hg ci -mtest
287 $ hg ci -mtest
288
288
289 $ hg strip . --config extensions.strip=
289 $ hg strip . --config extensions.strip=
290 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
290 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
291 saved backup bundle to $TESTTMP/a/.hg/strip-backup/*-backup.hg (glob)
291 saved backup bundle to $TESTTMP/a/.hg/strip-backup/*-backup.hg (glob)
292
292
293 Graft again:
293 Graft again:
294
294
295 $ hg graft 1 5 4 3 'merge()' 2
295 $ hg graft 1 5 4 3 'merge()' 2
296 skipping ungraftable merge revision 6
296 skipping ungraftable merge revision 6
297 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
297 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
298 skipping revision 1:5d205f8b35b6 (already grafted to 8:6b9e5368ca4e)
298 skipping revision 1:5d205f8b35b6 (already grafted to 8:6b9e5368ca4e)
299 skipping revision 5:97f8bfe72746 (already grafted to 9:1905859650ec)
299 skipping revision 5:97f8bfe72746 (already grafted to 9:1905859650ec)
300 grafting 4:9c233e8e184d "4"
300 grafting 4:9c233e8e184d "4"
301 merging e
301 merging e
302 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
302 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
303 abort: unresolved conflicts, can't continue
303 abort: unresolved conflicts, can't continue
304 (use 'hg resolve' and 'hg graft --continue')
304 (use 'hg resolve' and 'hg graft --continue')
305 [255]
305 [255]
306
306
307 Continue without resolve should fail:
307 Continue without resolve should fail:
308
308
309 $ hg graft -c
309 $ hg graft -c
310 grafting 4:9c233e8e184d "4"
310 grafting 4:9c233e8e184d "4"
311 abort: unresolved merge conflicts (see 'hg help resolve')
311 abort: unresolved merge conflicts (see 'hg help resolve')
312 [255]
312 [255]
313
313
314 Fix up:
314 Fix up:
315
315
316 $ echo b > e
316 $ echo b > e
317 $ hg resolve -m e
317 $ hg resolve -m e
318 (no more unresolved files)
318 (no more unresolved files)
319 continue: hg graft --continue
319 continue: hg graft --continue
320
320
321 Continue with a revision should fail:
321 Continue with a revision should fail:
322
322
323 $ hg graft -c 6
323 $ hg graft -c 6
324 abort: can't specify --continue and revisions
324 abort: can't specify --continue and revisions
325 [255]
325 [255]
326
326
327 $ hg graft -c -r 6
327 $ hg graft -c -r 6
328 abort: can't specify --continue and revisions
328 abort: can't specify --continue and revisions
329 [255]
329 [255]
330
330
331 Continue for real, clobber usernames
331 Continue for real, clobber usernames
332
332
333 $ hg graft -c -U
333 $ hg graft -c -U
334 grafting 4:9c233e8e184d "4"
334 grafting 4:9c233e8e184d "4"
335 grafting 3:4c60f11aa304 "3"
335 grafting 3:4c60f11aa304 "3"
336
336
337 Compare with original:
337 Compare with original:
338
338
339 $ hg diff -r 6
339 $ hg diff -r 6
340 $ hg status --rev 0:. -C
340 $ hg status --rev 0:. -C
341 M d
341 M d
342 M e
342 M e
343 A b
343 A b
344 a
344 a
345 A c
345 A c
346 a
346 a
347 R a
347 R a
348
348
349 View graph:
349 View graph:
350
350
351 $ hg log -G --template '{author}@{rev}.{phase}: {desc}\n'
351 $ hg log -G --template '{author}@{rev}.{phase}: {desc}\n'
352 @ test@11.draft: 3
352 @ test@11.draft: 3
353 |
353 |
354 o test@10.draft: 4
354 o test@10.draft: 4
355 |
355 |
356 o test@9.draft: 5
356 o test@9.draft: 5
357 |
357 |
358 o bar@8.draft: 1
358 o bar@8.draft: 1
359 |
359 |
360 o foo@7.draft: 2
360 o foo@7.draft: 2
361 |
361 |
362 | o test@6.secret: 6
362 | o test@6.secret: 6
363 | |\
363 | |\
364 | | o test@5.draft: 5
364 | | o test@5.draft: 5
365 | | |
365 | | |
366 | o | test@4.draft: 4
366 | o | test@4.draft: 4
367 | |/
367 | |/
368 | o baz@3.public: 3
368 | o baz@3.public: 3
369 | |
369 | |
370 | o test@2.public: 2
370 | o test@2.public: 2
371 | |
371 | |
372 | o bar@1.public: 1
372 | o bar@1.public: 1
373 |/
373 |/
374 o test@0.public: 0
374 o test@0.public: 0
375
375
376 Graft again onto another branch should preserve the original source
376 Graft again onto another branch should preserve the original source
377 $ hg up -q 0
377 $ hg up -q 0
378 $ echo 'g'>g
378 $ echo 'g'>g
379 $ hg add g
379 $ hg add g
380 $ hg ci -m 7
380 $ hg ci -m 7
381 created new head
381 created new head
382 $ hg graft 7
382 $ hg graft 7
383 grafting 7:ef0ef43d49e7 "2"
383 grafting 7:ef0ef43d49e7 "2"
384
384
385 $ hg log -r 7 --template '{rev}:{node}\n'
385 $ hg log -r 7 --template '{rev}:{node}\n'
386 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82
386 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82
387 $ hg log -r 2 --template '{rev}:{node}\n'
387 $ hg log -r 2 --template '{rev}:{node}\n'
388 2:5c095ad7e90f871700f02dd1fa5012cb4498a2d4
388 2:5c095ad7e90f871700f02dd1fa5012cb4498a2d4
389
389
390 $ hg log --debug -r tip
390 $ hg log --debug -r tip
391 changeset: 13:7a4785234d87ec1aa420ed6b11afe40fa73e12a9
391 changeset: 13:7a4785234d87ec1aa420ed6b11afe40fa73e12a9
392 tag: tip
392 tag: tip
393 phase: draft
393 phase: draft
394 parent: 12:b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
394 parent: 12:b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
395 parent: -1:0000000000000000000000000000000000000000
395 parent: -1:0000000000000000000000000000000000000000
396 manifest: 13:dc313617b8c32457c0d589e0dbbedfe71f3cd637
396 manifest: 13:dc313617b8c32457c0d589e0dbbedfe71f3cd637
397 user: foo
397 user: foo
398 date: Thu Jan 01 00:00:00 1970 +0000
398 date: Thu Jan 01 00:00:00 1970 +0000
399 files+: b
399 files+: b
400 files-: a
400 files-: a
401 extra: branch=default
401 extra: branch=default
402 extra: intermediate-source=ef0ef43d49e79e81ddafdc7997401ba0041efc82
402 extra: intermediate-source=ef0ef43d49e79e81ddafdc7997401ba0041efc82
403 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
403 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
404 description:
404 description:
405 2
405 2
406
406
407
407
408 Disallow grafting an already grafted cset onto its original branch
408 Disallow grafting an already grafted cset onto its original branch
409 $ hg up -q 6
409 $ hg up -q 6
410 $ hg graft 7
410 $ hg graft 7
411 skipping already grafted revision 7:ef0ef43d49e7 (was grafted from 2:5c095ad7e90f)
411 skipping already grafted revision 7:ef0ef43d49e7 (was grafted from 2:5c095ad7e90f)
412 [255]
412 [255]
413
413
414 $ hg pdiff --config extensions.extdiff= --patch -r 2 -r 13
414 $ hg pdiff --config extensions.extdiff= --patch -r 2 -r 13
415 --- */hg-5c095ad7e90f.patch * (glob)
415 --- */hg-5c095ad7e90f.patch * (glob)
416 +++ */hg-7a4785234d87.patch * (glob)
416 +++ */hg-7a4785234d87.patch * (glob)
417 @@ -1,18 +1,18 @@
417 @@ -1,18 +1,18 @@
418 # HG changeset patch
418 # HG changeset patch
419 -# User test
419 -# User test
420 +# User foo
420 +# User foo
421 # Date 0 0
421 # Date 0 0
422 # Thu Jan 01 00:00:00 1970 +0000
422 # Thu Jan 01 00:00:00 1970 +0000
423 -# Node ID 5c095ad7e90f871700f02dd1fa5012cb4498a2d4
423 -# Node ID 5c095ad7e90f871700f02dd1fa5012cb4498a2d4
424 -# Parent 5d205f8b35b66bc36375c9534ffd3237730e8f04
424 -# Parent 5d205f8b35b66bc36375c9534ffd3237730e8f04
425 +# Node ID 7a4785234d87ec1aa420ed6b11afe40fa73e12a9
425 +# Node ID 7a4785234d87ec1aa420ed6b11afe40fa73e12a9
426 +# Parent b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
426 +# Parent b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
427 2
427 2
428
428
429 -diff -r 5d205f8b35b6 -r 5c095ad7e90f a
429 -diff -r 5d205f8b35b6 -r 5c095ad7e90f a
430 +diff -r b592ea63bb0c -r 7a4785234d87 a
430 +diff -r b592ea63bb0c -r 7a4785234d87 a
431 --- a/a Thu Jan 01 00:00:00 1970 +0000
431 --- a/a Thu Jan 01 00:00:00 1970 +0000
432 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
432 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
433 @@ -1,1 +0,0 @@
433 @@ -1,1 +0,0 @@
434 --b
434 --b
435 -diff -r 5d205f8b35b6 -r 5c095ad7e90f b
435 -diff -r 5d205f8b35b6 -r 5c095ad7e90f b
436 +-a
436 +-a
437 +diff -r b592ea63bb0c -r 7a4785234d87 b
437 +diff -r b592ea63bb0c -r 7a4785234d87 b
438 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
438 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
439 +++ b/b Thu Jan 01 00:00:00 1970 +0000
439 +++ b/b Thu Jan 01 00:00:00 1970 +0000
440 @@ -0,0 +1,1 @@
440 @@ -0,0 +1,1 @@
441 -+b
441 -+b
442 ++a
442 ++a
443 [1]
443 [1]
444
444
445 $ hg pdiff --config extensions.extdiff= --patch -r 2 -r 13 -X .
445 $ hg pdiff --config extensions.extdiff= --patch -r 2 -r 13 -X .
446 --- */hg-5c095ad7e90f.patch * (glob)
446 --- */hg-5c095ad7e90f.patch * (glob)
447 +++ */hg-7a4785234d87.patch * (glob)
447 +++ */hg-7a4785234d87.patch * (glob)
448 @@ -1,8 +1,8 @@
448 @@ -1,8 +1,8 @@
449 # HG changeset patch
449 # HG changeset patch
450 -# User test
450 -# User test
451 +# User foo
451 +# User foo
452 # Date 0 0
452 # Date 0 0
453 # Thu Jan 01 00:00:00 1970 +0000
453 # Thu Jan 01 00:00:00 1970 +0000
454 -# Node ID 5c095ad7e90f871700f02dd1fa5012cb4498a2d4
454 -# Node ID 5c095ad7e90f871700f02dd1fa5012cb4498a2d4
455 -# Parent 5d205f8b35b66bc36375c9534ffd3237730e8f04
455 -# Parent 5d205f8b35b66bc36375c9534ffd3237730e8f04
456 +# Node ID 7a4785234d87ec1aa420ed6b11afe40fa73e12a9
456 +# Node ID 7a4785234d87ec1aa420ed6b11afe40fa73e12a9
457 +# Parent b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
457 +# Parent b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
458 2
458 2
459
459
460 [1]
460 [1]
461
461
462 Disallow grafting already grafted csets with the same origin onto each other
462 Disallow grafting already grafted csets with the same origin onto each other
463 $ hg up -q 13
463 $ hg up -q 13
464 $ hg graft 2
464 $ hg graft 2
465 skipping revision 2:5c095ad7e90f (already grafted to 13:7a4785234d87)
465 skipping revision 2:5c095ad7e90f (already grafted to 13:7a4785234d87)
466 [255]
466 [255]
467 $ hg graft 7
467 $ hg graft 7
468 skipping already grafted revision 7:ef0ef43d49e7 (13:7a4785234d87 also has origin 2:5c095ad7e90f)
468 skipping already grafted revision 7:ef0ef43d49e7 (13:7a4785234d87 also has origin 2:5c095ad7e90f)
469 [255]
469 [255]
470
470
471 $ hg up -q 7
471 $ hg up -q 7
472 $ hg graft 2
472 $ hg graft 2
473 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
473 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
474 [255]
474 [255]
475 $ hg graft tip
475 $ hg graft tip
476 skipping already grafted revision 13:7a4785234d87 (7:ef0ef43d49e7 also has origin 2:5c095ad7e90f)
476 skipping already grafted revision 13:7a4785234d87 (7:ef0ef43d49e7 also has origin 2:5c095ad7e90f)
477 [255]
477 [255]
478
478
479 Graft with --log
479 Graft with --log
480
480
481 $ hg up -Cq 1
481 $ hg up -Cq 1
482 $ hg graft 3 --log -u foo
482 $ hg graft 3 --log -u foo
483 grafting 3:4c60f11aa304 "3"
483 grafting 3:4c60f11aa304 "3"
484 warning: can't find ancestor for 'c' copied from 'b'!
484 warning: can't find ancestor for 'c' copied from 'b'!
485 $ hg log --template '{rev}:{node|short} {parents} {desc}\n' -r tip
485 $ hg log --template '{rev}:{node|short} {parents} {desc}\n' -r tip
486 14:0c921c65ef1e 1:5d205f8b35b6 3
486 14:0c921c65ef1e 1:5d205f8b35b6 3
487 (grafted from 4c60f11aa304a54ae1c199feb94e7fc771e51ed8)
487 (grafted from 4c60f11aa304a54ae1c199feb94e7fc771e51ed8)
488
488
489 Resolve conflicted graft
489 Resolve conflicted graft
490 $ hg up -q 0
490 $ hg up -q 0
491 $ echo b > a
491 $ echo b > a
492 $ hg ci -m 8
492 $ hg ci -m 8
493 created new head
493 created new head
494 $ echo c > a
494 $ echo c > a
495 $ hg ci -m 9
495 $ hg ci -m 9
496 $ hg graft 1 --tool internal:fail
496 $ hg graft 1 --tool internal:fail
497 grafting 1:5d205f8b35b6 "1"
497 grafting 1:5d205f8b35b6 "1"
498 abort: unresolved conflicts, can't continue
498 abort: unresolved conflicts, can't continue
499 (use 'hg resolve' and 'hg graft --continue')
499 (use 'hg resolve' and 'hg graft --continue')
500 [255]
500 [255]
501 $ hg resolve --all
501 $ hg resolve --all
502 merging a
502 merging a
503 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
503 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
504 [1]
504 [1]
505 $ cat a
505 $ cat a
506 <<<<<<< local: aaa4406d4f0a - test: 9
506 <<<<<<< local: aaa4406d4f0a - test: 9
507 c
507 c
508 =======
508 =======
509 b
509 b
510 >>>>>>> graft: 5d205f8b35b6 - bar: 1
510 >>>>>>> graft: 5d205f8b35b6 - bar: 1
511 $ echo b > a
511 $ echo b > a
512 $ hg resolve -m a
512 $ hg resolve -m a
513 (no more unresolved files)
513 (no more unresolved files)
514 continue: hg graft --continue
514 continue: hg graft --continue
515 $ hg graft -c
515 $ hg graft -c
516 grafting 1:5d205f8b35b6 "1"
516 grafting 1:5d205f8b35b6 "1"
517 $ hg export tip --git
517 $ hg export tip --git
518 # HG changeset patch
518 # HG changeset patch
519 # User bar
519 # User bar
520 # Date 0 0
520 # Date 0 0
521 # Thu Jan 01 00:00:00 1970 +0000
521 # Thu Jan 01 00:00:00 1970 +0000
522 # Node ID f67661df0c4804d301f064f332b57e7d5ddaf2be
522 # Node ID f67661df0c4804d301f064f332b57e7d5ddaf2be
523 # Parent aaa4406d4f0ae9befd6e58c82ec63706460cbca6
523 # Parent aaa4406d4f0ae9befd6e58c82ec63706460cbca6
524 1
524 1
525
525
526 diff --git a/a b/a
526 diff --git a/a b/a
527 --- a/a
527 --- a/a
528 +++ b/a
528 +++ b/a
529 @@ -1,1 +1,1 @@
529 @@ -1,1 +1,1 @@
530 -c
530 -c
531 +b
531 +b
532
532
533 Resolve conflicted graft with rename
533 Resolve conflicted graft with rename
534 $ echo c > a
534 $ echo c > a
535 $ hg ci -m 10
535 $ hg ci -m 10
536 $ hg graft 2 --tool internal:fail
536 $ hg graft 2 --tool internal:fail
537 grafting 2:5c095ad7e90f "2"
537 grafting 2:5c095ad7e90f "2"
538 abort: unresolved conflicts, can't continue
538 abort: unresolved conflicts, can't continue
539 (use 'hg resolve' and 'hg graft --continue')
539 (use 'hg resolve' and 'hg graft --continue')
540 [255]
540 [255]
541 $ hg resolve --all
541 $ hg resolve --all
542 merging a and b to b
542 merging a and b to b
543 (no more unresolved files)
543 (no more unresolved files)
544 continue: hg graft --continue
544 continue: hg graft --continue
545 $ hg graft -c
545 $ hg graft -c
546 grafting 2:5c095ad7e90f "2"
546 grafting 2:5c095ad7e90f "2"
547 $ hg export tip --git
547 $ hg export tip --git
548 # HG changeset patch
548 # HG changeset patch
549 # User test
549 # User test
550 # Date 0 0
550 # Date 0 0
551 # Thu Jan 01 00:00:00 1970 +0000
551 # Thu Jan 01 00:00:00 1970 +0000
552 # Node ID 9627f653b421c61fc1ea4c4e366745070fa3d2bc
552 # Node ID 9627f653b421c61fc1ea4c4e366745070fa3d2bc
553 # Parent ee295f490a40b97f3d18dd4c4f1c8936c233b612
553 # Parent ee295f490a40b97f3d18dd4c4f1c8936c233b612
554 2
554 2
555
555
556 diff --git a/a b/b
556 diff --git a/a b/b
557 rename from a
557 rename from a
558 rename to b
558 rename to b
559
559
560 Test simple origin(), with and without args
560 Test simple origin(), with and without args
561 $ hg log -r 'origin()'
561 $ hg log -r 'origin()'
562 changeset: 1:5d205f8b35b6
562 changeset: 1:5d205f8b35b6
563 user: bar
563 user: bar
564 date: Thu Jan 01 00:00:00 1970 +0000
564 date: Thu Jan 01 00:00:00 1970 +0000
565 summary: 1
565 summary: 1
566
566
567 changeset: 2:5c095ad7e90f
567 changeset: 2:5c095ad7e90f
568 user: test
568 user: test
569 date: Thu Jan 01 00:00:00 1970 +0000
569 date: Thu Jan 01 00:00:00 1970 +0000
570 summary: 2
570 summary: 2
571
571
572 changeset: 3:4c60f11aa304
572 changeset: 3:4c60f11aa304
573 user: baz
573 user: baz
574 date: Thu Jan 01 00:00:00 1970 +0000
574 date: Thu Jan 01 00:00:00 1970 +0000
575 summary: 3
575 summary: 3
576
576
577 changeset: 4:9c233e8e184d
577 changeset: 4:9c233e8e184d
578 user: test
578 user: test
579 date: Thu Jan 01 00:00:00 1970 +0000
579 date: Thu Jan 01 00:00:00 1970 +0000
580 summary: 4
580 summary: 4
581
581
582 changeset: 5:97f8bfe72746
582 changeset: 5:97f8bfe72746
583 branch: stable
583 branch: stable
584 parent: 3:4c60f11aa304
584 parent: 3:4c60f11aa304
585 user: test
585 user: test
586 date: Thu Jan 01 00:00:00 1970 +0000
586 date: Thu Jan 01 00:00:00 1970 +0000
587 summary: 5
587 summary: 5
588
588
589 $ hg log -r 'origin(7)'
589 $ hg log -r 'origin(7)'
590 changeset: 2:5c095ad7e90f
590 changeset: 2:5c095ad7e90f
591 user: test
591 user: test
592 date: Thu Jan 01 00:00:00 1970 +0000
592 date: Thu Jan 01 00:00:00 1970 +0000
593 summary: 2
593 summary: 2
594
594
595 Now transplant a graft to test following through copies
595 Now transplant a graft to test following through copies
596 $ hg up -q 0
596 $ hg up -q 0
597 $ hg branch -q dev
597 $ hg branch -q dev
598 $ hg ci -qm "dev branch"
598 $ hg ci -qm "dev branch"
599 $ hg --config extensions.transplant= transplant -q 7
599 $ hg --config extensions.transplant= transplant -q 7
600 $ hg log -r 'origin(.)'
600 $ hg log -r 'origin(.)'
601 changeset: 2:5c095ad7e90f
601 changeset: 2:5c095ad7e90f
602 user: test
602 user: test
603 date: Thu Jan 01 00:00:00 1970 +0000
603 date: Thu Jan 01 00:00:00 1970 +0000
604 summary: 2
604 summary: 2
605
605
606 Test that the graft and transplant markers in extra are converted, allowing
606 Test that the graft and transplant markers in extra are converted, allowing
607 origin() to still work. Note that these recheck the immediately preceeding two
607 origin() to still work. Note that these recheck the immediately preceeding two
608 tests.
608 tests.
609 $ hg --quiet --config extensions.convert= --config convert.hg.saverev=True convert . ../converted
609 $ hg --quiet --config extensions.convert= --config convert.hg.saverev=True convert . ../converted
610
610
611 The graft case
611 The graft case
612 $ hg -R ../converted log -r 7 --template "{rev}: {node}\n{join(extras, '\n')}\n"
612 $ hg -R ../converted log -r 7 --template "{rev}: {node}\n{join(extras, '\n')}\n"
613 7: 7ae846e9111fc8f57745634250c7b9ac0a60689b
613 7: 7ae846e9111fc8f57745634250c7b9ac0a60689b
614 branch=default
614 branch=default
615 convert_revision=ef0ef43d49e79e81ddafdc7997401ba0041efc82
615 convert_revision=ef0ef43d49e79e81ddafdc7997401ba0041efc82
616 source=e0213322b2c1a5d5d236c74e79666441bee67a7d
616 source=e0213322b2c1a5d5d236c74e79666441bee67a7d
617 $ hg -R ../converted log -r 'origin(7)'
617 $ hg -R ../converted log -r 'origin(7)'
618 changeset: 2:e0213322b2c1
618 changeset: 2:e0213322b2c1
619 user: test
619 user: test
620 date: Thu Jan 01 00:00:00 1970 +0000
620 date: Thu Jan 01 00:00:00 1970 +0000
621 summary: 2
621 summary: 2
622
622
623 Test that template correctly expands more than one 'extra' (issue4362), and that
623 Test that template correctly expands more than one 'extra' (issue4362), and that
624 'intermediate-source' is converted.
624 'intermediate-source' is converted.
625 $ hg -R ../converted log -r 13 --template "{extras % ' Extra: {extra}\n'}"
625 $ hg -R ../converted log -r 13 --template "{extras % ' Extra: {extra}\n'}"
626 Extra: branch=default
626 Extra: branch=default
627 Extra: convert_revision=7a4785234d87ec1aa420ed6b11afe40fa73e12a9
627 Extra: convert_revision=7a4785234d87ec1aa420ed6b11afe40fa73e12a9
628 Extra: intermediate-source=7ae846e9111fc8f57745634250c7b9ac0a60689b
628 Extra: intermediate-source=7ae846e9111fc8f57745634250c7b9ac0a60689b
629 Extra: source=e0213322b2c1a5d5d236c74e79666441bee67a7d
629 Extra: source=e0213322b2c1a5d5d236c74e79666441bee67a7d
630
630
631 The transplant case
631 The transplant case
632 $ hg -R ../converted log -r tip --template "{rev}: {node}\n{join(extras, '\n')}\n"
632 $ hg -R ../converted log -r tip --template "{rev}: {node}\n{join(extras, '\n')}\n"
633 21: fbb6c5cc81002f2b4b49c9d731404688bcae5ade
633 21: fbb6c5cc81002f2b4b49c9d731404688bcae5ade
634 branch=dev
634 branch=dev
635 convert_revision=7e61b508e709a11d28194a5359bc3532d910af21
635 convert_revision=7e61b508e709a11d28194a5359bc3532d910af21
636 transplant_source=z\xe8F\xe9\x11\x1f\xc8\xf5wEcBP\xc7\xb9\xac\n`h\x9b
636 transplant_source=z\xe8F\xe9\x11\x1f\xc8\xf5wEcBP\xc7\xb9\xac\n`h\x9b
637 $ hg -R ../converted log -r 'origin(tip)'
637 $ hg -R ../converted log -r 'origin(tip)'
638 changeset: 2:e0213322b2c1
638 changeset: 2:e0213322b2c1
639 user: test
639 user: test
640 date: Thu Jan 01 00:00:00 1970 +0000
640 date: Thu Jan 01 00:00:00 1970 +0000
641 summary: 2
641 summary: 2
642
642
643
643
644 Test simple destination
644 Test simple destination
645 $ hg log -r 'destination()'
645 $ hg log -r 'destination()'
646 changeset: 7:ef0ef43d49e7
646 changeset: 7:ef0ef43d49e7
647 parent: 0:68795b066622
647 parent: 0:68795b066622
648 user: foo
648 user: foo
649 date: Thu Jan 01 00:00:00 1970 +0000
649 date: Thu Jan 01 00:00:00 1970 +0000
650 summary: 2
650 summary: 2
651
651
652 changeset: 8:6b9e5368ca4e
652 changeset: 8:6b9e5368ca4e
653 user: bar
653 user: bar
654 date: Thu Jan 01 00:00:00 1970 +0000
654 date: Thu Jan 01 00:00:00 1970 +0000
655 summary: 1
655 summary: 1
656
656
657 changeset: 9:1905859650ec
657 changeset: 9:1905859650ec
658 user: test
658 user: test
659 date: Thu Jan 01 00:00:00 1970 +0000
659 date: Thu Jan 01 00:00:00 1970 +0000
660 summary: 5
660 summary: 5
661
661
662 changeset: 10:52dc0b4c6907
662 changeset: 10:52dc0b4c6907
663 user: test
663 user: test
664 date: Thu Jan 01 00:00:00 1970 +0000
664 date: Thu Jan 01 00:00:00 1970 +0000
665 summary: 4
665 summary: 4
666
666
667 changeset: 11:882b35362a6b
667 changeset: 11:882b35362a6b
668 user: test
668 user: test
669 date: Thu Jan 01 00:00:00 1970 +0000
669 date: Thu Jan 01 00:00:00 1970 +0000
670 summary: 3
670 summary: 3
671
671
672 changeset: 13:7a4785234d87
672 changeset: 13:7a4785234d87
673 user: foo
673 user: foo
674 date: Thu Jan 01 00:00:00 1970 +0000
674 date: Thu Jan 01 00:00:00 1970 +0000
675 summary: 2
675 summary: 2
676
676
677 changeset: 14:0c921c65ef1e
677 changeset: 14:0c921c65ef1e
678 parent: 1:5d205f8b35b6
678 parent: 1:5d205f8b35b6
679 user: foo
679 user: foo
680 date: Thu Jan 01 00:00:00 1970 +0000
680 date: Thu Jan 01 00:00:00 1970 +0000
681 summary: 3
681 summary: 3
682
682
683 changeset: 17:f67661df0c48
683 changeset: 17:f67661df0c48
684 user: bar
684 user: bar
685 date: Thu Jan 01 00:00:00 1970 +0000
685 date: Thu Jan 01 00:00:00 1970 +0000
686 summary: 1
686 summary: 1
687
687
688 changeset: 19:9627f653b421
688 changeset: 19:9627f653b421
689 user: test
689 user: test
690 date: Thu Jan 01 00:00:00 1970 +0000
690 date: Thu Jan 01 00:00:00 1970 +0000
691 summary: 2
691 summary: 2
692
692
693 changeset: 21:7e61b508e709
693 changeset: 21:7e61b508e709
694 branch: dev
694 branch: dev
695 tag: tip
695 tag: tip
696 user: foo
696 user: foo
697 date: Thu Jan 01 00:00:00 1970 +0000
697 date: Thu Jan 01 00:00:00 1970 +0000
698 summary: 2
698 summary: 2
699
699
700 $ hg log -r 'destination(2)'
700 $ hg log -r 'destination(2)'
701 changeset: 7:ef0ef43d49e7
701 changeset: 7:ef0ef43d49e7
702 parent: 0:68795b066622
702 parent: 0:68795b066622
703 user: foo
703 user: foo
704 date: Thu Jan 01 00:00:00 1970 +0000
704 date: Thu Jan 01 00:00:00 1970 +0000
705 summary: 2
705 summary: 2
706
706
707 changeset: 13:7a4785234d87
707 changeset: 13:7a4785234d87
708 user: foo
708 user: foo
709 date: Thu Jan 01 00:00:00 1970 +0000
709 date: Thu Jan 01 00:00:00 1970 +0000
710 summary: 2
710 summary: 2
711
711
712 changeset: 19:9627f653b421
712 changeset: 19:9627f653b421
713 user: test
713 user: test
714 date: Thu Jan 01 00:00:00 1970 +0000
714 date: Thu Jan 01 00:00:00 1970 +0000
715 summary: 2
715 summary: 2
716
716
717 changeset: 21:7e61b508e709
717 changeset: 21:7e61b508e709
718 branch: dev
718 branch: dev
719 tag: tip
719 tag: tip
720 user: foo
720 user: foo
721 date: Thu Jan 01 00:00:00 1970 +0000
721 date: Thu Jan 01 00:00:00 1970 +0000
722 summary: 2
722 summary: 2
723
723
724 Transplants of grafts can find a destination...
724 Transplants of grafts can find a destination...
725 $ hg log -r 'destination(7)'
725 $ hg log -r 'destination(7)'
726 changeset: 21:7e61b508e709
726 changeset: 21:7e61b508e709
727 branch: dev
727 branch: dev
728 tag: tip
728 tag: tip
729 user: foo
729 user: foo
730 date: Thu Jan 01 00:00:00 1970 +0000
730 date: Thu Jan 01 00:00:00 1970 +0000
731 summary: 2
731 summary: 2
732
732
733 ... grafts of grafts unfortunately can't
733 ... grafts of grafts unfortunately can't
734 $ hg graft -q 13 --debug
734 $ hg graft -q 13 --debug
735 scanning for duplicate grafts
735 scanning for duplicate grafts
736 grafting 13:7a4785234d87 "2"
736 grafting 13:7a4785234d87 "2"
737 searching for copies back to rev 12
737 searching for copies back to rev 12
738 unmatched files in other (from topological common ancestor):
738 unmatched files in other (from topological common ancestor):
739 g
739 g
740 unmatched files new in both:
740 unmatched files new in both:
741 b
741 b
742 resolving manifests
742 resolving manifests
743 branchmerge: True, force: True, partial: False
743 branchmerge: True, force: True, partial: False
744 ancestor: b592ea63bb0c, local: 7e61b508e709+, remote: 7a4785234d87
744 ancestor: b592ea63bb0c, local: 7e61b508e709+, remote: 7a4785234d87
745 starting 4 threads for background file closing (?)
745 starting 4 threads for background file closing (?)
746 committing files:
746 committing files:
747 b
747 b
748 warning: can't find ancestor for 'b' copied from 'a'!
748 warning: can't find ancestor for 'b' copied from 'a'!
749 reusing manifest form p1 (listed files actually unchanged)
749 reusing manifest form p1 (listed files actually unchanged)
750 committing changelog
750 committing changelog
751 updating the branch cache
751 updating the branch cache
752 $ hg log -r 'destination(13)'
752 $ hg log -r 'destination(13)'
753 All copies of a cset
753 All copies of a cset
754 $ hg log -r 'origin(13) or destination(origin(13))'
754 $ hg log -r 'origin(13) or destination(origin(13))'
755 changeset: 2:5c095ad7e90f
755 changeset: 2:5c095ad7e90f
756 user: test
756 user: test
757 date: Thu Jan 01 00:00:00 1970 +0000
757 date: Thu Jan 01 00:00:00 1970 +0000
758 summary: 2
758 summary: 2
759
759
760 changeset: 7:ef0ef43d49e7
760 changeset: 7:ef0ef43d49e7
761 parent: 0:68795b066622
761 parent: 0:68795b066622
762 user: foo
762 user: foo
763 date: Thu Jan 01 00:00:00 1970 +0000
763 date: Thu Jan 01 00:00:00 1970 +0000
764 summary: 2
764 summary: 2
765
765
766 changeset: 13:7a4785234d87
766 changeset: 13:7a4785234d87
767 user: foo
767 user: foo
768 date: Thu Jan 01 00:00:00 1970 +0000
768 date: Thu Jan 01 00:00:00 1970 +0000
769 summary: 2
769 summary: 2
770
770
771 changeset: 19:9627f653b421
771 changeset: 19:9627f653b421
772 user: test
772 user: test
773 date: Thu Jan 01 00:00:00 1970 +0000
773 date: Thu Jan 01 00:00:00 1970 +0000
774 summary: 2
774 summary: 2
775
775
776 changeset: 21:7e61b508e709
776 changeset: 21:7e61b508e709
777 branch: dev
777 branch: dev
778 user: foo
778 user: foo
779 date: Thu Jan 01 00:00:00 1970 +0000
779 date: Thu Jan 01 00:00:00 1970 +0000
780 summary: 2
780 summary: 2
781
781
782 changeset: 22:3a4e92d81b97
782 changeset: 22:3a4e92d81b97
783 branch: dev
783 branch: dev
784 tag: tip
784 tag: tip
785 user: foo
785 user: foo
786 date: Thu Jan 01 00:00:00 1970 +0000
786 date: Thu Jan 01 00:00:00 1970 +0000
787 summary: 2
787 summary: 2
788
788
789
789
790 graft works on complex revset
790 graft works on complex revset
791
791
792 $ hg graft 'origin(13) or destination(origin(13))'
792 $ hg graft 'origin(13) or destination(origin(13))'
793 skipping ancestor revision 21:7e61b508e709
793 skipping ancestor revision 21:7e61b508e709
794 skipping ancestor revision 22:3a4e92d81b97
794 skipping ancestor revision 22:3a4e92d81b97
795 skipping revision 2:5c095ad7e90f (already grafted to 22:3a4e92d81b97)
795 skipping revision 2:5c095ad7e90f (already grafted to 22:3a4e92d81b97)
796 grafting 7:ef0ef43d49e7 "2"
796 grafting 7:ef0ef43d49e7 "2"
797 warning: can't find ancestor for 'b' copied from 'a'!
797 warning: can't find ancestor for 'b' copied from 'a'!
798 grafting 13:7a4785234d87 "2"
798 grafting 13:7a4785234d87 "2"
799 warning: can't find ancestor for 'b' copied from 'a'!
799 warning: can't find ancestor for 'b' copied from 'a'!
800 grafting 19:9627f653b421 "2"
800 grafting 19:9627f653b421 "2"
801 merging b
801 merging b
802 warning: can't find ancestor for 'b' copied from 'a'!
802 warning: can't find ancestor for 'b' copied from 'a'!
803
803
804 graft with --force (still doesn't graft merges)
804 graft with --force (still doesn't graft merges)
805
805
806 $ hg graft 19 0 6
806 $ hg graft 19 0 6
807 skipping ungraftable merge revision 6
807 skipping ungraftable merge revision 6
808 skipping ancestor revision 0:68795b066622
808 skipping ancestor revision 0:68795b066622
809 skipping already grafted revision 19:9627f653b421 (22:3a4e92d81b97 also has origin 2:5c095ad7e90f)
809 skipping already grafted revision 19:9627f653b421 (22:3a4e92d81b97 also has origin 2:5c095ad7e90f)
810 [255]
810 [255]
811 $ hg graft 19 0 6 --force
811 $ hg graft 19 0 6 --force
812 skipping ungraftable merge revision 6
812 skipping ungraftable merge revision 6
813 grafting 19:9627f653b421 "2"
813 grafting 19:9627f653b421 "2"
814 merging b
814 merging b
815 warning: can't find ancestor for 'b' copied from 'a'!
815 warning: can't find ancestor for 'b' copied from 'a'!
816 grafting 0:68795b066622 "0"
816 grafting 0:68795b066622 "0"
817
817
818 graft --force after backout
818 graft --force after backout
819
819
820 $ echo abc > a
820 $ echo abc > a
821 $ hg ci -m 28
821 $ hg ci -m 28
822 $ hg backout 28
822 $ hg backout 28
823 reverting a
823 reverting a
824 changeset 29:9d95e865b00c backs out changeset 28:cc20d29aec8d
824 changeset 29:9d95e865b00c backs out changeset 28:cc20d29aec8d
825 $ hg graft 28
825 $ hg graft 28
826 skipping ancestor revision 28:cc20d29aec8d
826 skipping ancestor revision 28:cc20d29aec8d
827 [255]
827 [255]
828 $ hg graft 28 --force
828 $ hg graft 28 --force
829 grafting 28:cc20d29aec8d "28"
829 grafting 28:cc20d29aec8d "28"
830 merging a
830 merging a
831 $ cat a
831 $ cat a
832 abc
832 abc
833
833
834 graft --continue after --force
834 graft --continue after --force
835
835
836 $ echo def > a
836 $ echo def > a
837 $ hg ci -m 31
837 $ hg ci -m 31
838 $ hg graft 28 --force --tool internal:fail
838 $ hg graft 28 --force --tool internal:fail
839 grafting 28:cc20d29aec8d "28"
839 grafting 28:cc20d29aec8d "28"
840 abort: unresolved conflicts, can't continue
840 abort: unresolved conflicts, can't continue
841 (use 'hg resolve' and 'hg graft --continue')
841 (use 'hg resolve' and 'hg graft --continue')
842 [255]
842 [255]
843 $ hg resolve --all
843 $ hg resolve --all
844 merging a
844 merging a
845 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
845 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
846 [1]
846 [1]
847 $ echo abc > a
847 $ echo abc > a
848 $ hg resolve -m a
848 $ hg resolve -m a
849 (no more unresolved files)
849 (no more unresolved files)
850 continue: hg graft --continue
850 continue: hg graft --continue
851 $ hg graft -c
851 $ hg graft -c
852 grafting 28:cc20d29aec8d "28"
852 grafting 28:cc20d29aec8d "28"
853 $ cat a
853 $ cat a
854 abc
854 abc
855
855
856 Continue testing same origin policy, using revision numbers from test above
856 Continue testing same origin policy, using revision numbers from test above
857 but do some destructive editing of the repo:
857 but do some destructive editing of the repo:
858
858
859 $ hg up -qC 7
859 $ hg up -qC 7
860 $ hg tag -l -r 13 tmp
860 $ hg tag -l -r 13 tmp
861 $ hg --config extensions.strip= strip 2
861 $ hg --config extensions.strip= strip 2
862 saved backup bundle to $TESTTMP/a/.hg/strip-backup/5c095ad7e90f-d323a1e4-backup.hg
862 saved backup bundle to $TESTTMP/a/.hg/strip-backup/5c095ad7e90f-d323a1e4-backup.hg
863 $ hg graft tmp
863 $ hg graft tmp
864 skipping already grafted revision 8:7a4785234d87 (2:ef0ef43d49e7 also has unknown origin 5c095ad7e90f)
864 skipping already grafted revision 8:7a4785234d87 (2:ef0ef43d49e7 also has unknown origin 5c095ad7e90f)
865 [255]
865 [255]
866
866
867 Empty graft
867 Empty graft
868
868
869 $ hg up -qr 26
869 $ hg up -qr 26
870 $ hg tag -f something
870 $ hg tag -f something
871 $ hg graft -qr 27
871 $ hg graft -qr 27
872 $ hg graft -f 27
872 $ hg graft -f 27
873 grafting 27:17d42b8f5d50 "28"
873 grafting 27:17d42b8f5d50 "28"
874 note: graft of 27:17d42b8f5d50 created no changes to commit
874 note: graft of 27:17d42b8f5d50 created no changes to commit
875
875
876 $ cd ..
876 $ cd ..
877
877
878 Graft to duplicate a commit
878 Graft to duplicate a commit
879
879
880 $ hg init graftsibling
880 $ hg init graftsibling
881 $ cd graftsibling
881 $ cd graftsibling
882 $ touch a
882 $ touch a
883 $ hg commit -qAm a
883 $ hg commit -qAm a
884 $ touch b
884 $ touch b
885 $ hg commit -qAm b
885 $ hg commit -qAm b
886 $ hg log -G -T '{rev}\n'
886 $ hg log -G -T '{rev}\n'
887 @ 1
887 @ 1
888 |
888 |
889 o 0
889 o 0
890
890
891 $ hg up -q 0
891 $ hg up -q 0
892 $ hg graft -r 1
892 $ hg graft -r 1
893 grafting 1:0e067c57feba "b" (tip)
893 grafting 1:0e067c57feba "b" (tip)
894 $ hg log -G -T '{rev}\n'
894 $ hg log -G -T '{rev}\n'
895 @ 2
895 @ 2
896 |
896 |
897 | o 1
897 | o 1
898 |/
898 |/
899 o 0
899 o 0
900
900
901 Graft to duplicate a commit twice
901 Graft to duplicate a commit twice
902
902
903 $ hg up -q 0
903 $ hg up -q 0
904 $ hg graft -r 2
904 $ hg graft -r 2
905 grafting 2:044ec77f6389 "b" (tip)
905 grafting 2:044ec77f6389 "b" (tip)
906 $ hg log -G -T '{rev}\n'
906 $ hg log -G -T '{rev}\n'
907 @ 3
907 @ 3
908 |
908 |
909 | o 2
909 | o 2
910 |/
910 |/
911 | o 1
911 | o 1
912 |/
912 |/
913 o 0
913 o 0
914
914
915 Graft from behind a move or rename
915 Graft from behind a move or rename
916 ==================================
916 ==================================
917
917
918 NOTE: This is affected by issue5343, and will need updating when it's fixed
918 NOTE: This is affected by issue5343, and will need updating when it's fixed
919
919
920 Possible cases during a regular graft (when ca is between cta and c2):
920 Possible cases during a regular graft (when ca is between cta and c2):
921
921
922 name | c1<-cta | cta<->ca | ca->c2
922 name | c1<-cta | cta<->ca | ca->c2
923 A.0 | | |
923 A.0 | | |
924 A.1 | X | |
924 A.1 | X | |
925 A.2 | | X |
925 A.2 | | X |
926 A.3 | | | X
926 A.3 | | | X
927 A.4 | X | X |
927 A.4 | X | X |
928 A.5 | X | | X
928 A.5 | X | | X
929 A.6 | | X | X
929 A.6 | | X | X
930 A.7 | X | X | X
930 A.7 | X | X | X
931
931
932 A.0 is trivial, and doesn't need copy tracking.
932 A.0 is trivial, and doesn't need copy tracking.
933 For A.1, a forward rename is recorded in the c1 pass, to be followed later.
933 For A.1, a forward rename is recorded in the c1 pass, to be followed later.
934 In A.2, the rename is recorded in the c2 pass and followed backwards.
934 In A.2, the rename is recorded in the c2 pass and followed backwards.
935 A.3 is recorded in the c2 pass as a forward rename to be duplicated on target.
935 A.3 is recorded in the c2 pass as a forward rename to be duplicated on target.
936 In A.4, both passes of checkcopies record incomplete renames, which are
936 In A.4, both passes of checkcopies record incomplete renames, which are
937 then joined in mergecopies to record a rename to be followed.
937 then joined in mergecopies to record a rename to be followed.
938 In A.5 and A.7, the c1 pass records an incomplete rename, while the c2 pass
938 In A.5 and A.7, the c1 pass records an incomplete rename, while the c2 pass
939 records an incomplete divergence. The incomplete rename is then joined to the
939 records an incomplete divergence. The incomplete rename is then joined to the
940 appropriate side of the incomplete divergence, and the result is recorded as a
940 appropriate side of the incomplete divergence, and the result is recorded as a
941 divergence. The code doesn't distinguish at all between these two cases, since
941 divergence. The code doesn't distinguish at all between these two cases, since
942 the end result of them is the same: an incomplete divergence joined with an
942 the end result of them is the same: an incomplete divergence joined with an
943 incomplete rename into a divergence.
943 incomplete rename into a divergence.
944 Finally, A.6 records a divergence entirely in the c2 pass.
944 Finally, A.6 records a divergence entirely in the c2 pass.
945
945
946 A.4 has a degenerate case a<-b<-a->a, where checkcopies isn't needed at all.
946 A.4 has a degenerate case a<-b<-a->a, where checkcopies isn't needed at all.
947 A.5 has a special case a<-b<-b->a, which is treated like a<-b->a in a merge.
947 A.5 has a special case a<-b<-b->a, which is treated like a<-b->a in a merge.
948 A.6 has a special case a<-a<-b->a. Here, checkcopies will find a spurious
948 A.6 has a special case a<-a<-b->a. Here, checkcopies will find a spurious
949 incomplete divergence, which is in fact complete. This is handled later in
949 incomplete divergence, which is in fact complete. This is handled later in
950 mergecopies.
950 mergecopies.
951 A.7 has 4 special cases: a<-b<-a->b (the "ping-pong" case), a<-b<-c->b,
951 A.7 has 4 special cases: a<-b<-a->b (the "ping-pong" case), a<-b<-c->b,
952 a<-b<-a->c and a<-b<-c->a. Of these, only the "ping-pong" case is interesting,
952 a<-b<-a->c and a<-b<-c->a. Of these, only the "ping-pong" case is interesting,
953 the others are fairly trivial (a<-b<-c->b and a<-b<-a->c proceed like the base
953 the others are fairly trivial (a<-b<-c->b and a<-b<-a->c proceed like the base
954 case, a<-b<-c->a is treated the same as a<-b<-b->a).
954 case, a<-b<-c->a is treated the same as a<-b<-b->a).
955
955
956 f5a therefore tests the "ping-pong" rename case, where a file is renamed to the
956 f5a therefore tests the "ping-pong" rename case, where a file is renamed to the
957 same name on both branches, then the rename is backed out on one branch, and
957 same name on both branches, then the rename is backed out on one branch, and
958 the backout is grafted to the other branch. This creates a challenging rename
958 the backout is grafted to the other branch. This creates a challenging rename
959 sequence of a<-b<-a->b in the graft target, topological CA, graft CA and graft
959 sequence of a<-b<-a->b in the graft target, topological CA, graft CA and graft
960 source, respectively. Since rename detection will run on the c1 side for such a
960 source, respectively. Since rename detection will run on the c1 side for such a
961 sequence (as for technical reasons, we split the c1 and c2 sides not at the
961 sequence (as for technical reasons, we split the c1 and c2 sides not at the
962 graft CA, but rather at the topological CA), it will pick up a false rename,
962 graft CA, but rather at the topological CA), it will pick up a false rename,
963 and cause a spurious merge conflict. This false rename is always exactly the
963 and cause a spurious merge conflict. This false rename is always exactly the
964 reverse of the true rename that would be detected on the c2 side, so we can
964 reverse of the true rename that would be detected on the c2 side, so we can
965 correct for it by detecting this condition and reversing as necessary.
965 correct for it by detecting this condition and reversing as necessary.
966
966
967 First, set up the repository with commits to be grafted
967 First, set up the repository with commits to be grafted
968
968
969 $ hg init ../graftmove
969 $ hg init ../graftmove
970 $ cd ../graftmove
970 $ cd ../graftmove
971 $ echo c1a > f1a
971 $ echo c1a > f1a
972 $ echo c2a > f2a
972 $ echo c2a > f2a
973 $ echo c3a > f3a
973 $ echo c3a > f3a
974 $ echo c4a > f4a
974 $ echo c4a > f4a
975 $ echo c5a > f5a
975 $ echo c5a > f5a
976 $ hg ci -qAm A0
976 $ hg ci -qAm A0
977 $ hg mv f1a f1b
977 $ hg mv f1a f1b
978 $ hg mv f3a f3b
978 $ hg mv f3a f3b
979 $ hg mv f5a f5b
979 $ hg mv f5a f5b
980 $ hg ci -qAm B0
980 $ hg ci -qAm B0
981 $ echo c1c > f1b
981 $ echo c1c > f1b
982 $ hg mv f2a f2c
982 $ hg mv f2a f2c
983 $ hg mv f5b f5a
983 $ hg mv f5b f5a
984 $ echo c5c > f5a
984 $ echo c5c > f5a
985 $ hg ci -qAm C0
985 $ hg ci -qAm C0
986 $ hg mv f3b f3d
986 $ hg mv f3b f3d
987 $ echo c4d > f4a
987 $ echo c4d > f4a
988 $ hg ci -qAm D0
988 $ hg ci -qAm D0
989 $ hg log -G
989 $ hg log -G
990 @ changeset: 3:b69f5839d2d9
990 @ changeset: 3:b69f5839d2d9
991 | tag: tip
991 | tag: tip
992 | user: test
992 | user: test
993 | date: Thu Jan 01 00:00:00 1970 +0000
993 | date: Thu Jan 01 00:00:00 1970 +0000
994 | summary: D0
994 | summary: D0
995 |
995 |
996 o changeset: 2:f58c7e2b28fa
996 o changeset: 2:f58c7e2b28fa
997 | user: test
997 | user: test
998 | date: Thu Jan 01 00:00:00 1970 +0000
998 | date: Thu Jan 01 00:00:00 1970 +0000
999 | summary: C0
999 | summary: C0
1000 |
1000 |
1001 o changeset: 1:3d7bba921b5d
1001 o changeset: 1:3d7bba921b5d
1002 | user: test
1002 | user: test
1003 | date: Thu Jan 01 00:00:00 1970 +0000
1003 | date: Thu Jan 01 00:00:00 1970 +0000
1004 | summary: B0
1004 | summary: B0
1005 |
1005 |
1006 o changeset: 0:11f7a1b56675
1006 o changeset: 0:11f7a1b56675
1007 user: test
1007 user: test
1008 date: Thu Jan 01 00:00:00 1970 +0000
1008 date: Thu Jan 01 00:00:00 1970 +0000
1009 summary: A0
1009 summary: A0
1010
1010
1011
1011
1012 Test the cases A.2 (f1x), A.3 (f2x) and a special case of A.6 (f5x) where the
1012 Test the cases A.2 (f1x), A.3 (f2x) and a special case of A.6 (f5x) where the
1013 two renames actually converge to the same name (thus no actual divergence).
1013 two renames actually converge to the same name (thus no actual divergence).
1014
1014
1015 $ hg up -q 'desc("A0")'
1015 $ hg up -q 'desc("A0")'
1016 $ HGEDITOR="echo C1 >" hg graft -r 'desc("C0")' --edit
1016 $ HGEDITOR="echo C1 >" hg graft -r 'desc("C0")' --edit
1017 grafting 2:f58c7e2b28fa "C0"
1017 grafting 2:f58c7e2b28fa "C0"
1018 merging f1a and f1b to f1a
1018 merging f1a and f1b to f1a
1019 merging f5a
1019 merging f5a
1020 warning: can't find ancestor for 'f5a' copied from 'f5b'!
1020 warning: can't find ancestor for 'f5a' copied from 'f5b'!
1021 $ hg status --change .
1021 $ hg status --change .
1022 M f1a
1022 M f1a
1023 M f5a
1023 M f5a
1024 A f2c
1024 A f2c
1025 R f2a
1025 R f2a
1026 $ hg cat f1a
1026 $ hg cat f1a
1027 c1c
1027 c1c
1028 $ hg cat f1b
1028 $ hg cat f1b
1029 f1b: no such file in rev c9763722f9bd
1029 f1b: no such file in rev c9763722f9bd
1030 [1]
1030 [1]
1031
1031
1032 Test the cases A.0 (f4x) and A.6 (f3x)
1032 Test the cases A.0 (f4x) and A.6 (f3x)
1033
1033
1034 $ HGEDITOR="echo D1 >" hg graft -r 'desc("D0")' --edit
1034 $ HGEDITOR="echo D1 >" hg graft -r 'desc("D0")' --edit
1035 grafting 3:b69f5839d2d9 "D0"
1035 grafting 3:b69f5839d2d9 "D0"
1036 note: possible conflict - f3b was renamed multiple times to:
1036 note: possible conflict - f3b was renamed multiple times to:
1037 f3d
1037 f3d
1038 f3a
1038 f3a
1039 warning: can't find ancestor for 'f3d' copied from 'f3b'!
1039 warning: can't find ancestor for 'f3d' copied from 'f3b'!
1040
1040
1041 Set up the repository for some further tests
1041 Set up the repository for some further tests
1042
1042
1043 $ hg up -q "min(desc("A0"))"
1043 $ hg up -q "min(desc("A0"))"
1044 $ hg mv f1a f1e
1044 $ hg mv f1a f1e
1045 $ echo c2e > f2a
1045 $ echo c2e > f2a
1046 $ hg mv f3a f3e
1046 $ hg mv f3a f3e
1047 $ hg mv f4a f4e
1047 $ hg mv f4a f4e
1048 $ hg mv f5a f5b
1048 $ hg mv f5a f5b
1049 $ hg ci -qAm "E0"
1049 $ hg ci -qAm "E0"
1050 $ hg log -G
1050 $ hg log -G
1051 @ changeset: 6:6bd1736cab86
1051 @ changeset: 6:6bd1736cab86
1052 | tag: tip
1052 | tag: tip
1053 | parent: 0:11f7a1b56675
1053 | parent: 0:11f7a1b56675
1054 | user: test
1054 | user: test
1055 | date: Thu Jan 01 00:00:00 1970 +0000
1055 | date: Thu Jan 01 00:00:00 1970 +0000
1056 | summary: E0
1056 | summary: E0
1057 |
1057 |
1058 | o changeset: 5:560daee679da
1058 | o changeset: 5:560daee679da
1059 | | user: test
1059 | | user: test
1060 | | date: Thu Jan 01 00:00:00 1970 +0000
1060 | | date: Thu Jan 01 00:00:00 1970 +0000
1061 | | summary: D1
1061 | | summary: D1
1062 | |
1062 | |
1063 | o changeset: 4:c9763722f9bd
1063 | o changeset: 4:c9763722f9bd
1064 |/ parent: 0:11f7a1b56675
1064 |/ parent: 0:11f7a1b56675
1065 | user: test
1065 | user: test
1066 | date: Thu Jan 01 00:00:00 1970 +0000
1066 | date: Thu Jan 01 00:00:00 1970 +0000
1067 | summary: C1
1067 | summary: C1
1068 |
1068 |
1069 | o changeset: 3:b69f5839d2d9
1069 | o changeset: 3:b69f5839d2d9
1070 | | user: test
1070 | | user: test
1071 | | date: Thu Jan 01 00:00:00 1970 +0000
1071 | | date: Thu Jan 01 00:00:00 1970 +0000
1072 | | summary: D0
1072 | | summary: D0
1073 | |
1073 | |
1074 | o changeset: 2:f58c7e2b28fa
1074 | o changeset: 2:f58c7e2b28fa
1075 | | user: test
1075 | | user: test
1076 | | date: Thu Jan 01 00:00:00 1970 +0000
1076 | | date: Thu Jan 01 00:00:00 1970 +0000
1077 | | summary: C0
1077 | | summary: C0
1078 | |
1078 | |
1079 | o changeset: 1:3d7bba921b5d
1079 | o changeset: 1:3d7bba921b5d
1080 |/ user: test
1080 |/ user: test
1081 | date: Thu Jan 01 00:00:00 1970 +0000
1081 | date: Thu Jan 01 00:00:00 1970 +0000
1082 | summary: B0
1082 | summary: B0
1083 |
1083 |
1084 o changeset: 0:11f7a1b56675
1084 o changeset: 0:11f7a1b56675
1085 user: test
1085 user: test
1086 date: Thu Jan 01 00:00:00 1970 +0000
1086 date: Thu Jan 01 00:00:00 1970 +0000
1087 summary: A0
1087 summary: A0
1088
1088
1089
1089
1090 Test the cases A.4 (f1x), the "ping-pong" special case of A.7 (f5x),
1090 Test the cases A.4 (f1x), the "ping-pong" special case of A.7 (f5x),
1091 and A.3 with a local content change to be preserved (f2x).
1091 and A.3 with a local content change to be preserved (f2x).
1092
1092
1093 $ HGEDITOR="echo C2 >" hg graft -r 'desc("C0")' --edit
1093 $ HGEDITOR="echo C2 >" hg graft -r 'desc("C0")' --edit
1094 grafting 2:f58c7e2b28fa "C0"
1094 grafting 2:f58c7e2b28fa "C0"
1095 merging f1e and f1b to f1e
1095 merging f1e and f1b to f1e
1096 merging f2a and f2c to f2c
1096 merging f2a and f2c to f2c
1097 merging f5b and f5a to f5a
1097 merging f5b and f5a to f5a
1098
1098
1099 Test the cases A.1 (f4x) and A.7 (f3x).
1099 Test the cases A.1 (f4x) and A.7 (f3x).
1100
1100
1101 $ HGEDITOR="echo D2 >" hg graft -r 'desc("D0")' --edit
1101 $ HGEDITOR="echo D2 >" hg graft -r 'desc("D0")' --edit
1102 grafting 3:b69f5839d2d9 "D0"
1102 grafting 3:b69f5839d2d9 "D0"
1103 note: possible conflict - f3b was renamed multiple times to:
1103 note: possible conflict - f3b was renamed multiple times to:
1104 f3e
1104 f3e
1105 f3d
1105 f3d
1106 merging f4e and f4a to f4e
1106 merging f4e and f4a to f4e
1107 warning: can't find ancestor for 'f3d' copied from 'f3b'!
1107 warning: can't find ancestor for 'f3d' copied from 'f3b'!
1108
1108
1109 Check the results of the grafts tested
1109 Check the results of the grafts tested
1110
1110
1111 $ hg log -CGv --patch --git
1111 $ hg log -CGv --patch --git
1112 @ changeset: 8:93ee502e8b0a
1112 @ changeset: 8:93ee502e8b0a
1113 | tag: tip
1113 | tag: tip
1114 | user: test
1114 | user: test
1115 | date: Thu Jan 01 00:00:00 1970 +0000
1115 | date: Thu Jan 01 00:00:00 1970 +0000
1116 | files: f3d f4e
1116 | files: f3d f4e
1117 | description:
1117 | description:
1118 | D2
1118 | D2
1119 |
1119 |
1120 |
1120 |
1121 | diff --git a/f3d b/f3d
1121 | diff --git a/f3d b/f3d
1122 | new file mode 100644
1122 | new file mode 100644
1123 | --- /dev/null
1123 | --- /dev/null
1124 | +++ b/f3d
1124 | +++ b/f3d
1125 | @@ -0,0 +1,1 @@
1125 | @@ -0,0 +1,1 @@
1126 | +c3a
1126 | +c3a
1127 | diff --git a/f4e b/f4e
1127 | diff --git a/f4e b/f4e
1128 | --- a/f4e
1128 | --- a/f4e
1129 | +++ b/f4e
1129 | +++ b/f4e
1130 | @@ -1,1 +1,1 @@
1130 | @@ -1,1 +1,1 @@
1131 | -c4a
1131 | -c4a
1132 | +c4d
1132 | +c4d
1133 |
1133 |
1134 o changeset: 7:539cf145f496
1134 o changeset: 7:539cf145f496
1135 | user: test
1135 | user: test
1136 | date: Thu Jan 01 00:00:00 1970 +0000
1136 | date: Thu Jan 01 00:00:00 1970 +0000
1137 | files: f1e f2a f2c f5a f5b
1137 | files: f1e f2a f2c f5a f5b
1138 | copies: f2c (f2a) f5a (f5b)
1138 | copies: f2c (f2a) f5a (f5b)
1139 | description:
1139 | description:
1140 | C2
1140 | C2
1141 |
1141 |
1142 |
1142 |
1143 | diff --git a/f1e b/f1e
1143 | diff --git a/f1e b/f1e
1144 | --- a/f1e
1144 | --- a/f1e
1145 | +++ b/f1e
1145 | +++ b/f1e
1146 | @@ -1,1 +1,1 @@
1146 | @@ -1,1 +1,1 @@
1147 | -c1a
1147 | -c1a
1148 | +c1c
1148 | +c1c
1149 | diff --git a/f2a b/f2c
1149 | diff --git a/f2a b/f2c
1150 | rename from f2a
1150 | rename from f2a
1151 | rename to f2c
1151 | rename to f2c
1152 | diff --git a/f5b b/f5a
1152 | diff --git a/f5b b/f5a
1153 | rename from f5b
1153 | rename from f5b
1154 | rename to f5a
1154 | rename to f5a
1155 | --- a/f5b
1155 | --- a/f5b
1156 | +++ b/f5a
1156 | +++ b/f5a
1157 | @@ -1,1 +1,1 @@
1157 | @@ -1,1 +1,1 @@
1158 | -c5a
1158 | -c5a
1159 | +c5c
1159 | +c5c
1160 |
1160 |
1161 o changeset: 6:6bd1736cab86
1161 o changeset: 6:6bd1736cab86
1162 | parent: 0:11f7a1b56675
1162 | parent: 0:11f7a1b56675
1163 | user: test
1163 | user: test
1164 | date: Thu Jan 01 00:00:00 1970 +0000
1164 | date: Thu Jan 01 00:00:00 1970 +0000
1165 | files: f1a f1e f2a f3a f3e f4a f4e f5a f5b
1165 | files: f1a f1e f2a f3a f3e f4a f4e f5a f5b
1166 | copies: f1e (f1a) f3e (f3a) f4e (f4a) f5b (f5a)
1166 | copies: f1e (f1a) f3e (f3a) f4e (f4a) f5b (f5a)
1167 | description:
1167 | description:
1168 | E0
1168 | E0
1169 |
1169 |
1170 |
1170 |
1171 | diff --git a/f1a b/f1e
1171 | diff --git a/f1a b/f1e
1172 | rename from f1a
1172 | rename from f1a
1173 | rename to f1e
1173 | rename to f1e
1174 | diff --git a/f2a b/f2a
1174 | diff --git a/f2a b/f2a
1175 | --- a/f2a
1175 | --- a/f2a
1176 | +++ b/f2a
1176 | +++ b/f2a
1177 | @@ -1,1 +1,1 @@
1177 | @@ -1,1 +1,1 @@
1178 | -c2a
1178 | -c2a
1179 | +c2e
1179 | +c2e
1180 | diff --git a/f3a b/f3e
1180 | diff --git a/f3a b/f3e
1181 | rename from f3a
1181 | rename from f3a
1182 | rename to f3e
1182 | rename to f3e
1183 | diff --git a/f4a b/f4e
1183 | diff --git a/f4a b/f4e
1184 | rename from f4a
1184 | rename from f4a
1185 | rename to f4e
1185 | rename to f4e
1186 | diff --git a/f5a b/f5b
1186 | diff --git a/f5a b/f5b
1187 | rename from f5a
1187 | rename from f5a
1188 | rename to f5b
1188 | rename to f5b
1189 |
1189 |
1190 | o changeset: 5:560daee679da
1190 | o changeset: 5:560daee679da
1191 | | user: test
1191 | | user: test
1192 | | date: Thu Jan 01 00:00:00 1970 +0000
1192 | | date: Thu Jan 01 00:00:00 1970 +0000
1193 | | files: f3d f4a
1193 | | files: f3d f4a
1194 | | description:
1194 | | description:
1195 | | D1
1195 | | D1
1196 | |
1196 | |
1197 | |
1197 | |
1198 | | diff --git a/f3d b/f3d
1198 | | diff --git a/f3d b/f3d
1199 | | new file mode 100644
1199 | | new file mode 100644
1200 | | --- /dev/null
1200 | | --- /dev/null
1201 | | +++ b/f3d
1201 | | +++ b/f3d
1202 | | @@ -0,0 +1,1 @@
1202 | | @@ -0,0 +1,1 @@
1203 | | +c3a
1203 | | +c3a
1204 | | diff --git a/f4a b/f4a
1204 | | diff --git a/f4a b/f4a
1205 | | --- a/f4a
1205 | | --- a/f4a
1206 | | +++ b/f4a
1206 | | +++ b/f4a
1207 | | @@ -1,1 +1,1 @@
1207 | | @@ -1,1 +1,1 @@
1208 | | -c4a
1208 | | -c4a
1209 | | +c4d
1209 | | +c4d
1210 | |
1210 | |
1211 | o changeset: 4:c9763722f9bd
1211 | o changeset: 4:c9763722f9bd
1212 |/ parent: 0:11f7a1b56675
1212 |/ parent: 0:11f7a1b56675
1213 | user: test
1213 | user: test
1214 | date: Thu Jan 01 00:00:00 1970 +0000
1214 | date: Thu Jan 01 00:00:00 1970 +0000
1215 | files: f1a f2a f2c f5a
1215 | files: f1a f2a f2c f5a
1216 | copies: f2c (f2a)
1216 | copies: f2c (f2a)
1217 | description:
1217 | description:
1218 | C1
1218 | C1
1219 |
1219 |
1220 |
1220 |
1221 | diff --git a/f1a b/f1a
1221 | diff --git a/f1a b/f1a
1222 | --- a/f1a
1222 | --- a/f1a
1223 | +++ b/f1a
1223 | +++ b/f1a
1224 | @@ -1,1 +1,1 @@
1224 | @@ -1,1 +1,1 @@
1225 | -c1a
1225 | -c1a
1226 | +c1c
1226 | +c1c
1227 | diff --git a/f2a b/f2c
1227 | diff --git a/f2a b/f2c
1228 | rename from f2a
1228 | rename from f2a
1229 | rename to f2c
1229 | rename to f2c
1230 | diff --git a/f5a b/f5a
1230 | diff --git a/f5a b/f5a
1231 | --- a/f5a
1231 | --- a/f5a
1232 | +++ b/f5a
1232 | +++ b/f5a
1233 | @@ -1,1 +1,1 @@
1233 | @@ -1,1 +1,1 @@
1234 | -c5a
1234 | -c5a
1235 | +c5c
1235 | +c5c
1236 |
1236 |
1237 | o changeset: 3:b69f5839d2d9
1237 | o changeset: 3:b69f5839d2d9
1238 | | user: test
1238 | | user: test
1239 | | date: Thu Jan 01 00:00:00 1970 +0000
1239 | | date: Thu Jan 01 00:00:00 1970 +0000
1240 | | files: f3b f3d f4a
1240 | | files: f3b f3d f4a
1241 | | copies: f3d (f3b)
1241 | | copies: f3d (f3b)
1242 | | description:
1242 | | description:
1243 | | D0
1243 | | D0
1244 | |
1244 | |
1245 | |
1245 | |
1246 | | diff --git a/f3b b/f3d
1246 | | diff --git a/f3b b/f3d
1247 | | rename from f3b
1247 | | rename from f3b
1248 | | rename to f3d
1248 | | rename to f3d
1249 | | diff --git a/f4a b/f4a
1249 | | diff --git a/f4a b/f4a
1250 | | --- a/f4a
1250 | | --- a/f4a
1251 | | +++ b/f4a
1251 | | +++ b/f4a
1252 | | @@ -1,1 +1,1 @@
1252 | | @@ -1,1 +1,1 @@
1253 | | -c4a
1253 | | -c4a
1254 | | +c4d
1254 | | +c4d
1255 | |
1255 | |
1256 | o changeset: 2:f58c7e2b28fa
1256 | o changeset: 2:f58c7e2b28fa
1257 | | user: test
1257 | | user: test
1258 | | date: Thu Jan 01 00:00:00 1970 +0000
1258 | | date: Thu Jan 01 00:00:00 1970 +0000
1259 | | files: f1b f2a f2c f5a f5b
1259 | | files: f1b f2a f2c f5a f5b
1260 | | copies: f2c (f2a) f5a (f5b)
1260 | | copies: f2c (f2a) f5a (f5b)
1261 | | description:
1261 | | description:
1262 | | C0
1262 | | C0
1263 | |
1263 | |
1264 | |
1264 | |
1265 | | diff --git a/f1b b/f1b
1265 | | diff --git a/f1b b/f1b
1266 | | --- a/f1b
1266 | | --- a/f1b
1267 | | +++ b/f1b
1267 | | +++ b/f1b
1268 | | @@ -1,1 +1,1 @@
1268 | | @@ -1,1 +1,1 @@
1269 | | -c1a
1269 | | -c1a
1270 | | +c1c
1270 | | +c1c
1271 | | diff --git a/f2a b/f2c
1271 | | diff --git a/f2a b/f2c
1272 | | rename from f2a
1272 | | rename from f2a
1273 | | rename to f2c
1273 | | rename to f2c
1274 | | diff --git a/f5b b/f5a
1274 | | diff --git a/f5b b/f5a
1275 | | rename from f5b
1275 | | rename from f5b
1276 | | rename to f5a
1276 | | rename to f5a
1277 | | --- a/f5b
1277 | | --- a/f5b
1278 | | +++ b/f5a
1278 | | +++ b/f5a
1279 | | @@ -1,1 +1,1 @@
1279 | | @@ -1,1 +1,1 @@
1280 | | -c5a
1280 | | -c5a
1281 | | +c5c
1281 | | +c5c
1282 | |
1282 | |
1283 | o changeset: 1:3d7bba921b5d
1283 | o changeset: 1:3d7bba921b5d
1284 |/ user: test
1284 |/ user: test
1285 | date: Thu Jan 01 00:00:00 1970 +0000
1285 | date: Thu Jan 01 00:00:00 1970 +0000
1286 | files: f1a f1b f3a f3b f5a f5b
1286 | files: f1a f1b f3a f3b f5a f5b
1287 | copies: f1b (f1a) f3b (f3a) f5b (f5a)
1287 | copies: f1b (f1a) f3b (f3a) f5b (f5a)
1288 | description:
1288 | description:
1289 | B0
1289 | B0
1290 |
1290 |
1291 |
1291 |
1292 | diff --git a/f1a b/f1b
1292 | diff --git a/f1a b/f1b
1293 | rename from f1a
1293 | rename from f1a
1294 | rename to f1b
1294 | rename to f1b
1295 | diff --git a/f3a b/f3b
1295 | diff --git a/f3a b/f3b
1296 | rename from f3a
1296 | rename from f3a
1297 | rename to f3b
1297 | rename to f3b
1298 | diff --git a/f5a b/f5b
1298 | diff --git a/f5a b/f5b
1299 | rename from f5a
1299 | rename from f5a
1300 | rename to f5b
1300 | rename to f5b
1301 |
1301 |
1302 o changeset: 0:11f7a1b56675
1302 o changeset: 0:11f7a1b56675
1303 user: test
1303 user: test
1304 date: Thu Jan 01 00:00:00 1970 +0000
1304 date: Thu Jan 01 00:00:00 1970 +0000
1305 files: f1a f2a f3a f4a f5a
1305 files: f1a f2a f3a f4a f5a
1306 description:
1306 description:
1307 A0
1307 A0
1308
1308
1309
1309
1310 diff --git a/f1a b/f1a
1310 diff --git a/f1a b/f1a
1311 new file mode 100644
1311 new file mode 100644
1312 --- /dev/null
1312 --- /dev/null
1313 +++ b/f1a
1313 +++ b/f1a
1314 @@ -0,0 +1,1 @@
1314 @@ -0,0 +1,1 @@
1315 +c1a
1315 +c1a
1316 diff --git a/f2a b/f2a
1316 diff --git a/f2a b/f2a
1317 new file mode 100644
1317 new file mode 100644
1318 --- /dev/null
1318 --- /dev/null
1319 +++ b/f2a
1319 +++ b/f2a
1320 @@ -0,0 +1,1 @@
1320 @@ -0,0 +1,1 @@
1321 +c2a
1321 +c2a
1322 diff --git a/f3a b/f3a
1322 diff --git a/f3a b/f3a
1323 new file mode 100644
1323 new file mode 100644
1324 --- /dev/null
1324 --- /dev/null
1325 +++ b/f3a
1325 +++ b/f3a
1326 @@ -0,0 +1,1 @@
1326 @@ -0,0 +1,1 @@
1327 +c3a
1327 +c3a
1328 diff --git a/f4a b/f4a
1328 diff --git a/f4a b/f4a
1329 new file mode 100644
1329 new file mode 100644
1330 --- /dev/null
1330 --- /dev/null
1331 +++ b/f4a
1331 +++ b/f4a
1332 @@ -0,0 +1,1 @@
1332 @@ -0,0 +1,1 @@
1333 +c4a
1333 +c4a
1334 diff --git a/f5a b/f5a
1334 diff --git a/f5a b/f5a
1335 new file mode 100644
1335 new file mode 100644
1336 --- /dev/null
1336 --- /dev/null
1337 +++ b/f5a
1337 +++ b/f5a
1338 @@ -0,0 +1,1 @@
1338 @@ -0,0 +1,1 @@
1339 +c5a
1339 +c5a
1340
1340
1341 $ hg cat f2c
1341 $ hg cat f2c
1342 c2e
1342 c2e
1343
1343
1344 Check superfluous filemerge of files renamed in the past but untouched by graft
1344 Check superfluous filemerge of files renamed in the past but untouched by graft
1345
1345
1346 $ echo a > a
1346 $ echo a > a
1347 $ hg ci -qAma
1347 $ hg ci -qAma
1348 $ hg mv a b
1348 $ hg mv a b
1349 $ echo b > b
1349 $ echo b > b
1350 $ hg ci -qAmb
1350 $ hg ci -qAmb
1351 $ echo c > c
1351 $ echo c > c
1352 $ hg ci -qAmc
1352 $ hg ci -qAmc
1353 $ hg up -q .~2
1353 $ hg up -q .~2
1354 $ hg graft tip -qt:fail
1354 $ hg graft tip -qt:fail
1355
1355
1356 $ cd ..
1356 $ cd ..
1357
1357
1358 Graft a change into a new file previously grafted into a renamed directory
1358 Graft a change into a new file previously grafted into a renamed directory
1359
1359
1360 $ hg init dirmovenewfile
1360 $ hg init dirmovenewfile
1361 $ cd dirmovenewfile
1361 $ cd dirmovenewfile
1362 $ mkdir a
1362 $ mkdir a
1363 $ echo a > a/a
1363 $ echo a > a/a
1364 $ hg ci -qAma
1364 $ hg ci -qAma
1365 $ echo x > a/x
1365 $ echo x > a/x
1366 $ hg ci -qAmx
1366 $ hg ci -qAmx
1367 $ hg up -q 0
1367 $ hg up -q 0
1368 $ hg mv -q a b
1368 $ hg mv -q a b
1369 $ hg ci -qAmb
1369 $ hg ci -qAmb
1370 $ hg graft -q 1 # a/x grafted as b/x, but no copy information recorded
1370 $ hg graft -q 1 # a/x grafted as b/x, but no copy information recorded
1371 $ hg up -q 1
1371 $ hg up -q 1
1372 $ echo y > a/x
1372 $ echo y > a/x
1373 $ hg ci -qAmy
1373 $ hg ci -qAmy
1374 $ hg up -q 3
1374 $ hg up -q 3
1375 $ hg graft -q 4
1375 $ hg graft -q 4
1376 $ hg status --change .
1376 $ hg status --change .
1377 M b/x
1377 M b/x
1378
1378
1379 Prepare for test of skipped changesets and how merges can influence it:
1379 Prepare for test of skipped changesets and how merges can influence it:
1380
1380
1381 $ hg merge -q -r 1 --tool :local
1381 $ hg merge -q -r 1 --tool :local
1382 $ hg ci -m m
1382 $ hg ci -m m
1383 $ echo xx >> b/x
1383 $ echo xx >> b/x
1384 $ hg ci -m xx
1384 $ hg ci -m xx
1385
1385
1386 $ hg log -G -T '{rev} {desc|firstline}'
1386 $ hg log -G -T '{rev} {desc|firstline}'
1387 @ 7 xx
1387 @ 7 xx
1388 |
1388 |
1389 o 6 m
1389 o 6 m
1390 |\
1390 |\
1391 | o 5 y
1391 | o 5 y
1392 | |
1392 | |
1393 +---o 4 y
1393 +---o 4 y
1394 | |
1394 | |
1395 | o 3 x
1395 | o 3 x
1396 | |
1396 | |
1397 | o 2 b
1397 | o 2 b
1398 | |
1398 | |
1399 o | 1 x
1399 o | 1 x
1400 |/
1400 |/
1401 o 0 a
1401 o 0 a
1402
1402
1403 Grafting of plain changes correctly detects that 3 and 5 should be skipped:
1403 Grafting of plain changes correctly detects that 3 and 5 should be skipped:
1404
1404
1405 $ hg up -qCr 4
1405 $ hg up -qCr 4
1406 $ hg graft --tool :local -r 2::5
1406 $ hg graft --tool :local -r 2::5
1407 skipping already grafted revision 3:ca093ca2f1d9 (was grafted from 1:13ec5badbf2a)
1407 skipping already grafted revision 3:ca093ca2f1d9 (was grafted from 1:13ec5badbf2a)
1408 skipping already grafted revision 5:43e9eb70dab0 (was grafted from 4:6c9a1289e5f1)
1408 skipping already grafted revision 5:43e9eb70dab0 (was grafted from 4:6c9a1289e5f1)
1409 grafting 2:42127f193bcd "b"
1409 grafting 2:42127f193bcd "b"
1410
1410
1411 Extending the graft range to include a (skipped) merge of 3 will not prevent us from
1411 Extending the graft range to include a (skipped) merge of 3 will not prevent us from
1412 also detecting that both 3 and 5 should be skipped:
1412 also detecting that both 3 and 5 should be skipped:
1413
1413
1414 $ hg up -qCr 4
1414 $ hg up -qCr 4
1415 $ hg graft --tool :local -r 2::7
1415 $ hg graft --tool :local -r 2::7
1416 skipping ungraftable merge revision 6
1416 skipping ungraftable merge revision 6
1417 skipping already grafted revision 3:ca093ca2f1d9 (was grafted from 1:13ec5badbf2a)
1417 skipping already grafted revision 3:ca093ca2f1d9 (was grafted from 1:13ec5badbf2a)
1418 skipping already grafted revision 5:43e9eb70dab0 (was grafted from 4:6c9a1289e5f1)
1418 skipping already grafted revision 5:43e9eb70dab0 (was grafted from 4:6c9a1289e5f1)
1419 grafting 2:42127f193bcd "b"
1419 grafting 2:42127f193bcd "b"
1420 grafting 7:d3c3f2b38ecc "xx"
1420 grafting 7:d3c3f2b38ecc "xx"
1421 note: graft of 7:d3c3f2b38ecc created no changes to commit
1421 note: graft of 7:d3c3f2b38ecc created no changes to commit
1422
1422
1423 $ cd ..
1423 $ cd ..
1424
1424
1425 Grafted revision should be warned and skipped only once. (issue6024)
1426
1427 $ mkdir issue6024
1428 $ cd issue6024
1429
1430 $ hg init base
1431 $ cd base
1432 $ touch x
1433 $ hg commit -qAminit
1434 $ echo a > x
1435 $ hg commit -mchange
1436 $ hg update -q 0
1437 $ hg graft -r 1
1438 grafting 1:a0b923c546aa "change" (tip)
1439 $ cd ..
1440
1441 $ hg clone -qr 2 base clone
1442 $ cd clone
1443 $ hg pull -q
1444 $ hg merge -q 2
1445 $ hg commit -mmerge
1446 $ hg update -q 0
1447 $ hg graft -r 1
1448 grafting 1:04fc6d444368 "change"
1449 $ hg update -q 3
1450 $ hg log -G -T '{rev}:{node|shortest} <- {extras.source|shortest}\n'
1451 o 4:4e16 <- a0b9
1452 |
1453 | @ 3:f0ac <-
1454 | |\
1455 +---o 2:a0b9 <-
1456 | |
1457 | o 1:04fc <- a0b9
1458 |/
1459 o 0:7848 <-
1460
1461
1462 the source of rev 4 is an ancestor of the working parent, and was also
1463 grafted as rev 1. it should be stripped from the target revisions only once.
1464
1465 $ hg graft -r 4
1466 skipping already grafted revision 4:4e16bab40c9c (1:04fc6d444368 also has origin 2:a0b923c546aa)
1467 [255]
1468
1469 $ cd ../..
1470
1425 Testing the reading of old format graftstate file with newer mercurial
1471 Testing the reading of old format graftstate file with newer mercurial
1426
1472
1427 $ hg init oldgraft
1473 $ hg init oldgraft
1428 $ cd oldgraft
1474 $ cd oldgraft
1429 $ for ch in a b c; do echo foo > $ch; hg add $ch; hg ci -Aqm "added "$ch; done;
1475 $ for ch in a b c; do echo foo > $ch; hg add $ch; hg ci -Aqm "added "$ch; done;
1430 $ hg log -GT "{rev}:{node|short} {desc}\n"
1476 $ hg log -GT "{rev}:{node|short} {desc}\n"
1431 @ 2:8be98ac1a569 added c
1477 @ 2:8be98ac1a569 added c
1432 |
1478 |
1433 o 1:80e6d2c47cfe added b
1479 o 1:80e6d2c47cfe added b
1434 |
1480 |
1435 o 0:f7ad41964313 added a
1481 o 0:f7ad41964313 added a
1436
1482
1437 $ hg up 0
1483 $ hg up 0
1438 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
1484 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
1439 $ echo bar > b
1485 $ echo bar > b
1440 $ hg add b
1486 $ hg add b
1441 $ hg ci -m "bar to b"
1487 $ hg ci -m "bar to b"
1442 created new head
1488 created new head
1443 $ hg graft -r 1 -r 2
1489 $ hg graft -r 1 -r 2
1444 grafting 1:80e6d2c47cfe "added b"
1490 grafting 1:80e6d2c47cfe "added b"
1445 merging b
1491 merging b
1446 warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
1492 warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
1447 abort: unresolved conflicts, can't continue
1493 abort: unresolved conflicts, can't continue
1448 (use 'hg resolve' and 'hg graft --continue')
1494 (use 'hg resolve' and 'hg graft --continue')
1449 [255]
1495 [255]
1450
1496
1451 Writing the nodes in old format to graftstate
1497 Writing the nodes in old format to graftstate
1452
1498
1453 $ hg log -r 1 -r 2 -T '{node}\n' > .hg/graftstate
1499 $ hg log -r 1 -r 2 -T '{node}\n' > .hg/graftstate
1454 $ echo foo > b
1500 $ echo foo > b
1455 $ hg resolve -m
1501 $ hg resolve -m
1456 (no more unresolved files)
1502 (no more unresolved files)
1457 continue: hg graft --continue
1503 continue: hg graft --continue
1458 $ hg graft --continue
1504 $ hg graft --continue
1459 grafting 1:80e6d2c47cfe "added b"
1505 grafting 1:80e6d2c47cfe "added b"
1460 grafting 2:8be98ac1a569 "added c"
1506 grafting 2:8be98ac1a569 "added c"
1461
1507
1462 Testing that --user is preserved during conflicts and value is reused while
1508 Testing that --user is preserved during conflicts and value is reused while
1463 running `hg graft --continue`
1509 running `hg graft --continue`
1464
1510
1465 $ hg log -G
1511 $ hg log -G
1466 @ changeset: 5:711e9fa999f1
1512 @ changeset: 5:711e9fa999f1
1467 | tag: tip
1513 | tag: tip
1468 | user: test
1514 | user: test
1469 | date: Thu Jan 01 00:00:00 1970 +0000
1515 | date: Thu Jan 01 00:00:00 1970 +0000
1470 | summary: added c
1516 | summary: added c
1471 |
1517 |
1472 o changeset: 4:e5ad7353b408
1518 o changeset: 4:e5ad7353b408
1473 | user: test
1519 | user: test
1474 | date: Thu Jan 01 00:00:00 1970 +0000
1520 | date: Thu Jan 01 00:00:00 1970 +0000
1475 | summary: added b
1521 | summary: added b
1476 |
1522 |
1477 o changeset: 3:9e887f7a939c
1523 o changeset: 3:9e887f7a939c
1478 | parent: 0:f7ad41964313
1524 | parent: 0:f7ad41964313
1479 | user: test
1525 | user: test
1480 | date: Thu Jan 01 00:00:00 1970 +0000
1526 | date: Thu Jan 01 00:00:00 1970 +0000
1481 | summary: bar to b
1527 | summary: bar to b
1482 |
1528 |
1483 | o changeset: 2:8be98ac1a569
1529 | o changeset: 2:8be98ac1a569
1484 | | user: test
1530 | | user: test
1485 | | date: Thu Jan 01 00:00:00 1970 +0000
1531 | | date: Thu Jan 01 00:00:00 1970 +0000
1486 | | summary: added c
1532 | | summary: added c
1487 | |
1533 | |
1488 | o changeset: 1:80e6d2c47cfe
1534 | o changeset: 1:80e6d2c47cfe
1489 |/ user: test
1535 |/ user: test
1490 | date: Thu Jan 01 00:00:00 1970 +0000
1536 | date: Thu Jan 01 00:00:00 1970 +0000
1491 | summary: added b
1537 | summary: added b
1492 |
1538 |
1493 o changeset: 0:f7ad41964313
1539 o changeset: 0:f7ad41964313
1494 user: test
1540 user: test
1495 date: Thu Jan 01 00:00:00 1970 +0000
1541 date: Thu Jan 01 00:00:00 1970 +0000
1496 summary: added a
1542 summary: added a
1497
1543
1498
1544
1499 $ hg up '.^^'
1545 $ hg up '.^^'
1500 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1546 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1501
1547
1502 $ hg graft -r 1 -r 2 --user batman
1548 $ hg graft -r 1 -r 2 --user batman
1503 grafting 1:80e6d2c47cfe "added b"
1549 grafting 1:80e6d2c47cfe "added b"
1504 merging b
1550 merging b
1505 warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
1551 warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
1506 abort: unresolved conflicts, can't continue
1552 abort: unresolved conflicts, can't continue
1507 (use 'hg resolve' and 'hg graft --continue')
1553 (use 'hg resolve' and 'hg graft --continue')
1508 [255]
1554 [255]
1509
1555
1510 $ echo wat > b
1556 $ echo wat > b
1511 $ hg resolve -m
1557 $ hg resolve -m
1512 (no more unresolved files)
1558 (no more unresolved files)
1513 continue: hg graft --continue
1559 continue: hg graft --continue
1514
1560
1515 $ hg graft --continue
1561 $ hg graft --continue
1516 grafting 1:80e6d2c47cfe "added b"
1562 grafting 1:80e6d2c47cfe "added b"
1517 grafting 2:8be98ac1a569 "added c"
1563 grafting 2:8be98ac1a569 "added c"
1518
1564
1519 $ hg log -Gr 3::
1565 $ hg log -Gr 3::
1520 @ changeset: 7:11a36ffaacf2
1566 @ changeset: 7:11a36ffaacf2
1521 | tag: tip
1567 | tag: tip
1522 | user: batman
1568 | user: batman
1523 | date: Thu Jan 01 00:00:00 1970 +0000
1569 | date: Thu Jan 01 00:00:00 1970 +0000
1524 | summary: added c
1570 | summary: added c
1525 |
1571 |
1526 o changeset: 6:76803afc6511
1572 o changeset: 6:76803afc6511
1527 | parent: 3:9e887f7a939c
1573 | parent: 3:9e887f7a939c
1528 | user: batman
1574 | user: batman
1529 | date: Thu Jan 01 00:00:00 1970 +0000
1575 | date: Thu Jan 01 00:00:00 1970 +0000
1530 | summary: added b
1576 | summary: added b
1531 |
1577 |
1532 | o changeset: 5:711e9fa999f1
1578 | o changeset: 5:711e9fa999f1
1533 | | user: test
1579 | | user: test
1534 | | date: Thu Jan 01 00:00:00 1970 +0000
1580 | | date: Thu Jan 01 00:00:00 1970 +0000
1535 | | summary: added c
1581 | | summary: added c
1536 | |
1582 | |
1537 | o changeset: 4:e5ad7353b408
1583 | o changeset: 4:e5ad7353b408
1538 |/ user: test
1584 |/ user: test
1539 | date: Thu Jan 01 00:00:00 1970 +0000
1585 | date: Thu Jan 01 00:00:00 1970 +0000
1540 | summary: added b
1586 | summary: added b
1541 |
1587 |
1542 o changeset: 3:9e887f7a939c
1588 o changeset: 3:9e887f7a939c
1543 | parent: 0:f7ad41964313
1589 | parent: 0:f7ad41964313
1544 ~ user: test
1590 ~ user: test
1545 date: Thu Jan 01 00:00:00 1970 +0000
1591 date: Thu Jan 01 00:00:00 1970 +0000
1546 summary: bar to b
1592 summary: bar to b
1547
1593
1548 Test that --date is preserved and reused in `hg graft --continue`
1594 Test that --date is preserved and reused in `hg graft --continue`
1549
1595
1550 $ hg up '.^^'
1596 $ hg up '.^^'
1551 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1597 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1552 $ hg graft -r 1 -r 2 --date '1234560000 120'
1598 $ hg graft -r 1 -r 2 --date '1234560000 120'
1553 grafting 1:80e6d2c47cfe "added b"
1599 grafting 1:80e6d2c47cfe "added b"
1554 merging b
1600 merging b
1555 warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
1601 warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
1556 abort: unresolved conflicts, can't continue
1602 abort: unresolved conflicts, can't continue
1557 (use 'hg resolve' and 'hg graft --continue')
1603 (use 'hg resolve' and 'hg graft --continue')
1558 [255]
1604 [255]
1559
1605
1560 $ echo foobar > b
1606 $ echo foobar > b
1561 $ hg resolve -m
1607 $ hg resolve -m
1562 (no more unresolved files)
1608 (no more unresolved files)
1563 continue: hg graft --continue
1609 continue: hg graft --continue
1564 $ hg graft --continue
1610 $ hg graft --continue
1565 grafting 1:80e6d2c47cfe "added b"
1611 grafting 1:80e6d2c47cfe "added b"
1566 grafting 2:8be98ac1a569 "added c"
1612 grafting 2:8be98ac1a569 "added c"
1567
1613
1568 $ hg log -Gr '.^^::.'
1614 $ hg log -Gr '.^^::.'
1569 @ changeset: 9:1896b76e007a
1615 @ changeset: 9:1896b76e007a
1570 | tag: tip
1616 | tag: tip
1571 | user: test
1617 | user: test
1572 | date: Fri Feb 13 21:18:00 2009 -0002
1618 | date: Fri Feb 13 21:18:00 2009 -0002
1573 | summary: added c
1619 | summary: added c
1574 |
1620 |
1575 o changeset: 8:ce2b4f1632af
1621 o changeset: 8:ce2b4f1632af
1576 | parent: 3:9e887f7a939c
1622 | parent: 3:9e887f7a939c
1577 | user: test
1623 | user: test
1578 | date: Fri Feb 13 21:18:00 2009 -0002
1624 | date: Fri Feb 13 21:18:00 2009 -0002
1579 | summary: added b
1625 | summary: added b
1580 |
1626 |
1581 o changeset: 3:9e887f7a939c
1627 o changeset: 3:9e887f7a939c
1582 | parent: 0:f7ad41964313
1628 | parent: 0:f7ad41964313
1583 ~ user: test
1629 ~ user: test
1584 date: Thu Jan 01 00:00:00 1970 +0000
1630 date: Thu Jan 01 00:00:00 1970 +0000
1585 summary: bar to b
1631 summary: bar to b
1586
1632
1587 Test that --log is preserved and reused in `hg graft --continue`
1633 Test that --log is preserved and reused in `hg graft --continue`
1588
1634
1589 $ hg up '.^^'
1635 $ hg up '.^^'
1590 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1636 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1591 $ hg graft -r 1 -r 2 --log
1637 $ hg graft -r 1 -r 2 --log
1592 grafting 1:80e6d2c47cfe "added b"
1638 grafting 1:80e6d2c47cfe "added b"
1593 merging b
1639 merging b
1594 warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
1640 warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
1595 abort: unresolved conflicts, can't continue
1641 abort: unresolved conflicts, can't continue
1596 (use 'hg resolve' and 'hg graft --continue')
1642 (use 'hg resolve' and 'hg graft --continue')
1597 [255]
1643 [255]
1598
1644
1599 $ echo foobar > b
1645 $ echo foobar > b
1600 $ hg resolve -m
1646 $ hg resolve -m
1601 (no more unresolved files)
1647 (no more unresolved files)
1602 continue: hg graft --continue
1648 continue: hg graft --continue
1603
1649
1604 $ hg graft --continue
1650 $ hg graft --continue
1605 grafting 1:80e6d2c47cfe "added b"
1651 grafting 1:80e6d2c47cfe "added b"
1606 grafting 2:8be98ac1a569 "added c"
1652 grafting 2:8be98ac1a569 "added c"
1607
1653
1608 $ hg log -GT "{rev}:{node|short} {desc}" -r '.^^::.'
1654 $ hg log -GT "{rev}:{node|short} {desc}" -r '.^^::.'
1609 @ 11:30c1050a58b2 added c
1655 @ 11:30c1050a58b2 added c
1610 | (grafted from 8be98ac1a56990c2d9ca6861041b8390af7bd6f3)
1656 | (grafted from 8be98ac1a56990c2d9ca6861041b8390af7bd6f3)
1611 o 10:ec7eda2313e2 added b
1657 o 10:ec7eda2313e2 added b
1612 | (grafted from 80e6d2c47cfe5b3185519568327a17a061c7efb6)
1658 | (grafted from 80e6d2c47cfe5b3185519568327a17a061c7efb6)
1613 o 3:9e887f7a939c bar to b
1659 o 3:9e887f7a939c bar to b
1614 |
1660 |
1615 ~
1661 ~
1616
1662
1617 $ cd ..
1663 $ cd ..
1618
1664
1619 Testing the --stop flag of `hg graft` which stops the interrupted graft
1665 Testing the --stop flag of `hg graft` which stops the interrupted graft
1620
1666
1621 $ hg init stopgraft
1667 $ hg init stopgraft
1622 $ cd stopgraft
1668 $ cd stopgraft
1623 $ for ch in a b c d; do echo $ch > $ch; hg add $ch; hg ci -Aqm "added "$ch; done;
1669 $ for ch in a b c d; do echo $ch > $ch; hg add $ch; hg ci -Aqm "added "$ch; done;
1624
1670
1625 $ hg log -G
1671 $ hg log -G
1626 @ changeset: 3:9150fe93bec6
1672 @ changeset: 3:9150fe93bec6
1627 | tag: tip
1673 | tag: tip
1628 | user: test
1674 | user: test
1629 | date: Thu Jan 01 00:00:00 1970 +0000
1675 | date: Thu Jan 01 00:00:00 1970 +0000
1630 | summary: added d
1676 | summary: added d
1631 |
1677 |
1632 o changeset: 2:155349b645be
1678 o changeset: 2:155349b645be
1633 | user: test
1679 | user: test
1634 | date: Thu Jan 01 00:00:00 1970 +0000
1680 | date: Thu Jan 01 00:00:00 1970 +0000
1635 | summary: added c
1681 | summary: added c
1636 |
1682 |
1637 o changeset: 1:5f6d8a4bf34a
1683 o changeset: 1:5f6d8a4bf34a
1638 | user: test
1684 | user: test
1639 | date: Thu Jan 01 00:00:00 1970 +0000
1685 | date: Thu Jan 01 00:00:00 1970 +0000
1640 | summary: added b
1686 | summary: added b
1641 |
1687 |
1642 o changeset: 0:9092f1db7931
1688 o changeset: 0:9092f1db7931
1643 user: test
1689 user: test
1644 date: Thu Jan 01 00:00:00 1970 +0000
1690 date: Thu Jan 01 00:00:00 1970 +0000
1645 summary: added a
1691 summary: added a
1646
1692
1647 $ hg up '.^^'
1693 $ hg up '.^^'
1648 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
1694 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
1649
1695
1650 $ echo foo > d
1696 $ echo foo > d
1651 $ hg ci -Aqm "added foo to d"
1697 $ hg ci -Aqm "added foo to d"
1652
1698
1653 $ hg graft --stop
1699 $ hg graft --stop
1654 abort: no interrupted graft found
1700 abort: no interrupted graft found
1655 [255]
1701 [255]
1656
1702
1657 $ hg graft -r 3
1703 $ hg graft -r 3
1658 grafting 3:9150fe93bec6 "added d"
1704 grafting 3:9150fe93bec6 "added d"
1659 merging d
1705 merging d
1660 warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
1706 warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
1661 abort: unresolved conflicts, can't continue
1707 abort: unresolved conflicts, can't continue
1662 (use 'hg resolve' and 'hg graft --continue')
1708 (use 'hg resolve' and 'hg graft --continue')
1663 [255]
1709 [255]
1664
1710
1665 $ hg graft --stop --continue
1711 $ hg graft --stop --continue
1666 abort: cannot use '--continue' and '--stop' together
1712 abort: cannot use '--continue' and '--stop' together
1667 [255]
1713 [255]
1668
1714
1669 $ hg graft --stop -U
1715 $ hg graft --stop -U
1670 abort: cannot specify any other flag with '--stop'
1716 abort: cannot specify any other flag with '--stop'
1671 [255]
1717 [255]
1672 $ hg graft --stop --rev 4
1718 $ hg graft --stop --rev 4
1673 abort: cannot specify any other flag with '--stop'
1719 abort: cannot specify any other flag with '--stop'
1674 [255]
1720 [255]
1675 $ hg graft --stop --log
1721 $ hg graft --stop --log
1676 abort: cannot specify any other flag with '--stop'
1722 abort: cannot specify any other flag with '--stop'
1677 [255]
1723 [255]
1678
1724
1679 $ hg graft --stop
1725 $ hg graft --stop
1680 stopped the interrupted graft
1726 stopped the interrupted graft
1681 working directory is now at a0deacecd59d
1727 working directory is now at a0deacecd59d
1682
1728
1683 $ hg diff
1729 $ hg diff
1684
1730
1685 $ hg log -Gr '.'
1731 $ hg log -Gr '.'
1686 @ changeset: 4:a0deacecd59d
1732 @ changeset: 4:a0deacecd59d
1687 | tag: tip
1733 | tag: tip
1688 ~ parent: 1:5f6d8a4bf34a
1734 ~ parent: 1:5f6d8a4bf34a
1689 user: test
1735 user: test
1690 date: Thu Jan 01 00:00:00 1970 +0000
1736 date: Thu Jan 01 00:00:00 1970 +0000
1691 summary: added foo to d
1737 summary: added foo to d
1692
1738
1693 $ hg graft -r 2 -r 3
1739 $ hg graft -r 2 -r 3
1694 grafting 2:155349b645be "added c"
1740 grafting 2:155349b645be "added c"
1695 grafting 3:9150fe93bec6 "added d"
1741 grafting 3:9150fe93bec6 "added d"
1696 merging d
1742 merging d
1697 warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
1743 warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
1698 abort: unresolved conflicts, can't continue
1744 abort: unresolved conflicts, can't continue
1699 (use 'hg resolve' and 'hg graft --continue')
1745 (use 'hg resolve' and 'hg graft --continue')
1700 [255]
1746 [255]
1701
1747
1702 $ hg graft --stop
1748 $ hg graft --stop
1703 stopped the interrupted graft
1749 stopped the interrupted graft
1704 working directory is now at 75b447541a9e
1750 working directory is now at 75b447541a9e
1705
1751
1706 $ hg diff
1752 $ hg diff
1707
1753
1708 $ hg log -G -T "{rev}:{node|short} {desc}"
1754 $ hg log -G -T "{rev}:{node|short} {desc}"
1709 @ 5:75b447541a9e added c
1755 @ 5:75b447541a9e added c
1710 |
1756 |
1711 o 4:a0deacecd59d added foo to d
1757 o 4:a0deacecd59d added foo to d
1712 |
1758 |
1713 | o 3:9150fe93bec6 added d
1759 | o 3:9150fe93bec6 added d
1714 | |
1760 | |
1715 | o 2:155349b645be added c
1761 | o 2:155349b645be added c
1716 |/
1762 |/
1717 o 1:5f6d8a4bf34a added b
1763 o 1:5f6d8a4bf34a added b
1718 |
1764 |
1719 o 0:9092f1db7931 added a
1765 o 0:9092f1db7931 added a
1720
1766
1721 $ cd ..
1767 $ cd ..
1722
1768
1723 Testing the --abort flag for `hg graft` which aborts and rollback to state
1769 Testing the --abort flag for `hg graft` which aborts and rollback to state
1724 before the graft
1770 before the graft
1725
1771
1726 $ hg init abortgraft
1772 $ hg init abortgraft
1727 $ cd abortgraft
1773 $ cd abortgraft
1728 $ for ch in a b c d; do echo $ch > $ch; hg add $ch; hg ci -Aqm "added "$ch; done;
1774 $ for ch in a b c d; do echo $ch > $ch; hg add $ch; hg ci -Aqm "added "$ch; done;
1729
1775
1730 $ hg up '.^^'
1776 $ hg up '.^^'
1731 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
1777 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
1732
1778
1733 $ echo x > x
1779 $ echo x > x
1734 $ hg ci -Aqm "added x"
1780 $ hg ci -Aqm "added x"
1735 $ hg up '.^'
1781 $ hg up '.^'
1736 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1782 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1737 $ echo foo > c
1783 $ echo foo > c
1738 $ hg ci -Aqm "added foo to c"
1784 $ hg ci -Aqm "added foo to c"
1739
1785
1740 $ hg log -GT "{rev}:{node|short} {desc}"
1786 $ hg log -GT "{rev}:{node|short} {desc}"
1741 @ 5:36b793615f78 added foo to c
1787 @ 5:36b793615f78 added foo to c
1742 |
1788 |
1743 | o 4:863a25e1a9ea added x
1789 | o 4:863a25e1a9ea added x
1744 |/
1790 |/
1745 | o 3:9150fe93bec6 added d
1791 | o 3:9150fe93bec6 added d
1746 | |
1792 | |
1747 | o 2:155349b645be added c
1793 | o 2:155349b645be added c
1748 |/
1794 |/
1749 o 1:5f6d8a4bf34a added b
1795 o 1:5f6d8a4bf34a added b
1750 |
1796 |
1751 o 0:9092f1db7931 added a
1797 o 0:9092f1db7931 added a
1752
1798
1753 $ hg up 9150fe93bec6
1799 $ hg up 9150fe93bec6
1754 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1800 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1755
1801
1756 $ hg graft --abort
1802 $ hg graft --abort
1757 abort: no interrupted graft to abort
1803 abort: no interrupted graft to abort
1758 [255]
1804 [255]
1759
1805
1760 when stripping is required
1806 when stripping is required
1761 $ hg graft -r 4 -r 5
1807 $ hg graft -r 4 -r 5
1762 grafting 4:863a25e1a9ea "added x"
1808 grafting 4:863a25e1a9ea "added x"
1763 grafting 5:36b793615f78 "added foo to c" (tip)
1809 grafting 5:36b793615f78 "added foo to c" (tip)
1764 merging c
1810 merging c
1765 warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
1811 warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
1766 abort: unresolved conflicts, can't continue
1812 abort: unresolved conflicts, can't continue
1767 (use 'hg resolve' and 'hg graft --continue')
1813 (use 'hg resolve' and 'hg graft --continue')
1768 [255]
1814 [255]
1769
1815
1770 $ hg graft --continue --abort
1816 $ hg graft --continue --abort
1771 abort: cannot use '--continue' and '--abort' together
1817 abort: cannot use '--continue' and '--abort' together
1772 [255]
1818 [255]
1773
1819
1774 $ hg graft --abort --stop
1820 $ hg graft --abort --stop
1775 abort: cannot use '--abort' and '--stop' together
1821 abort: cannot use '--abort' and '--stop' together
1776 [255]
1822 [255]
1777
1823
1778 $ hg graft --abort --currentuser
1824 $ hg graft --abort --currentuser
1779 abort: cannot specify any other flag with '--abort'
1825 abort: cannot specify any other flag with '--abort'
1780 [255]
1826 [255]
1781
1827
1782 $ hg graft --abort --edit
1828 $ hg graft --abort --edit
1783 abort: cannot specify any other flag with '--abort'
1829 abort: cannot specify any other flag with '--abort'
1784 [255]
1830 [255]
1785
1831
1786 $ hg graft --abort
1832 $ hg graft --abort
1787 graft aborted
1833 graft aborted
1788 working directory is now at 9150fe93bec6
1834 working directory is now at 9150fe93bec6
1789 $ hg log -GT "{rev}:{node|short} {desc}"
1835 $ hg log -GT "{rev}:{node|short} {desc}"
1790 o 5:36b793615f78 added foo to c
1836 o 5:36b793615f78 added foo to c
1791 |
1837 |
1792 | o 4:863a25e1a9ea added x
1838 | o 4:863a25e1a9ea added x
1793 |/
1839 |/
1794 | @ 3:9150fe93bec6 added d
1840 | @ 3:9150fe93bec6 added d
1795 | |
1841 | |
1796 | o 2:155349b645be added c
1842 | o 2:155349b645be added c
1797 |/
1843 |/
1798 o 1:5f6d8a4bf34a added b
1844 o 1:5f6d8a4bf34a added b
1799 |
1845 |
1800 o 0:9092f1db7931 added a
1846 o 0:9092f1db7931 added a
1801
1847
1802 when stripping is not required
1848 when stripping is not required
1803 $ hg graft -r 5
1849 $ hg graft -r 5
1804 grafting 5:36b793615f78 "added foo to c" (tip)
1850 grafting 5:36b793615f78 "added foo to c" (tip)
1805 merging c
1851 merging c
1806 warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
1852 warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
1807 abort: unresolved conflicts, can't continue
1853 abort: unresolved conflicts, can't continue
1808 (use 'hg resolve' and 'hg graft --continue')
1854 (use 'hg resolve' and 'hg graft --continue')
1809 [255]
1855 [255]
1810
1856
1811 $ hg graft --abort
1857 $ hg graft --abort
1812 graft aborted
1858 graft aborted
1813 working directory is now at 9150fe93bec6
1859 working directory is now at 9150fe93bec6
1814 $ hg log -GT "{rev}:{node|short} {desc}"
1860 $ hg log -GT "{rev}:{node|short} {desc}"
1815 o 5:36b793615f78 added foo to c
1861 o 5:36b793615f78 added foo to c
1816 |
1862 |
1817 | o 4:863a25e1a9ea added x
1863 | o 4:863a25e1a9ea added x
1818 |/
1864 |/
1819 | @ 3:9150fe93bec6 added d
1865 | @ 3:9150fe93bec6 added d
1820 | |
1866 | |
1821 | o 2:155349b645be added c
1867 | o 2:155349b645be added c
1822 |/
1868 |/
1823 o 1:5f6d8a4bf34a added b
1869 o 1:5f6d8a4bf34a added b
1824 |
1870 |
1825 o 0:9092f1db7931 added a
1871 o 0:9092f1db7931 added a
1826
1872
1827 when some of the changesets became public
1873 when some of the changesets became public
1828
1874
1829 $ hg graft -r 4 -r 5
1875 $ hg graft -r 4 -r 5
1830 grafting 4:863a25e1a9ea "added x"
1876 grafting 4:863a25e1a9ea "added x"
1831 grafting 5:36b793615f78 "added foo to c" (tip)
1877 grafting 5:36b793615f78 "added foo to c" (tip)
1832 merging c
1878 merging c
1833 warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
1879 warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
1834 abort: unresolved conflicts, can't continue
1880 abort: unresolved conflicts, can't continue
1835 (use 'hg resolve' and 'hg graft --continue')
1881 (use 'hg resolve' and 'hg graft --continue')
1836 [255]
1882 [255]
1837
1883
1838 $ hg log -GT "{rev}:{node|short} {desc}"
1884 $ hg log -GT "{rev}:{node|short} {desc}"
1839 @ 6:6ec71c037d94 added x
1885 @ 6:6ec71c037d94 added x
1840 |
1886 |
1841 | o 5:36b793615f78 added foo to c
1887 | o 5:36b793615f78 added foo to c
1842 | |
1888 | |
1843 | | o 4:863a25e1a9ea added x
1889 | | o 4:863a25e1a9ea added x
1844 | |/
1890 | |/
1845 o | 3:9150fe93bec6 added d
1891 o | 3:9150fe93bec6 added d
1846 | |
1892 | |
1847 o | 2:155349b645be added c
1893 o | 2:155349b645be added c
1848 |/
1894 |/
1849 o 1:5f6d8a4bf34a added b
1895 o 1:5f6d8a4bf34a added b
1850 |
1896 |
1851 o 0:9092f1db7931 added a
1897 o 0:9092f1db7931 added a
1852
1898
1853 $ hg phase -r 6 --public
1899 $ hg phase -r 6 --public
1854
1900
1855 $ hg graft --abort
1901 $ hg graft --abort
1856 cannot clean up public changesets 6ec71c037d94
1902 cannot clean up public changesets 6ec71c037d94
1857 graft aborted
1903 graft aborted
1858 working directory is now at 6ec71c037d94
1904 working directory is now at 6ec71c037d94
1859
1905
1860 when we created new changesets on top of existing one
1906 when we created new changesets on top of existing one
1861
1907
1862 $ hg up '.^^'
1908 $ hg up '.^^'
1863 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
1909 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
1864 $ echo y > y
1910 $ echo y > y
1865 $ hg ci -Aqm "added y"
1911 $ hg ci -Aqm "added y"
1866 $ echo z > z
1912 $ echo z > z
1867 $ hg ci -Aqm "added z"
1913 $ hg ci -Aqm "added z"
1868
1914
1869 $ hg up 3
1915 $ hg up 3
1870 1 files updated, 0 files merged, 3 files removed, 0 files unresolved
1916 1 files updated, 0 files merged, 3 files removed, 0 files unresolved
1871 $ hg log -GT "{rev}:{node|short} {desc}"
1917 $ hg log -GT "{rev}:{node|short} {desc}"
1872 o 8:637f9e9bbfd4 added z
1918 o 8:637f9e9bbfd4 added z
1873 |
1919 |
1874 o 7:123221671fd4 added y
1920 o 7:123221671fd4 added y
1875 |
1921 |
1876 | o 6:6ec71c037d94 added x
1922 | o 6:6ec71c037d94 added x
1877 | |
1923 | |
1878 | | o 5:36b793615f78 added foo to c
1924 | | o 5:36b793615f78 added foo to c
1879 | | |
1925 | | |
1880 | | | o 4:863a25e1a9ea added x
1926 | | | o 4:863a25e1a9ea added x
1881 | | |/
1927 | | |/
1882 | @ | 3:9150fe93bec6 added d
1928 | @ | 3:9150fe93bec6 added d
1883 |/ /
1929 |/ /
1884 o / 2:155349b645be added c
1930 o / 2:155349b645be added c
1885 |/
1931 |/
1886 o 1:5f6d8a4bf34a added b
1932 o 1:5f6d8a4bf34a added b
1887 |
1933 |
1888 o 0:9092f1db7931 added a
1934 o 0:9092f1db7931 added a
1889
1935
1890 $ hg graft -r 8 -r 7 -r 5
1936 $ hg graft -r 8 -r 7 -r 5
1891 grafting 8:637f9e9bbfd4 "added z" (tip)
1937 grafting 8:637f9e9bbfd4 "added z" (tip)
1892 grafting 7:123221671fd4 "added y"
1938 grafting 7:123221671fd4 "added y"
1893 grafting 5:36b793615f78 "added foo to c"
1939 grafting 5:36b793615f78 "added foo to c"
1894 merging c
1940 merging c
1895 warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
1941 warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
1896 abort: unresolved conflicts, can't continue
1942 abort: unresolved conflicts, can't continue
1897 (use 'hg resolve' and 'hg graft --continue')
1943 (use 'hg resolve' and 'hg graft --continue')
1898 [255]
1944 [255]
1899
1945
1900 $ cd ..
1946 $ cd ..
1901 $ hg init pullrepo
1947 $ hg init pullrepo
1902 $ cd pullrepo
1948 $ cd pullrepo
1903 $ cat >> .hg/hgrc <<EOF
1949 $ cat >> .hg/hgrc <<EOF
1904 > [phases]
1950 > [phases]
1905 > publish=False
1951 > publish=False
1906 > EOF
1952 > EOF
1907 $ hg pull ../abortgraft --config phases.publish=False
1953 $ hg pull ../abortgraft --config phases.publish=False
1908 pulling from ../abortgraft
1954 pulling from ../abortgraft
1909 requesting all changes
1955 requesting all changes
1910 adding changesets
1956 adding changesets
1911 adding manifests
1957 adding manifests
1912 adding file changes
1958 adding file changes
1913 added 11 changesets with 9 changes to 8 files (+4 heads)
1959 added 11 changesets with 9 changes to 8 files (+4 heads)
1914 new changesets 9092f1db7931:6b98ff0062dd (6 drafts)
1960 new changesets 9092f1db7931:6b98ff0062dd (6 drafts)
1915 (run 'hg heads' to see heads, 'hg merge' to merge)
1961 (run 'hg heads' to see heads, 'hg merge' to merge)
1916 $ hg up 9
1962 $ hg up 9
1917 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
1963 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
1918 $ echo w > w
1964 $ echo w > w
1919 $ hg ci -Aqm "added w" --config phases.publish=False
1965 $ hg ci -Aqm "added w" --config phases.publish=False
1920
1966
1921 $ cd ../abortgraft
1967 $ cd ../abortgraft
1922 $ hg pull ../pullrepo
1968 $ hg pull ../pullrepo
1923 pulling from ../pullrepo
1969 pulling from ../pullrepo
1924 searching for changes
1970 searching for changes
1925 adding changesets
1971 adding changesets
1926 adding manifests
1972 adding manifests
1927 adding file changes
1973 adding file changes
1928 added 1 changesets with 1 changes to 1 files (+1 heads)
1974 added 1 changesets with 1 changes to 1 files (+1 heads)
1929 new changesets 311dfc6cf3bf (1 drafts)
1975 new changesets 311dfc6cf3bf (1 drafts)
1930 (run 'hg heads .' to see heads, 'hg merge' to merge)
1976 (run 'hg heads .' to see heads, 'hg merge' to merge)
1931
1977
1932 $ hg graft --abort
1978 $ hg graft --abort
1933 new changesets detected on destination branch, can't strip
1979 new changesets detected on destination branch, can't strip
1934 graft aborted
1980 graft aborted
1935 working directory is now at 6b98ff0062dd
1981 working directory is now at 6b98ff0062dd
1936
1982
1937 $ cd ..
1983 $ cd ..
1938
1984
1939 ============================
1985 ============================
1940 Testing --no-commit option:|
1986 Testing --no-commit option:|
1941 ============================
1987 ============================
1942
1988
1943 $ hg init nocommit
1989 $ hg init nocommit
1944 $ cd nocommit
1990 $ cd nocommit
1945 $ echo a > a
1991 $ echo a > a
1946 $ hg ci -qAma
1992 $ hg ci -qAma
1947 $ echo b > b
1993 $ echo b > b
1948 $ hg ci -qAmb
1994 $ hg ci -qAmb
1949 $ hg up -q 0
1995 $ hg up -q 0
1950 $ echo c > c
1996 $ echo c > c
1951 $ hg ci -qAmc
1997 $ hg ci -qAmc
1952 $ hg log -GT "{rev}:{node|short} {desc}\n"
1998 $ hg log -GT "{rev}:{node|short} {desc}\n"
1953 @ 2:d36c0562f908 c
1999 @ 2:d36c0562f908 c
1954 |
2000 |
1955 | o 1:d2ae7f538514 b
2001 | o 1:d2ae7f538514 b
1956 |/
2002 |/
1957 o 0:cb9a9f314b8b a
2003 o 0:cb9a9f314b8b a
1958
2004
1959
2005
1960 Check reporting when --no-commit used with non-applicable options:
2006 Check reporting when --no-commit used with non-applicable options:
1961
2007
1962 $ hg graft 1 --no-commit -e
2008 $ hg graft 1 --no-commit -e
1963 abort: cannot specify --no-commit and --edit together
2009 abort: cannot specify --no-commit and --edit together
1964 [255]
2010 [255]
1965
2011
1966 $ hg graft 1 --no-commit --log
2012 $ hg graft 1 --no-commit --log
1967 abort: cannot specify --no-commit and --log together
2013 abort: cannot specify --no-commit and --log together
1968 [255]
2014 [255]
1969
2015
1970 $ hg graft 1 --no-commit -D
2016 $ hg graft 1 --no-commit -D
1971 abort: cannot specify --no-commit and --currentdate together
2017 abort: cannot specify --no-commit and --currentdate together
1972 [255]
2018 [255]
1973
2019
1974 Test --no-commit is working:
2020 Test --no-commit is working:
1975 $ hg graft 1 --no-commit
2021 $ hg graft 1 --no-commit
1976 grafting 1:d2ae7f538514 "b"
2022 grafting 1:d2ae7f538514 "b"
1977
2023
1978 $ hg log -GT "{rev}:{node|short} {desc}\n"
2024 $ hg log -GT "{rev}:{node|short} {desc}\n"
1979 @ 2:d36c0562f908 c
2025 @ 2:d36c0562f908 c
1980 |
2026 |
1981 | o 1:d2ae7f538514 b
2027 | o 1:d2ae7f538514 b
1982 |/
2028 |/
1983 o 0:cb9a9f314b8b a
2029 o 0:cb9a9f314b8b a
1984
2030
1985
2031
1986 $ hg diff
2032 $ hg diff
1987 diff -r d36c0562f908 b
2033 diff -r d36c0562f908 b
1988 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2034 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1989 +++ b/b Thu Jan 01 00:00:00 1970 +0000
2035 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1990 @@ -0,0 +1,1 @@
2036 @@ -0,0 +1,1 @@
1991 +b
2037 +b
1992
2038
1993 Prepare wrdir to check --no-commit is resepected after --continue:
2039 Prepare wrdir to check --no-commit is resepected after --continue:
1994
2040
1995 $ hg up -qC
2041 $ hg up -qC
1996 $ echo A>a
2042 $ echo A>a
1997 $ hg ci -qm "A in file a"
2043 $ hg ci -qm "A in file a"
1998 $ hg up -q 1
2044 $ hg up -q 1
1999 $ echo B>a
2045 $ echo B>a
2000 $ hg ci -qm "B in file a"
2046 $ hg ci -qm "B in file a"
2001 $ hg log -GT "{rev}:{node|short} {desc}\n"
2047 $ hg log -GT "{rev}:{node|short} {desc}\n"
2002 @ 4:2aa9ad1006ff B in file a
2048 @ 4:2aa9ad1006ff B in file a
2003 |
2049 |
2004 | o 3:09e253b87e17 A in file a
2050 | o 3:09e253b87e17 A in file a
2005 | |
2051 | |
2006 | o 2:d36c0562f908 c
2052 | o 2:d36c0562f908 c
2007 | |
2053 | |
2008 o | 1:d2ae7f538514 b
2054 o | 1:d2ae7f538514 b
2009 |/
2055 |/
2010 o 0:cb9a9f314b8b a
2056 o 0:cb9a9f314b8b a
2011
2057
2012
2058
2013 $ hg graft 3 --no-commit
2059 $ hg graft 3 --no-commit
2014 grafting 3:09e253b87e17 "A in file a"
2060 grafting 3:09e253b87e17 "A in file a"
2015 merging a
2061 merging a
2016 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
2062 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
2017 abort: unresolved conflicts, can't continue
2063 abort: unresolved conflicts, can't continue
2018 (use 'hg resolve' and 'hg graft --continue')
2064 (use 'hg resolve' and 'hg graft --continue')
2019 [255]
2065 [255]
2020
2066
2021 Resolve conflict:
2067 Resolve conflict:
2022 $ echo A>a
2068 $ echo A>a
2023 $ hg resolve --mark
2069 $ hg resolve --mark
2024 (no more unresolved files)
2070 (no more unresolved files)
2025 continue: hg graft --continue
2071 continue: hg graft --continue
2026
2072
2027 $ hg graft --continue
2073 $ hg graft --continue
2028 grafting 3:09e253b87e17 "A in file a"
2074 grafting 3:09e253b87e17 "A in file a"
2029 $ hg log -GT "{rev}:{node|short} {desc}\n"
2075 $ hg log -GT "{rev}:{node|short} {desc}\n"
2030 @ 4:2aa9ad1006ff B in file a
2076 @ 4:2aa9ad1006ff B in file a
2031 |
2077 |
2032 | o 3:09e253b87e17 A in file a
2078 | o 3:09e253b87e17 A in file a
2033 | |
2079 | |
2034 | o 2:d36c0562f908 c
2080 | o 2:d36c0562f908 c
2035 | |
2081 | |
2036 o | 1:d2ae7f538514 b
2082 o | 1:d2ae7f538514 b
2037 |/
2083 |/
2038 o 0:cb9a9f314b8b a
2084 o 0:cb9a9f314b8b a
2039
2085
2040 $ hg diff
2086 $ hg diff
2041 diff -r 2aa9ad1006ff a
2087 diff -r 2aa9ad1006ff a
2042 --- a/a Thu Jan 01 00:00:00 1970 +0000
2088 --- a/a Thu Jan 01 00:00:00 1970 +0000
2043 +++ b/a Thu Jan 01 00:00:00 1970 +0000
2089 +++ b/a Thu Jan 01 00:00:00 1970 +0000
2044 @@ -1,1 +1,1 @@
2090 @@ -1,1 +1,1 @@
2045 -B
2091 -B
2046 +A
2092 +A
2047
2093
2048 $ hg up -qC
2094 $ hg up -qC
2049
2095
2050 Check --no-commit is resepected when passed with --continue:
2096 Check --no-commit is resepected when passed with --continue:
2051
2097
2052 $ hg graft 3
2098 $ hg graft 3
2053 grafting 3:09e253b87e17 "A in file a"
2099 grafting 3:09e253b87e17 "A in file a"
2054 merging a
2100 merging a
2055 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
2101 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
2056 abort: unresolved conflicts, can't continue
2102 abort: unresolved conflicts, can't continue
2057 (use 'hg resolve' and 'hg graft --continue')
2103 (use 'hg resolve' and 'hg graft --continue')
2058 [255]
2104 [255]
2059
2105
2060 Resolve conflict:
2106 Resolve conflict:
2061 $ echo A>a
2107 $ echo A>a
2062 $ hg resolve --mark
2108 $ hg resolve --mark
2063 (no more unresolved files)
2109 (no more unresolved files)
2064 continue: hg graft --continue
2110 continue: hg graft --continue
2065
2111
2066 $ hg graft --continue --no-commit
2112 $ hg graft --continue --no-commit
2067 grafting 3:09e253b87e17 "A in file a"
2113 grafting 3:09e253b87e17 "A in file a"
2068 $ hg diff
2114 $ hg diff
2069 diff -r 2aa9ad1006ff a
2115 diff -r 2aa9ad1006ff a
2070 --- a/a Thu Jan 01 00:00:00 1970 +0000
2116 --- a/a Thu Jan 01 00:00:00 1970 +0000
2071 +++ b/a Thu Jan 01 00:00:00 1970 +0000
2117 +++ b/a Thu Jan 01 00:00:00 1970 +0000
2072 @@ -1,1 +1,1 @@
2118 @@ -1,1 +1,1 @@
2073 -B
2119 -B
2074 +A
2120 +A
2075
2121
2076 $ hg log -GT "{rev}:{node|short} {desc}\n"
2122 $ hg log -GT "{rev}:{node|short} {desc}\n"
2077 @ 4:2aa9ad1006ff B in file a
2123 @ 4:2aa9ad1006ff B in file a
2078 |
2124 |
2079 | o 3:09e253b87e17 A in file a
2125 | o 3:09e253b87e17 A in file a
2080 | |
2126 | |
2081 | o 2:d36c0562f908 c
2127 | o 2:d36c0562f908 c
2082 | |
2128 | |
2083 o | 1:d2ae7f538514 b
2129 o | 1:d2ae7f538514 b
2084 |/
2130 |/
2085 o 0:cb9a9f314b8b a
2131 o 0:cb9a9f314b8b a
2086
2132
2087 $ hg up -qC
2133 $ hg up -qC
2088
2134
2089 Test --no-commit when graft multiple revisions:
2135 Test --no-commit when graft multiple revisions:
2090 When there is conflict:
2136 When there is conflict:
2091 $ hg graft -r "2::3" --no-commit
2137 $ hg graft -r "2::3" --no-commit
2092 grafting 2:d36c0562f908 "c"
2138 grafting 2:d36c0562f908 "c"
2093 grafting 3:09e253b87e17 "A in file a"
2139 grafting 3:09e253b87e17 "A in file a"
2094 merging a
2140 merging a
2095 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
2141 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
2096 abort: unresolved conflicts, can't continue
2142 abort: unresolved conflicts, can't continue
2097 (use 'hg resolve' and 'hg graft --continue')
2143 (use 'hg resolve' and 'hg graft --continue')
2098 [255]
2144 [255]
2099
2145
2100 $ echo A>a
2146 $ echo A>a
2101 $ hg resolve --mark
2147 $ hg resolve --mark
2102 (no more unresolved files)
2148 (no more unresolved files)
2103 continue: hg graft --continue
2149 continue: hg graft --continue
2104 $ hg graft --continue
2150 $ hg graft --continue
2105 grafting 3:09e253b87e17 "A in file a"
2151 grafting 3:09e253b87e17 "A in file a"
2106 $ hg diff
2152 $ hg diff
2107 diff -r 2aa9ad1006ff a
2153 diff -r 2aa9ad1006ff a
2108 --- a/a Thu Jan 01 00:00:00 1970 +0000
2154 --- a/a Thu Jan 01 00:00:00 1970 +0000
2109 +++ b/a Thu Jan 01 00:00:00 1970 +0000
2155 +++ b/a Thu Jan 01 00:00:00 1970 +0000
2110 @@ -1,1 +1,1 @@
2156 @@ -1,1 +1,1 @@
2111 -B
2157 -B
2112 +A
2158 +A
2113 diff -r 2aa9ad1006ff c
2159 diff -r 2aa9ad1006ff c
2114 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2160 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2115 +++ b/c Thu Jan 01 00:00:00 1970 +0000
2161 +++ b/c Thu Jan 01 00:00:00 1970 +0000
2116 @@ -0,0 +1,1 @@
2162 @@ -0,0 +1,1 @@
2117 +c
2163 +c
2118
2164
2119 $ hg log -GT "{rev}:{node|short} {desc}\n"
2165 $ hg log -GT "{rev}:{node|short} {desc}\n"
2120 @ 4:2aa9ad1006ff B in file a
2166 @ 4:2aa9ad1006ff B in file a
2121 |
2167 |
2122 | o 3:09e253b87e17 A in file a
2168 | o 3:09e253b87e17 A in file a
2123 | |
2169 | |
2124 | o 2:d36c0562f908 c
2170 | o 2:d36c0562f908 c
2125 | |
2171 | |
2126 o | 1:d2ae7f538514 b
2172 o | 1:d2ae7f538514 b
2127 |/
2173 |/
2128 o 0:cb9a9f314b8b a
2174 o 0:cb9a9f314b8b a
2129
2175
2130 $ hg up -qC
2176 $ hg up -qC
2131
2177
2132 When there is no conflict:
2178 When there is no conflict:
2133 $ echo d>d
2179 $ echo d>d
2134 $ hg add d -q
2180 $ hg add d -q
2135 $ hg ci -qmd
2181 $ hg ci -qmd
2136 $ hg up 3 -q
2182 $ hg up 3 -q
2137 $ hg log -GT "{rev}:{node|short} {desc}\n"
2183 $ hg log -GT "{rev}:{node|short} {desc}\n"
2138 o 5:baefa8927fc0 d
2184 o 5:baefa8927fc0 d
2139 |
2185 |
2140 o 4:2aa9ad1006ff B in file a
2186 o 4:2aa9ad1006ff B in file a
2141 |
2187 |
2142 | @ 3:09e253b87e17 A in file a
2188 | @ 3:09e253b87e17 A in file a
2143 | |
2189 | |
2144 | o 2:d36c0562f908 c
2190 | o 2:d36c0562f908 c
2145 | |
2191 | |
2146 o | 1:d2ae7f538514 b
2192 o | 1:d2ae7f538514 b
2147 |/
2193 |/
2148 o 0:cb9a9f314b8b a
2194 o 0:cb9a9f314b8b a
2149
2195
2150
2196
2151 $ hg graft -r 1 -r 5 --no-commit
2197 $ hg graft -r 1 -r 5 --no-commit
2152 grafting 1:d2ae7f538514 "b"
2198 grafting 1:d2ae7f538514 "b"
2153 grafting 5:baefa8927fc0 "d" (tip)
2199 grafting 5:baefa8927fc0 "d" (tip)
2154 $ hg diff
2200 $ hg diff
2155 diff -r 09e253b87e17 b
2201 diff -r 09e253b87e17 b
2156 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2202 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2157 +++ b/b Thu Jan 01 00:00:00 1970 +0000
2203 +++ b/b Thu Jan 01 00:00:00 1970 +0000
2158 @@ -0,0 +1,1 @@
2204 @@ -0,0 +1,1 @@
2159 +b
2205 +b
2160 diff -r 09e253b87e17 d
2206 diff -r 09e253b87e17 d
2161 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2207 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2162 +++ b/d Thu Jan 01 00:00:00 1970 +0000
2208 +++ b/d Thu Jan 01 00:00:00 1970 +0000
2163 @@ -0,0 +1,1 @@
2209 @@ -0,0 +1,1 @@
2164 +d
2210 +d
2165 $ hg log -GT "{rev}:{node|short} {desc}\n"
2211 $ hg log -GT "{rev}:{node|short} {desc}\n"
2166 o 5:baefa8927fc0 d
2212 o 5:baefa8927fc0 d
2167 |
2213 |
2168 o 4:2aa9ad1006ff B in file a
2214 o 4:2aa9ad1006ff B in file a
2169 |
2215 |
2170 | @ 3:09e253b87e17 A in file a
2216 | @ 3:09e253b87e17 A in file a
2171 | |
2217 | |
2172 | o 2:d36c0562f908 c
2218 | o 2:d36c0562f908 c
2173 | |
2219 | |
2174 o | 1:d2ae7f538514 b
2220 o | 1:d2ae7f538514 b
2175 |/
2221 |/
2176 o 0:cb9a9f314b8b a
2222 o 0:cb9a9f314b8b a
2177
2223
2178 $ cd ..
2224 $ cd ..
@@ -1,412 +1,414 b''
1 #require serve
1 #require serve
2
2
3 This test is a duplicate of 'test-http.t', feel free to factor out
3 This test is a duplicate of 'test-http.t', feel free to factor out
4 parts that are not bundle1/bundle2 specific.
4 parts that are not bundle1/bundle2 specific.
5
5
6 $ cat << EOF >> $HGRCPATH
6 $ cat << EOF >> $HGRCPATH
7 > [devel]
7 > [devel]
8 > # This test is dedicated to interaction through old bundle
8 > # This test is dedicated to interaction through old bundle
9 > legacy.exchange = bundle1
9 > legacy.exchange = bundle1
10 > EOF
10 > EOF
11
11
12 $ hg init test
12 $ hg init test
13 $ cd test
13 $ cd test
14 $ echo foo>foo
14 $ echo foo>foo
15 $ mkdir foo.d foo.d/bAr.hg.d foo.d/baR.d.hg
15 $ mkdir foo.d foo.d/bAr.hg.d foo.d/baR.d.hg
16 $ echo foo>foo.d/foo
16 $ echo foo>foo.d/foo
17 $ echo bar>foo.d/bAr.hg.d/BaR
17 $ echo bar>foo.d/bAr.hg.d/BaR
18 $ echo bar>foo.d/baR.d.hg/bAR
18 $ echo bar>foo.d/baR.d.hg/bAR
19 $ hg commit -A -m 1
19 $ hg commit -A -m 1
20 adding foo
20 adding foo
21 adding foo.d/bAr.hg.d/BaR
21 adding foo.d/bAr.hg.d/BaR
22 adding foo.d/baR.d.hg/bAR
22 adding foo.d/baR.d.hg/bAR
23 adding foo.d/foo
23 adding foo.d/foo
24 $ hg serve -p $HGPORT -d --pid-file=../hg1.pid -E ../error.log
24 $ hg serve -p $HGPORT -d --pid-file=../hg1.pid -E ../error.log
25 $ hg serve --config server.uncompressed=False -p $HGPORT1 -d --pid-file=../hg2.pid
25 $ hg serve --config server.uncompressed=False -p $HGPORT1 -d --pid-file=../hg2.pid
26
26
27 Test server address cannot be reused
27 Test server address cannot be reused
28
28
29 $ hg serve -p $HGPORT1 2>&1
29 $ hg serve -p $HGPORT1 2>&1
30 abort: cannot start server at 'localhost:$HGPORT1': $EADDRINUSE$
30 abort: cannot start server at 'localhost:$HGPORT1': $EADDRINUSE$
31 [255]
31 [255]
32
32
33 $ cd ..
33 $ cd ..
34 $ cat hg1.pid hg2.pid >> $DAEMON_PIDS
34 $ cat hg1.pid hg2.pid >> $DAEMON_PIDS
35
35
36 clone via stream
36 clone via stream
37
37
38 #if no-reposimplestore
38 #if no-reposimplestore
39 $ hg clone --stream http://localhost:$HGPORT/ copy 2>&1
39 $ hg clone --stream http://localhost:$HGPORT/ copy 2>&1
40 streaming all changes
40 streaming all changes
41 6 files to transfer, 606 bytes of data
41 6 files to transfer, 606 bytes of data
42 transferred * bytes in * seconds (*/sec) (glob)
42 transferred * bytes in * seconds (*/sec) (glob)
43 searching for changes
43 searching for changes
44 no changes found
44 no changes found
45 updating to branch default
45 updating to branch default
46 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
46 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
47 $ hg verify -R copy
47 $ hg verify -R copy
48 checking changesets
48 checking changesets
49 checking manifests
49 checking manifests
50 crosschecking files in changesets and manifests
50 crosschecking files in changesets and manifests
51 checking files
51 checking files
52 checked 1 changesets with 4 changes to 4 files
52 checked 1 changesets with 4 changes to 4 files
53 #endif
53 #endif
54
54
55 try to clone via stream, should use pull instead
55 try to clone via stream, should use pull instead
56
56
57 $ hg clone --stream http://localhost:$HGPORT1/ copy2
57 $ hg clone --stream http://localhost:$HGPORT1/ copy2
58 warning: stream clone requested but server has them disabled
58 warning: stream clone requested but server has them disabled
59 requesting all changes
59 requesting all changes
60 adding changesets
60 adding changesets
61 adding manifests
61 adding manifests
62 adding file changes
62 adding file changes
63 added 1 changesets with 4 changes to 4 files
63 added 1 changesets with 4 changes to 4 files
64 new changesets 8b6053c928fe
64 new changesets 8b6053c928fe
65 updating to branch default
65 updating to branch default
66 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
66 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
67
67
68 try to clone via stream but missing requirements, so should use pull instead
68 try to clone via stream but missing requirements, so should use pull instead
69
69
70 $ cat > $TESTTMP/removesupportedformat.py << EOF
70 $ cat > $TESTTMP/removesupportedformat.py << EOF
71 > from mercurial import localrepo
71 > from mercurial import localrepo
72 > def extsetup(ui):
72 > def extsetup(ui):
73 > localrepo.localrepository.supportedformats.remove(b'generaldelta')
73 > localrepo.localrepository.supportedformats.remove(b'generaldelta')
74 > EOF
74 > EOF
75
75
76 $ hg clone --config extensions.rsf=$TESTTMP/removesupportedformat.py --stream http://localhost:$HGPORT/ copy3
76 $ hg clone --config extensions.rsf=$TESTTMP/removesupportedformat.py --stream http://localhost:$HGPORT/ copy3
77 warning: stream clone requested but client is missing requirements: generaldelta
77 warning: stream clone requested but client is missing requirements: generaldelta
78 (see https://www.mercurial-scm.org/wiki/MissingRequirement for more information)
78 (see https://www.mercurial-scm.org/wiki/MissingRequirement for more information)
79 requesting all changes
79 requesting all changes
80 adding changesets
80 adding changesets
81 adding manifests
81 adding manifests
82 adding file changes
82 adding file changes
83 added 1 changesets with 4 changes to 4 files
83 added 1 changesets with 4 changes to 4 files
84 new changesets 8b6053c928fe
84 new changesets 8b6053c928fe
85 updating to branch default
85 updating to branch default
86 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
86 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
87
87
88 clone via pull
88 clone via pull
89
89
90 $ hg clone http://localhost:$HGPORT1/ copy-pull
90 $ hg clone http://localhost:$HGPORT1/ copy-pull
91 requesting all changes
91 requesting all changes
92 adding changesets
92 adding changesets
93 adding manifests
93 adding manifests
94 adding file changes
94 adding file changes
95 added 1 changesets with 4 changes to 4 files
95 added 1 changesets with 4 changes to 4 files
96 new changesets 8b6053c928fe
96 new changesets 8b6053c928fe
97 updating to branch default
97 updating to branch default
98 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
98 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
99 $ hg verify -R copy-pull
99 $ hg verify -R copy-pull
100 checking changesets
100 checking changesets
101 checking manifests
101 checking manifests
102 crosschecking files in changesets and manifests
102 crosschecking files in changesets and manifests
103 checking files
103 checking files
104 checked 1 changesets with 4 changes to 4 files
104 checked 1 changesets with 4 changes to 4 files
105 $ cd test
105 $ cd test
106 $ echo bar > bar
106 $ echo bar > bar
107 $ hg commit -A -d '1 0' -m 2
107 $ hg commit -A -d '1 0' -m 2
108 adding bar
108 adding bar
109 $ cd ..
109 $ cd ..
110
110
111 clone over http with --update
111 clone over http with --update
112
112
113 $ hg clone http://localhost:$HGPORT1/ updated --update 0
113 $ hg clone http://localhost:$HGPORT1/ updated --update 0
114 requesting all changes
114 requesting all changes
115 adding changesets
115 adding changesets
116 adding manifests
116 adding manifests
117 adding file changes
117 adding file changes
118 added 2 changesets with 5 changes to 5 files
118 added 2 changesets with 5 changes to 5 files
119 new changesets 8b6053c928fe:5fed3813f7f5
119 new changesets 8b6053c928fe:5fed3813f7f5
120 updating to branch default
120 updating to branch default
121 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
121 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
122 $ hg log -r . -R updated
122 $ hg log -r . -R updated
123 changeset: 0:8b6053c928fe
123 changeset: 0:8b6053c928fe
124 user: test
124 user: test
125 date: Thu Jan 01 00:00:00 1970 +0000
125 date: Thu Jan 01 00:00:00 1970 +0000
126 summary: 1
126 summary: 1
127
127
128 $ rm -rf updated
128 $ rm -rf updated
129
129
130 incoming via HTTP
130 incoming via HTTP
131
131
132 $ hg clone http://localhost:$HGPORT1/ --rev 0 partial
132 $ hg clone http://localhost:$HGPORT1/ --rev 0 partial
133 adding changesets
133 adding changesets
134 adding manifests
134 adding manifests
135 adding file changes
135 adding file changes
136 added 1 changesets with 4 changes to 4 files
136 added 1 changesets with 4 changes to 4 files
137 new changesets 8b6053c928fe
137 new changesets 8b6053c928fe
138 updating to branch default
138 updating to branch default
139 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
139 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
140 $ cd partial
140 $ cd partial
141 $ touch LOCAL
141 $ touch LOCAL
142 $ hg ci -qAm LOCAL
142 $ hg ci -qAm LOCAL
143 $ hg incoming http://localhost:$HGPORT1/ --template '{desc}\n'
143 $ hg incoming http://localhost:$HGPORT1/ --template '{desc}\n'
144 comparing with http://localhost:$HGPORT1/
144 comparing with http://localhost:$HGPORT1/
145 searching for changes
145 searching for changes
146 2
146 2
147 $ cd ..
147 $ cd ..
148
148
149 pull
149 pull
150
150
151 $ cd copy-pull
151 $ cd copy-pull
152 $ cat >> .hg/hgrc <<EOF
152 $ cat >> .hg/hgrc <<EOF
153 > [hooks]
153 > [hooks]
154 > changegroup = sh -c "printenv.py changegroup"
154 > changegroup = sh -c "printenv.py changegroup"
155 > EOF
155 > EOF
156 $ hg pull
156 $ hg pull
157 pulling from http://localhost:$HGPORT1/
157 pulling from http://localhost:$HGPORT1/
158 searching for changes
158 searching for changes
159 adding changesets
159 adding changesets
160 adding manifests
160 adding manifests
161 adding file changes
161 adding file changes
162 added 1 changesets with 1 changes to 1 files
162 added 1 changesets with 1 changes to 1 files
163 new changesets 5fed3813f7f5
163 new changesets 5fed3813f7f5
164 changegroup hook: HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_NODE_LAST=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_URL=http://localhost:$HGPORT1/
164 changegroup hook: HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_NODE_LAST=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_URL=http://localhost:$HGPORT1/
165 (run 'hg update' to get a working copy)
165 (run 'hg update' to get a working copy)
166 $ cd ..
166 $ cd ..
167
167
168 clone from invalid URL
168 clone from invalid URL
169
169
170 $ hg clone http://localhost:$HGPORT/bad
170 $ hg clone http://localhost:$HGPORT/bad
171 abort: HTTP Error 404: Not Found
171 abort: HTTP Error 404: Not Found
172 [255]
172 [255]
173
173
174 test http authentication
174 test http authentication
175 + use the same server to test server side streaming preference
175 + use the same server to test server side streaming preference
176
176
177 $ cd test
177 $ cd test
178 $ cat << EOT > userpass.py
178 $ cat << EOT > userpass.py
179 > import base64
179 > import base64
180 > from mercurial.hgweb import common
180 > from mercurial.hgweb import common
181 > def perform_authentication(hgweb, req, op):
181 > def perform_authentication(hgweb, req, op):
182 > auth = req.headers.get(b'Authorization')
182 > auth = req.headers.get(b'Authorization')
183 > if not auth:
183 > if not auth:
184 > raise common.ErrorResponse(common.HTTP_UNAUTHORIZED, b'who',
184 > raise common.ErrorResponse(common.HTTP_UNAUTHORIZED, b'who',
185 > [(b'WWW-Authenticate', b'Basic Realm="mercurial"')])
185 > [(b'WWW-Authenticate', b'Basic Realm="mercurial"')])
186 > if base64.b64decode(auth.split()[1]).split(b':', 1) != [b'user',
186 > if base64.b64decode(auth.split()[1]).split(b':', 1) != [b'user',
187 > b'pass']:
187 > b'pass']:
188 > raise common.ErrorResponse(common.HTTP_FORBIDDEN, b'no')
188 > raise common.ErrorResponse(common.HTTP_FORBIDDEN, b'no')
189 > def extsetup():
189 > def extsetup():
190 > common.permhooks.insert(0, perform_authentication)
190 > common.permhooks.insert(0, perform_authentication)
191 > EOT
191 > EOT
192 $ hg serve --config extensions.x=userpass.py -p $HGPORT2 -d --pid-file=pid \
192 $ hg serve --config extensions.x=userpass.py -p $HGPORT2 -d --pid-file=pid \
193 > --config server.preferuncompressed=True \
193 > --config server.preferuncompressed=True \
194 > --config web.push_ssl=False --config web.allow_push=* -A ../access.log
194 > --config web.push_ssl=False --config web.allow_push=* -A ../access.log
195 $ cat pid >> $DAEMON_PIDS
195 $ cat pid >> $DAEMON_PIDS
196
196
197 $ cat << EOF > get_pass.py
197 $ cat << EOF > get_pass.py
198 > import getpass
198 > import getpass
199 > def newgetpass(arg):
199 > def newgetpass(arg):
200 > return "pass"
200 > return "pass"
201 > getpass.getpass = newgetpass
201 > getpass.getpass = newgetpass
202 > EOF
202 > EOF
203
203
204 $ hg id http://localhost:$HGPORT2/
204 $ hg id http://localhost:$HGPORT2/
205 abort: http authorization required for http://localhost:$HGPORT2/
205 abort: http authorization required for http://localhost:$HGPORT2/
206 [255]
206 [255]
207 $ hg id http://localhost:$HGPORT2/
207 $ hg id http://localhost:$HGPORT2/
208 abort: http authorization required for http://localhost:$HGPORT2/
208 abort: http authorization required for http://localhost:$HGPORT2/
209 [255]
209 [255]
210 $ hg id --config ui.interactive=true --config extensions.getpass=get_pass.py http://user@localhost:$HGPORT2/
210 $ hg id --config ui.interactive=true --config extensions.getpass=get_pass.py http://user@localhost:$HGPORT2/
211 http authorization required for http://localhost:$HGPORT2/
211 http authorization required for http://localhost:$HGPORT2/
212 realm: mercurial
212 realm: mercurial
213 user: user
213 user: user
214 password: 5fed3813f7f5
214 password: 5fed3813f7f5
215 $ hg id http://user:pass@localhost:$HGPORT2/
215 $ hg id http://user:pass@localhost:$HGPORT2/
216 5fed3813f7f5
216 5fed3813f7f5
217 $ echo '[auth]' >> .hg/hgrc
217 $ echo '[auth]' >> .hg/hgrc
218 $ echo 'l.schemes=http' >> .hg/hgrc
218 $ echo 'l.schemes=http' >> .hg/hgrc
219 $ echo 'l.prefix=lo' >> .hg/hgrc
219 $ echo 'l.prefix=lo' >> .hg/hgrc
220 $ echo 'l.username=user' >> .hg/hgrc
220 $ echo 'l.username=user' >> .hg/hgrc
221 $ echo 'l.password=pass' >> .hg/hgrc
221 $ echo 'l.password=pass' >> .hg/hgrc
222 $ hg id http://localhost:$HGPORT2/
222 $ hg id http://localhost:$HGPORT2/
223 5fed3813f7f5
223 5fed3813f7f5
224 $ hg id http://localhost:$HGPORT2/
224 $ hg id http://localhost:$HGPORT2/
225 5fed3813f7f5
225 5fed3813f7f5
226 $ hg id http://user@localhost:$HGPORT2/
226 $ hg id http://user@localhost:$HGPORT2/
227 5fed3813f7f5
227 5fed3813f7f5
228
228
229 #if no-reposimplestore
229 #if no-reposimplestore
230 $ hg clone http://user:pass@localhost:$HGPORT2/ dest 2>&1
230 $ hg clone http://user:pass@localhost:$HGPORT2/ dest 2>&1
231 streaming all changes
231 streaming all changes
232 7 files to transfer, 916 bytes of data
232 7 files to transfer, 916 bytes of data
233 transferred * bytes in * seconds (*/sec) (glob)
233 transferred * bytes in * seconds (*/sec) (glob)
234 searching for changes
234 searching for changes
235 no changes found
235 no changes found
236 updating to branch default
236 updating to branch default
237 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
237 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
238 #endif
238 #endif
239
239
240 --pull should override server's preferuncompressed
240 --pull should override server's preferuncompressed
241
241
242 $ hg clone --pull http://user:pass@localhost:$HGPORT2/ dest-pull 2>&1
242 $ hg clone --pull http://user:pass@localhost:$HGPORT2/ dest-pull 2>&1
243 requesting all changes
243 requesting all changes
244 adding changesets
244 adding changesets
245 adding manifests
245 adding manifests
246 adding file changes
246 adding file changes
247 added 2 changesets with 5 changes to 5 files
247 added 2 changesets with 5 changes to 5 files
248 new changesets 8b6053c928fe:5fed3813f7f5
248 new changesets 8b6053c928fe:5fed3813f7f5
249 updating to branch default
249 updating to branch default
250 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
250 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
251
251
252 $ hg id http://user2@localhost:$HGPORT2/
252 $ hg id http://user2@localhost:$HGPORT2/
253 abort: http authorization required for http://localhost:$HGPORT2/
253 abort: http authorization required for http://localhost:$HGPORT2/
254 [255]
254 [255]
255 $ hg id http://user:pass2@localhost:$HGPORT2/
255 $ hg id http://user:pass2@localhost:$HGPORT2/
256 abort: HTTP Error 403: no
256 abort: HTTP Error 403: no
257 [255]
257 [255]
258
258
259 $ hg -R dest-pull tag -r tip top
259 $ hg -R dest-pull tag -r tip top
260 $ hg -R dest-pull push http://user:pass@localhost:$HGPORT2/
260 $ hg -R dest-pull push http://user:pass@localhost:$HGPORT2/
261 pushing to http://user:***@localhost:$HGPORT2/
261 pushing to http://user:***@localhost:$HGPORT2/
262 searching for changes
262 searching for changes
263 remote: adding changesets
263 remote: adding changesets
264 remote: adding manifests
264 remote: adding manifests
265 remote: adding file changes
265 remote: adding file changes
266 remote: added 1 changesets with 1 changes to 1 files
266 remote: added 1 changesets with 1 changes to 1 files
267 $ hg rollback -q
267 $ hg rollback -q
268
268
269 $ sed 's/.*] "/"/' < ../access.log
269 $ sed 's/.*] "/"/' < ../access.log
270 "GET /?cmd=capabilities HTTP/1.1" 401 -
270 "GET /?cmd=capabilities HTTP/1.1" 401 -
271 "GET /?cmd=capabilities HTTP/1.1" 401 -
271 "GET /?cmd=capabilities HTTP/1.1" 401 -
272 "GET /?cmd=capabilities HTTP/1.1" 401 -
272 "GET /?cmd=capabilities HTTP/1.1" 401 -
273 "GET /?cmd=capabilities HTTP/1.1" 200 -
273 "GET /?cmd=capabilities HTTP/1.1" 200 -
274 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
274 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
275 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
275 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
276 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
276 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
277 "GET /?cmd=capabilities HTTP/1.1" 401 -
277 "GET /?cmd=capabilities HTTP/1.1" 401 -
278 "GET /?cmd=capabilities HTTP/1.1" 200 -
278 "GET /?cmd=capabilities HTTP/1.1" 200 -
279 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
279 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
280 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
280 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
281 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
281 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
282 "GET /?cmd=capabilities HTTP/1.1" 401 -
282 "GET /?cmd=capabilities HTTP/1.1" 401 -
283 "GET /?cmd=capabilities HTTP/1.1" 200 -
283 "GET /?cmd=capabilities HTTP/1.1" 200 -
284 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
284 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
285 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
285 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
286 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
286 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
287 "GET /?cmd=capabilities HTTP/1.1" 401 -
287 "GET /?cmd=capabilities HTTP/1.1" 401 -
288 "GET /?cmd=capabilities HTTP/1.1" 200 -
288 "GET /?cmd=capabilities HTTP/1.1" 200 -
289 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
289 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
290 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
290 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
291 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
291 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
292 "GET /?cmd=capabilities HTTP/1.1" 401 -
292 "GET /?cmd=capabilities HTTP/1.1" 401 -
293 "GET /?cmd=capabilities HTTP/1.1" 200 -
293 "GET /?cmd=capabilities HTTP/1.1" 200 -
294 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
294 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
295 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
295 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
296 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
296 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
297 "GET /?cmd=capabilities HTTP/1.1" 401 - (no-reposimplestore !)
297 "GET /?cmd=capabilities HTTP/1.1" 401 - (no-reposimplestore !)
298 "GET /?cmd=capabilities HTTP/1.1" 200 - (no-reposimplestore !)
298 "GET /?cmd=capabilities HTTP/1.1" 200 - (no-reposimplestore !)
299 "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
299 "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
300 "GET /?cmd=stream_out HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
300 "GET /?cmd=stream_out HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
301 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
301 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
302 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D5fed3813f7f5e1824344fdc9cf8f63bb662c292d x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
302 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D5fed3813f7f5e1824344fdc9cf8f63bb662c292d x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
303 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
303 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
304 "GET /?cmd=capabilities HTTP/1.1" 401 - (no-reposimplestore !)
304 "GET /?cmd=capabilities HTTP/1.1" 401 - (no-reposimplestore !)
305 "GET /?cmd=capabilities HTTP/1.1" 200 - (no-reposimplestore !)
305 "GET /?cmd=capabilities HTTP/1.1" 200 - (no-reposimplestore !)
306 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
306 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
307 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
307 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
308 "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:common=0000000000000000000000000000000000000000&heads=5fed3813f7f5e1824344fdc9cf8f63bb662c292d x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
308 "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:common=0000000000000000000000000000000000000000&heads=5fed3813f7f5e1824344fdc9cf8f63bb662c292d x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
309 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
309 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
310 "GET /?cmd=capabilities HTTP/1.1" 401 -
310 "GET /?cmd=capabilities HTTP/1.1" 401 -
311 "GET /?cmd=capabilities HTTP/1.1" 401 -
311 "GET /?cmd=capabilities HTTP/1.1" 401 -
312 "GET /?cmd=capabilities HTTP/1.1" 403 -
312 "GET /?cmd=capabilities HTTP/1.1" 403 -
313 "GET /?cmd=capabilities HTTP/1.1" 401 -
313 "GET /?cmd=capabilities HTTP/1.1" 401 -
314 "GET /?cmd=capabilities HTTP/1.1" 200 -
314 "GET /?cmd=capabilities HTTP/1.1" 200 -
315 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D7f4e523d01f2cc3765ac8934da3d14db775ff872 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
315 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D7f4e523d01f2cc3765ac8934da3d14db775ff872 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
316 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
316 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
317 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
317 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
318 "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
318 "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
319 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
319 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
320 "POST /?cmd=unbundle HTTP/1.1" 200 - x-hgarg-1:heads=686173686564+5eb5abfefeea63c80dd7553bcc3783f37e0c5524* (glob)
320 "POST /?cmd=unbundle HTTP/1.1" 200 - x-hgarg-1:heads=686173686564+5eb5abfefeea63c80dd7553bcc3783f37e0c5524* (glob)
321 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
321 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
322
322
323 $ cd ..
323 $ cd ..
324
324
325 clone of serve with repo in root and unserved subrepo (issue2970)
325 clone of serve with repo in root and unserved subrepo (issue2970)
326
326
327 $ hg --cwd test init sub
327 $ hg --cwd test init sub
328 $ echo empty > test/sub/empty
328 $ echo empty > test/sub/empty
329 $ hg --cwd test/sub add empty
329 $ hg --cwd test/sub add empty
330 $ hg --cwd test/sub commit -qm 'add empty'
330 $ hg --cwd test/sub commit -qm 'add empty'
331 $ hg --cwd test/sub tag -r 0 something
331 $ hg --cwd test/sub tag -r 0 something
332 $ echo sub = sub > test/.hgsub
332 $ echo sub = sub > test/.hgsub
333 $ hg --cwd test add .hgsub
333 $ hg --cwd test add .hgsub
334 $ hg --cwd test commit -qm 'add subrepo'
334 $ hg --cwd test commit -qm 'add subrepo'
335 $ hg clone http://localhost:$HGPORT noslash-clone
335 $ hg clone http://localhost:$HGPORT noslash-clone
336 requesting all changes
336 requesting all changes
337 adding changesets
337 adding changesets
338 adding manifests
338 adding manifests
339 adding file changes
339 adding file changes
340 added 3 changesets with 7 changes to 7 files
340 added 3 changesets with 7 changes to 7 files
341 new changesets 8b6053c928fe:56f9bc90cce6
341 new changesets 8b6053c928fe:56f9bc90cce6
342 updating to branch default
342 updating to branch default
343 cloning subrepo sub from http://localhost:$HGPORT/sub
343 abort: HTTP Error 404: Not Found
344 abort: HTTP Error 404: Not Found
344 [255]
345 [255]
345 $ hg clone http://localhost:$HGPORT/ slash-clone
346 $ hg clone http://localhost:$HGPORT/ slash-clone
346 requesting all changes
347 requesting all changes
347 adding changesets
348 adding changesets
348 adding manifests
349 adding manifests
349 adding file changes
350 adding file changes
350 added 3 changesets with 7 changes to 7 files
351 added 3 changesets with 7 changes to 7 files
351 new changesets 8b6053c928fe:56f9bc90cce6
352 new changesets 8b6053c928fe:56f9bc90cce6
352 updating to branch default
353 updating to branch default
354 cloning subrepo sub from http://localhost:$HGPORT/sub
353 abort: HTTP Error 404: Not Found
355 abort: HTTP Error 404: Not Found
354 [255]
356 [255]
355
357
356 check error log
358 check error log
357
359
358 $ cat error.log
360 $ cat error.log
359
361
360 Check error reporting while pulling/cloning
362 Check error reporting while pulling/cloning
361
363
362 $ $RUNTESTDIR/killdaemons.py
364 $ $RUNTESTDIR/killdaemons.py
363 $ hg serve -R test -p $HGPORT -d --pid-file=hg3.pid -E error.log --config extensions.crash=${TESTDIR}/crashgetbundler.py
365 $ hg serve -R test -p $HGPORT -d --pid-file=hg3.pid -E error.log --config extensions.crash=${TESTDIR}/crashgetbundler.py
364 $ cat hg3.pid >> $DAEMON_PIDS
366 $ cat hg3.pid >> $DAEMON_PIDS
365 $ hg clone http://localhost:$HGPORT/ abort-clone
367 $ hg clone http://localhost:$HGPORT/ abort-clone
366 requesting all changes
368 requesting all changes
367 abort: remote error:
369 abort: remote error:
368 this is an exercise
370 this is an exercise
369 [255]
371 [255]
370 $ cat error.log
372 $ cat error.log
371
373
372 disable pull-based clones
374 disable pull-based clones
373
375
374 $ hg serve -R test -p $HGPORT1 -d --pid-file=hg4.pid -E error.log --config server.disablefullbundle=True
376 $ hg serve -R test -p $HGPORT1 -d --pid-file=hg4.pid -E error.log --config server.disablefullbundle=True
375 $ cat hg4.pid >> $DAEMON_PIDS
377 $ cat hg4.pid >> $DAEMON_PIDS
376 $ hg clone http://localhost:$HGPORT1/ disable-pull-clone
378 $ hg clone http://localhost:$HGPORT1/ disable-pull-clone
377 requesting all changes
379 requesting all changes
378 abort: remote error:
380 abort: remote error:
379 server has pull-based clones disabled
381 server has pull-based clones disabled
380 [255]
382 [255]
381
383
382 #if no-reposimplestore
384 #if no-reposimplestore
383 ... but keep stream clones working
385 ... but keep stream clones working
384
386
385 $ hg clone --stream --noupdate http://localhost:$HGPORT1/ test-stream-clone
387 $ hg clone --stream --noupdate http://localhost:$HGPORT1/ test-stream-clone
386 streaming all changes
388 streaming all changes
387 * files to transfer, * of data (glob)
389 * files to transfer, * of data (glob)
388 transferred * in * seconds (* KB/sec) (glob)
390 transferred * in * seconds (* KB/sec) (glob)
389 searching for changes
391 searching for changes
390 no changes found
392 no changes found
391 #endif
393 #endif
392
394
393 ... and also keep partial clones and pulls working
395 ... and also keep partial clones and pulls working
394 $ hg clone http://localhost:$HGPORT1 --rev 0 test-partial-clone
396 $ hg clone http://localhost:$HGPORT1 --rev 0 test-partial-clone
395 adding changesets
397 adding changesets
396 adding manifests
398 adding manifests
397 adding file changes
399 adding file changes
398 added 1 changesets with 4 changes to 4 files
400 added 1 changesets with 4 changes to 4 files
399 new changesets 8b6053c928fe
401 new changesets 8b6053c928fe
400 updating to branch default
402 updating to branch default
401 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
403 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
402 $ hg pull -R test-partial-clone
404 $ hg pull -R test-partial-clone
403 pulling from http://localhost:$HGPORT1/
405 pulling from http://localhost:$HGPORT1/
404 searching for changes
406 searching for changes
405 adding changesets
407 adding changesets
406 adding manifests
408 adding manifests
407 adding file changes
409 adding file changes
408 added 2 changesets with 3 changes to 3 files
410 added 2 changesets with 3 changes to 3 files
409 new changesets 5fed3813f7f5:56f9bc90cce6
411 new changesets 5fed3813f7f5:56f9bc90cce6
410 (run 'hg update' to get a working copy)
412 (run 'hg update' to get a working copy)
411
413
412 $ cat error.log
414 $ cat error.log
@@ -1,555 +1,557 b''
1 #require serve
1 #require serve
2
2
3 $ hg init test
3 $ hg init test
4 $ cd test
4 $ cd test
5 $ echo foo>foo
5 $ echo foo>foo
6 $ mkdir foo.d foo.d/bAr.hg.d foo.d/baR.d.hg
6 $ mkdir foo.d foo.d/bAr.hg.d foo.d/baR.d.hg
7 $ echo foo>foo.d/foo
7 $ echo foo>foo.d/foo
8 $ echo bar>foo.d/bAr.hg.d/BaR
8 $ echo bar>foo.d/bAr.hg.d/BaR
9 $ echo bar>foo.d/baR.d.hg/bAR
9 $ echo bar>foo.d/baR.d.hg/bAR
10 $ hg commit -A -m 1
10 $ hg commit -A -m 1
11 adding foo
11 adding foo
12 adding foo.d/bAr.hg.d/BaR
12 adding foo.d/bAr.hg.d/BaR
13 adding foo.d/baR.d.hg/bAR
13 adding foo.d/baR.d.hg/bAR
14 adding foo.d/foo
14 adding foo.d/foo
15 $ hg serve -p $HGPORT -d --pid-file=../hg1.pid -E ../error.log
15 $ hg serve -p $HGPORT -d --pid-file=../hg1.pid -E ../error.log
16 $ hg serve --config server.uncompressed=False -p $HGPORT1 -d --pid-file=../hg2.pid
16 $ hg serve --config server.uncompressed=False -p $HGPORT1 -d --pid-file=../hg2.pid
17
17
18 Test server address cannot be reused
18 Test server address cannot be reused
19
19
20 $ hg serve -p $HGPORT1 2>&1
20 $ hg serve -p $HGPORT1 2>&1
21 abort: cannot start server at 'localhost:$HGPORT1': $EADDRINUSE$
21 abort: cannot start server at 'localhost:$HGPORT1': $EADDRINUSE$
22 [255]
22 [255]
23
23
24 $ cd ..
24 $ cd ..
25 $ cat hg1.pid hg2.pid >> $DAEMON_PIDS
25 $ cat hg1.pid hg2.pid >> $DAEMON_PIDS
26
26
27 clone via stream
27 clone via stream
28
28
29 #if no-reposimplestore
29 #if no-reposimplestore
30 $ hg clone --stream http://localhost:$HGPORT/ copy 2>&1
30 $ hg clone --stream http://localhost:$HGPORT/ copy 2>&1
31 streaming all changes
31 streaming all changes
32 9 files to transfer, 715 bytes of data
32 9 files to transfer, 715 bytes of data
33 transferred * bytes in * seconds (*/sec) (glob)
33 transferred * bytes in * seconds (*/sec) (glob)
34 updating to branch default
34 updating to branch default
35 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
35 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
36 $ hg verify -R copy
36 $ hg verify -R copy
37 checking changesets
37 checking changesets
38 checking manifests
38 checking manifests
39 crosschecking files in changesets and manifests
39 crosschecking files in changesets and manifests
40 checking files
40 checking files
41 checked 1 changesets with 4 changes to 4 files
41 checked 1 changesets with 4 changes to 4 files
42 #endif
42 #endif
43
43
44 try to clone via stream, should use pull instead
44 try to clone via stream, should use pull instead
45
45
46 $ hg clone --stream http://localhost:$HGPORT1/ copy2
46 $ hg clone --stream http://localhost:$HGPORT1/ copy2
47 warning: stream clone requested but server has them disabled
47 warning: stream clone requested but server has them disabled
48 requesting all changes
48 requesting all changes
49 adding changesets
49 adding changesets
50 adding manifests
50 adding manifests
51 adding file changes
51 adding file changes
52 added 1 changesets with 4 changes to 4 files
52 added 1 changesets with 4 changes to 4 files
53 new changesets 8b6053c928fe
53 new changesets 8b6053c928fe
54 updating to branch default
54 updating to branch default
55 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
55 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
56
56
57 try to clone via stream but missing requirements, so should use pull instead
57 try to clone via stream but missing requirements, so should use pull instead
58
58
59 $ cat > $TESTTMP/removesupportedformat.py << EOF
59 $ cat > $TESTTMP/removesupportedformat.py << EOF
60 > from mercurial import localrepo
60 > from mercurial import localrepo
61 > def extsetup(ui):
61 > def extsetup(ui):
62 > localrepo.localrepository.supportedformats.remove(b'generaldelta')
62 > localrepo.localrepository.supportedformats.remove(b'generaldelta')
63 > EOF
63 > EOF
64
64
65 $ hg clone --config extensions.rsf=$TESTTMP/removesupportedformat.py --stream http://localhost:$HGPORT/ copy3
65 $ hg clone --config extensions.rsf=$TESTTMP/removesupportedformat.py --stream http://localhost:$HGPORT/ copy3
66 warning: stream clone requested but client is missing requirements: generaldelta
66 warning: stream clone requested but client is missing requirements: generaldelta
67 (see https://www.mercurial-scm.org/wiki/MissingRequirement for more information)
67 (see https://www.mercurial-scm.org/wiki/MissingRequirement for more information)
68 requesting all changes
68 requesting all changes
69 adding changesets
69 adding changesets
70 adding manifests
70 adding manifests
71 adding file changes
71 adding file changes
72 added 1 changesets with 4 changes to 4 files
72 added 1 changesets with 4 changes to 4 files
73 new changesets 8b6053c928fe
73 new changesets 8b6053c928fe
74 updating to branch default
74 updating to branch default
75 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
75 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
76
76
77 clone via pull
77 clone via pull
78
78
79 $ hg clone http://localhost:$HGPORT1/ copy-pull
79 $ hg clone http://localhost:$HGPORT1/ copy-pull
80 requesting all changes
80 requesting all changes
81 adding changesets
81 adding changesets
82 adding manifests
82 adding manifests
83 adding file changes
83 adding file changes
84 added 1 changesets with 4 changes to 4 files
84 added 1 changesets with 4 changes to 4 files
85 new changesets 8b6053c928fe
85 new changesets 8b6053c928fe
86 updating to branch default
86 updating to branch default
87 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
87 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
88 $ hg verify -R copy-pull
88 $ hg verify -R copy-pull
89 checking changesets
89 checking changesets
90 checking manifests
90 checking manifests
91 crosschecking files in changesets and manifests
91 crosschecking files in changesets and manifests
92 checking files
92 checking files
93 checked 1 changesets with 4 changes to 4 files
93 checked 1 changesets with 4 changes to 4 files
94 $ cd test
94 $ cd test
95 $ echo bar > bar
95 $ echo bar > bar
96 $ hg commit -A -d '1 0' -m 2
96 $ hg commit -A -d '1 0' -m 2
97 adding bar
97 adding bar
98 $ cd ..
98 $ cd ..
99
99
100 clone over http with --update
100 clone over http with --update
101
101
102 $ hg clone http://localhost:$HGPORT1/ updated --update 0
102 $ hg clone http://localhost:$HGPORT1/ updated --update 0
103 requesting all changes
103 requesting all changes
104 adding changesets
104 adding changesets
105 adding manifests
105 adding manifests
106 adding file changes
106 adding file changes
107 added 2 changesets with 5 changes to 5 files
107 added 2 changesets with 5 changes to 5 files
108 new changesets 8b6053c928fe:5fed3813f7f5
108 new changesets 8b6053c928fe:5fed3813f7f5
109 updating to branch default
109 updating to branch default
110 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
110 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
111 $ hg log -r . -R updated
111 $ hg log -r . -R updated
112 changeset: 0:8b6053c928fe
112 changeset: 0:8b6053c928fe
113 user: test
113 user: test
114 date: Thu Jan 01 00:00:00 1970 +0000
114 date: Thu Jan 01 00:00:00 1970 +0000
115 summary: 1
115 summary: 1
116
116
117 $ rm -rf updated
117 $ rm -rf updated
118
118
119 incoming via HTTP
119 incoming via HTTP
120
120
121 $ hg clone http://localhost:$HGPORT1/ --rev 0 partial
121 $ hg clone http://localhost:$HGPORT1/ --rev 0 partial
122 adding changesets
122 adding changesets
123 adding manifests
123 adding manifests
124 adding file changes
124 adding file changes
125 added 1 changesets with 4 changes to 4 files
125 added 1 changesets with 4 changes to 4 files
126 new changesets 8b6053c928fe
126 new changesets 8b6053c928fe
127 updating to branch default
127 updating to branch default
128 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
128 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
129 $ cd partial
129 $ cd partial
130 $ touch LOCAL
130 $ touch LOCAL
131 $ hg ci -qAm LOCAL
131 $ hg ci -qAm LOCAL
132 $ hg incoming http://localhost:$HGPORT1/ --template '{desc}\n'
132 $ hg incoming http://localhost:$HGPORT1/ --template '{desc}\n'
133 comparing with http://localhost:$HGPORT1/
133 comparing with http://localhost:$HGPORT1/
134 searching for changes
134 searching for changes
135 2
135 2
136 $ cd ..
136 $ cd ..
137
137
138 pull
138 pull
139
139
140 $ cd copy-pull
140 $ cd copy-pull
141 $ cat >> .hg/hgrc <<EOF
141 $ cat >> .hg/hgrc <<EOF
142 > [hooks]
142 > [hooks]
143 > changegroup = sh -c "printenv.py changegroup"
143 > changegroup = sh -c "printenv.py changegroup"
144 > EOF
144 > EOF
145 $ hg pull
145 $ hg pull
146 pulling from http://localhost:$HGPORT1/
146 pulling from http://localhost:$HGPORT1/
147 searching for changes
147 searching for changes
148 adding changesets
148 adding changesets
149 adding manifests
149 adding manifests
150 adding file changes
150 adding file changes
151 added 1 changesets with 1 changes to 1 files
151 added 1 changesets with 1 changes to 1 files
152 new changesets 5fed3813f7f5
152 new changesets 5fed3813f7f5
153 changegroup hook: HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_NODE_LAST=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_URL=http://localhost:$HGPORT1/
153 changegroup hook: HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_NODE_LAST=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_URL=http://localhost:$HGPORT1/
154 (run 'hg update' to get a working copy)
154 (run 'hg update' to get a working copy)
155 $ cd ..
155 $ cd ..
156
156
157 clone from invalid URL
157 clone from invalid URL
158
158
159 $ hg clone http://localhost:$HGPORT/bad
159 $ hg clone http://localhost:$HGPORT/bad
160 abort: HTTP Error 404: Not Found
160 abort: HTTP Error 404: Not Found
161 [255]
161 [255]
162
162
163 test http authentication
163 test http authentication
164 + use the same server to test server side streaming preference
164 + use the same server to test server side streaming preference
165
165
166 $ cd test
166 $ cd test
167 $ cat << EOT > userpass.py
167 $ cat << EOT > userpass.py
168 > import base64
168 > import base64
169 > from mercurial.hgweb import common
169 > from mercurial.hgweb import common
170 > def perform_authentication(hgweb, req, op):
170 > def perform_authentication(hgweb, req, op):
171 > auth = req.headers.get(b'Authorization')
171 > auth = req.headers.get(b'Authorization')
172 > if not auth:
172 > if not auth:
173 > raise common.ErrorResponse(common.HTTP_UNAUTHORIZED, b'who',
173 > raise common.ErrorResponse(common.HTTP_UNAUTHORIZED, b'who',
174 > [(b'WWW-Authenticate', b'Basic Realm="mercurial"')])
174 > [(b'WWW-Authenticate', b'Basic Realm="mercurial"')])
175 > if base64.b64decode(auth.split()[1]).split(b':', 1) != [b'user', b'pass']:
175 > if base64.b64decode(auth.split()[1]).split(b':', 1) != [b'user', b'pass']:
176 > raise common.ErrorResponse(common.HTTP_FORBIDDEN, b'no')
176 > raise common.ErrorResponse(common.HTTP_FORBIDDEN, b'no')
177 > def extsetup():
177 > def extsetup():
178 > common.permhooks.insert(0, perform_authentication)
178 > common.permhooks.insert(0, perform_authentication)
179 > EOT
179 > EOT
180 $ hg serve --config extensions.x=userpass.py -p $HGPORT2 -d --pid-file=pid \
180 $ hg serve --config extensions.x=userpass.py -p $HGPORT2 -d --pid-file=pid \
181 > --config server.preferuncompressed=True \
181 > --config server.preferuncompressed=True \
182 > --config web.push_ssl=False --config web.allow_push=* -A ../access.log
182 > --config web.push_ssl=False --config web.allow_push=* -A ../access.log
183 $ cat pid >> $DAEMON_PIDS
183 $ cat pid >> $DAEMON_PIDS
184
184
185 $ cat << EOF > get_pass.py
185 $ cat << EOF > get_pass.py
186 > import getpass
186 > import getpass
187 > def newgetpass(arg):
187 > def newgetpass(arg):
188 > return "pass"
188 > return "pass"
189 > getpass.getpass = newgetpass
189 > getpass.getpass = newgetpass
190 > EOF
190 > EOF
191
191
192 $ hg id http://localhost:$HGPORT2/
192 $ hg id http://localhost:$HGPORT2/
193 abort: http authorization required for http://localhost:$HGPORT2/
193 abort: http authorization required for http://localhost:$HGPORT2/
194 [255]
194 [255]
195 $ hg id http://localhost:$HGPORT2/
195 $ hg id http://localhost:$HGPORT2/
196 abort: http authorization required for http://localhost:$HGPORT2/
196 abort: http authorization required for http://localhost:$HGPORT2/
197 [255]
197 [255]
198 $ hg id --config ui.interactive=true --config extensions.getpass=get_pass.py http://user@localhost:$HGPORT2/
198 $ hg id --config ui.interactive=true --config extensions.getpass=get_pass.py http://user@localhost:$HGPORT2/
199 http authorization required for http://localhost:$HGPORT2/
199 http authorization required for http://localhost:$HGPORT2/
200 realm: mercurial
200 realm: mercurial
201 user: user
201 user: user
202 password: 5fed3813f7f5
202 password: 5fed3813f7f5
203 $ hg id http://user:pass@localhost:$HGPORT2/
203 $ hg id http://user:pass@localhost:$HGPORT2/
204 5fed3813f7f5
204 5fed3813f7f5
205 $ echo '[auth]' >> .hg/hgrc
205 $ echo '[auth]' >> .hg/hgrc
206 $ echo 'l.schemes=http' >> .hg/hgrc
206 $ echo 'l.schemes=http' >> .hg/hgrc
207 $ echo 'l.prefix=lo' >> .hg/hgrc
207 $ echo 'l.prefix=lo' >> .hg/hgrc
208 $ echo 'l.username=user' >> .hg/hgrc
208 $ echo 'l.username=user' >> .hg/hgrc
209 $ echo 'l.password=pass' >> .hg/hgrc
209 $ echo 'l.password=pass' >> .hg/hgrc
210 $ hg id http://localhost:$HGPORT2/
210 $ hg id http://localhost:$HGPORT2/
211 5fed3813f7f5
211 5fed3813f7f5
212 $ hg id http://localhost:$HGPORT2/
212 $ hg id http://localhost:$HGPORT2/
213 5fed3813f7f5
213 5fed3813f7f5
214 $ hg id http://user@localhost:$HGPORT2/
214 $ hg id http://user@localhost:$HGPORT2/
215 5fed3813f7f5
215 5fed3813f7f5
216
216
217 #if no-reposimplestore
217 #if no-reposimplestore
218 $ hg clone http://user:pass@localhost:$HGPORT2/ dest 2>&1
218 $ hg clone http://user:pass@localhost:$HGPORT2/ dest 2>&1
219 streaming all changes
219 streaming all changes
220 10 files to transfer, 1.01 KB of data
220 10 files to transfer, 1.01 KB of data
221 transferred * KB in * seconds (*/sec) (glob)
221 transferred * KB in * seconds (*/sec) (glob)
222 updating to branch default
222 updating to branch default
223 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
223 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
224 #endif
224 #endif
225
225
226 --pull should override server's preferuncompressed
226 --pull should override server's preferuncompressed
227 $ hg clone --pull http://user:pass@localhost:$HGPORT2/ dest-pull 2>&1
227 $ hg clone --pull http://user:pass@localhost:$HGPORT2/ dest-pull 2>&1
228 requesting all changes
228 requesting all changes
229 adding changesets
229 adding changesets
230 adding manifests
230 adding manifests
231 adding file changes
231 adding file changes
232 added 2 changesets with 5 changes to 5 files
232 added 2 changesets with 5 changes to 5 files
233 new changesets 8b6053c928fe:5fed3813f7f5
233 new changesets 8b6053c928fe:5fed3813f7f5
234 updating to branch default
234 updating to branch default
235 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
235 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
236
236
237 $ hg id http://user2@localhost:$HGPORT2/
237 $ hg id http://user2@localhost:$HGPORT2/
238 abort: http authorization required for http://localhost:$HGPORT2/
238 abort: http authorization required for http://localhost:$HGPORT2/
239 [255]
239 [255]
240 $ hg id http://user:pass2@localhost:$HGPORT2/
240 $ hg id http://user:pass2@localhost:$HGPORT2/
241 abort: HTTP Error 403: no
241 abort: HTTP Error 403: no
242 [255]
242 [255]
243
243
244 $ hg -R dest-pull tag -r tip top
244 $ hg -R dest-pull tag -r tip top
245 $ hg -R dest-pull push http://user:pass@localhost:$HGPORT2/
245 $ hg -R dest-pull push http://user:pass@localhost:$HGPORT2/
246 pushing to http://user:***@localhost:$HGPORT2/
246 pushing to http://user:***@localhost:$HGPORT2/
247 searching for changes
247 searching for changes
248 remote: adding changesets
248 remote: adding changesets
249 remote: adding manifests
249 remote: adding manifests
250 remote: adding file changes
250 remote: adding file changes
251 remote: added 1 changesets with 1 changes to 1 files
251 remote: added 1 changesets with 1 changes to 1 files
252 $ hg rollback -q
252 $ hg rollback -q
253 $ hg -R dest-pull push http://user:pass@localhost:$HGPORT2/ --debug --config devel.debug.peer-request=yes
253 $ hg -R dest-pull push http://user:pass@localhost:$HGPORT2/ --debug --config devel.debug.peer-request=yes
254 pushing to http://user:***@localhost:$HGPORT2/
254 pushing to http://user:***@localhost:$HGPORT2/
255 using http://localhost:$HGPORT2/
255 using http://localhost:$HGPORT2/
256 http auth: user user, password ****
256 http auth: user user, password ****
257 sending capabilities command
257 sending capabilities command
258 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=capabilities
258 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=capabilities
259 http auth: user user, password ****
259 http auth: user user, password ****
260 devel-peer-request: finished in *.???? seconds (200) (glob)
260 devel-peer-request: finished in *.???? seconds (200) (glob)
261 query 1; heads
261 query 1; heads
262 devel-peer-request: batched-content
262 devel-peer-request: batched-content
263 devel-peer-request: - heads (0 arguments)
263 devel-peer-request: - heads (0 arguments)
264 devel-peer-request: - known (1 arguments)
264 devel-peer-request: - known (1 arguments)
265 sending batch command
265 sending batch command
266 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=batch
266 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=batch
267 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
267 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
268 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
268 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
269 devel-peer-request: 68 bytes of commands arguments in headers
269 devel-peer-request: 68 bytes of commands arguments in headers
270 devel-peer-request: finished in *.???? seconds (200) (glob)
270 devel-peer-request: finished in *.???? seconds (200) (glob)
271 searching for changes
271 searching for changes
272 all remote heads known locally
272 all remote heads known locally
273 preparing listkeys for "phases"
273 preparing listkeys for "phases"
274 sending listkeys command
274 sending listkeys command
275 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=listkeys
275 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=listkeys
276 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
276 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
277 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
277 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
278 devel-peer-request: 16 bytes of commands arguments in headers
278 devel-peer-request: 16 bytes of commands arguments in headers
279 devel-peer-request: finished in *.???? seconds (200) (glob)
279 devel-peer-request: finished in *.???? seconds (200) (glob)
280 received listkey for "phases": 58 bytes
280 received listkey for "phases": 58 bytes
281 checking for updated bookmarks
281 checking for updated bookmarks
282 preparing listkeys for "bookmarks"
282 preparing listkeys for "bookmarks"
283 sending listkeys command
283 sending listkeys command
284 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=listkeys
284 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=listkeys
285 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
285 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
286 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
286 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
287 devel-peer-request: 19 bytes of commands arguments in headers
287 devel-peer-request: 19 bytes of commands arguments in headers
288 devel-peer-request: finished in *.???? seconds (200) (glob)
288 devel-peer-request: finished in *.???? seconds (200) (glob)
289 received listkey for "bookmarks": 0 bytes
289 received listkey for "bookmarks": 0 bytes
290 sending branchmap command
290 sending branchmap command
291 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=branchmap
291 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=branchmap
292 devel-peer-request: Vary X-HgProto-1
292 devel-peer-request: Vary X-HgProto-1
293 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
293 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
294 devel-peer-request: finished in *.???? seconds (200) (glob)
294 devel-peer-request: finished in *.???? seconds (200) (glob)
295 preparing listkeys for "bookmarks"
295 preparing listkeys for "bookmarks"
296 sending listkeys command
296 sending listkeys command
297 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=listkeys
297 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=listkeys
298 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
298 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
299 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
299 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
300 devel-peer-request: 19 bytes of commands arguments in headers
300 devel-peer-request: 19 bytes of commands arguments in headers
301 devel-peer-request: finished in *.???? seconds (200) (glob)
301 devel-peer-request: finished in *.???? seconds (200) (glob)
302 received listkey for "bookmarks": 0 bytes
302 received listkey for "bookmarks": 0 bytes
303 1 changesets found
303 1 changesets found
304 list of changesets:
304 list of changesets:
305 7f4e523d01f2cc3765ac8934da3d14db775ff872
305 7f4e523d01f2cc3765ac8934da3d14db775ff872
306 bundle2-output-bundle: "HG20", 5 parts total
306 bundle2-output-bundle: "HG20", 5 parts total
307 bundle2-output-part: "replycaps" 205 bytes payload
307 bundle2-output-part: "replycaps" 205 bytes payload
308 bundle2-output-part: "check:phases" 24 bytes payload
308 bundle2-output-part: "check:phases" 24 bytes payload
309 bundle2-output-part: "check:heads" streamed payload
309 bundle2-output-part: "check:heads" streamed payload
310 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
310 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
311 bundle2-output-part: "phase-heads" 24 bytes payload
311 bundle2-output-part: "phase-heads" 24 bytes payload
312 sending unbundle command
312 sending unbundle command
313 sending 1013 bytes
313 sending 1013 bytes
314 devel-peer-request: POST http://localhost:$HGPORT2/?cmd=unbundle
314 devel-peer-request: POST http://localhost:$HGPORT2/?cmd=unbundle
315 devel-peer-request: Content-length 1013
315 devel-peer-request: Content-length 1013
316 devel-peer-request: Content-type application/mercurial-0.1
316 devel-peer-request: Content-type application/mercurial-0.1
317 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
317 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
318 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
318 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
319 devel-peer-request: 16 bytes of commands arguments in headers
319 devel-peer-request: 16 bytes of commands arguments in headers
320 devel-peer-request: 1013 bytes of data
320 devel-peer-request: 1013 bytes of data
321 devel-peer-request: finished in *.???? seconds (200) (glob)
321 devel-peer-request: finished in *.???? seconds (200) (glob)
322 bundle2-input-bundle: no-transaction
322 bundle2-input-bundle: no-transaction
323 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
323 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
324 bundle2-input-part: "output" (advisory) (params: 0 advisory) supported
324 bundle2-input-part: "output" (advisory) (params: 0 advisory) supported
325 bundle2-input-part: total payload size 100
325 bundle2-input-part: total payload size 100
326 remote: adding changesets
326 remote: adding changesets
327 remote: adding manifests
327 remote: adding manifests
328 remote: adding file changes
328 remote: adding file changes
329 remote: added 1 changesets with 1 changes to 1 files
329 remote: added 1 changesets with 1 changes to 1 files
330 bundle2-input-part: "output" (advisory) supported
330 bundle2-input-part: "output" (advisory) supported
331 bundle2-input-bundle: 2 parts total
331 bundle2-input-bundle: 2 parts total
332 preparing listkeys for "phases"
332 preparing listkeys for "phases"
333 sending listkeys command
333 sending listkeys command
334 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=listkeys
334 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=listkeys
335 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
335 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
336 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
336 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
337 devel-peer-request: 16 bytes of commands arguments in headers
337 devel-peer-request: 16 bytes of commands arguments in headers
338 devel-peer-request: finished in *.???? seconds (200) (glob)
338 devel-peer-request: finished in *.???? seconds (200) (glob)
339 received listkey for "phases": 15 bytes
339 received listkey for "phases": 15 bytes
340 $ hg rollback -q
340 $ hg rollback -q
341
341
342 $ sed 's/.*] "/"/' < ../access.log
342 $ sed 's/.*] "/"/' < ../access.log
343 "GET /?cmd=capabilities HTTP/1.1" 401 -
343 "GET /?cmd=capabilities HTTP/1.1" 401 -
344 "GET /?cmd=capabilities HTTP/1.1" 401 -
344 "GET /?cmd=capabilities HTTP/1.1" 401 -
345 "GET /?cmd=capabilities HTTP/1.1" 401 -
345 "GET /?cmd=capabilities HTTP/1.1" 401 -
346 "GET /?cmd=capabilities HTTP/1.1" 200 -
346 "GET /?cmd=capabilities HTTP/1.1" 200 -
347 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
347 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
348 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
348 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
349 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
349 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
350 "GET /?cmd=capabilities HTTP/1.1" 401 -
350 "GET /?cmd=capabilities HTTP/1.1" 401 -
351 "GET /?cmd=capabilities HTTP/1.1" 200 -
351 "GET /?cmd=capabilities HTTP/1.1" 200 -
352 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
352 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
353 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
353 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
354 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
354 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
355 "GET /?cmd=capabilities HTTP/1.1" 401 -
355 "GET /?cmd=capabilities HTTP/1.1" 401 -
356 "GET /?cmd=capabilities HTTP/1.1" 200 -
356 "GET /?cmd=capabilities HTTP/1.1" 200 -
357 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
357 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
358 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
358 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
359 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
359 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
360 "GET /?cmd=capabilities HTTP/1.1" 401 -
360 "GET /?cmd=capabilities HTTP/1.1" 401 -
361 "GET /?cmd=capabilities HTTP/1.1" 200 -
361 "GET /?cmd=capabilities HTTP/1.1" 200 -
362 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
362 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
363 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
363 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
364 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
364 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
365 "GET /?cmd=capabilities HTTP/1.1" 401 -
365 "GET /?cmd=capabilities HTTP/1.1" 401 -
366 "GET /?cmd=capabilities HTTP/1.1" 200 -
366 "GET /?cmd=capabilities HTTP/1.1" 200 -
367 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
367 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
368 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
368 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
369 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
369 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
370 "GET /?cmd=capabilities HTTP/1.1" 401 - (no-reposimplestore !)
370 "GET /?cmd=capabilities HTTP/1.1" 401 - (no-reposimplestore !)
371 "GET /?cmd=capabilities HTTP/1.1" 200 - (no-reposimplestore !)
371 "GET /?cmd=capabilities HTTP/1.1" 200 - (no-reposimplestore !)
372 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
372 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
373 "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bookmarks=1&$USUAL_BUNDLE_CAPS$&cg=0&common=0000000000000000000000000000000000000000&heads=5fed3813f7f5e1824344fdc9cf8f63bb662c292d&listkeys=bookmarks&stream=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
373 "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bookmarks=1&$USUAL_BUNDLE_CAPS$&cg=0&common=0000000000000000000000000000000000000000&heads=5fed3813f7f5e1824344fdc9cf8f63bb662c292d&listkeys=bookmarks&stream=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
374 "GET /?cmd=capabilities HTTP/1.1" 401 - (no-reposimplestore !)
374 "GET /?cmd=capabilities HTTP/1.1" 401 - (no-reposimplestore !)
375 "GET /?cmd=capabilities HTTP/1.1" 200 - (no-reposimplestore !)
375 "GET /?cmd=capabilities HTTP/1.1" 200 - (no-reposimplestore !)
376 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
376 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
377 "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bookmarks=1&$USUAL_BUNDLE_CAPS$&cg=1&common=0000000000000000000000000000000000000000&heads=5fed3813f7f5e1824344fdc9cf8f63bb662c292d&listkeys=bookmarks&phases=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
377 "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bookmarks=1&$USUAL_BUNDLE_CAPS$&cg=1&common=0000000000000000000000000000000000000000&heads=5fed3813f7f5e1824344fdc9cf8f63bb662c292d&listkeys=bookmarks&phases=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
378 "GET /?cmd=capabilities HTTP/1.1" 401 -
378 "GET /?cmd=capabilities HTTP/1.1" 401 -
379 "GET /?cmd=capabilities HTTP/1.1" 401 -
379 "GET /?cmd=capabilities HTTP/1.1" 401 -
380 "GET /?cmd=capabilities HTTP/1.1" 403 -
380 "GET /?cmd=capabilities HTTP/1.1" 403 -
381 "GET /?cmd=capabilities HTTP/1.1" 401 -
381 "GET /?cmd=capabilities HTTP/1.1" 401 -
382 "GET /?cmd=capabilities HTTP/1.1" 200 -
382 "GET /?cmd=capabilities HTTP/1.1" 200 -
383 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D7f4e523d01f2cc3765ac8934da3d14db775ff872 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
383 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D7f4e523d01f2cc3765ac8934da3d14db775ff872 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
384 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
384 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
385 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
385 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
386 "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
386 "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
387 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
387 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
388 "POST /?cmd=unbundle HTTP/1.1" 200 - x-hgarg-1:heads=666f726365* (glob)
388 "POST /?cmd=unbundle HTTP/1.1" 200 - x-hgarg-1:heads=666f726365* (glob)
389 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
389 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
390 "GET /?cmd=capabilities HTTP/1.1" 401 -
390 "GET /?cmd=capabilities HTTP/1.1" 401 -
391 "GET /?cmd=capabilities HTTP/1.1" 200 -
391 "GET /?cmd=capabilities HTTP/1.1" 200 -
392 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D7f4e523d01f2cc3765ac8934da3d14db775ff872 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
392 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D7f4e523d01f2cc3765ac8934da3d14db775ff872 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
393 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
393 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
394 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
394 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
395 "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
395 "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
396 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
396 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
397 "POST /?cmd=unbundle HTTP/1.1" 200 - x-hgarg-1:heads=666f726365 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
397 "POST /?cmd=unbundle HTTP/1.1" 200 - x-hgarg-1:heads=666f726365 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
398 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
398 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
399
399
400 $ cd ..
400 $ cd ..
401
401
402 clone of serve with repo in root and unserved subrepo (issue2970)
402 clone of serve with repo in root and unserved subrepo (issue2970)
403
403
404 $ hg --cwd test init sub
404 $ hg --cwd test init sub
405 $ echo empty > test/sub/empty
405 $ echo empty > test/sub/empty
406 $ hg --cwd test/sub add empty
406 $ hg --cwd test/sub add empty
407 $ hg --cwd test/sub commit -qm 'add empty'
407 $ hg --cwd test/sub commit -qm 'add empty'
408 $ hg --cwd test/sub tag -r 0 something
408 $ hg --cwd test/sub tag -r 0 something
409 $ echo sub = sub > test/.hgsub
409 $ echo sub = sub > test/.hgsub
410 $ hg --cwd test add .hgsub
410 $ hg --cwd test add .hgsub
411 $ hg --cwd test commit -qm 'add subrepo'
411 $ hg --cwd test commit -qm 'add subrepo'
412 $ hg clone http://localhost:$HGPORT noslash-clone
412 $ hg clone http://localhost:$HGPORT noslash-clone
413 requesting all changes
413 requesting all changes
414 adding changesets
414 adding changesets
415 adding manifests
415 adding manifests
416 adding file changes
416 adding file changes
417 added 3 changesets with 7 changes to 7 files
417 added 3 changesets with 7 changes to 7 files
418 new changesets 8b6053c928fe:56f9bc90cce6
418 new changesets 8b6053c928fe:56f9bc90cce6
419 updating to branch default
419 updating to branch default
420 cloning subrepo sub from http://localhost:$HGPORT/sub
420 abort: HTTP Error 404: Not Found
421 abort: HTTP Error 404: Not Found
421 [255]
422 [255]
422 $ hg clone http://localhost:$HGPORT/ slash-clone
423 $ hg clone http://localhost:$HGPORT/ slash-clone
423 requesting all changes
424 requesting all changes
424 adding changesets
425 adding changesets
425 adding manifests
426 adding manifests
426 adding file changes
427 adding file changes
427 added 3 changesets with 7 changes to 7 files
428 added 3 changesets with 7 changes to 7 files
428 new changesets 8b6053c928fe:56f9bc90cce6
429 new changesets 8b6053c928fe:56f9bc90cce6
429 updating to branch default
430 updating to branch default
431 cloning subrepo sub from http://localhost:$HGPORT/sub
430 abort: HTTP Error 404: Not Found
432 abort: HTTP Error 404: Not Found
431 [255]
433 [255]
432
434
433 check error log
435 check error log
434
436
435 $ cat error.log
437 $ cat error.log
436
438
437 check abort error reporting while pulling/cloning
439 check abort error reporting while pulling/cloning
438
440
439 $ $RUNTESTDIR/killdaemons.py
441 $ $RUNTESTDIR/killdaemons.py
440 $ hg serve -R test -p $HGPORT -d --pid-file=hg3.pid -E error.log --config extensions.crash=${TESTDIR}/crashgetbundler.py
442 $ hg serve -R test -p $HGPORT -d --pid-file=hg3.pid -E error.log --config extensions.crash=${TESTDIR}/crashgetbundler.py
441 $ cat hg3.pid >> $DAEMON_PIDS
443 $ cat hg3.pid >> $DAEMON_PIDS
442 $ hg clone http://localhost:$HGPORT/ abort-clone
444 $ hg clone http://localhost:$HGPORT/ abort-clone
443 requesting all changes
445 requesting all changes
444 remote: abort: this is an exercise
446 remote: abort: this is an exercise
445 abort: pull failed on remote
447 abort: pull failed on remote
446 [255]
448 [255]
447 $ cat error.log
449 $ cat error.log
448
450
449 disable pull-based clones
451 disable pull-based clones
450
452
451 $ hg serve -R test -p $HGPORT1 -d --pid-file=hg4.pid -E error.log --config server.disablefullbundle=True
453 $ hg serve -R test -p $HGPORT1 -d --pid-file=hg4.pid -E error.log --config server.disablefullbundle=True
452 $ cat hg4.pid >> $DAEMON_PIDS
454 $ cat hg4.pid >> $DAEMON_PIDS
453 $ hg clone http://localhost:$HGPORT1/ disable-pull-clone
455 $ hg clone http://localhost:$HGPORT1/ disable-pull-clone
454 requesting all changes
456 requesting all changes
455 remote: abort: server has pull-based clones disabled
457 remote: abort: server has pull-based clones disabled
456 abort: pull failed on remote
458 abort: pull failed on remote
457 (remove --pull if specified or upgrade Mercurial)
459 (remove --pull if specified or upgrade Mercurial)
458 [255]
460 [255]
459
461
460 #if no-reposimplestore
462 #if no-reposimplestore
461 ... but keep stream clones working
463 ... but keep stream clones working
462
464
463 $ hg clone --stream --noupdate http://localhost:$HGPORT1/ test-stream-clone
465 $ hg clone --stream --noupdate http://localhost:$HGPORT1/ test-stream-clone
464 streaming all changes
466 streaming all changes
465 * files to transfer, * of data (glob)
467 * files to transfer, * of data (glob)
466 transferred * in * seconds (*/sec) (glob)
468 transferred * in * seconds (*/sec) (glob)
467 $ cat error.log
469 $ cat error.log
468 #endif
470 #endif
469
471
470 ... and also keep partial clones and pulls working
472 ... and also keep partial clones and pulls working
471 $ hg clone http://localhost:$HGPORT1 --rev 0 test/partial/clone
473 $ hg clone http://localhost:$HGPORT1 --rev 0 test/partial/clone
472 adding changesets
474 adding changesets
473 adding manifests
475 adding manifests
474 adding file changes
476 adding file changes
475 added 1 changesets with 4 changes to 4 files
477 added 1 changesets with 4 changes to 4 files
476 new changesets 8b6053c928fe
478 new changesets 8b6053c928fe
477 updating to branch default
479 updating to branch default
478 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
480 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
479 $ hg pull -R test/partial/clone
481 $ hg pull -R test/partial/clone
480 pulling from http://localhost:$HGPORT1/
482 pulling from http://localhost:$HGPORT1/
481 searching for changes
483 searching for changes
482 adding changesets
484 adding changesets
483 adding manifests
485 adding manifests
484 adding file changes
486 adding file changes
485 added 2 changesets with 3 changes to 3 files
487 added 2 changesets with 3 changes to 3 files
486 new changesets 5fed3813f7f5:56f9bc90cce6
488 new changesets 5fed3813f7f5:56f9bc90cce6
487 (run 'hg update' to get a working copy)
489 (run 'hg update' to get a working copy)
488
490
489 $ hg clone -U -r 0 test/partial/clone test/another/clone
491 $ hg clone -U -r 0 test/partial/clone test/another/clone
490 adding changesets
492 adding changesets
491 adding manifests
493 adding manifests
492 adding file changes
494 adding file changes
493 added 1 changesets with 4 changes to 4 files
495 added 1 changesets with 4 changes to 4 files
494 new changesets 8b6053c928fe
496 new changesets 8b6053c928fe
495
497
496 corrupt cookies file should yield a warning
498 corrupt cookies file should yield a warning
497
499
498 $ cat > $TESTTMP/cookies.txt << EOF
500 $ cat > $TESTTMP/cookies.txt << EOF
499 > bad format
501 > bad format
500 > EOF
502 > EOF
501
503
502 $ hg --config auth.cookiefile=$TESTTMP/cookies.txt id http://localhost:$HGPORT/
504 $ hg --config auth.cookiefile=$TESTTMP/cookies.txt id http://localhost:$HGPORT/
503 (error loading cookie file $TESTTMP/cookies.txt: '*/cookies.txt' does not look like a Netscape format cookies file; continuing without cookies) (glob)
505 (error loading cookie file $TESTTMP/cookies.txt: '*/cookies.txt' does not look like a Netscape format cookies file; continuing without cookies) (glob)
504 56f9bc90cce6
506 56f9bc90cce6
505
507
506 $ killdaemons.py
508 $ killdaemons.py
507
509
508 Create dummy authentication handler that looks for cookies. It doesn't do anything
510 Create dummy authentication handler that looks for cookies. It doesn't do anything
509 useful. It just raises an HTTP 500 with details about the Cookie request header.
511 useful. It just raises an HTTP 500 with details about the Cookie request header.
510 We raise HTTP 500 because its message is printed in the abort message.
512 We raise HTTP 500 because its message is printed in the abort message.
511
513
512 $ cat > cookieauth.py << EOF
514 $ cat > cookieauth.py << EOF
513 > from mercurial import util
515 > from mercurial import util
514 > from mercurial.hgweb import common
516 > from mercurial.hgweb import common
515 > def perform_authentication(hgweb, req, op):
517 > def perform_authentication(hgweb, req, op):
516 > cookie = req.headers.get(b'Cookie')
518 > cookie = req.headers.get(b'Cookie')
517 > if not cookie:
519 > if not cookie:
518 > raise common.ErrorResponse(common.HTTP_SERVER_ERROR, b'no-cookie')
520 > raise common.ErrorResponse(common.HTTP_SERVER_ERROR, b'no-cookie')
519 > raise common.ErrorResponse(common.HTTP_SERVER_ERROR, b'Cookie: %s' % cookie)
521 > raise common.ErrorResponse(common.HTTP_SERVER_ERROR, b'Cookie: %s' % cookie)
520 > def extsetup():
522 > def extsetup():
521 > common.permhooks.insert(0, perform_authentication)
523 > common.permhooks.insert(0, perform_authentication)
522 > EOF
524 > EOF
523
525
524 $ hg serve --config extensions.cookieauth=cookieauth.py -R test -p $HGPORT -d --pid-file=pid
526 $ hg serve --config extensions.cookieauth=cookieauth.py -R test -p $HGPORT -d --pid-file=pid
525 $ cat pid > $DAEMON_PIDS
527 $ cat pid > $DAEMON_PIDS
526
528
527 Request without cookie sent should fail due to lack of cookie
529 Request without cookie sent should fail due to lack of cookie
528
530
529 $ hg id http://localhost:$HGPORT
531 $ hg id http://localhost:$HGPORT
530 abort: HTTP Error 500: no-cookie
532 abort: HTTP Error 500: no-cookie
531 [255]
533 [255]
532
534
533 Populate a cookies file
535 Populate a cookies file
534
536
535 $ cat > cookies.txt << EOF
537 $ cat > cookies.txt << EOF
536 > # HTTP Cookie File
538 > # HTTP Cookie File
537 > # Expiration is 2030-01-01 at midnight
539 > # Expiration is 2030-01-01 at midnight
538 > .example.com TRUE / FALSE 1893456000 hgkey examplevalue
540 > .example.com TRUE / FALSE 1893456000 hgkey examplevalue
539 > EOF
541 > EOF
540
542
541 Should not send a cookie for another domain
543 Should not send a cookie for another domain
542
544
543 $ hg --config auth.cookiefile=cookies.txt id http://localhost:$HGPORT/
545 $ hg --config auth.cookiefile=cookies.txt id http://localhost:$HGPORT/
544 abort: HTTP Error 500: no-cookie
546 abort: HTTP Error 500: no-cookie
545 [255]
547 [255]
546
548
547 Add a cookie entry for our test server and verify it is sent
549 Add a cookie entry for our test server and verify it is sent
548
550
549 $ cat >> cookies.txt << EOF
551 $ cat >> cookies.txt << EOF
550 > localhost.local FALSE / FALSE 1893456000 hgkey localhostvalue
552 > localhost.local FALSE / FALSE 1893456000 hgkey localhostvalue
551 > EOF
553 > EOF
552
554
553 $ hg --config auth.cookiefile=cookies.txt id http://localhost:$HGPORT/
555 $ hg --config auth.cookiefile=cookies.txt id http://localhost:$HGPORT/
554 abort: HTTP Error 500: Cookie: hgkey=localhostvalue
556 abort: HTTP Error 500: Cookie: hgkey=localhostvalue
555 [255]
557 [255]
@@ -1,173 +1,176 b''
1 #require unix-permissions
1 #require unix-permissions
2
2
3 test that new files created in .hg inherit the permissions from .hg/store
3 test that new files created in .hg inherit the permissions from .hg/store
4
4
5 $ mkdir dir
5 $ mkdir dir
6
6
7 just in case somebody has a strange $TMPDIR
7 just in case somebody has a strange $TMPDIR
8
8
9 $ chmod g-s dir
9 $ chmod g-s dir
10 $ cd dir
10 $ cd dir
11
11
12 $ cat >printmodes.py <<EOF
12 $ cat >printmodes.py <<EOF
13 > from __future__ import absolute_import, print_function
13 > from __future__ import absolute_import, print_function
14 > import os
14 > import os
15 > import sys
15 > import sys
16 >
16 >
17 > allnames = []
17 > allnames = []
18 > isdir = {}
18 > isdir = {}
19 > for root, dirs, files in os.walk(sys.argv[1]):
19 > for root, dirs, files in os.walk(sys.argv[1]):
20 > for d in dirs:
20 > for d in dirs:
21 > name = os.path.join(root, d)
21 > name = os.path.join(root, d)
22 > isdir[name] = 1
22 > isdir[name] = 1
23 > allnames.append(name)
23 > allnames.append(name)
24 > for f in files:
24 > for f in files:
25 > name = os.path.join(root, f)
25 > name = os.path.join(root, f)
26 > allnames.append(name)
26 > allnames.append(name)
27 > allnames.sort()
27 > allnames.sort()
28 > for name in allnames:
28 > for name in allnames:
29 > suffix = name in isdir and '/' or ''
29 > suffix = name in isdir and '/' or ''
30 > print('%05o %s%s' % (os.lstat(name).st_mode & 0o7777, name, suffix))
30 > print('%05o %s%s' % (os.lstat(name).st_mode & 0o7777, name, suffix))
31 > EOF
31 > EOF
32
32
33 $ cat >mode.py <<EOF
33 $ cat >mode.py <<EOF
34 > from __future__ import absolute_import, print_function
34 > from __future__ import absolute_import, print_function
35 > import os
35 > import os
36 > import sys
36 > import sys
37 > print('%05o' % os.lstat(sys.argv[1]).st_mode)
37 > print('%05o' % os.lstat(sys.argv[1]).st_mode)
38 > EOF
38 > EOF
39
39
40 $ umask 077
40 $ umask 077
41
41
42 $ hg init repo
42 $ hg init repo
43 $ cd repo
43 $ cd repo
44
44
45 $ chmod 0770 .hg/store
45 $ chmod 0770 .hg/store
46
46
47 before commit
47 before commit
48 store can be written by the group, other files cannot
48 store can be written by the group, other files cannot
49 store is setgid
49 store is setgid
50
50
51 $ "$PYTHON" ../printmodes.py .
51 $ "$PYTHON" ../printmodes.py .
52 00700 ./.hg/
52 00700 ./.hg/
53 00600 ./.hg/00changelog.i
53 00600 ./.hg/00changelog.i
54 00600 ./.hg/requires
54 00600 ./.hg/requires
55 00770 ./.hg/store/
55 00770 ./.hg/store/
56
56
57 $ mkdir dir
57 $ mkdir dir
58 $ touch foo dir/bar
58 $ touch foo dir/bar
59 $ hg ci -qAm 'add files'
59 $ hg ci -qAm 'add files'
60
60
61 after commit
61 after commit
62 working dir files can only be written by the owner
62 working dir files can only be written by the owner
63 files created in .hg can be written by the group
63 files created in .hg can be written by the group
64 (in particular, store/**, dirstate, branch cache file, undo files)
64 (in particular, store/**, dirstate, branch cache file, undo files)
65 new directories are setgid
65 new directories are setgid
66
66
67 $ "$PYTHON" ../printmodes.py .
67 $ "$PYTHON" ../printmodes.py .
68 00700 ./.hg/
68 00700 ./.hg/
69 00600 ./.hg/00changelog.i
69 00600 ./.hg/00changelog.i
70 00770 ./.hg/cache/
70 00770 ./.hg/cache/
71 00660 ./.hg/cache/branch2-served
71 00660 ./.hg/cache/branch2-served
72 00711 ./.hg/cache/checkisexec
73 00777 ./.hg/cache/checklink
74 00600 ./.hg/cache/checklink-target
72 00660 ./.hg/cache/manifestfulltextcache (reporevlogstore !)
75 00660 ./.hg/cache/manifestfulltextcache (reporevlogstore !)
73 00660 ./.hg/cache/rbc-names-v1
76 00660 ./.hg/cache/rbc-names-v1
74 00660 ./.hg/cache/rbc-revs-v1
77 00660 ./.hg/cache/rbc-revs-v1
75 00660 ./.hg/dirstate
78 00660 ./.hg/dirstate
76 00660 ./.hg/fsmonitor.state (fsmonitor !)
79 00660 ./.hg/fsmonitor.state (fsmonitor !)
77 00660 ./.hg/last-message.txt
80 00660 ./.hg/last-message.txt
78 00600 ./.hg/requires
81 00600 ./.hg/requires
79 00770 ./.hg/store/
82 00770 ./.hg/store/
80 00660 ./.hg/store/00changelog.i
83 00660 ./.hg/store/00changelog.i
81 00660 ./.hg/store/00manifest.i
84 00660 ./.hg/store/00manifest.i
82 00770 ./.hg/store/data/
85 00770 ./.hg/store/data/
83 00770 ./.hg/store/data/dir/
86 00770 ./.hg/store/data/dir/
84 00660 ./.hg/store/data/dir/bar.i (reporevlogstore !)
87 00660 ./.hg/store/data/dir/bar.i (reporevlogstore !)
85 00660 ./.hg/store/data/foo.i (reporevlogstore !)
88 00660 ./.hg/store/data/foo.i (reporevlogstore !)
86 00770 ./.hg/store/data/dir/bar/ (reposimplestore !)
89 00770 ./.hg/store/data/dir/bar/ (reposimplestore !)
87 00660 ./.hg/store/data/dir/bar/b80de5d138758541c5f05265ad144ab9fa86d1db (reposimplestore !)
90 00660 ./.hg/store/data/dir/bar/b80de5d138758541c5f05265ad144ab9fa86d1db (reposimplestore !)
88 00660 ./.hg/store/data/dir/bar/index (reposimplestore !)
91 00660 ./.hg/store/data/dir/bar/index (reposimplestore !)
89 00770 ./.hg/store/data/foo/ (reposimplestore !)
92 00770 ./.hg/store/data/foo/ (reposimplestore !)
90 00660 ./.hg/store/data/foo/b80de5d138758541c5f05265ad144ab9fa86d1db (reposimplestore !)
93 00660 ./.hg/store/data/foo/b80de5d138758541c5f05265ad144ab9fa86d1db (reposimplestore !)
91 00660 ./.hg/store/data/foo/index (reposimplestore !)
94 00660 ./.hg/store/data/foo/index (reposimplestore !)
92 00660 ./.hg/store/fncache (repofncache !)
95 00660 ./.hg/store/fncache (repofncache !)
93 00660 ./.hg/store/phaseroots
96 00660 ./.hg/store/phaseroots
94 00660 ./.hg/store/undo
97 00660 ./.hg/store/undo
95 00660 ./.hg/store/undo.backupfiles
98 00660 ./.hg/store/undo.backupfiles
96 00660 ./.hg/store/undo.phaseroots
99 00660 ./.hg/store/undo.phaseroots
97 00660 ./.hg/undo.backup.dirstate
100 00660 ./.hg/undo.backup.dirstate
98 00660 ./.hg/undo.bookmarks
101 00660 ./.hg/undo.bookmarks
99 00660 ./.hg/undo.branch
102 00660 ./.hg/undo.branch
100 00660 ./.hg/undo.desc
103 00660 ./.hg/undo.desc
101 00660 ./.hg/undo.dirstate
104 00660 ./.hg/undo.dirstate
102 00700 ./dir/
105 00700 ./dir/
103 00600 ./dir/bar
106 00600 ./dir/bar
104 00600 ./foo
107 00600 ./foo
105
108
106 $ umask 007
109 $ umask 007
107 $ hg init ../push
110 $ hg init ../push
108
111
109 before push
112 before push
110 group can write everything
113 group can write everything
111
114
112 $ "$PYTHON" ../printmodes.py ../push
115 $ "$PYTHON" ../printmodes.py ../push
113 00770 ../push/.hg/
116 00770 ../push/.hg/
114 00660 ../push/.hg/00changelog.i
117 00660 ../push/.hg/00changelog.i
115 00660 ../push/.hg/requires
118 00660 ../push/.hg/requires
116 00770 ../push/.hg/store/
119 00770 ../push/.hg/store/
117
120
118 $ umask 077
121 $ umask 077
119 $ hg -q push ../push
122 $ hg -q push ../push
120
123
121 after push
124 after push
122 group can still write everything
125 group can still write everything
123
126
124 $ "$PYTHON" ../printmodes.py ../push
127 $ "$PYTHON" ../printmodes.py ../push
125 00770 ../push/.hg/
128 00770 ../push/.hg/
126 00660 ../push/.hg/00changelog.i
129 00660 ../push/.hg/00changelog.i
127 00770 ../push/.hg/cache/
130 00770 ../push/.hg/cache/
128 00660 ../push/.hg/cache/branch2-base
131 00660 ../push/.hg/cache/branch2-base
129 00660 ../push/.hg/dirstate
132 00660 ../push/.hg/dirstate
130 00660 ../push/.hg/requires
133 00660 ../push/.hg/requires
131 00770 ../push/.hg/store/
134 00770 ../push/.hg/store/
132 00660 ../push/.hg/store/00changelog.i
135 00660 ../push/.hg/store/00changelog.i
133 00660 ../push/.hg/store/00manifest.i
136 00660 ../push/.hg/store/00manifest.i
134 00770 ../push/.hg/store/data/
137 00770 ../push/.hg/store/data/
135 00770 ../push/.hg/store/data/dir/
138 00770 ../push/.hg/store/data/dir/
136 00660 ../push/.hg/store/data/dir/bar.i (reporevlogstore !)
139 00660 ../push/.hg/store/data/dir/bar.i (reporevlogstore !)
137 00660 ../push/.hg/store/data/foo.i (reporevlogstore !)
140 00660 ../push/.hg/store/data/foo.i (reporevlogstore !)
138 00770 ../push/.hg/store/data/dir/bar/ (reposimplestore !)
141 00770 ../push/.hg/store/data/dir/bar/ (reposimplestore !)
139 00660 ../push/.hg/store/data/dir/bar/b80de5d138758541c5f05265ad144ab9fa86d1db (reposimplestore !)
142 00660 ../push/.hg/store/data/dir/bar/b80de5d138758541c5f05265ad144ab9fa86d1db (reposimplestore !)
140 00660 ../push/.hg/store/data/dir/bar/index (reposimplestore !)
143 00660 ../push/.hg/store/data/dir/bar/index (reposimplestore !)
141 00770 ../push/.hg/store/data/foo/ (reposimplestore !)
144 00770 ../push/.hg/store/data/foo/ (reposimplestore !)
142 00660 ../push/.hg/store/data/foo/b80de5d138758541c5f05265ad144ab9fa86d1db (reposimplestore !)
145 00660 ../push/.hg/store/data/foo/b80de5d138758541c5f05265ad144ab9fa86d1db (reposimplestore !)
143 00660 ../push/.hg/store/data/foo/index (reposimplestore !)
146 00660 ../push/.hg/store/data/foo/index (reposimplestore !)
144 00660 ../push/.hg/store/fncache (repofncache !)
147 00660 ../push/.hg/store/fncache (repofncache !)
145 00660 ../push/.hg/store/undo
148 00660 ../push/.hg/store/undo
146 00660 ../push/.hg/store/undo.backupfiles
149 00660 ../push/.hg/store/undo.backupfiles
147 00660 ../push/.hg/store/undo.phaseroots
150 00660 ../push/.hg/store/undo.phaseroots
148 00660 ../push/.hg/undo.bookmarks
151 00660 ../push/.hg/undo.bookmarks
149 00660 ../push/.hg/undo.branch
152 00660 ../push/.hg/undo.branch
150 00660 ../push/.hg/undo.desc
153 00660 ../push/.hg/undo.desc
151 00660 ../push/.hg/undo.dirstate
154 00660 ../push/.hg/undo.dirstate
152
155
153
156
154 Test that we don't lose the setgid bit when we call chmod.
157 Test that we don't lose the setgid bit when we call chmod.
155 Not all systems support setgid directories (e.g. HFS+), so
158 Not all systems support setgid directories (e.g. HFS+), so
156 just check that directories have the same mode.
159 just check that directories have the same mode.
157
160
158 $ cd ..
161 $ cd ..
159 $ hg init setgid
162 $ hg init setgid
160 $ cd setgid
163 $ cd setgid
161 $ chmod g+rwx .hg/store
164 $ chmod g+rwx .hg/store
162 $ chmod g+s .hg/store 2> /dev/null || true
165 $ chmod g+s .hg/store 2> /dev/null || true
163 $ mkdir dir
166 $ mkdir dir
164 $ touch dir/file
167 $ touch dir/file
165 $ hg ci -qAm 'add dir/file'
168 $ hg ci -qAm 'add dir/file'
166 $ storemode=`"$PYTHON" ../mode.py .hg/store`
169 $ storemode=`"$PYTHON" ../mode.py .hg/store`
167 $ dirmode=`"$PYTHON" ../mode.py .hg/store/data/dir`
170 $ dirmode=`"$PYTHON" ../mode.py .hg/store/data/dir`
168 $ if [ "$storemode" != "$dirmode" ]; then
171 $ if [ "$storemode" != "$dirmode" ]; then
169 > echo "$storemode != $dirmode"
172 > echo "$storemode != $dirmode"
170 > fi
173 > fi
171 $ cd ..
174 $ cd ..
172
175
173 $ cd .. # g-s dir
176 $ cd .. # g-s dir
@@ -1,468 +1,474 b''
1 $ echo "[extensions]" >> $HGRCPATH
1 $ echo "[extensions]" >> $HGRCPATH
2 $ echo "share = " >> $HGRCPATH
2 $ echo "share = " >> $HGRCPATH
3
3
4 prepare repo1
4 prepare repo1
5
5
6 $ hg init repo1
6 $ hg init repo1
7 $ cd repo1
7 $ cd repo1
8 $ echo a > a
8 $ echo a > a
9 $ hg commit -A -m'init'
9 $ hg commit -A -m'init'
10 adding a
10 adding a
11
11
12 share it
12 share it
13
13
14 $ cd ..
14 $ cd ..
15 $ hg share repo1 repo2
15 $ hg share repo1 repo2
16 updating working directory
16 updating working directory
17 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
17 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
18
18
19 share shouldn't have a store dir
19 share shouldn't have a store dir
20
20
21 $ cd repo2
21 $ cd repo2
22 $ test -d .hg/store
22 $ test -d .hg/store
23 [1]
23 [1]
24
24
25 share shouldn't have a cache dir, original repo should
25 share shouldn't have a full cache dir, original repo should
26
26
27 $ hg branches
27 $ hg branches
28 default 0:d3873e73d99e
28 default 0:d3873e73d99e
29 $ hg tags
29 $ hg tags
30 tip 0:d3873e73d99e
30 tip 0:d3873e73d99e
31 $ test -d .hg/cache
31 $ ls -1 .hg/cache || true
32 [1]
32 ls: .hg/cache: $ENOENT$ (no-execbit no-symlink !)
33 checkisexec (execbit !)
34 checklink (symlink !)
35 checklink-target (symlink !)
33 $ ls -1 ../repo1/.hg/cache
36 $ ls -1 ../repo1/.hg/cache
34 branch2-served
37 branch2-served
38 checkisexec (execbit !)
39 checklink (symlink !)
40 checklink-target (symlink !)
35 manifestfulltextcache (reporevlogstore !)
41 manifestfulltextcache (reporevlogstore !)
36 rbc-names-v1
42 rbc-names-v1
37 rbc-revs-v1
43 rbc-revs-v1
38 tags2-visible
44 tags2-visible
39
45
40 Some sed versions appends newline, some don't, and some just fails
46 Some sed versions appends newline, some don't, and some just fails
41
47
42 $ cat .hg/sharedpath; echo
48 $ cat .hg/sharedpath; echo
43 $TESTTMP/repo1/.hg
49 $TESTTMP/repo1/.hg
44
50
45 trailing newline on .hg/sharedpath is ok
51 trailing newline on .hg/sharedpath is ok
46 $ hg tip -q
52 $ hg tip -q
47 0:d3873e73d99e
53 0:d3873e73d99e
48 $ echo '' >> .hg/sharedpath
54 $ echo '' >> .hg/sharedpath
49 $ cat .hg/sharedpath
55 $ cat .hg/sharedpath
50 $TESTTMP/repo1/.hg
56 $TESTTMP/repo1/.hg
51 $ hg tip -q
57 $ hg tip -q
52 0:d3873e73d99e
58 0:d3873e73d99e
53
59
54 commit in shared clone
60 commit in shared clone
55
61
56 $ echo a >> a
62 $ echo a >> a
57 $ hg commit -m'change in shared clone'
63 $ hg commit -m'change in shared clone'
58
64
59 check original
65 check original
60
66
61 $ cd ../repo1
67 $ cd ../repo1
62 $ hg log
68 $ hg log
63 changeset: 1:8af4dc49db9e
69 changeset: 1:8af4dc49db9e
64 tag: tip
70 tag: tip
65 user: test
71 user: test
66 date: Thu Jan 01 00:00:00 1970 +0000
72 date: Thu Jan 01 00:00:00 1970 +0000
67 summary: change in shared clone
73 summary: change in shared clone
68
74
69 changeset: 0:d3873e73d99e
75 changeset: 0:d3873e73d99e
70 user: test
76 user: test
71 date: Thu Jan 01 00:00:00 1970 +0000
77 date: Thu Jan 01 00:00:00 1970 +0000
72 summary: init
78 summary: init
73
79
74 $ hg update
80 $ hg update
75 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
81 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
76 $ cat a # should be two lines of "a"
82 $ cat a # should be two lines of "a"
77 a
83 a
78 a
84 a
79
85
80 commit in original
86 commit in original
81
87
82 $ echo b > b
88 $ echo b > b
83 $ hg commit -A -m'another file'
89 $ hg commit -A -m'another file'
84 adding b
90 adding b
85
91
86 check in shared clone
92 check in shared clone
87
93
88 $ cd ../repo2
94 $ cd ../repo2
89 $ hg log
95 $ hg log
90 changeset: 2:c2e0ac586386
96 changeset: 2:c2e0ac586386
91 tag: tip
97 tag: tip
92 user: test
98 user: test
93 date: Thu Jan 01 00:00:00 1970 +0000
99 date: Thu Jan 01 00:00:00 1970 +0000
94 summary: another file
100 summary: another file
95
101
96 changeset: 1:8af4dc49db9e
102 changeset: 1:8af4dc49db9e
97 user: test
103 user: test
98 date: Thu Jan 01 00:00:00 1970 +0000
104 date: Thu Jan 01 00:00:00 1970 +0000
99 summary: change in shared clone
105 summary: change in shared clone
100
106
101 changeset: 0:d3873e73d99e
107 changeset: 0:d3873e73d99e
102 user: test
108 user: test
103 date: Thu Jan 01 00:00:00 1970 +0000
109 date: Thu Jan 01 00:00:00 1970 +0000
104 summary: init
110 summary: init
105
111
106 $ hg update
112 $ hg update
107 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
113 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
108 $ cat b # should exist with one "b"
114 $ cat b # should exist with one "b"
109 b
115 b
110
116
111 hg serve shared clone
117 hg serve shared clone
112
118
113 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid
119 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid
114 $ cat hg.pid >> $DAEMON_PIDS
120 $ cat hg.pid >> $DAEMON_PIDS
115 $ get-with-headers.py localhost:$HGPORT 'raw-file/'
121 $ get-with-headers.py localhost:$HGPORT 'raw-file/'
116 200 Script output follows
122 200 Script output follows
117
123
118
124
119 -rw-r--r-- 4 a
125 -rw-r--r-- 4 a
120 -rw-r--r-- 2 b
126 -rw-r--r-- 2 b
121
127
122
128
123
129
124 test unshare command
130 test unshare command
125
131
126 $ hg unshare
132 $ hg unshare
127 $ test -d .hg/store
133 $ test -d .hg/store
128 $ test -f .hg/sharedpath
134 $ test -f .hg/sharedpath
129 [1]
135 [1]
130 $ grep shared .hg/requires
136 $ grep shared .hg/requires
131 [1]
137 [1]
132 $ hg unshare
138 $ hg unshare
133 abort: this is not a shared repo
139 abort: this is not a shared repo
134 [255]
140 [255]
135
141
136 check that a change does not propagate
142 check that a change does not propagate
137
143
138 $ echo b >> b
144 $ echo b >> b
139 $ hg commit -m'change in unshared'
145 $ hg commit -m'change in unshared'
140 $ cd ../repo1
146 $ cd ../repo1
141 $ hg id -r tip
147 $ hg id -r tip
142 c2e0ac586386 tip
148 c2e0ac586386 tip
143
149
144 $ cd ..
150 $ cd ..
145
151
146
152
147 test sharing bookmarks
153 test sharing bookmarks
148
154
149 $ hg share -B repo1 repo3
155 $ hg share -B repo1 repo3
150 updating working directory
156 updating working directory
151 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
157 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
152 $ cd repo1
158 $ cd repo1
153 $ hg bookmark bm1
159 $ hg bookmark bm1
154 $ hg bookmarks
160 $ hg bookmarks
155 * bm1 2:c2e0ac586386
161 * bm1 2:c2e0ac586386
156 $ cd ../repo2
162 $ cd ../repo2
157 $ hg book bm2
163 $ hg book bm2
158 $ hg bookmarks
164 $ hg bookmarks
159 * bm2 3:0e6e70d1d5f1
165 * bm2 3:0e6e70d1d5f1
160 $ cd ../repo3
166 $ cd ../repo3
161 $ hg bookmarks
167 $ hg bookmarks
162 bm1 2:c2e0ac586386
168 bm1 2:c2e0ac586386
163 $ hg book bm3
169 $ hg book bm3
164 $ hg bookmarks
170 $ hg bookmarks
165 bm1 2:c2e0ac586386
171 bm1 2:c2e0ac586386
166 * bm3 2:c2e0ac586386
172 * bm3 2:c2e0ac586386
167 $ cd ../repo1
173 $ cd ../repo1
168 $ hg bookmarks
174 $ hg bookmarks
169 * bm1 2:c2e0ac586386
175 * bm1 2:c2e0ac586386
170 bm3 2:c2e0ac586386
176 bm3 2:c2e0ac586386
171
177
172 check whether HG_PENDING makes pending changes only in relatd
178 check whether HG_PENDING makes pending changes only in relatd
173 repositories visible to an external hook.
179 repositories visible to an external hook.
174
180
175 In "hg share" case, another transaction can't run in other
181 In "hg share" case, another transaction can't run in other
176 repositories sharing same source repository, because starting
182 repositories sharing same source repository, because starting
177 transaction requires locking store of source repository.
183 transaction requires locking store of source repository.
178
184
179 Therefore, this test scenario ignores checking visibility of
185 Therefore, this test scenario ignores checking visibility of
180 .hg/bookmakrs.pending in repo2, which shares repo1 without bookmarks.
186 .hg/bookmakrs.pending in repo2, which shares repo1 without bookmarks.
181
187
182 $ cat > $TESTTMP/checkbookmarks.sh <<EOF
188 $ cat > $TESTTMP/checkbookmarks.sh <<EOF
183 > echo "@repo1"
189 > echo "@repo1"
184 > hg -R "$TESTTMP/repo1" bookmarks
190 > hg -R "$TESTTMP/repo1" bookmarks
185 > echo "@repo2"
191 > echo "@repo2"
186 > hg -R "$TESTTMP/repo2" bookmarks
192 > hg -R "$TESTTMP/repo2" bookmarks
187 > echo "@repo3"
193 > echo "@repo3"
188 > hg -R "$TESTTMP/repo3" bookmarks
194 > hg -R "$TESTTMP/repo3" bookmarks
189 > exit 1 # to avoid adding new bookmark for subsequent tests
195 > exit 1 # to avoid adding new bookmark for subsequent tests
190 > EOF
196 > EOF
191
197
192 $ cd ../repo1
198 $ cd ../repo1
193 $ hg --config hooks.pretxnclose="sh $TESTTMP/checkbookmarks.sh" -q book bmX
199 $ hg --config hooks.pretxnclose="sh $TESTTMP/checkbookmarks.sh" -q book bmX
194 @repo1
200 @repo1
195 bm1 2:c2e0ac586386
201 bm1 2:c2e0ac586386
196 bm3 2:c2e0ac586386
202 bm3 2:c2e0ac586386
197 * bmX 2:c2e0ac586386
203 * bmX 2:c2e0ac586386
198 @repo2
204 @repo2
199 * bm2 3:0e6e70d1d5f1
205 * bm2 3:0e6e70d1d5f1
200 @repo3
206 @repo3
201 bm1 2:c2e0ac586386
207 bm1 2:c2e0ac586386
202 * bm3 2:c2e0ac586386
208 * bm3 2:c2e0ac586386
203 bmX 2:c2e0ac586386
209 bmX 2:c2e0ac586386
204 transaction abort!
210 transaction abort!
205 rollback completed
211 rollback completed
206 abort: pretxnclose hook exited with status 1
212 abort: pretxnclose hook exited with status 1
207 [255]
213 [255]
208 $ hg book bm1
214 $ hg book bm1
209
215
210 FYI, in contrast to above test, bmX is invisible in repo1 (= shared
216 FYI, in contrast to above test, bmX is invisible in repo1 (= shared
211 src), because (1) HG_PENDING refers only repo3 and (2)
217 src), because (1) HG_PENDING refers only repo3 and (2)
212 "bookmarks.pending" is written only into repo3.
218 "bookmarks.pending" is written only into repo3.
213
219
214 $ cd ../repo3
220 $ cd ../repo3
215 $ hg --config hooks.pretxnclose="sh $TESTTMP/checkbookmarks.sh" -q book bmX
221 $ hg --config hooks.pretxnclose="sh $TESTTMP/checkbookmarks.sh" -q book bmX
216 @repo1
222 @repo1
217 * bm1 2:c2e0ac586386
223 * bm1 2:c2e0ac586386
218 bm3 2:c2e0ac586386
224 bm3 2:c2e0ac586386
219 @repo2
225 @repo2
220 * bm2 3:0e6e70d1d5f1
226 * bm2 3:0e6e70d1d5f1
221 @repo3
227 @repo3
222 bm1 2:c2e0ac586386
228 bm1 2:c2e0ac586386
223 bm3 2:c2e0ac586386
229 bm3 2:c2e0ac586386
224 * bmX 2:c2e0ac586386
230 * bmX 2:c2e0ac586386
225 transaction abort!
231 transaction abort!
226 rollback completed
232 rollback completed
227 abort: pretxnclose hook exited with status 1
233 abort: pretxnclose hook exited with status 1
228 [255]
234 [255]
229 $ hg book bm3
235 $ hg book bm3
230
236
231 $ cd ../repo1
237 $ cd ../repo1
232
238
233 test that commits work
239 test that commits work
234
240
235 $ echo 'shared bookmarks' > a
241 $ echo 'shared bookmarks' > a
236 $ hg commit -m 'testing shared bookmarks'
242 $ hg commit -m 'testing shared bookmarks'
237 $ hg bookmarks
243 $ hg bookmarks
238 * bm1 3:b87954705719
244 * bm1 3:b87954705719
239 bm3 2:c2e0ac586386
245 bm3 2:c2e0ac586386
240 $ cd ../repo3
246 $ cd ../repo3
241 $ hg bookmarks
247 $ hg bookmarks
242 bm1 3:b87954705719
248 bm1 3:b87954705719
243 * bm3 2:c2e0ac586386
249 * bm3 2:c2e0ac586386
244 $ echo 'more shared bookmarks' > a
250 $ echo 'more shared bookmarks' > a
245 $ hg commit -m 'testing shared bookmarks'
251 $ hg commit -m 'testing shared bookmarks'
246 created new head
252 created new head
247 $ hg bookmarks
253 $ hg bookmarks
248 bm1 3:b87954705719
254 bm1 3:b87954705719
249 * bm3 4:62f4ded848e4
255 * bm3 4:62f4ded848e4
250 $ cd ../repo1
256 $ cd ../repo1
251 $ hg bookmarks
257 $ hg bookmarks
252 * bm1 3:b87954705719
258 * bm1 3:b87954705719
253 bm3 4:62f4ded848e4
259 bm3 4:62f4ded848e4
254 $ cd ..
260 $ cd ..
255
261
256 non largefiles repos won't enable largefiles
262 non largefiles repos won't enable largefiles
257
263
258 $ hg share --config extensions.largefiles= repo3 sharedrepo
264 $ hg share --config extensions.largefiles= repo3 sharedrepo
259 The fsmonitor extension is incompatible with the largefiles extension and has been disabled. (fsmonitor !)
265 The fsmonitor extension is incompatible with the largefiles extension and has been disabled. (fsmonitor !)
260 The fsmonitor extension is incompatible with the largefiles extension and has been disabled. (fsmonitor !)
266 The fsmonitor extension is incompatible with the largefiles extension and has been disabled. (fsmonitor !)
261 updating working directory
267 updating working directory
262 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
268 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
263 $ [ -f sharedrepo/.hg/hgrc ]
269 $ [ -f sharedrepo/.hg/hgrc ]
264 [1]
270 [1]
265
271
266 test pushing bookmarks works
272 test pushing bookmarks works
267
273
268 $ hg clone repo3 repo4
274 $ hg clone repo3 repo4
269 updating to branch default
275 updating to branch default
270 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
276 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
271 $ cd repo4
277 $ cd repo4
272 $ hg boo bm4
278 $ hg boo bm4
273 $ echo foo > b
279 $ echo foo > b
274 $ hg commit -m 'foo in b'
280 $ hg commit -m 'foo in b'
275 $ hg boo
281 $ hg boo
276 bm1 3:b87954705719
282 bm1 3:b87954705719
277 bm3 4:62f4ded848e4
283 bm3 4:62f4ded848e4
278 * bm4 5:92793bfc8cad
284 * bm4 5:92793bfc8cad
279 $ hg push -B bm4
285 $ hg push -B bm4
280 pushing to $TESTTMP/repo3
286 pushing to $TESTTMP/repo3
281 searching for changes
287 searching for changes
282 adding changesets
288 adding changesets
283 adding manifests
289 adding manifests
284 adding file changes
290 adding file changes
285 added 1 changesets with 1 changes to 1 files
291 added 1 changesets with 1 changes to 1 files
286 exporting bookmark bm4
292 exporting bookmark bm4
287 $ cd ../repo1
293 $ cd ../repo1
288 $ hg bookmarks
294 $ hg bookmarks
289 * bm1 3:b87954705719
295 * bm1 3:b87954705719
290 bm3 4:62f4ded848e4
296 bm3 4:62f4ded848e4
291 bm4 5:92793bfc8cad
297 bm4 5:92793bfc8cad
292 $ cd ../repo3
298 $ cd ../repo3
293 $ hg bookmarks
299 $ hg bookmarks
294 bm1 3:b87954705719
300 bm1 3:b87954705719
295 * bm3 4:62f4ded848e4
301 * bm3 4:62f4ded848e4
296 bm4 5:92793bfc8cad
302 bm4 5:92793bfc8cad
297 $ cd ..
303 $ cd ..
298
304
299 test behavior when sharing a shared repo
305 test behavior when sharing a shared repo
300
306
301 $ hg share -B repo3 missingdir/repo5
307 $ hg share -B repo3 missingdir/repo5
302 updating working directory
308 updating working directory
303 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
309 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
304 $ cd missingdir/repo5
310 $ cd missingdir/repo5
305 $ hg book
311 $ hg book
306 bm1 3:b87954705719
312 bm1 3:b87954705719
307 bm3 4:62f4ded848e4
313 bm3 4:62f4ded848e4
308 bm4 5:92793bfc8cad
314 bm4 5:92793bfc8cad
309 $ cd ../..
315 $ cd ../..
310
316
311 test what happens when an active bookmark is deleted
317 test what happens when an active bookmark is deleted
312
318
313 $ cd repo1
319 $ cd repo1
314 $ hg boo -d bm3
320 $ hg boo -d bm3
315 $ hg boo
321 $ hg boo
316 * bm1 3:b87954705719
322 * bm1 3:b87954705719
317 bm4 5:92793bfc8cad
323 bm4 5:92793bfc8cad
318 $ cd ../repo3
324 $ cd ../repo3
319 $ hg boo
325 $ hg boo
320 bm1 3:b87954705719
326 bm1 3:b87954705719
321 bm4 5:92793bfc8cad
327 bm4 5:92793bfc8cad
322 $ cd ..
328 $ cd ..
323
329
324 verify that bookmarks are not written on failed transaction
330 verify that bookmarks are not written on failed transaction
325
331
326 $ cat > failpullbookmarks.py << EOF
332 $ cat > failpullbookmarks.py << EOF
327 > """A small extension that makes bookmark pulls fail, for testing"""
333 > """A small extension that makes bookmark pulls fail, for testing"""
328 > from __future__ import absolute_import
334 > from __future__ import absolute_import
329 > from mercurial import (
335 > from mercurial import (
330 > error,
336 > error,
331 > exchange,
337 > exchange,
332 > extensions,
338 > extensions,
333 > )
339 > )
334 > def _pullbookmarks(orig, pullop):
340 > def _pullbookmarks(orig, pullop):
335 > orig(pullop)
341 > orig(pullop)
336 > raise error.HookAbort('forced failure by extension')
342 > raise error.HookAbort('forced failure by extension')
337 > def extsetup(ui):
343 > def extsetup(ui):
338 > extensions.wrapfunction(exchange, '_pullbookmarks', _pullbookmarks)
344 > extensions.wrapfunction(exchange, '_pullbookmarks', _pullbookmarks)
339 > EOF
345 > EOF
340 $ cd repo4
346 $ cd repo4
341 $ hg boo
347 $ hg boo
342 bm1 3:b87954705719
348 bm1 3:b87954705719
343 bm3 4:62f4ded848e4
349 bm3 4:62f4ded848e4
344 * bm4 5:92793bfc8cad
350 * bm4 5:92793bfc8cad
345 $ cd ../repo3
351 $ cd ../repo3
346 $ hg boo
352 $ hg boo
347 bm1 3:b87954705719
353 bm1 3:b87954705719
348 bm4 5:92793bfc8cad
354 bm4 5:92793bfc8cad
349 $ hg --config "extensions.failpullbookmarks=$TESTTMP/failpullbookmarks.py" pull $TESTTMP/repo4
355 $ hg --config "extensions.failpullbookmarks=$TESTTMP/failpullbookmarks.py" pull $TESTTMP/repo4
350 pulling from $TESTTMP/repo4
356 pulling from $TESTTMP/repo4
351 searching for changes
357 searching for changes
352 no changes found
358 no changes found
353 adding remote bookmark bm3
359 adding remote bookmark bm3
354 abort: forced failure by extension
360 abort: forced failure by extension
355 [255]
361 [255]
356 $ hg boo
362 $ hg boo
357 bm1 3:b87954705719
363 bm1 3:b87954705719
358 bm4 5:92793bfc8cad
364 bm4 5:92793bfc8cad
359 $ hg pull $TESTTMP/repo4
365 $ hg pull $TESTTMP/repo4
360 pulling from $TESTTMP/repo4
366 pulling from $TESTTMP/repo4
361 searching for changes
367 searching for changes
362 no changes found
368 no changes found
363 adding remote bookmark bm3
369 adding remote bookmark bm3
364 1 local changesets published
370 1 local changesets published
365 $ hg boo
371 $ hg boo
366 bm1 3:b87954705719
372 bm1 3:b87954705719
367 * bm3 4:62f4ded848e4
373 * bm3 4:62f4ded848e4
368 bm4 5:92793bfc8cad
374 bm4 5:92793bfc8cad
369 $ cd ..
375 $ cd ..
370
376
371 verify bookmark behavior after unshare
377 verify bookmark behavior after unshare
372
378
373 $ cd repo3
379 $ cd repo3
374 $ hg unshare
380 $ hg unshare
375 $ hg boo
381 $ hg boo
376 bm1 3:b87954705719
382 bm1 3:b87954705719
377 * bm3 4:62f4ded848e4
383 * bm3 4:62f4ded848e4
378 bm4 5:92793bfc8cad
384 bm4 5:92793bfc8cad
379 $ hg boo -d bm4
385 $ hg boo -d bm4
380 $ hg boo bm5
386 $ hg boo bm5
381 $ hg boo
387 $ hg boo
382 bm1 3:b87954705719
388 bm1 3:b87954705719
383 bm3 4:62f4ded848e4
389 bm3 4:62f4ded848e4
384 * bm5 4:62f4ded848e4
390 * bm5 4:62f4ded848e4
385 $ cd ../repo1
391 $ cd ../repo1
386 $ hg boo
392 $ hg boo
387 * bm1 3:b87954705719
393 * bm1 3:b87954705719
388 bm3 4:62f4ded848e4
394 bm3 4:62f4ded848e4
389 bm4 5:92793bfc8cad
395 bm4 5:92793bfc8cad
390 $ cd ..
396 $ cd ..
391
397
392 test shared clones using relative paths work
398 test shared clones using relative paths work
393
399
394 $ mkdir thisdir
400 $ mkdir thisdir
395 $ hg init thisdir/orig
401 $ hg init thisdir/orig
396 $ hg share -U thisdir/orig thisdir/abs
402 $ hg share -U thisdir/orig thisdir/abs
397 $ hg share -U --relative thisdir/abs thisdir/rel
403 $ hg share -U --relative thisdir/abs thisdir/rel
398 $ cat thisdir/rel/.hg/sharedpath
404 $ cat thisdir/rel/.hg/sharedpath
399 ../../orig/.hg (no-eol)
405 ../../orig/.hg (no-eol)
400 $ grep shared thisdir/*/.hg/requires
406 $ grep shared thisdir/*/.hg/requires
401 thisdir/abs/.hg/requires:shared
407 thisdir/abs/.hg/requires:shared
402 thisdir/rel/.hg/requires:relshared
408 thisdir/rel/.hg/requires:relshared
403 thisdir/rel/.hg/requires:shared
409 thisdir/rel/.hg/requires:shared
404
410
405 test that relative shared paths aren't relative to $PWD
411 test that relative shared paths aren't relative to $PWD
406
412
407 $ cd thisdir
413 $ cd thisdir
408 $ hg -R rel root
414 $ hg -R rel root
409 $TESTTMP/thisdir/rel
415 $TESTTMP/thisdir/rel
410 $ cd ..
416 $ cd ..
411
417
412 now test that relative paths really are relative, survive across
418 now test that relative paths really are relative, survive across
413 renames and changes of PWD
419 renames and changes of PWD
414
420
415 $ hg -R thisdir/abs root
421 $ hg -R thisdir/abs root
416 $TESTTMP/thisdir/abs
422 $TESTTMP/thisdir/abs
417 $ hg -R thisdir/rel root
423 $ hg -R thisdir/rel root
418 $TESTTMP/thisdir/rel
424 $TESTTMP/thisdir/rel
419 $ mv thisdir thatdir
425 $ mv thisdir thatdir
420 $ hg -R thatdir/abs root
426 $ hg -R thatdir/abs root
421 abort: .hg/sharedpath points to nonexistent directory $TESTTMP/thisdir/orig/.hg!
427 abort: .hg/sharedpath points to nonexistent directory $TESTTMP/thisdir/orig/.hg!
422 [255]
428 [255]
423 $ hg -R thatdir/rel root
429 $ hg -R thatdir/rel root
424 $TESTTMP/thatdir/rel
430 $TESTTMP/thatdir/rel
425
431
426 test unshare relshared repo
432 test unshare relshared repo
427
433
428 $ cd thatdir/rel
434 $ cd thatdir/rel
429 $ hg unshare
435 $ hg unshare
430 $ test -d .hg/store
436 $ test -d .hg/store
431 $ test -f .hg/sharedpath
437 $ test -f .hg/sharedpath
432 [1]
438 [1]
433 $ grep shared .hg/requires
439 $ grep shared .hg/requires
434 [1]
440 [1]
435 $ hg unshare
441 $ hg unshare
436 abort: this is not a shared repo
442 abort: this is not a shared repo
437 [255]
443 [255]
438 $ cd ../..
444 $ cd ../..
439
445
440 $ rm -r thatdir
446 $ rm -r thatdir
441
447
442 Demonstrate buggy behavior around requirements validation
448 Demonstrate buggy behavior around requirements validation
443 See comment in localrepo.py:makelocalrepository() for more.
449 See comment in localrepo.py:makelocalrepository() for more.
444
450
445 $ hg init sharenewrequires
451 $ hg init sharenewrequires
446 $ hg share sharenewrequires shareoldrequires
452 $ hg share sharenewrequires shareoldrequires
447 updating working directory
453 updating working directory
448 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
454 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
449
455
450 $ cat >> sharenewrequires/.hg/requires << EOF
456 $ cat >> sharenewrequires/.hg/requires << EOF
451 > missing-requirement
457 > missing-requirement
452 > EOF
458 > EOF
453
459
454 We cannot open the repo with the unknown requirement
460 We cannot open the repo with the unknown requirement
455
461
456 $ hg -R sharenewrequires status
462 $ hg -R sharenewrequires status
457 abort: repository requires features unknown to this Mercurial: missing-requirement!
463 abort: repository requires features unknown to this Mercurial: missing-requirement!
458 (see https://mercurial-scm.org/wiki/MissingRequirement for more information)
464 (see https://mercurial-scm.org/wiki/MissingRequirement for more information)
459 [255]
465 [255]
460
466
461 BUG: we don't get the same error when opening the shared repo pointing to it
467 BUG: we don't get the same error when opening the shared repo pointing to it
462
468
463 $ hg -R shareoldrequires status
469 $ hg -R shareoldrequires status
464
470
465 Explicitly kill daemons to let the test exit on Windows
471 Explicitly kill daemons to let the test exit on Windows
466
472
467 $ killdaemons.py
473 $ killdaemons.py
468
474
@@ -1,1176 +1,1177 b''
1 $ cat >> $HGRCPATH <<EOF
1 $ cat >> $HGRCPATH <<EOF
2 > [extdiff]
2 > [extdiff]
3 > # for portability:
3 > # for portability:
4 > pdiff = sh "$RUNTESTDIR/pdiff"
4 > pdiff = sh "$RUNTESTDIR/pdiff"
5 > [progress]
5 > [progress]
6 > disable=False
6 > disable=False
7 > assume-tty = 1
7 > assume-tty = 1
8 > delay = 0
8 > delay = 0
9 > # set changedelay really large so we don't see nested topics
9 > # set changedelay really large so we don't see nested topics
10 > changedelay = 30000
10 > changedelay = 30000
11 > format = topic bar number
11 > format = topic bar number
12 > refresh = 0
12 > refresh = 0
13 > width = 60
13 > width = 60
14 > EOF
14 > EOF
15
15
16 Preparing the subrepository 'sub2'
16 Preparing the subrepository 'sub2'
17
17
18 $ hg init sub2
18 $ hg init sub2
19 $ echo sub2 > sub2/sub2
19 $ echo sub2 > sub2/sub2
20 $ hg add -R sub2
20 $ hg add -R sub2
21 adding sub2/sub2
21 adding sub2/sub2
22 $ hg commit -R sub2 -m "sub2 import"
22 $ hg commit -R sub2 -m "sub2 import"
23
23
24 Preparing the 'sub1' repo which depends on the subrepo 'sub2'
24 Preparing the 'sub1' repo which depends on the subrepo 'sub2'
25
25
26 $ hg init sub1
26 $ hg init sub1
27 $ echo sub1 > sub1/sub1
27 $ echo sub1 > sub1/sub1
28 $ echo "sub2 = ../sub2" > sub1/.hgsub
28 $ echo "sub2 = ../sub2" > sub1/.hgsub
29 $ hg clone sub2 sub1/sub2
29 $ hg clone sub2 sub1/sub2
30 \r (no-eol) (esc)
30 \r (no-eol) (esc)
31 linking [ <=> ] 1\r (no-eol) (esc)
31 linking [ <=> ] 1\r (no-eol) (esc)
32 linking [ <=> ] 2\r (no-eol) (esc)
32 linking [ <=> ] 2\r (no-eol) (esc)
33 linking [ <=> ] 3\r (no-eol) (esc)
33 linking [ <=> ] 3\r (no-eol) (esc)
34 linking [ <=> ] 4\r (no-eol) (esc)
34 linking [ <=> ] 4\r (no-eol) (esc)
35 linking [ <=> ] 5\r (no-eol) (esc)
35 linking [ <=> ] 5\r (no-eol) (esc)
36 linking [ <=> ] 6\r (no-eol) (esc)
36 linking [ <=> ] 6\r (no-eol) (esc)
37 \r (no-eol) (esc)
37 \r (no-eol) (esc)
38 \r (no-eol) (esc)
38 \r (no-eol) (esc)
39 updating [===========================================>] 1/1\r (no-eol) (esc)
39 updating [===========================================>] 1/1\r (no-eol) (esc)
40 \r (no-eol) (esc)
40 \r (no-eol) (esc)
41 updating to branch default
41 updating to branch default
42 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
42 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
43 $ hg add -R sub1
43 $ hg add -R sub1
44 adding sub1/.hgsub
44 adding sub1/.hgsub
45 adding sub1/sub1
45 adding sub1/sub1
46 $ hg commit -R sub1 -m "sub1 import"
46 $ hg commit -R sub1 -m "sub1 import"
47
47
48 Preparing the 'main' repo which depends on the subrepo 'sub1'
48 Preparing the 'main' repo which depends on the subrepo 'sub1'
49
49
50 $ hg init main
50 $ hg init main
51 $ echo main > main/main
51 $ echo main > main/main
52 $ echo "sub1 = ../sub1" > main/.hgsub
52 $ echo "sub1 = ../sub1" > main/.hgsub
53 $ hg clone sub1 main/sub1
53 $ hg clone sub1 main/sub1
54 \r (no-eol) (esc)
54 \r (no-eol) (esc)
55 linking [ <=> ] 1\r (no-eol) (esc)
55 linking [ <=> ] 1\r (no-eol) (esc)
56 linking [ <=> ] 2\r (no-eol) (esc)
56 linking [ <=> ] 2\r (no-eol) (esc)
57 linking [ <=> ] 3\r (no-eol) (esc)
57 linking [ <=> ] 3\r (no-eol) (esc)
58 linking [ <=> ] 4\r (no-eol) (esc)
58 linking [ <=> ] 4\r (no-eol) (esc)
59 linking [ <=> ] 5\r (no-eol) (esc)
59 linking [ <=> ] 5\r (no-eol) (esc)
60 linking [ <=> ] 6\r (no-eol) (esc)
60 linking [ <=> ] 6\r (no-eol) (esc)
61 linking [ <=> ] 7\r (no-eol) (esc)
61 linking [ <=> ] 7\r (no-eol) (esc)
62 linking [ <=> ] 8\r (no-eol) (esc)
62 linking [ <=> ] 8\r (no-eol) (esc)
63 linking [ <=> ] 9\r (no-eol) (esc) (reposimplestore !)
63 linking [ <=> ] 9\r (no-eol) (esc) (reposimplestore !)
64 linking [ <=> ] 10\r (no-eol) (esc) (reposimplestore !)
64 linking [ <=> ] 10\r (no-eol) (esc) (reposimplestore !)
65 \r (no-eol) (esc)
65 \r (no-eol) (esc)
66 \r (no-eol) (esc)
66 \r (no-eol) (esc)
67 updating [===========================================>] 3/3\r (no-eol) (esc)
67 updating [===========================================>] 3/3\r (no-eol) (esc)
68 \r (no-eol) (esc)
68 \r (no-eol) (esc)
69 \r (no-eol) (esc)
69 \r (no-eol) (esc)
70 linking [ <=> ] 1\r (no-eol) (esc)
70 linking [ <=> ] 1\r (no-eol) (esc)
71 linking [ <=> ] 2\r (no-eol) (esc)
71 linking [ <=> ] 2\r (no-eol) (esc)
72 linking [ <=> ] 3\r (no-eol) (esc)
72 linking [ <=> ] 3\r (no-eol) (esc)
73 linking [ <=> ] 4\r (no-eol) (esc)
73 linking [ <=> ] 4\r (no-eol) (esc)
74 linking [ <=> ] 5\r (no-eol) (esc)
74 linking [ <=> ] 5\r (no-eol) (esc)
75 linking [ <=> ] 6\r (no-eol) (esc)
75 linking [ <=> ] 6\r (no-eol) (esc)
76 updating [===========================================>] 1/1\r (no-eol) (esc)
76 updating [===========================================>] 1/1\r (no-eol) (esc)
77 \r (no-eol) (esc)
77 \r (no-eol) (esc)
78 updating to branch default
78 updating to branch default
79 cloning subrepo sub2 from $TESTTMP/sub2
79 cloning subrepo sub2 from $TESTTMP/sub2
80 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
80 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
81 $ hg add -R main
81 $ hg add -R main
82 adding main/.hgsub
82 adding main/.hgsub
83 adding main/main
83 adding main/main
84 $ hg commit -R main -m "main import"
84 $ hg commit -R main -m "main import"
85
85
86 #if serve
86 #if serve
87
87
88 Unfortunately, subrepos not at their nominal location cannot be cloned. But
88 Unfortunately, subrepos not at their nominal location cannot be cloned. But
89 they are still served from their location within the local repository. The only
89 they are still served from their location within the local repository. The only
90 reason why 'main' can be cloned via the filesystem is because 'sub1' and 'sub2'
90 reason why 'main' can be cloned via the filesystem is because 'sub1' and 'sub2'
91 are also available as siblings of 'main'.
91 are also available as siblings of 'main'.
92
92
93 $ hg serve -R main --debug -S -p $HGPORT -d --pid-file=hg1.pid -E error.log -A access.log
93 $ hg serve -R main --debug -S -p $HGPORT -d --pid-file=hg1.pid -E error.log -A access.log
94 adding = $TESTTMP/main
94 adding = $TESTTMP/main
95 adding sub1 = $TESTTMP/main/sub1
95 adding sub1 = $TESTTMP/main/sub1
96 adding sub1/sub2 = $TESTTMP/main/sub1/sub2
96 adding sub1/sub2 = $TESTTMP/main/sub1/sub2
97 listening at http://*:$HGPORT/ (bound to *:$HGPORT) (glob) (?)
97 listening at http://*:$HGPORT/ (bound to *:$HGPORT) (glob) (?)
98 adding = $TESTTMP/main (?)
98 adding = $TESTTMP/main (?)
99 adding sub1 = $TESTTMP/main/sub1 (?)
99 adding sub1 = $TESTTMP/main/sub1 (?)
100 adding sub1/sub2 = $TESTTMP/main/sub1/sub2 (?)
100 adding sub1/sub2 = $TESTTMP/main/sub1/sub2 (?)
101 $ cat hg1.pid >> $DAEMON_PIDS
101 $ cat hg1.pid >> $DAEMON_PIDS
102
102
103 $ hg clone http://localhost:$HGPORT httpclone --config progress.disable=True
103 $ hg clone http://localhost:$HGPORT httpclone --config progress.disable=True
104 requesting all changes
104 requesting all changes
105 adding changesets
105 adding changesets
106 adding manifests
106 adding manifests
107 adding file changes
107 adding file changes
108 added 1 changesets with 3 changes to 3 files
108 added 1 changesets with 3 changes to 3 files
109 new changesets 7f491f53a367
109 new changesets 7f491f53a367
110 updating to branch default
110 updating to branch default
111 cloning subrepo sub1 from http://localhost:$HGPORT/../sub1
111 abort: HTTP Error 404: Not Found
112 abort: HTTP Error 404: Not Found
112 [255]
113 [255]
113
114
114 $ cat access.log
115 $ cat access.log
115 * "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
116 * "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
116 * "GET /?cmd=batch HTTP/1.1" 200 - * (glob)
117 * "GET /?cmd=batch HTTP/1.1" 200 - * (glob)
117 * "GET /?cmd=getbundle HTTP/1.1" 200 - * (glob)
118 * "GET /?cmd=getbundle HTTP/1.1" 200 - * (glob)
118 * "GET /../sub1?cmd=capabilities HTTP/1.1" 404 - (glob)
119 * "GET /../sub1?cmd=capabilities HTTP/1.1" 404 - (glob)
119 $ cat error.log
120 $ cat error.log
120
121
121 $ killdaemons.py
122 $ killdaemons.py
122 $ rm hg1.pid error.log access.log
123 $ rm hg1.pid error.log access.log
123 #endif
124 #endif
124
125
125 Cleaning both repositories, just as a clone -U
126 Cleaning both repositories, just as a clone -U
126
127
127 $ hg up -C -R sub2 null
128 $ hg up -C -R sub2 null
128 \r (no-eol) (esc)
129 \r (no-eol) (esc)
129 updating [===========================================>] 1/1\r (no-eol) (esc)
130 updating [===========================================>] 1/1\r (no-eol) (esc)
130 \r (no-eol) (esc)
131 \r (no-eol) (esc)
131 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
132 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
132 $ hg up -C -R sub1 null
133 $ hg up -C -R sub1 null
133 \r (no-eol) (esc)
134 \r (no-eol) (esc)
134 updating [===========================================>] 1/1\r (no-eol) (esc)
135 updating [===========================================>] 1/1\r (no-eol) (esc)
135 \r (no-eol) (esc)
136 \r (no-eol) (esc)
136 \r (no-eol) (esc)
137 \r (no-eol) (esc)
137 updating [===========================================>] 3/3\r (no-eol) (esc)
138 updating [===========================================>] 3/3\r (no-eol) (esc)
138 \r (no-eol) (esc)
139 \r (no-eol) (esc)
139 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
140 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
140 $ hg up -C -R main null
141 $ hg up -C -R main null
141 \r (no-eol) (esc)
142 \r (no-eol) (esc)
142 updating [===========================================>] 1/1\r (no-eol) (esc)
143 updating [===========================================>] 1/1\r (no-eol) (esc)
143 \r (no-eol) (esc)
144 \r (no-eol) (esc)
144 \r (no-eol) (esc)
145 \r (no-eol) (esc)
145 updating [===========================================>] 3/3\r (no-eol) (esc)
146 updating [===========================================>] 3/3\r (no-eol) (esc)
146 \r (no-eol) (esc)
147 \r (no-eol) (esc)
147 \r (no-eol) (esc)
148 \r (no-eol) (esc)
148 updating [===========================================>] 3/3\r (no-eol) (esc)
149 updating [===========================================>] 3/3\r (no-eol) (esc)
149 \r (no-eol) (esc)
150 \r (no-eol) (esc)
150 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
151 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
151 $ rm -rf main/sub1
152 $ rm -rf main/sub1
152 $ rm -rf sub1/sub2
153 $ rm -rf sub1/sub2
153
154
154 Clone main
155 Clone main
155
156
156 $ hg --config extensions.largefiles= clone main cloned
157 $ hg --config extensions.largefiles= clone main cloned
157 \r (no-eol) (esc)
158 \r (no-eol) (esc)
158 linking [ <=> ] 1\r (no-eol) (esc)
159 linking [ <=> ] 1\r (no-eol) (esc)
159 linking [ <=> ] 2\r (no-eol) (esc)
160 linking [ <=> ] 2\r (no-eol) (esc)
160 linking [ <=> ] 3\r (no-eol) (esc)
161 linking [ <=> ] 3\r (no-eol) (esc)
161 linking [ <=> ] 4\r (no-eol) (esc)
162 linking [ <=> ] 4\r (no-eol) (esc)
162 linking [ <=> ] 5\r (no-eol) (esc)
163 linking [ <=> ] 5\r (no-eol) (esc)
163 linking [ <=> ] 6\r (no-eol) (esc)
164 linking [ <=> ] 6\r (no-eol) (esc)
164 linking [ <=> ] 7\r (no-eol) (esc)
165 linking [ <=> ] 7\r (no-eol) (esc)
165 linking [ <=> ] 8\r (no-eol) (esc)
166 linking [ <=> ] 8\r (no-eol) (esc)
166 linking [ <=> ] 9\r (no-eol) (esc) (reposimplestore !)
167 linking [ <=> ] 9\r (no-eol) (esc) (reposimplestore !)
167 linking [ <=> ] 10\r (no-eol) (esc) (reposimplestore !)
168 linking [ <=> ] 10\r (no-eol) (esc) (reposimplestore !)
168 \r (no-eol) (esc)
169 \r (no-eol) (esc)
169 \r (no-eol) (esc)
170 \r (no-eol) (esc)
170 updating [===========================================>] 3/3\r (no-eol) (esc)
171 updating [===========================================>] 3/3\r (no-eol) (esc)
171 \r (no-eol) (esc)
172 \r (no-eol) (esc)
172 \r (no-eol) (esc)
173 \r (no-eol) (esc)
173 linking [ <=> ] 1\r (no-eol) (esc)
174 linking [ <=> ] 1\r (no-eol) (esc)
174 linking [ <=> ] 2\r (no-eol) (esc)
175 linking [ <=> ] 2\r (no-eol) (esc)
175 linking [ <=> ] 3\r (no-eol) (esc)
176 linking [ <=> ] 3\r (no-eol) (esc)
176 linking [ <=> ] 4\r (no-eol) (esc)
177 linking [ <=> ] 4\r (no-eol) (esc)
177 linking [ <=> ] 5\r (no-eol) (esc)
178 linking [ <=> ] 5\r (no-eol) (esc)
178 linking [ <=> ] 6\r (no-eol) (esc)
179 linking [ <=> ] 6\r (no-eol) (esc)
179 linking [ <=> ] 7\r (no-eol) (esc)
180 linking [ <=> ] 7\r (no-eol) (esc)
180 linking [ <=> ] 8\r (no-eol) (esc)
181 linking [ <=> ] 8\r (no-eol) (esc)
181 linking [ <=> ] 9\r (no-eol) (esc) (reposimplestore !)
182 linking [ <=> ] 9\r (no-eol) (esc) (reposimplestore !)
182 linking [ <=> ] 10\r (no-eol) (esc) (reposimplestore !)
183 linking [ <=> ] 10\r (no-eol) (esc) (reposimplestore !)
183 updating [===========================================>] 3/3\r (no-eol) (esc)
184 updating [===========================================>] 3/3\r (no-eol) (esc)
184 \r (no-eol) (esc)
185 \r (no-eol) (esc)
185 \r (no-eol) (esc)
186 \r (no-eol) (esc)
186 linking [ <=> ] 1\r (no-eol) (esc) (reporevlogstore !)
187 linking [ <=> ] 1\r (no-eol) (esc) (reporevlogstore !)
187 linking [ <=> ] 2\r (no-eol) (esc) (reporevlogstore !)
188 linking [ <=> ] 2\r (no-eol) (esc) (reporevlogstore !)
188 linking [ <=> ] 3\r (no-eol) (esc) (reporevlogstore !)
189 linking [ <=> ] 3\r (no-eol) (esc) (reporevlogstore !)
189 linking [ <=> ] 4\r (no-eol) (esc) (reporevlogstore !)
190 linking [ <=> ] 4\r (no-eol) (esc) (reporevlogstore !)
190 linking [ <=> ] 5\r (no-eol) (esc) (reporevlogstore !)
191 linking [ <=> ] 5\r (no-eol) (esc) (reporevlogstore !)
191 linking [ <=> ] 6\r (no-eol) (esc) (reporevlogstore !)
192 linking [ <=> ] 6\r (no-eol) (esc) (reporevlogstore !)
192 linking [ <=> ] 1\r (no-eol) (esc) (reposimplestore !)
193 linking [ <=> ] 1\r (no-eol) (esc) (reposimplestore !)
193 linking [ <=> ] 2\r (no-eol) (esc) (reposimplestore !)
194 linking [ <=> ] 2\r (no-eol) (esc) (reposimplestore !)
194 linking [ <=> ] 3\r (no-eol) (esc) (reposimplestore !)
195 linking [ <=> ] 3\r (no-eol) (esc) (reposimplestore !)
195 linking [ <=> ] 4\r (no-eol) (esc) (reposimplestore !)
196 linking [ <=> ] 4\r (no-eol) (esc) (reposimplestore !)
196 linking [ <=> ] 5\r (no-eol) (esc) (reposimplestore !)
197 linking [ <=> ] 5\r (no-eol) (esc) (reposimplestore !)
197 linking [ <=> ] 6\r (no-eol) (esc) (reposimplestore !)
198 linking [ <=> ] 6\r (no-eol) (esc) (reposimplestore !)
198 updating [===========================================>] 1/1\r (no-eol) (esc)
199 updating [===========================================>] 1/1\r (no-eol) (esc)
199 \r (no-eol) (esc)
200 \r (no-eol) (esc)
200 updating to branch default
201 updating to branch default
201 cloning subrepo sub1 from $TESTTMP/sub1
202 cloning subrepo sub1 from $TESTTMP/sub1
202 cloning subrepo sub1/sub2 from $TESTTMP/sub2
203 cloning subrepo sub1/sub2 from $TESTTMP/sub2
203 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
204 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
204
205
205 Largefiles is NOT enabled in the clone if the source repo doesn't require it
206 Largefiles is NOT enabled in the clone if the source repo doesn't require it
206 $ grep largefiles cloned/.hg/hgrc
207 $ grep largefiles cloned/.hg/hgrc
207 [1]
208 [1]
208
209
209 Checking cloned repo ids
210 Checking cloned repo ids
210
211
211 $ printf "cloned " ; hg id -R cloned
212 $ printf "cloned " ; hg id -R cloned
212 cloned 7f491f53a367 tip
213 cloned 7f491f53a367 tip
213 $ printf "cloned/sub1 " ; hg id -R cloned/sub1
214 $ printf "cloned/sub1 " ; hg id -R cloned/sub1
214 cloned/sub1 fc3b4ce2696f tip
215 cloned/sub1 fc3b4ce2696f tip
215 $ printf "cloned/sub1/sub2 " ; hg id -R cloned/sub1/sub2
216 $ printf "cloned/sub1/sub2 " ; hg id -R cloned/sub1/sub2
216 cloned/sub1/sub2 c57a0840e3ba tip
217 cloned/sub1/sub2 c57a0840e3ba tip
217
218
218 debugsub output for main and sub1
219 debugsub output for main and sub1
219
220
220 $ hg debugsub -R cloned
221 $ hg debugsub -R cloned
221 path sub1
222 path sub1
222 source ../sub1
223 source ../sub1
223 revision fc3b4ce2696f7741438c79207583768f2ce6b0dd
224 revision fc3b4ce2696f7741438c79207583768f2ce6b0dd
224 $ hg debugsub -R cloned/sub1
225 $ hg debugsub -R cloned/sub1
225 path sub2
226 path sub2
226 source ../sub2
227 source ../sub2
227 revision c57a0840e3badd667ef3c3ef65471609acb2ba3c
228 revision c57a0840e3badd667ef3c3ef65471609acb2ba3c
228
229
229 Modifying deeply nested 'sub2'
230 Modifying deeply nested 'sub2'
230
231
231 $ echo modified > cloned/sub1/sub2/sub2
232 $ echo modified > cloned/sub1/sub2/sub2
232 $ hg commit --subrepos -m "deep nested modif should trigger a commit" -R cloned
233 $ hg commit --subrepos -m "deep nested modif should trigger a commit" -R cloned
233 committing subrepository sub1
234 committing subrepository sub1
234 committing subrepository sub1/sub2
235 committing subrepository sub1/sub2
235
236
236 Checking modified node ids
237 Checking modified node ids
237
238
238 $ printf "cloned " ; hg id -R cloned
239 $ printf "cloned " ; hg id -R cloned
239 cloned ffe6649062fe tip
240 cloned ffe6649062fe tip
240 $ printf "cloned/sub1 " ; hg id -R cloned/sub1
241 $ printf "cloned/sub1 " ; hg id -R cloned/sub1
241 cloned/sub1 2ecb03bf44a9 tip
242 cloned/sub1 2ecb03bf44a9 tip
242 $ printf "cloned/sub1/sub2 " ; hg id -R cloned/sub1/sub2
243 $ printf "cloned/sub1/sub2 " ; hg id -R cloned/sub1/sub2
243 cloned/sub1/sub2 53dd3430bcaf tip
244 cloned/sub1/sub2 53dd3430bcaf tip
244
245
245 debugsub output for main and sub1
246 debugsub output for main and sub1
246
247
247 $ hg debugsub -R cloned
248 $ hg debugsub -R cloned
248 path sub1
249 path sub1
249 source ../sub1
250 source ../sub1
250 revision 2ecb03bf44a94e749e8669481dd9069526ce7cb9
251 revision 2ecb03bf44a94e749e8669481dd9069526ce7cb9
251 $ hg debugsub -R cloned/sub1
252 $ hg debugsub -R cloned/sub1
252 path sub2
253 path sub2
253 source ../sub2
254 source ../sub2
254 revision 53dd3430bcaf5ab4a7c48262bcad6d441f510487
255 revision 53dd3430bcaf5ab4a7c48262bcad6d441f510487
255
256
256 Check that deep archiving works
257 Check that deep archiving works
257
258
258 $ cd cloned
259 $ cd cloned
259 $ echo 'test' > sub1/sub2/test.txt
260 $ echo 'test' > sub1/sub2/test.txt
260 $ hg --config extensions.largefiles=! add sub1/sub2/test.txt
261 $ hg --config extensions.largefiles=! add sub1/sub2/test.txt
261 $ mkdir sub1/sub2/folder
262 $ mkdir sub1/sub2/folder
262 $ echo 'subfolder' > sub1/sub2/folder/test.txt
263 $ echo 'subfolder' > sub1/sub2/folder/test.txt
263 $ hg ci -ASm "add test.txt"
264 $ hg ci -ASm "add test.txt"
264 adding sub1/sub2/folder/test.txt
265 adding sub1/sub2/folder/test.txt
265 committing subrepository sub1
266 committing subrepository sub1
266 committing subrepository sub1/sub2
267 committing subrepository sub1/sub2
267
268
268 $ rm -r main
269 $ rm -r main
269 $ hg archive -S -qr 'wdir()' ../wdir
270 $ hg archive -S -qr 'wdir()' ../wdir
270 $ cat ../wdir/.hg_archival.txt
271 $ cat ../wdir/.hg_archival.txt
271 repo: 7f491f53a367861f47ee64a80eb997d1f341b77a
272 repo: 7f491f53a367861f47ee64a80eb997d1f341b77a
272 node: 9bb10eebee29dc0f1201dcf5977b811a540255fd+
273 node: 9bb10eebee29dc0f1201dcf5977b811a540255fd+
273 branch: default
274 branch: default
274 latesttag: null
275 latesttag: null
275 latesttagdistance: 4
276 latesttagdistance: 4
276 changessincelatesttag: 4
277 changessincelatesttag: 4
277 $ hg update -Cq .
278 $ hg update -Cq .
278
279
279 A deleted subrepo file is flagged as dirty, like the top level repo
280 A deleted subrepo file is flagged as dirty, like the top level repo
280
281
281 $ rm -r ../wdir sub1/sub2/folder/test.txt
282 $ rm -r ../wdir sub1/sub2/folder/test.txt
282 $ hg archive -S -qr 'wdir()' ../wdir
283 $ hg archive -S -qr 'wdir()' ../wdir
283 $ cat ../wdir/.hg_archival.txt
284 $ cat ../wdir/.hg_archival.txt
284 repo: 7f491f53a367861f47ee64a80eb997d1f341b77a
285 repo: 7f491f53a367861f47ee64a80eb997d1f341b77a
285 node: 9bb10eebee29dc0f1201dcf5977b811a540255fd+
286 node: 9bb10eebee29dc0f1201dcf5977b811a540255fd+
286 branch: default
287 branch: default
287 latesttag: null
288 latesttag: null
288 latesttagdistance: 4
289 latesttagdistance: 4
289 changessincelatesttag: 4
290 changessincelatesttag: 4
290 $ hg update -Cq .
291 $ hg update -Cq .
291 $ rm -r ../wdir
292 $ rm -r ../wdir
292
293
293 $ hg archive -S -qr 'wdir()' ../wdir \
294 $ hg archive -S -qr 'wdir()' ../wdir \
294 > --config 'experimental.archivemetatemplate=archived {node|short}\n'
295 > --config 'experimental.archivemetatemplate=archived {node|short}\n'
295 $ cat ../wdir/.hg_archival.txt
296 $ cat ../wdir/.hg_archival.txt
296 archived ffffffffffff
297 archived ffffffffffff
297 $ rm -r ../wdir
298 $ rm -r ../wdir
298
299
299 .. but first take a detour through some deep removal testing
300 .. but first take a detour through some deep removal testing
300
301
301 $ hg remove -S -I 're:.*.txt' .
302 $ hg remove -S -I 're:.*.txt' .
302 \r (no-eol) (esc)
303 \r (no-eol) (esc)
303 searching [==========================================>] 1/1\r (no-eol) (esc)
304 searching [==========================================>] 1/1\r (no-eol) (esc)
304 searching [==========================================>] 1/1\r (no-eol) (esc)
305 searching [==========================================>] 1/1\r (no-eol) (esc)
305 \r (no-eol) (esc)
306 \r (no-eol) (esc)
306 \r (no-eol) (esc)
307 \r (no-eol) (esc)
307 deleting [=====================> ] 1/2\r (no-eol) (esc)
308 deleting [=====================> ] 1/2\r (no-eol) (esc)
308 \r (no-eol) (esc)
309 \r (no-eol) (esc)
309 \r (no-eol) (esc)
310 \r (no-eol) (esc)
310 deleting [===========================================>] 2/2\r (no-eol) (esc)
311 deleting [===========================================>] 2/2\r (no-eol) (esc)
311 \r (no-eol) (esc)
312 \r (no-eol) (esc)
312 removing sub1/sub2/folder/test.txt
313 removing sub1/sub2/folder/test.txt
313 removing sub1/sub2/test.txt
314 removing sub1/sub2/test.txt
314 $ hg status -S
315 $ hg status -S
315 R sub1/sub2/folder/test.txt
316 R sub1/sub2/folder/test.txt
316 R sub1/sub2/test.txt
317 R sub1/sub2/test.txt
317 $ hg update -Cq
318 $ hg update -Cq
318 $ hg remove -I 're:.*.txt' sub1
319 $ hg remove -I 're:.*.txt' sub1
319 \r (no-eol) (esc)
320 \r (no-eol) (esc)
320 searching [==========================================>] 1/1\r (no-eol) (esc)
321 searching [==========================================>] 1/1\r (no-eol) (esc)
321 \r (no-eol) (esc)
322 \r (no-eol) (esc)
322 \r (no-eol) (esc)
323 \r (no-eol) (esc)
323 deleting [===========================================>] 1/1\r (no-eol) (esc)
324 deleting [===========================================>] 1/1\r (no-eol) (esc)
324 \r (no-eol) (esc)
325 \r (no-eol) (esc)
325 $ hg status -S
326 $ hg status -S
326 $ hg remove sub1/sub2/folder/test.txt
327 $ hg remove sub1/sub2/folder/test.txt
327 \r (no-eol) (esc)
328 \r (no-eol) (esc)
328 searching [==========================================>] 1/1\r (no-eol) (esc)
329 searching [==========================================>] 1/1\r (no-eol) (esc)
329 searching [==========================================>] 1/1\r (no-eol) (esc)
330 searching [==========================================>] 1/1\r (no-eol) (esc)
330 \r (no-eol) (esc)
331 \r (no-eol) (esc)
331 \r (no-eol) (esc)
332 \r (no-eol) (esc)
332 deleting [===========================================>] 1/1\r (no-eol) (esc)
333 deleting [===========================================>] 1/1\r (no-eol) (esc)
333 \r (no-eol) (esc)
334 \r (no-eol) (esc)
334 \r (no-eol) (esc)
335 \r (no-eol) (esc)
335 deleting [===========================================>] 1/1\r (no-eol) (esc)
336 deleting [===========================================>] 1/1\r (no-eol) (esc)
336 \r (no-eol) (esc)
337 \r (no-eol) (esc)
337 \r (no-eol) (esc)
338 \r (no-eol) (esc)
338 deleting [===========================================>] 1/1\r (no-eol) (esc)
339 deleting [===========================================>] 1/1\r (no-eol) (esc)
339 \r (no-eol) (esc)
340 \r (no-eol) (esc)
340 $ hg remove sub1/.hgsubstate
341 $ hg remove sub1/.hgsubstate
341 \r (no-eol) (esc)
342 \r (no-eol) (esc)
342 searching [==========================================>] 1/1\r (no-eol) (esc)
343 searching [==========================================>] 1/1\r (no-eol) (esc)
343 \r (no-eol) (esc)
344 \r (no-eol) (esc)
344 \r (no-eol) (esc)
345 \r (no-eol) (esc)
345 deleting [===========================================>] 1/1\r (no-eol) (esc)
346 deleting [===========================================>] 1/1\r (no-eol) (esc)
346 \r (no-eol) (esc)
347 \r (no-eol) (esc)
347 \r (no-eol) (esc)
348 \r (no-eol) (esc)
348 deleting [===========================================>] 1/1\r (no-eol) (esc)
349 deleting [===========================================>] 1/1\r (no-eol) (esc)
349 \r (no-eol) (esc)
350 \r (no-eol) (esc)
350 $ mv sub1/.hgsub sub1/x.hgsub
351 $ mv sub1/.hgsub sub1/x.hgsub
351 $ hg status -S
352 $ hg status -S
352 warning: subrepo spec file 'sub1/.hgsub' not found
353 warning: subrepo spec file 'sub1/.hgsub' not found
353 R sub1/.hgsubstate
354 R sub1/.hgsubstate
354 R sub1/sub2/folder/test.txt
355 R sub1/sub2/folder/test.txt
355 ! sub1/.hgsub
356 ! sub1/.hgsub
356 ? sub1/x.hgsub
357 ? sub1/x.hgsub
357 $ mv sub1/x.hgsub sub1/.hgsub
358 $ mv sub1/x.hgsub sub1/.hgsub
358 $ hg update -Cq
359 $ hg update -Cq
359 $ touch sub1/foo
360 $ touch sub1/foo
360 $ hg forget sub1/sub2/folder/test.txt
361 $ hg forget sub1/sub2/folder/test.txt
361 $ rm sub1/sub2/test.txt
362 $ rm sub1/sub2/test.txt
362
363
363 Test relative path printing + subrepos
364 Test relative path printing + subrepos
364 $ mkdir -p foo/bar
365 $ mkdir -p foo/bar
365 $ cd foo
366 $ cd foo
366 $ touch bar/abc
367 $ touch bar/abc
367 $ hg addremove -S ..
368 $ hg addremove -S ..
368 \r (no-eol) (esc)
369 \r (no-eol) (esc)
369 searching for exact renames [========================>] 1/1\r (no-eol) (esc)
370 searching for exact renames [========================>] 1/1\r (no-eol) (esc)
370 \r (no-eol) (esc)
371 \r (no-eol) (esc)
371 adding ../sub1/sub2/folder/test.txt
372 adding ../sub1/sub2/folder/test.txt
372 removing ../sub1/sub2/test.txt
373 removing ../sub1/sub2/test.txt
373 adding ../sub1/foo
374 adding ../sub1/foo
374 adding bar/abc
375 adding bar/abc
375 $ cd ..
376 $ cd ..
376 $ hg status -S
377 $ hg status -S
377 A foo/bar/abc
378 A foo/bar/abc
378 A sub1/foo
379 A sub1/foo
379 R sub1/sub2/test.txt
380 R sub1/sub2/test.txt
380
381
381 Archive wdir() with subrepos
382 Archive wdir() with subrepos
382 $ hg rm main
383 $ hg rm main
383 \r (no-eol) (esc)
384 \r (no-eol) (esc)
384 deleting [===========================================>] 1/1\r (no-eol) (esc)
385 deleting [===========================================>] 1/1\r (no-eol) (esc)
385 \r (no-eol) (esc)
386 \r (no-eol) (esc)
386 $ hg archive -S -r 'wdir()' ../wdir
387 $ hg archive -S -r 'wdir()' ../wdir
387 \r (no-eol) (esc)
388 \r (no-eol) (esc)
388 archiving [ ] 0/3\r (no-eol) (esc)
389 archiving [ ] 0/3\r (no-eol) (esc)
389 archiving [=============> ] 1/3\r (no-eol) (esc)
390 archiving [=============> ] 1/3\r (no-eol) (esc)
390 archiving [===========================> ] 2/3\r (no-eol) (esc)
391 archiving [===========================> ] 2/3\r (no-eol) (esc)
391 archiving [==========================================>] 3/3\r (no-eol) (esc)
392 archiving [==========================================>] 3/3\r (no-eol) (esc)
392 \r (no-eol) (esc)
393 \r (no-eol) (esc)
393 \r (no-eol) (esc)
394 \r (no-eol) (esc)
394 archiving (sub1) [ ] 0/4\r (no-eol) (esc)
395 archiving (sub1) [ ] 0/4\r (no-eol) (esc)
395 archiving (sub1) [========> ] 1/4\r (no-eol) (esc)
396 archiving (sub1) [========> ] 1/4\r (no-eol) (esc)
396 archiving (sub1) [=================> ] 2/4\r (no-eol) (esc)
397 archiving (sub1) [=================> ] 2/4\r (no-eol) (esc)
397 archiving (sub1) [==========================> ] 3/4\r (no-eol) (esc)
398 archiving (sub1) [==========================> ] 3/4\r (no-eol) (esc)
398 archiving (sub1) [===================================>] 4/4\r (no-eol) (esc)
399 archiving (sub1) [===================================>] 4/4\r (no-eol) (esc)
399 \r (no-eol) (esc)
400 \r (no-eol) (esc)
400 \r (no-eol) (esc)
401 \r (no-eol) (esc)
401 archiving (sub1/sub2) [ ] 0/2\r (no-eol) (esc)
402 archiving (sub1/sub2) [ ] 0/2\r (no-eol) (esc)
402 archiving (sub1/sub2) [==============> ] 1/2\r (no-eol) (esc)
403 archiving (sub1/sub2) [==============> ] 1/2\r (no-eol) (esc)
403 archiving (sub1/sub2) [==============================>] 2/2\r (no-eol) (esc)
404 archiving (sub1/sub2) [==============================>] 2/2\r (no-eol) (esc)
404 \r (no-eol) (esc)
405 \r (no-eol) (esc)
405 $ diff -r . ../wdir | egrep -v '\.hg$|^Common subdirectories:'
406 $ diff -r . ../wdir | egrep -v '\.hg$|^Common subdirectories:'
406 Only in ../wdir: .hg_archival.txt
407 Only in ../wdir: .hg_archival.txt
407
408
408 $ find ../wdir -type f | sort
409 $ find ../wdir -type f | sort
409 ../wdir/.hg_archival.txt
410 ../wdir/.hg_archival.txt
410 ../wdir/.hgsub
411 ../wdir/.hgsub
411 ../wdir/.hgsubstate
412 ../wdir/.hgsubstate
412 ../wdir/foo/bar/abc
413 ../wdir/foo/bar/abc
413 ../wdir/sub1/.hgsub
414 ../wdir/sub1/.hgsub
414 ../wdir/sub1/.hgsubstate
415 ../wdir/sub1/.hgsubstate
415 ../wdir/sub1/foo
416 ../wdir/sub1/foo
416 ../wdir/sub1/sub1
417 ../wdir/sub1/sub1
417 ../wdir/sub1/sub2/folder/test.txt
418 ../wdir/sub1/sub2/folder/test.txt
418 ../wdir/sub1/sub2/sub2
419 ../wdir/sub1/sub2/sub2
419
420
420 $ cat ../wdir/.hg_archival.txt
421 $ cat ../wdir/.hg_archival.txt
421 repo: 7f491f53a367861f47ee64a80eb997d1f341b77a
422 repo: 7f491f53a367861f47ee64a80eb997d1f341b77a
422 node: 9bb10eebee29dc0f1201dcf5977b811a540255fd+
423 node: 9bb10eebee29dc0f1201dcf5977b811a540255fd+
423 branch: default
424 branch: default
424 latesttag: null
425 latesttag: null
425 latesttagdistance: 4
426 latesttagdistance: 4
426 changessincelatesttag: 4
427 changessincelatesttag: 4
427
428
428 Attempting to archive 'wdir()' with a missing file is handled gracefully
429 Attempting to archive 'wdir()' with a missing file is handled gracefully
429 $ rm sub1/sub1
430 $ rm sub1/sub1
430 $ rm -r ../wdir
431 $ rm -r ../wdir
431 $ hg archive -v -S -r 'wdir()' ../wdir
432 $ hg archive -v -S -r 'wdir()' ../wdir
432 \r (no-eol) (esc)
433 \r (no-eol) (esc)
433 archiving [ ] 0/3\r (no-eol) (esc)
434 archiving [ ] 0/3\r (no-eol) (esc)
434 archiving [=============> ] 1/3\r (no-eol) (esc)
435 archiving [=============> ] 1/3\r (no-eol) (esc)
435 archiving [===========================> ] 2/3\r (no-eol) (esc)
436 archiving [===========================> ] 2/3\r (no-eol) (esc)
436 archiving [==========================================>] 3/3\r (no-eol) (esc)
437 archiving [==========================================>] 3/3\r (no-eol) (esc)
437 \r (no-eol) (esc)
438 \r (no-eol) (esc)
438 \r (no-eol) (esc)
439 \r (no-eol) (esc)
439 archiving (sub1) [ ] 0/3\r (no-eol) (esc)
440 archiving (sub1) [ ] 0/3\r (no-eol) (esc)
440 archiving (sub1) [===========> ] 1/3\r (no-eol) (esc)
441 archiving (sub1) [===========> ] 1/3\r (no-eol) (esc)
441 archiving (sub1) [=======================> ] 2/3\r (no-eol) (esc)
442 archiving (sub1) [=======================> ] 2/3\r (no-eol) (esc)
442 archiving (sub1) [===================================>] 3/3\r (no-eol) (esc)
443 archiving (sub1) [===================================>] 3/3\r (no-eol) (esc)
443 \r (no-eol) (esc)
444 \r (no-eol) (esc)
444 \r (no-eol) (esc)
445 \r (no-eol) (esc)
445 archiving (sub1/sub2) [ ] 0/2\r (no-eol) (esc)
446 archiving (sub1/sub2) [ ] 0/2\r (no-eol) (esc)
446 archiving (sub1/sub2) [==============> ] 1/2\r (no-eol) (esc)
447 archiving (sub1/sub2) [==============> ] 1/2\r (no-eol) (esc)
447 archiving (sub1/sub2) [==============================>] 2/2\r (no-eol) (esc)
448 archiving (sub1/sub2) [==============================>] 2/2\r (no-eol) (esc)
448 \r (no-eol) (esc)
449 \r (no-eol) (esc)
449 $ find ../wdir -type f | sort
450 $ find ../wdir -type f | sort
450 ../wdir/.hg_archival.txt
451 ../wdir/.hg_archival.txt
451 ../wdir/.hgsub
452 ../wdir/.hgsub
452 ../wdir/.hgsubstate
453 ../wdir/.hgsubstate
453 ../wdir/foo/bar/abc
454 ../wdir/foo/bar/abc
454 ../wdir/sub1/.hgsub
455 ../wdir/sub1/.hgsub
455 ../wdir/sub1/.hgsubstate
456 ../wdir/sub1/.hgsubstate
456 ../wdir/sub1/foo
457 ../wdir/sub1/foo
457 ../wdir/sub1/sub2/folder/test.txt
458 ../wdir/sub1/sub2/folder/test.txt
458 ../wdir/sub1/sub2/sub2
459 ../wdir/sub1/sub2/sub2
459
460
460 Continue relative path printing + subrepos
461 Continue relative path printing + subrepos
461 $ hg update -Cq
462 $ hg update -Cq
462 $ rm -r ../wdir
463 $ rm -r ../wdir
463 $ hg archive -S -r 'wdir()' ../wdir
464 $ hg archive -S -r 'wdir()' ../wdir
464 \r (no-eol) (esc)
465 \r (no-eol) (esc)
465 archiving [ ] 0/3\r (no-eol) (esc)
466 archiving [ ] 0/3\r (no-eol) (esc)
466 archiving [=============> ] 1/3\r (no-eol) (esc)
467 archiving [=============> ] 1/3\r (no-eol) (esc)
467 archiving [===========================> ] 2/3\r (no-eol) (esc)
468 archiving [===========================> ] 2/3\r (no-eol) (esc)
468 archiving [==========================================>] 3/3\r (no-eol) (esc)
469 archiving [==========================================>] 3/3\r (no-eol) (esc)
469 \r (no-eol) (esc)
470 \r (no-eol) (esc)
470 \r (no-eol) (esc)
471 \r (no-eol) (esc)
471 archiving (sub1) [ ] 0/3\r (no-eol) (esc)
472 archiving (sub1) [ ] 0/3\r (no-eol) (esc)
472 archiving (sub1) [===========> ] 1/3\r (no-eol) (esc)
473 archiving (sub1) [===========> ] 1/3\r (no-eol) (esc)
473 archiving (sub1) [=======================> ] 2/3\r (no-eol) (esc)
474 archiving (sub1) [=======================> ] 2/3\r (no-eol) (esc)
474 archiving (sub1) [===================================>] 3/3\r (no-eol) (esc)
475 archiving (sub1) [===================================>] 3/3\r (no-eol) (esc)
475 \r (no-eol) (esc)
476 \r (no-eol) (esc)
476 \r (no-eol) (esc)
477 \r (no-eol) (esc)
477 archiving (sub1/sub2) [ ] 0/3\r (no-eol) (esc)
478 archiving (sub1/sub2) [ ] 0/3\r (no-eol) (esc)
478 archiving (sub1/sub2) [=========> ] 1/3\r (no-eol) (esc)
479 archiving (sub1/sub2) [=========> ] 1/3\r (no-eol) (esc)
479 archiving (sub1/sub2) [===================> ] 2/3\r (no-eol) (esc)
480 archiving (sub1/sub2) [===================> ] 2/3\r (no-eol) (esc)
480 archiving (sub1/sub2) [==============================>] 3/3\r (no-eol) (esc)
481 archiving (sub1/sub2) [==============================>] 3/3\r (no-eol) (esc)
481 \r (no-eol) (esc)
482 \r (no-eol) (esc)
482 $ cat ../wdir/.hg_archival.txt
483 $ cat ../wdir/.hg_archival.txt
483 repo: 7f491f53a367861f47ee64a80eb997d1f341b77a
484 repo: 7f491f53a367861f47ee64a80eb997d1f341b77a
484 node: 9bb10eebee29dc0f1201dcf5977b811a540255fd
485 node: 9bb10eebee29dc0f1201dcf5977b811a540255fd
485 branch: default
486 branch: default
486 latesttag: null
487 latesttag: null
487 latesttagdistance: 4
488 latesttagdistance: 4
488 changessincelatesttag: 4
489 changessincelatesttag: 4
489
490
490 $ touch sub1/sub2/folder/bar
491 $ touch sub1/sub2/folder/bar
491 $ hg addremove sub1/sub2
492 $ hg addremove sub1/sub2
492 adding sub1/sub2/folder/bar
493 adding sub1/sub2/folder/bar
493 $ hg status -S
494 $ hg status -S
494 A sub1/sub2/folder/bar
495 A sub1/sub2/folder/bar
495 ? foo/bar/abc
496 ? foo/bar/abc
496 ? sub1/foo
497 ? sub1/foo
497 $ hg update -Cq
498 $ hg update -Cq
498 $ hg addremove sub1
499 $ hg addremove sub1
499 adding sub1/sub2/folder/bar
500 adding sub1/sub2/folder/bar
500 adding sub1/foo
501 adding sub1/foo
501 $ hg update -Cq
502 $ hg update -Cq
502 $ rm sub1/sub2/folder/test.txt
503 $ rm sub1/sub2/folder/test.txt
503 $ rm sub1/sub2/test.txt
504 $ rm sub1/sub2/test.txt
504 $ hg ci -ASm "remove test.txt"
505 $ hg ci -ASm "remove test.txt"
505 adding sub1/sub2/folder/bar
506 adding sub1/sub2/folder/bar
506 removing sub1/sub2/folder/test.txt
507 removing sub1/sub2/folder/test.txt
507 removing sub1/sub2/test.txt
508 removing sub1/sub2/test.txt
508 adding sub1/foo
509 adding sub1/foo
509 adding foo/bar/abc
510 adding foo/bar/abc
510 committing subrepository sub1
511 committing subrepository sub1
511 committing subrepository sub1/sub2
512 committing subrepository sub1/sub2
512
513
513 $ hg forget sub1/sub2/sub2
514 $ hg forget sub1/sub2/sub2
514 $ echo x > sub1/sub2/x.txt
515 $ echo x > sub1/sub2/x.txt
515 $ hg add sub1/sub2/x.txt
516 $ hg add sub1/sub2/x.txt
516
517
517 Files sees uncommitted adds and removes in subrepos
518 Files sees uncommitted adds and removes in subrepos
518 $ hg files -S
519 $ hg files -S
519 .hgsub
520 .hgsub
520 .hgsubstate
521 .hgsubstate
521 foo/bar/abc
522 foo/bar/abc
522 main
523 main
523 sub1/.hgsub
524 sub1/.hgsub
524 sub1/.hgsubstate
525 sub1/.hgsubstate
525 sub1/foo
526 sub1/foo
526 sub1/sub1
527 sub1/sub1
527 sub1/sub2/folder/bar
528 sub1/sub2/folder/bar
528 sub1/sub2/x.txt
529 sub1/sub2/x.txt
529
530
530 $ hg files -S "set:eol('dos') or eol('unix') or size('<= 0')"
531 $ hg files -S "set:eol('dos') or eol('unix') or size('<= 0')"
531 .hgsub
532 .hgsub
532 .hgsubstate
533 .hgsubstate
533 foo/bar/abc
534 foo/bar/abc
534 main
535 main
535 sub1/.hgsub
536 sub1/.hgsub
536 sub1/.hgsubstate
537 sub1/.hgsubstate
537 sub1/foo
538 sub1/foo
538 sub1/sub1
539 sub1/sub1
539 sub1/sub2/folder/bar
540 sub1/sub2/folder/bar
540 sub1/sub2/x.txt
541 sub1/sub2/x.txt
541
542
542 $ hg files -r '.^' -S "set:eol('dos') or eol('unix')"
543 $ hg files -r '.^' -S "set:eol('dos') or eol('unix')"
543 .hgsub
544 .hgsub
544 .hgsubstate
545 .hgsubstate
545 main
546 main
546 sub1/.hgsub
547 sub1/.hgsub
547 sub1/.hgsubstate
548 sub1/.hgsubstate
548 sub1/sub1
549 sub1/sub1
549 sub1/sub2/folder/test.txt
550 sub1/sub2/folder/test.txt
550 sub1/sub2/sub2
551 sub1/sub2/sub2
551 sub1/sub2/test.txt
552 sub1/sub2/test.txt
552
553
553 $ hg files sub1
554 $ hg files sub1
554 sub1/.hgsub
555 sub1/.hgsub
555 sub1/.hgsubstate
556 sub1/.hgsubstate
556 sub1/foo
557 sub1/foo
557 sub1/sub1
558 sub1/sub1
558 sub1/sub2/folder/bar
559 sub1/sub2/folder/bar
559 sub1/sub2/x.txt
560 sub1/sub2/x.txt
560
561
561 $ hg files sub1/sub2
562 $ hg files sub1/sub2
562 sub1/sub2/folder/bar
563 sub1/sub2/folder/bar
563 sub1/sub2/x.txt
564 sub1/sub2/x.txt
564
565
565 $ hg files
566 $ hg files
566 .hgsub
567 .hgsub
567 .hgsubstate
568 .hgsubstate
568 foo/bar/abc
569 foo/bar/abc
569 main
570 main
570
571
571 $ hg files -S -r '.^' sub1/sub2/folder
572 $ hg files -S -r '.^' sub1/sub2/folder
572 sub1/sub2/folder/test.txt
573 sub1/sub2/folder/test.txt
573
574
574 $ hg files -S -r '.^' sub1/sub2/missing
575 $ hg files -S -r '.^' sub1/sub2/missing
575 sub1/sub2/missing: no such file in rev 78026e779ea6
576 sub1/sub2/missing: no such file in rev 78026e779ea6
576 [1]
577 [1]
577
578
578 $ hg files -r '.^' sub1/
579 $ hg files -r '.^' sub1/
579 sub1/.hgsub
580 sub1/.hgsub
580 sub1/.hgsubstate
581 sub1/.hgsubstate
581 sub1/sub1
582 sub1/sub1
582 sub1/sub2/folder/test.txt
583 sub1/sub2/folder/test.txt
583 sub1/sub2/sub2
584 sub1/sub2/sub2
584 sub1/sub2/test.txt
585 sub1/sub2/test.txt
585
586
586 $ hg files -r '.^' sub1/sub2
587 $ hg files -r '.^' sub1/sub2
587 sub1/sub2/folder/test.txt
588 sub1/sub2/folder/test.txt
588 sub1/sub2/sub2
589 sub1/sub2/sub2
589 sub1/sub2/test.txt
590 sub1/sub2/test.txt
590
591
591 $ hg rollback -q
592 $ hg rollback -q
592 $ hg up -Cq
593 $ hg up -Cq
593
594
594 $ hg --config extensions.largefiles=! archive -S ../archive_all
595 $ hg --config extensions.largefiles=! archive -S ../archive_all
595 \r (no-eol) (esc)
596 \r (no-eol) (esc)
596 archiving [ ] 0/3\r (no-eol) (esc)
597 archiving [ ] 0/3\r (no-eol) (esc)
597 archiving [=============> ] 1/3\r (no-eol) (esc)
598 archiving [=============> ] 1/3\r (no-eol) (esc)
598 archiving [===========================> ] 2/3\r (no-eol) (esc)
599 archiving [===========================> ] 2/3\r (no-eol) (esc)
599 archiving [==========================================>] 3/3\r (no-eol) (esc)
600 archiving [==========================================>] 3/3\r (no-eol) (esc)
600 \r (no-eol) (esc)
601 \r (no-eol) (esc)
601 \r (no-eol) (esc)
602 \r (no-eol) (esc)
602 archiving (sub1) [ ] 0/3\r (no-eol) (esc)
603 archiving (sub1) [ ] 0/3\r (no-eol) (esc)
603 archiving (sub1) [===========> ] 1/3\r (no-eol) (esc)
604 archiving (sub1) [===========> ] 1/3\r (no-eol) (esc)
604 archiving (sub1) [=======================> ] 2/3\r (no-eol) (esc)
605 archiving (sub1) [=======================> ] 2/3\r (no-eol) (esc)
605 archiving (sub1) [===================================>] 3/3\r (no-eol) (esc)
606 archiving (sub1) [===================================>] 3/3\r (no-eol) (esc)
606 \r (no-eol) (esc)
607 \r (no-eol) (esc)
607 \r (no-eol) (esc)
608 \r (no-eol) (esc)
608 archiving (sub1/sub2) [ ] 0/3\r (no-eol) (esc)
609 archiving (sub1/sub2) [ ] 0/3\r (no-eol) (esc)
609 archiving (sub1/sub2) [=========> ] 1/3\r (no-eol) (esc)
610 archiving (sub1/sub2) [=========> ] 1/3\r (no-eol) (esc)
610 archiving (sub1/sub2) [===================> ] 2/3\r (no-eol) (esc)
611 archiving (sub1/sub2) [===================> ] 2/3\r (no-eol) (esc)
611 archiving (sub1/sub2) [==============================>] 3/3\r (no-eol) (esc)
612 archiving (sub1/sub2) [==============================>] 3/3\r (no-eol) (esc)
612 \r (no-eol) (esc)
613 \r (no-eol) (esc)
613 $ find ../archive_all | sort
614 $ find ../archive_all | sort
614 ../archive_all
615 ../archive_all
615 ../archive_all/.hg_archival.txt
616 ../archive_all/.hg_archival.txt
616 ../archive_all/.hgsub
617 ../archive_all/.hgsub
617 ../archive_all/.hgsubstate
618 ../archive_all/.hgsubstate
618 ../archive_all/main
619 ../archive_all/main
619 ../archive_all/sub1
620 ../archive_all/sub1
620 ../archive_all/sub1/.hgsub
621 ../archive_all/sub1/.hgsub
621 ../archive_all/sub1/.hgsubstate
622 ../archive_all/sub1/.hgsubstate
622 ../archive_all/sub1/sub1
623 ../archive_all/sub1/sub1
623 ../archive_all/sub1/sub2
624 ../archive_all/sub1/sub2
624 ../archive_all/sub1/sub2/folder
625 ../archive_all/sub1/sub2/folder
625 ../archive_all/sub1/sub2/folder/test.txt
626 ../archive_all/sub1/sub2/folder/test.txt
626 ../archive_all/sub1/sub2/sub2
627 ../archive_all/sub1/sub2/sub2
627 ../archive_all/sub1/sub2/test.txt
628 ../archive_all/sub1/sub2/test.txt
628
629
629 Check that archive -X works in deep subrepos
630 Check that archive -X works in deep subrepos
630
631
631 $ hg --config extensions.largefiles=! archive -S -X '**test*' ../archive_exclude
632 $ hg --config extensions.largefiles=! archive -S -X '**test*' ../archive_exclude
632 \r (no-eol) (esc)
633 \r (no-eol) (esc)
633 archiving [ ] 0/3\r (no-eol) (esc)
634 archiving [ ] 0/3\r (no-eol) (esc)
634 archiving [=============> ] 1/3\r (no-eol) (esc)
635 archiving [=============> ] 1/3\r (no-eol) (esc)
635 archiving [===========================> ] 2/3\r (no-eol) (esc)
636 archiving [===========================> ] 2/3\r (no-eol) (esc)
636 archiving [==========================================>] 3/3\r (no-eol) (esc)
637 archiving [==========================================>] 3/3\r (no-eol) (esc)
637 \r (no-eol) (esc)
638 \r (no-eol) (esc)
638 \r (no-eol) (esc)
639 \r (no-eol) (esc)
639 archiving (sub1) [ ] 0/3\r (no-eol) (esc)
640 archiving (sub1) [ ] 0/3\r (no-eol) (esc)
640 archiving (sub1) [===========> ] 1/3\r (no-eol) (esc)
641 archiving (sub1) [===========> ] 1/3\r (no-eol) (esc)
641 archiving (sub1) [=======================> ] 2/3\r (no-eol) (esc)
642 archiving (sub1) [=======================> ] 2/3\r (no-eol) (esc)
642 archiving (sub1) [===================================>] 3/3\r (no-eol) (esc)
643 archiving (sub1) [===================================>] 3/3\r (no-eol) (esc)
643 \r (no-eol) (esc)
644 \r (no-eol) (esc)
644 \r (no-eol) (esc)
645 \r (no-eol) (esc)
645 archiving (sub1/sub2) [ ] 0/1\r (no-eol) (esc)
646 archiving (sub1/sub2) [ ] 0/1\r (no-eol) (esc)
646 archiving (sub1/sub2) [==============================>] 1/1\r (no-eol) (esc)
647 archiving (sub1/sub2) [==============================>] 1/1\r (no-eol) (esc)
647 \r (no-eol) (esc)
648 \r (no-eol) (esc)
648 $ find ../archive_exclude | sort
649 $ find ../archive_exclude | sort
649 ../archive_exclude
650 ../archive_exclude
650 ../archive_exclude/.hg_archival.txt
651 ../archive_exclude/.hg_archival.txt
651 ../archive_exclude/.hgsub
652 ../archive_exclude/.hgsub
652 ../archive_exclude/.hgsubstate
653 ../archive_exclude/.hgsubstate
653 ../archive_exclude/main
654 ../archive_exclude/main
654 ../archive_exclude/sub1
655 ../archive_exclude/sub1
655 ../archive_exclude/sub1/.hgsub
656 ../archive_exclude/sub1/.hgsub
656 ../archive_exclude/sub1/.hgsubstate
657 ../archive_exclude/sub1/.hgsubstate
657 ../archive_exclude/sub1/sub1
658 ../archive_exclude/sub1/sub1
658 ../archive_exclude/sub1/sub2
659 ../archive_exclude/sub1/sub2
659 ../archive_exclude/sub1/sub2/sub2
660 ../archive_exclude/sub1/sub2/sub2
660
661
661 $ hg --config extensions.largefiles=! archive -S -I '**test*' ../archive_include
662 $ hg --config extensions.largefiles=! archive -S -I '**test*' ../archive_include
662 \r (no-eol) (esc)
663 \r (no-eol) (esc)
663 archiving (sub1) [ <=> ] 0\r (no-eol) (esc)
664 archiving (sub1) [ <=> ] 0\r (no-eol) (esc)
664 \r (no-eol) (esc)
665 \r (no-eol) (esc)
665 \r (no-eol) (esc)
666 \r (no-eol) (esc)
666 archiving (sub1/sub2) [ ] 0/2\r (no-eol) (esc)
667 archiving (sub1/sub2) [ ] 0/2\r (no-eol) (esc)
667 archiving (sub1/sub2) [==============> ] 1/2\r (no-eol) (esc)
668 archiving (sub1/sub2) [==============> ] 1/2\r (no-eol) (esc)
668 archiving (sub1/sub2) [==============================>] 2/2\r (no-eol) (esc)
669 archiving (sub1/sub2) [==============================>] 2/2\r (no-eol) (esc)
669 \r (no-eol) (esc)
670 \r (no-eol) (esc)
670 $ find ../archive_include | sort
671 $ find ../archive_include | sort
671 ../archive_include
672 ../archive_include
672 ../archive_include/sub1
673 ../archive_include/sub1
673 ../archive_include/sub1/sub2
674 ../archive_include/sub1/sub2
674 ../archive_include/sub1/sub2/folder
675 ../archive_include/sub1/sub2/folder
675 ../archive_include/sub1/sub2/folder/test.txt
676 ../archive_include/sub1/sub2/folder/test.txt
676 ../archive_include/sub1/sub2/test.txt
677 ../archive_include/sub1/sub2/test.txt
677
678
678 Check that deep archive works with largefiles (which overrides hgsubrepo impl)
679 Check that deep archive works with largefiles (which overrides hgsubrepo impl)
679 This also tests the repo.ui regression in 43fb170a23bd, and that lf subrepo
680 This also tests the repo.ui regression in 43fb170a23bd, and that lf subrepo
680 subrepos are archived properly.
681 subrepos are archived properly.
681 Note that add --large through a subrepo currently adds the file as a normal file
682 Note that add --large through a subrepo currently adds the file as a normal file
682
683
683 $ echo "large" > sub1/sub2/large.bin
684 $ echo "large" > sub1/sub2/large.bin
684 $ hg --config extensions.largefiles= add --large -R sub1/sub2 sub1/sub2/large.bin
685 $ hg --config extensions.largefiles= add --large -R sub1/sub2 sub1/sub2/large.bin
685 $ echo "large" > large.bin
686 $ echo "large" > large.bin
686 $ hg --config extensions.largefiles= add --large large.bin
687 $ hg --config extensions.largefiles= add --large large.bin
687 $ hg --config extensions.largefiles= ci -S -m "add large files"
688 $ hg --config extensions.largefiles= ci -S -m "add large files"
688 committing subrepository sub1
689 committing subrepository sub1
689 committing subrepository sub1/sub2
690 committing subrepository sub1/sub2
690
691
691 $ hg --config extensions.largefiles= archive -S ../archive_lf
692 $ hg --config extensions.largefiles= archive -S ../archive_lf
692 $ find ../archive_lf | sort
693 $ find ../archive_lf | sort
693 ../archive_lf
694 ../archive_lf
694 ../archive_lf/.hg_archival.txt
695 ../archive_lf/.hg_archival.txt
695 ../archive_lf/.hgsub
696 ../archive_lf/.hgsub
696 ../archive_lf/.hgsubstate
697 ../archive_lf/.hgsubstate
697 ../archive_lf/large.bin
698 ../archive_lf/large.bin
698 ../archive_lf/main
699 ../archive_lf/main
699 ../archive_lf/sub1
700 ../archive_lf/sub1
700 ../archive_lf/sub1/.hgsub
701 ../archive_lf/sub1/.hgsub
701 ../archive_lf/sub1/.hgsubstate
702 ../archive_lf/sub1/.hgsubstate
702 ../archive_lf/sub1/sub1
703 ../archive_lf/sub1/sub1
703 ../archive_lf/sub1/sub2
704 ../archive_lf/sub1/sub2
704 ../archive_lf/sub1/sub2/folder
705 ../archive_lf/sub1/sub2/folder
705 ../archive_lf/sub1/sub2/folder/test.txt
706 ../archive_lf/sub1/sub2/folder/test.txt
706 ../archive_lf/sub1/sub2/large.bin
707 ../archive_lf/sub1/sub2/large.bin
707 ../archive_lf/sub1/sub2/sub2
708 ../archive_lf/sub1/sub2/sub2
708 ../archive_lf/sub1/sub2/test.txt
709 ../archive_lf/sub1/sub2/test.txt
709 $ rm -rf ../archive_lf
710 $ rm -rf ../archive_lf
710
711
711 Exclude large files from main and sub-sub repo
712 Exclude large files from main and sub-sub repo
712
713
713 $ hg --config extensions.largefiles= archive -S -X '**.bin' ../archive_lf
714 $ hg --config extensions.largefiles= archive -S -X '**.bin' ../archive_lf
714 $ find ../archive_lf | sort
715 $ find ../archive_lf | sort
715 ../archive_lf
716 ../archive_lf
716 ../archive_lf/.hg_archival.txt
717 ../archive_lf/.hg_archival.txt
717 ../archive_lf/.hgsub
718 ../archive_lf/.hgsub
718 ../archive_lf/.hgsubstate
719 ../archive_lf/.hgsubstate
719 ../archive_lf/main
720 ../archive_lf/main
720 ../archive_lf/sub1
721 ../archive_lf/sub1
721 ../archive_lf/sub1/.hgsub
722 ../archive_lf/sub1/.hgsub
722 ../archive_lf/sub1/.hgsubstate
723 ../archive_lf/sub1/.hgsubstate
723 ../archive_lf/sub1/sub1
724 ../archive_lf/sub1/sub1
724 ../archive_lf/sub1/sub2
725 ../archive_lf/sub1/sub2
725 ../archive_lf/sub1/sub2/folder
726 ../archive_lf/sub1/sub2/folder
726 ../archive_lf/sub1/sub2/folder/test.txt
727 ../archive_lf/sub1/sub2/folder/test.txt
727 ../archive_lf/sub1/sub2/sub2
728 ../archive_lf/sub1/sub2/sub2
728 ../archive_lf/sub1/sub2/test.txt
729 ../archive_lf/sub1/sub2/test.txt
729 $ rm -rf ../archive_lf
730 $ rm -rf ../archive_lf
730
731
731 Exclude normal files from main and sub-sub repo
732 Exclude normal files from main and sub-sub repo
732
733
733 $ hg --config extensions.largefiles= archive -S -X '**.txt' -p '.' ../archive_lf.tgz
734 $ hg --config extensions.largefiles= archive -S -X '**.txt' -p '.' ../archive_lf.tgz
734 $ tar -tzf ../archive_lf.tgz | sort
735 $ tar -tzf ../archive_lf.tgz | sort
735 .hgsub
736 .hgsub
736 .hgsubstate
737 .hgsubstate
737 large.bin
738 large.bin
738 main
739 main
739 sub1/.hgsub
740 sub1/.hgsub
740 sub1/.hgsubstate
741 sub1/.hgsubstate
741 sub1/sub1
742 sub1/sub1
742 sub1/sub2/large.bin
743 sub1/sub2/large.bin
743 sub1/sub2/sub2
744 sub1/sub2/sub2
744
745
745 Include normal files from within a largefiles subrepo
746 Include normal files from within a largefiles subrepo
746
747
747 $ hg --config extensions.largefiles= archive -S -I '**.txt' ../archive_lf
748 $ hg --config extensions.largefiles= archive -S -I '**.txt' ../archive_lf
748 $ find ../archive_lf | sort
749 $ find ../archive_lf | sort
749 ../archive_lf
750 ../archive_lf
750 ../archive_lf/.hg_archival.txt
751 ../archive_lf/.hg_archival.txt
751 ../archive_lf/sub1
752 ../archive_lf/sub1
752 ../archive_lf/sub1/sub2
753 ../archive_lf/sub1/sub2
753 ../archive_lf/sub1/sub2/folder
754 ../archive_lf/sub1/sub2/folder
754 ../archive_lf/sub1/sub2/folder/test.txt
755 ../archive_lf/sub1/sub2/folder/test.txt
755 ../archive_lf/sub1/sub2/test.txt
756 ../archive_lf/sub1/sub2/test.txt
756 $ rm -rf ../archive_lf
757 $ rm -rf ../archive_lf
757
758
758 Include large files from within a largefiles subrepo
759 Include large files from within a largefiles subrepo
759
760
760 $ hg --config extensions.largefiles= archive -S -I '**.bin' ../archive_lf
761 $ hg --config extensions.largefiles= archive -S -I '**.bin' ../archive_lf
761 $ find ../archive_lf | sort
762 $ find ../archive_lf | sort
762 ../archive_lf
763 ../archive_lf
763 ../archive_lf/large.bin
764 ../archive_lf/large.bin
764 ../archive_lf/sub1
765 ../archive_lf/sub1
765 ../archive_lf/sub1/sub2
766 ../archive_lf/sub1/sub2
766 ../archive_lf/sub1/sub2/large.bin
767 ../archive_lf/sub1/sub2/large.bin
767 $ rm -rf ../archive_lf
768 $ rm -rf ../archive_lf
768
769
769 Find an exact largefile match in a largefiles subrepo
770 Find an exact largefile match in a largefiles subrepo
770
771
771 $ hg --config extensions.largefiles= archive -S -I 'sub1/sub2/large.bin' ../archive_lf
772 $ hg --config extensions.largefiles= archive -S -I 'sub1/sub2/large.bin' ../archive_lf
772 $ find ../archive_lf | sort
773 $ find ../archive_lf | sort
773 ../archive_lf
774 ../archive_lf
774 ../archive_lf/sub1
775 ../archive_lf/sub1
775 ../archive_lf/sub1/sub2
776 ../archive_lf/sub1/sub2
776 ../archive_lf/sub1/sub2/large.bin
777 ../archive_lf/sub1/sub2/large.bin
777 $ rm -rf ../archive_lf
778 $ rm -rf ../archive_lf
778
779
779 The local repo enables largefiles if a largefiles repo is cloned
780 The local repo enables largefiles if a largefiles repo is cloned
780
781
781 $ hg showconfig extensions
782 $ hg showconfig extensions
782 extensions.largefiles=
783 extensions.largefiles=
783
784
784 $ hg --config extensions.largefiles= clone -qU . ../lfclone
785 $ hg --config extensions.largefiles= clone -qU . ../lfclone
785 $ grep largefiles ../lfclone/.hg/requires
786 $ grep largefiles ../lfclone/.hg/requires
786 largefiles
787 largefiles
787
788
788 Find an exact match to a standin (should archive nothing)
789 Find an exact match to a standin (should archive nothing)
789 $ hg --config extensions.largefiles= archive -S -I 'sub/sub2/.hglf/large.bin' ../archive_lf
790 $ hg --config extensions.largefiles= archive -S -I 'sub/sub2/.hglf/large.bin' ../archive_lf
790 $ find ../archive_lf 2> /dev/null | sort
791 $ find ../archive_lf 2> /dev/null | sort
791
792
792 $ cat >> $HGRCPATH <<EOF
793 $ cat >> $HGRCPATH <<EOF
793 > [extensions]
794 > [extensions]
794 > largefiles=
795 > largefiles=
795 > [largefiles]
796 > [largefiles]
796 > patterns=glob:**.dat
797 > patterns=glob:**.dat
797 > EOF
798 > EOF
798
799
799 Test forget through a deep subrepo with the largefiles extension, both a
800 Test forget through a deep subrepo with the largefiles extension, both a
800 largefile and a normal file. Then a largefile that hasn't been committed yet.
801 largefile and a normal file. Then a largefile that hasn't been committed yet.
801 $ touch sub1/sub2/untracked.txt
802 $ touch sub1/sub2/untracked.txt
802 $ touch sub1/sub2/large.dat
803 $ touch sub1/sub2/large.dat
803 $ hg forget sub1/sub2/large.bin sub1/sub2/test.txt sub1/sub2/untracked.txt
804 $ hg forget sub1/sub2/large.bin sub1/sub2/test.txt sub1/sub2/untracked.txt
804 not removing sub1/sub2/untracked.txt: file is already untracked
805 not removing sub1/sub2/untracked.txt: file is already untracked
805 [1]
806 [1]
806 $ hg add --large --dry-run -v sub1/sub2/untracked.txt
807 $ hg add --large --dry-run -v sub1/sub2/untracked.txt
807 adding sub1/sub2/untracked.txt as a largefile
808 adding sub1/sub2/untracked.txt as a largefile
808 $ hg add --large -v sub1/sub2/untracked.txt
809 $ hg add --large -v sub1/sub2/untracked.txt
809 adding sub1/sub2/untracked.txt as a largefile
810 adding sub1/sub2/untracked.txt as a largefile
810 $ hg add --normal -v sub1/sub2/large.dat
811 $ hg add --normal -v sub1/sub2/large.dat
811 adding sub1/sub2/large.dat
812 adding sub1/sub2/large.dat
812 $ hg forget -v sub1/sub2/untracked.txt
813 $ hg forget -v sub1/sub2/untracked.txt
813 removing sub1/sub2/untracked.txt
814 removing sub1/sub2/untracked.txt
814 $ hg status -S
815 $ hg status -S
815 A sub1/sub2/large.dat
816 A sub1/sub2/large.dat
816 R sub1/sub2/large.bin
817 R sub1/sub2/large.bin
817 R sub1/sub2/test.txt
818 R sub1/sub2/test.txt
818 ? foo/bar/abc
819 ? foo/bar/abc
819 ? sub1/sub2/untracked.txt
820 ? sub1/sub2/untracked.txt
820 ? sub1/sub2/x.txt
821 ? sub1/sub2/x.txt
821 $ hg add sub1/sub2
822 $ hg add sub1/sub2
822
823
823 $ hg archive -S -r 'wdir()' ../wdir2
824 $ hg archive -S -r 'wdir()' ../wdir2
824 $ diff -r . ../wdir2 | egrep -v '\.hg$|^Common subdirectories:'
825 $ diff -r . ../wdir2 | egrep -v '\.hg$|^Common subdirectories:'
825 Only in ../wdir2: .hg_archival.txt
826 Only in ../wdir2: .hg_archival.txt
826 Only in .: .hglf
827 Only in .: .hglf
827 Only in .: foo
828 Only in .: foo
828 Only in ./sub1/sub2: large.bin
829 Only in ./sub1/sub2: large.bin
829 Only in ./sub1/sub2: test.txt
830 Only in ./sub1/sub2: test.txt
830 Only in ./sub1/sub2: untracked.txt
831 Only in ./sub1/sub2: untracked.txt
831 Only in ./sub1/sub2: x.txt
832 Only in ./sub1/sub2: x.txt
832 $ find ../wdir2 -type f | sort
833 $ find ../wdir2 -type f | sort
833 ../wdir2/.hg_archival.txt
834 ../wdir2/.hg_archival.txt
834 ../wdir2/.hgsub
835 ../wdir2/.hgsub
835 ../wdir2/.hgsubstate
836 ../wdir2/.hgsubstate
836 ../wdir2/large.bin
837 ../wdir2/large.bin
837 ../wdir2/main
838 ../wdir2/main
838 ../wdir2/sub1/.hgsub
839 ../wdir2/sub1/.hgsub
839 ../wdir2/sub1/.hgsubstate
840 ../wdir2/sub1/.hgsubstate
840 ../wdir2/sub1/sub1
841 ../wdir2/sub1/sub1
841 ../wdir2/sub1/sub2/folder/test.txt
842 ../wdir2/sub1/sub2/folder/test.txt
842 ../wdir2/sub1/sub2/large.dat
843 ../wdir2/sub1/sub2/large.dat
843 ../wdir2/sub1/sub2/sub2
844 ../wdir2/sub1/sub2/sub2
844 $ hg status -S -mac -n | sort
845 $ hg status -S -mac -n | sort
845 .hgsub
846 .hgsub
846 .hgsubstate
847 .hgsubstate
847 large.bin
848 large.bin
848 main
849 main
849 sub1/.hgsub
850 sub1/.hgsub
850 sub1/.hgsubstate
851 sub1/.hgsubstate
851 sub1/sub1
852 sub1/sub1
852 sub1/sub2/folder/test.txt
853 sub1/sub2/folder/test.txt
853 sub1/sub2/large.dat
854 sub1/sub2/large.dat
854 sub1/sub2/sub2
855 sub1/sub2/sub2
855
856
856 $ hg ci -Sqm 'forget testing'
857 $ hg ci -Sqm 'forget testing'
857
858
858 Test 'wdir()' modified file archiving with largefiles
859 Test 'wdir()' modified file archiving with largefiles
859 $ echo 'mod' > main
860 $ echo 'mod' > main
860 $ echo 'mod' > large.bin
861 $ echo 'mod' > large.bin
861 $ echo 'mod' > sub1/sub2/large.dat
862 $ echo 'mod' > sub1/sub2/large.dat
862 $ hg archive -S -r 'wdir()' ../wdir3
863 $ hg archive -S -r 'wdir()' ../wdir3
863 $ diff -r . ../wdir3 | egrep -v '\.hg$|^Common subdirectories'
864 $ diff -r . ../wdir3 | egrep -v '\.hg$|^Common subdirectories'
864 Only in ../wdir3: .hg_archival.txt
865 Only in ../wdir3: .hg_archival.txt
865 Only in .: .hglf
866 Only in .: .hglf
866 Only in .: foo
867 Only in .: foo
867 Only in ./sub1/sub2: large.bin
868 Only in ./sub1/sub2: large.bin
868 Only in ./sub1/sub2: test.txt
869 Only in ./sub1/sub2: test.txt
869 Only in ./sub1/sub2: untracked.txt
870 Only in ./sub1/sub2: untracked.txt
870 Only in ./sub1/sub2: x.txt
871 Only in ./sub1/sub2: x.txt
871 $ find ../wdir3 -type f | sort
872 $ find ../wdir3 -type f | sort
872 ../wdir3/.hg_archival.txt
873 ../wdir3/.hg_archival.txt
873 ../wdir3/.hgsub
874 ../wdir3/.hgsub
874 ../wdir3/.hgsubstate
875 ../wdir3/.hgsubstate
875 ../wdir3/large.bin
876 ../wdir3/large.bin
876 ../wdir3/main
877 ../wdir3/main
877 ../wdir3/sub1/.hgsub
878 ../wdir3/sub1/.hgsub
878 ../wdir3/sub1/.hgsubstate
879 ../wdir3/sub1/.hgsubstate
879 ../wdir3/sub1/sub1
880 ../wdir3/sub1/sub1
880 ../wdir3/sub1/sub2/folder/test.txt
881 ../wdir3/sub1/sub2/folder/test.txt
881 ../wdir3/sub1/sub2/large.dat
882 ../wdir3/sub1/sub2/large.dat
882 ../wdir3/sub1/sub2/sub2
883 ../wdir3/sub1/sub2/sub2
883 $ hg up -Cq
884 $ hg up -Cq
884
885
885 Test issue4330: commit a directory where only normal files have changed
886 Test issue4330: commit a directory where only normal files have changed
886 $ touch foo/bar/large.dat
887 $ touch foo/bar/large.dat
887 $ hg add --large foo/bar/large.dat
888 $ hg add --large foo/bar/large.dat
888 $ hg ci -m 'add foo/bar/large.dat'
889 $ hg ci -m 'add foo/bar/large.dat'
889 $ touch a.txt
890 $ touch a.txt
890 $ touch a.dat
891 $ touch a.dat
891 $ hg add -v foo/bar/abc a.txt a.dat
892 $ hg add -v foo/bar/abc a.txt a.dat
892 adding a.dat as a largefile
893 adding a.dat as a largefile
893 adding a.txt
894 adding a.txt
894 adding foo/bar/abc
895 adding foo/bar/abc
895 $ hg ci -m 'dir commit with only normal file deltas' foo/bar
896 $ hg ci -m 'dir commit with only normal file deltas' foo/bar
896 $ hg status
897 $ hg status
897 A a.dat
898 A a.dat
898 A a.txt
899 A a.txt
899
900
900 Test a directory commit with a changed largefile and a changed normal file
901 Test a directory commit with a changed largefile and a changed normal file
901 $ echo changed > foo/bar/large.dat
902 $ echo changed > foo/bar/large.dat
902 $ echo changed > foo/bar/abc
903 $ echo changed > foo/bar/abc
903 $ hg ci -m 'dir commit with normal and lf file deltas' foo
904 $ hg ci -m 'dir commit with normal and lf file deltas' foo
904 $ hg status
905 $ hg status
905 A a.dat
906 A a.dat
906 A a.txt
907 A a.txt
907
908
908 $ hg ci -m "add a.*"
909 $ hg ci -m "add a.*"
909 $ hg mv a.dat b.dat
910 $ hg mv a.dat b.dat
910 $ hg mv foo/bar/abc foo/bar/def
911 $ hg mv foo/bar/abc foo/bar/def
911 $ hg status -C
912 $ hg status -C
912 A b.dat
913 A b.dat
913 a.dat
914 a.dat
914 A foo/bar/def
915 A foo/bar/def
915 foo/bar/abc
916 foo/bar/abc
916 R a.dat
917 R a.dat
917 R foo/bar/abc
918 R foo/bar/abc
918
919
919 $ hg ci -m "move large and normal"
920 $ hg ci -m "move large and normal"
920 $ hg status -C --rev '.^' --rev .
921 $ hg status -C --rev '.^' --rev .
921 A b.dat
922 A b.dat
922 a.dat
923 a.dat
923 A foo/bar/def
924 A foo/bar/def
924 foo/bar/abc
925 foo/bar/abc
925 R a.dat
926 R a.dat
926 R foo/bar/abc
927 R foo/bar/abc
927
928
928
929
929 $ echo foo > main
930 $ echo foo > main
930 $ hg ci -m "mod parent only"
931 $ hg ci -m "mod parent only"
931 $ hg init sub3
932 $ hg init sub3
932 $ echo "sub3 = sub3" >> .hgsub
933 $ echo "sub3 = sub3" >> .hgsub
933 $ echo xyz > sub3/a.txt
934 $ echo xyz > sub3/a.txt
934 $ hg add sub3/a.txt
935 $ hg add sub3/a.txt
935 $ hg ci -Sm "add sub3"
936 $ hg ci -Sm "add sub3"
936 committing subrepository sub3
937 committing subrepository sub3
937 $ cat .hgsub | grep -v sub3 > .hgsub1
938 $ cat .hgsub | grep -v sub3 > .hgsub1
938 $ mv .hgsub1 .hgsub
939 $ mv .hgsub1 .hgsub
939 $ hg ci -m "remove sub3"
940 $ hg ci -m "remove sub3"
940
941
941 $ hg log -r "subrepo()" --style compact
942 $ hg log -r "subrepo()" --style compact
942 0 7f491f53a367 1970-01-01 00:00 +0000 test
943 0 7f491f53a367 1970-01-01 00:00 +0000 test
943 main import
944 main import
944
945
945 1 ffe6649062fe 1970-01-01 00:00 +0000 test
946 1 ffe6649062fe 1970-01-01 00:00 +0000 test
946 deep nested modif should trigger a commit
947 deep nested modif should trigger a commit
947
948
948 2 9bb10eebee29 1970-01-01 00:00 +0000 test
949 2 9bb10eebee29 1970-01-01 00:00 +0000 test
949 add test.txt
950 add test.txt
950
951
951 3 7c64f035294f 1970-01-01 00:00 +0000 test
952 3 7c64f035294f 1970-01-01 00:00 +0000 test
952 add large files
953 add large files
953
954
954 4 f734a59e2e35 1970-01-01 00:00 +0000 test
955 4 f734a59e2e35 1970-01-01 00:00 +0000 test
955 forget testing
956 forget testing
956
957
957 11 9685a22af5db 1970-01-01 00:00 +0000 test
958 11 9685a22af5db 1970-01-01 00:00 +0000 test
958 add sub3
959 add sub3
959
960
960 12[tip] 2e0485b475b9 1970-01-01 00:00 +0000 test
961 12[tip] 2e0485b475b9 1970-01-01 00:00 +0000 test
961 remove sub3
962 remove sub3
962
963
963 $ hg log -r "subrepo('sub3')" --style compact
964 $ hg log -r "subrepo('sub3')" --style compact
964 11 9685a22af5db 1970-01-01 00:00 +0000 test
965 11 9685a22af5db 1970-01-01 00:00 +0000 test
965 add sub3
966 add sub3
966
967
967 12[tip] 2e0485b475b9 1970-01-01 00:00 +0000 test
968 12[tip] 2e0485b475b9 1970-01-01 00:00 +0000 test
968 remove sub3
969 remove sub3
969
970
970 $ hg log -r "subrepo('bogus')" --style compact
971 $ hg log -r "subrepo('bogus')" --style compact
971
972
972
973
973 Test .hgsubstate in the R state
974 Test .hgsubstate in the R state
974
975
975 $ hg rm .hgsub .hgsubstate
976 $ hg rm .hgsub .hgsubstate
976 \r (no-eol) (esc)
977 \r (no-eol) (esc)
977 deleting [=====================> ] 1/2\r (no-eol) (esc)
978 deleting [=====================> ] 1/2\r (no-eol) (esc)
978 deleting [===========================================>] 2/2\r (no-eol) (esc)
979 deleting [===========================================>] 2/2\r (no-eol) (esc)
979 \r (no-eol) (esc)
980 \r (no-eol) (esc)
980 $ hg ci -m 'trash subrepo tracking'
981 $ hg ci -m 'trash subrepo tracking'
981
982
982 $ hg log -r "subrepo('re:sub\d+')" --style compact
983 $ hg log -r "subrepo('re:sub\d+')" --style compact
983 0 7f491f53a367 1970-01-01 00:00 +0000 test
984 0 7f491f53a367 1970-01-01 00:00 +0000 test
984 main import
985 main import
985
986
986 1 ffe6649062fe 1970-01-01 00:00 +0000 test
987 1 ffe6649062fe 1970-01-01 00:00 +0000 test
987 deep nested modif should trigger a commit
988 deep nested modif should trigger a commit
988
989
989 2 9bb10eebee29 1970-01-01 00:00 +0000 test
990 2 9bb10eebee29 1970-01-01 00:00 +0000 test
990 add test.txt
991 add test.txt
991
992
992 3 7c64f035294f 1970-01-01 00:00 +0000 test
993 3 7c64f035294f 1970-01-01 00:00 +0000 test
993 add large files
994 add large files
994
995
995 4 f734a59e2e35 1970-01-01 00:00 +0000 test
996 4 f734a59e2e35 1970-01-01 00:00 +0000 test
996 forget testing
997 forget testing
997
998
998 11 9685a22af5db 1970-01-01 00:00 +0000 test
999 11 9685a22af5db 1970-01-01 00:00 +0000 test
999 add sub3
1000 add sub3
1000
1001
1001 12 2e0485b475b9 1970-01-01 00:00 +0000 test
1002 12 2e0485b475b9 1970-01-01 00:00 +0000 test
1002 remove sub3
1003 remove sub3
1003
1004
1004 13[tip] a68b2c361653 1970-01-01 00:00 +0000 test
1005 13[tip] a68b2c361653 1970-01-01 00:00 +0000 test
1005 trash subrepo tracking
1006 trash subrepo tracking
1006
1007
1007
1008
1008 Restore the trashed subrepo tracking
1009 Restore the trashed subrepo tracking
1009
1010
1010 $ hg rollback -q
1011 $ hg rollback -q
1011 $ hg update -Cq .
1012 $ hg update -Cq .
1012
1013
1013 Interaction with extdiff, largefiles and subrepos
1014 Interaction with extdiff, largefiles and subrepos
1014
1015
1015 $ hg --config extensions.extdiff= pdiff -S
1016 $ hg --config extensions.extdiff= pdiff -S
1016
1017
1017 $ hg --config extensions.extdiff= pdiff -r '.^' -S
1018 $ hg --config extensions.extdiff= pdiff -r '.^' -S
1018 \r (no-eol) (esc)
1019 \r (no-eol) (esc)
1019 archiving [ ] 0/2\r (no-eol) (esc)
1020 archiving [ ] 0/2\r (no-eol) (esc)
1020 archiving [====================> ] 1/2\r (no-eol) (esc)
1021 archiving [====================> ] 1/2\r (no-eol) (esc)
1021 archiving [==========================================>] 2/2\r (no-eol) (esc)
1022 archiving [==========================================>] 2/2\r (no-eol) (esc)
1022 \r (no-eol) (esc)
1023 \r (no-eol) (esc)
1023 \r (no-eol) (esc)
1024 \r (no-eol) (esc)
1024 archiving (sub1) [ <=> ] 0\r (no-eol) (esc)
1025 archiving (sub1) [ <=> ] 0\r (no-eol) (esc)
1025 \r (no-eol) (esc)
1026 \r (no-eol) (esc)
1026 \r (no-eol) (esc)
1027 \r (no-eol) (esc)
1027 archiving (sub1/sub2) [ <=> ] 0\r (no-eol) (esc)
1028 archiving (sub1/sub2) [ <=> ] 0\r (no-eol) (esc)
1028 \r (no-eol) (esc)
1029 \r (no-eol) (esc)
1029 \r (no-eol) (esc)
1030 \r (no-eol) (esc)
1030 archiving (sub3) [ <=> ] 0\r (no-eol) (esc)
1031 archiving (sub3) [ <=> ] 0\r (no-eol) (esc)
1031 \r (no-eol) (esc)
1032 \r (no-eol) (esc)
1032 \r (no-eol) (esc)
1033 \r (no-eol) (esc)
1033 archiving [ ] 0/2\r (no-eol) (esc)
1034 archiving [ ] 0/2\r (no-eol) (esc)
1034 archiving [====================> ] 1/2\r (no-eol) (esc)
1035 archiving [====================> ] 1/2\r (no-eol) (esc)
1035 archiving [==========================================>] 2/2\r (no-eol) (esc)
1036 archiving [==========================================>] 2/2\r (no-eol) (esc)
1036 \r (no-eol) (esc)
1037 \r (no-eol) (esc)
1037 \r (no-eol) (esc)
1038 \r (no-eol) (esc)
1038 archiving (sub1) [ <=> ] 0\r (no-eol) (esc)
1039 archiving (sub1) [ <=> ] 0\r (no-eol) (esc)
1039 \r (no-eol) (esc)
1040 \r (no-eol) (esc)
1040 \r (no-eol) (esc)
1041 \r (no-eol) (esc)
1041 archiving (sub1/sub2) [ <=> ] 0\r (no-eol) (esc)
1042 archiving (sub1/sub2) [ <=> ] 0\r (no-eol) (esc)
1042 \r (no-eol) (esc)
1043 \r (no-eol) (esc)
1043 diff -Nru cloned.*/.hgsub cloned/.hgsub (glob)
1044 diff -Nru cloned.*/.hgsub cloned/.hgsub (glob)
1044 --- cloned.*/.hgsub * (glob)
1045 --- cloned.*/.hgsub * (glob)
1045 +++ cloned/.hgsub * (glob)
1046 +++ cloned/.hgsub * (glob)
1046 @@ -1,2 +1* @@ (glob)
1047 @@ -1,2 +1* @@ (glob)
1047 sub1 = ../sub1
1048 sub1 = ../sub1
1048 -sub3 = sub3
1049 -sub3 = sub3
1049 diff -Nru cloned.*/.hgsubstate cloned/.hgsubstate (glob)
1050 diff -Nru cloned.*/.hgsubstate cloned/.hgsubstate (glob)
1050 --- cloned.*/.hgsubstate * (glob)
1051 --- cloned.*/.hgsubstate * (glob)
1051 +++ cloned/.hgsubstate * (glob)
1052 +++ cloned/.hgsubstate * (glob)
1052 @@ -1,2 +1* @@ (glob)
1053 @@ -1,2 +1* @@ (glob)
1053 7a36fa02b66e61f27f3d4a822809f159479b8ab2 sub1
1054 7a36fa02b66e61f27f3d4a822809f159479b8ab2 sub1
1054 -b1a26de6f2a045a9f079323693614ee322f1ff7e sub3
1055 -b1a26de6f2a045a9f079323693614ee322f1ff7e sub3
1055 [1]
1056 [1]
1056
1057
1057 $ hg --config extensions.extdiff= pdiff -r 0 -r '.^' -S
1058 $ hg --config extensions.extdiff= pdiff -r 0 -r '.^' -S
1058 \r (no-eol) (esc)
1059 \r (no-eol) (esc)
1059 archiving [ ] 0/3\r (no-eol) (esc)
1060 archiving [ ] 0/3\r (no-eol) (esc)
1060 archiving [=============> ] 1/3\r (no-eol) (esc)
1061 archiving [=============> ] 1/3\r (no-eol) (esc)
1061 archiving [===========================> ] 2/3\r (no-eol) (esc)
1062 archiving [===========================> ] 2/3\r (no-eol) (esc)
1062 archiving [==========================================>] 3/3\r (no-eol) (esc)
1063 archiving [==========================================>] 3/3\r (no-eol) (esc)
1063 \r (no-eol) (esc)
1064 \r (no-eol) (esc)
1064 \r (no-eol) (esc)
1065 \r (no-eol) (esc)
1065 archiving (sub1) [ ] 0/1\r (no-eol) (esc)
1066 archiving (sub1) [ ] 0/1\r (no-eol) (esc)
1066 archiving (sub1) [===================================>] 1/1\r (no-eol) (esc)
1067 archiving (sub1) [===================================>] 1/1\r (no-eol) (esc)
1067 \r (no-eol) (esc)
1068 \r (no-eol) (esc)
1068 \r (no-eol) (esc)
1069 \r (no-eol) (esc)
1069 archiving (sub1/sub2) [ ] 0/1\r (no-eol) (esc)
1070 archiving (sub1/sub2) [ ] 0/1\r (no-eol) (esc)
1070 archiving (sub1/sub2) [==============================>] 1/1\r (no-eol) (esc)
1071 archiving (sub1/sub2) [==============================>] 1/1\r (no-eol) (esc)
1071 \r (no-eol) (esc)
1072 \r (no-eol) (esc)
1072 \r (no-eol) (esc)
1073 \r (no-eol) (esc)
1073 archiving [ ] 0/8\r (no-eol) (esc)
1074 archiving [ ] 0/8\r (no-eol) (esc)
1074 archiving [====> ] 1/8\r (no-eol) (esc)
1075 archiving [====> ] 1/8\r (no-eol) (esc)
1075 archiving [=========> ] 2/8\r (no-eol) (esc)
1076 archiving [=========> ] 2/8\r (no-eol) (esc)
1076 archiving [===============> ] 3/8\r (no-eol) (esc)
1077 archiving [===============> ] 3/8\r (no-eol) (esc)
1077 archiving [====================> ] 4/8\r (no-eol) (esc)
1078 archiving [====================> ] 4/8\r (no-eol) (esc)
1078 archiving [=========================> ] 5/8\r (no-eol) (esc)
1079 archiving [=========================> ] 5/8\r (no-eol) (esc)
1079 archiving [===============================> ] 6/8\r (no-eol) (esc)
1080 archiving [===============================> ] 6/8\r (no-eol) (esc)
1080 archiving [====================================> ] 7/8\r (no-eol) (esc)
1081 archiving [====================================> ] 7/8\r (no-eol) (esc)
1081 archiving [==========================================>] 8/8\r (no-eol) (esc)
1082 archiving [==========================================>] 8/8\r (no-eol) (esc)
1082 \r (no-eol) (esc)
1083 \r (no-eol) (esc)
1083 \r (no-eol) (esc)
1084 \r (no-eol) (esc)
1084 archiving (sub1) [ ] 0/1\r (no-eol) (esc)
1085 archiving (sub1) [ ] 0/1\r (no-eol) (esc)
1085 archiving (sub1) [===================================>] 1/1\r (no-eol) (esc)
1086 archiving (sub1) [===================================>] 1/1\r (no-eol) (esc)
1086 \r (no-eol) (esc)
1087 \r (no-eol) (esc)
1087 \r (no-eol) (esc)
1088 \r (no-eol) (esc)
1088 archiving (sub1/sub2) [ ] 0/3\r (no-eol) (esc)
1089 archiving (sub1/sub2) [ ] 0/3\r (no-eol) (esc)
1089 archiving (sub1/sub2) [=========> ] 1/3\r (no-eol) (esc)
1090 archiving (sub1/sub2) [=========> ] 1/3\r (no-eol) (esc)
1090 archiving (sub1/sub2) [===================> ] 2/3\r (no-eol) (esc)
1091 archiving (sub1/sub2) [===================> ] 2/3\r (no-eol) (esc)
1091 archiving (sub1/sub2) [==============================>] 3/3\r (no-eol) (esc)
1092 archiving (sub1/sub2) [==============================>] 3/3\r (no-eol) (esc)
1092 \r (no-eol) (esc)
1093 \r (no-eol) (esc)
1093 \r (no-eol) (esc)
1094 \r (no-eol) (esc)
1094 archiving (sub3) [ ] 0/1\r (no-eol) (esc)
1095 archiving (sub3) [ ] 0/1\r (no-eol) (esc)
1095 archiving (sub3) [===================================>] 1/1\r (no-eol) (esc)
1096 archiving (sub3) [===================================>] 1/1\r (no-eol) (esc)
1096 \r (no-eol) (esc)
1097 \r (no-eol) (esc)
1097 diff -Nru cloned.*/.hglf/b.dat cloned.*/.hglf/b.dat (glob)
1098 diff -Nru cloned.*/.hglf/b.dat cloned.*/.hglf/b.dat (glob)
1098 --- cloned.*/.hglf/b.dat * (glob)
1099 --- cloned.*/.hglf/b.dat * (glob)
1099 +++ cloned.*/.hglf/b.dat * (glob)
1100 +++ cloned.*/.hglf/b.dat * (glob)
1100 @@ -*,0 +1* @@ (glob)
1101 @@ -*,0 +1* @@ (glob)
1101 +da39a3ee5e6b4b0d3255bfef95601890afd80709
1102 +da39a3ee5e6b4b0d3255bfef95601890afd80709
1102 diff -Nru cloned.*/.hglf/foo/bar/large.dat cloned.*/.hglf/foo/bar/large.dat (glob)
1103 diff -Nru cloned.*/.hglf/foo/bar/large.dat cloned.*/.hglf/foo/bar/large.dat (glob)
1103 --- cloned.*/.hglf/foo/bar/large.dat * (glob)
1104 --- cloned.*/.hglf/foo/bar/large.dat * (glob)
1104 +++ cloned.*/.hglf/foo/bar/large.dat * (glob)
1105 +++ cloned.*/.hglf/foo/bar/large.dat * (glob)
1105 @@ -*,0 +1* @@ (glob)
1106 @@ -*,0 +1* @@ (glob)
1106 +2f6933b5ee0f5fdd823d9717d8729f3c2523811b
1107 +2f6933b5ee0f5fdd823d9717d8729f3c2523811b
1107 diff -Nru cloned.*/.hglf/large.bin cloned.*/.hglf/large.bin (glob)
1108 diff -Nru cloned.*/.hglf/large.bin cloned.*/.hglf/large.bin (glob)
1108 --- cloned.*/.hglf/large.bin * (glob)
1109 --- cloned.*/.hglf/large.bin * (glob)
1109 +++ cloned.*/.hglf/large.bin * (glob)
1110 +++ cloned.*/.hglf/large.bin * (glob)
1110 @@ -*,0 +1* @@ (glob)
1111 @@ -*,0 +1* @@ (glob)
1111 +7f7097b041ccf68cc5561e9600da4655d21c6d18
1112 +7f7097b041ccf68cc5561e9600da4655d21c6d18
1112 diff -Nru cloned.*/.hgsub cloned.*/.hgsub (glob)
1113 diff -Nru cloned.*/.hgsub cloned.*/.hgsub (glob)
1113 --- cloned.*/.hgsub * (glob)
1114 --- cloned.*/.hgsub * (glob)
1114 +++ cloned.*/.hgsub * (glob)
1115 +++ cloned.*/.hgsub * (glob)
1115 @@ -1* +1,2 @@ (glob)
1116 @@ -1* +1,2 @@ (glob)
1116 sub1 = ../sub1
1117 sub1 = ../sub1
1117 +sub3 = sub3
1118 +sub3 = sub3
1118 diff -Nru cloned.*/.hgsubstate cloned.*/.hgsubstate (glob)
1119 diff -Nru cloned.*/.hgsubstate cloned.*/.hgsubstate (glob)
1119 --- cloned.*/.hgsubstate * (glob)
1120 --- cloned.*/.hgsubstate * (glob)
1120 +++ cloned.*/.hgsubstate * (glob)
1121 +++ cloned.*/.hgsubstate * (glob)
1121 @@ -1* +1,2 @@ (glob)
1122 @@ -1* +1,2 @@ (glob)
1122 -fc3b4ce2696f7741438c79207583768f2ce6b0dd sub1
1123 -fc3b4ce2696f7741438c79207583768f2ce6b0dd sub1
1123 +7a36fa02b66e61f27f3d4a822809f159479b8ab2 sub1
1124 +7a36fa02b66e61f27f3d4a822809f159479b8ab2 sub1
1124 +b1a26de6f2a045a9f079323693614ee322f1ff7e sub3
1125 +b1a26de6f2a045a9f079323693614ee322f1ff7e sub3
1125 diff -Nru cloned.*/foo/bar/def cloned.*/foo/bar/def (glob)
1126 diff -Nru cloned.*/foo/bar/def cloned.*/foo/bar/def (glob)
1126 --- cloned.*/foo/bar/def * (glob)
1127 --- cloned.*/foo/bar/def * (glob)
1127 +++ cloned.*/foo/bar/def * (glob)
1128 +++ cloned.*/foo/bar/def * (glob)
1128 @@ -*,0 +1* @@ (glob)
1129 @@ -*,0 +1* @@ (glob)
1129 +changed
1130 +changed
1130 diff -Nru cloned.*/main cloned.*/main (glob)
1131 diff -Nru cloned.*/main cloned.*/main (glob)
1131 --- cloned.*/main * (glob)
1132 --- cloned.*/main * (glob)
1132 +++ cloned.*/main * (glob)
1133 +++ cloned.*/main * (glob)
1133 @@ -1* +1* @@ (glob)
1134 @@ -1* +1* @@ (glob)
1134 -main
1135 -main
1135 +foo
1136 +foo
1136 diff -Nru cloned.*/sub1/.hgsubstate cloned.*/sub1/.hgsubstate (glob)
1137 diff -Nru cloned.*/sub1/.hgsubstate cloned.*/sub1/.hgsubstate (glob)
1137 --- cloned.*/sub1/.hgsubstate * (glob)
1138 --- cloned.*/sub1/.hgsubstate * (glob)
1138 +++ cloned.*/sub1/.hgsubstate * (glob)
1139 +++ cloned.*/sub1/.hgsubstate * (glob)
1139 @@ -1* +1* @@ (glob)
1140 @@ -1* +1* @@ (glob)
1140 -c57a0840e3badd667ef3c3ef65471609acb2ba3c sub2
1141 -c57a0840e3badd667ef3c3ef65471609acb2ba3c sub2
1141 +c77908c81ccea3794a896c79e98b0e004aee2e9e sub2
1142 +c77908c81ccea3794a896c79e98b0e004aee2e9e sub2
1142 diff -Nru cloned.*/sub1/sub2/folder/test.txt cloned.*/sub1/sub2/folder/test.txt (glob)
1143 diff -Nru cloned.*/sub1/sub2/folder/test.txt cloned.*/sub1/sub2/folder/test.txt (glob)
1143 --- cloned.*/sub1/sub2/folder/test.txt * (glob)
1144 --- cloned.*/sub1/sub2/folder/test.txt * (glob)
1144 +++ cloned.*/sub1/sub2/folder/test.txt * (glob)
1145 +++ cloned.*/sub1/sub2/folder/test.txt * (glob)
1145 @@ -*,0 +1* @@ (glob)
1146 @@ -*,0 +1* @@ (glob)
1146 +subfolder
1147 +subfolder
1147 diff -Nru cloned.*/sub1/sub2/sub2 cloned.*/sub1/sub2/sub2 (glob)
1148 diff -Nru cloned.*/sub1/sub2/sub2 cloned.*/sub1/sub2/sub2 (glob)
1148 --- cloned.*/sub1/sub2/sub2 * (glob)
1149 --- cloned.*/sub1/sub2/sub2 * (glob)
1149 +++ cloned.*/sub1/sub2/sub2 * (glob)
1150 +++ cloned.*/sub1/sub2/sub2 * (glob)
1150 @@ -1* +1* @@ (glob)
1151 @@ -1* +1* @@ (glob)
1151 -sub2
1152 -sub2
1152 +modified
1153 +modified
1153 diff -Nru cloned.*/sub3/a.txt cloned.*/sub3/a.txt (glob)
1154 diff -Nru cloned.*/sub3/a.txt cloned.*/sub3/a.txt (glob)
1154 --- cloned.*/sub3/a.txt * (glob)
1155 --- cloned.*/sub3/a.txt * (glob)
1155 +++ cloned.*/sub3/a.txt * (glob)
1156 +++ cloned.*/sub3/a.txt * (glob)
1156 @@ -*,0 +1* @@ (glob)
1157 @@ -*,0 +1* @@ (glob)
1157 +xyz
1158 +xyz
1158 [1]
1159 [1]
1159
1160
1160 $ echo mod > sub1/sub2/sub2
1161 $ echo mod > sub1/sub2/sub2
1161 $ hg --config extensions.extdiff= pdiff -S
1162 $ hg --config extensions.extdiff= pdiff -S
1162 \r (no-eol) (esc)
1163 \r (no-eol) (esc)
1163 archiving (sub1) [ <=> ] 0\r (no-eol) (esc)
1164 archiving (sub1) [ <=> ] 0\r (no-eol) (esc)
1164 \r (no-eol) (esc)
1165 \r (no-eol) (esc)
1165 \r (no-eol) (esc)
1166 \r (no-eol) (esc)
1166 archiving (sub1/sub2) [ ] 0/1\r (no-eol) (esc)
1167 archiving (sub1/sub2) [ ] 0/1\r (no-eol) (esc)
1167 archiving (sub1/sub2) [==============================>] 1/1\r (no-eol) (esc)
1168 archiving (sub1/sub2) [==============================>] 1/1\r (no-eol) (esc)
1168 \r (no-eol) (esc)
1169 \r (no-eol) (esc)
1169 --- */cloned.*/sub1/sub2/sub2 * (glob)
1170 --- */cloned.*/sub1/sub2/sub2 * (glob)
1170 +++ */cloned/sub1/sub2/sub2 * (glob)
1171 +++ */cloned/sub1/sub2/sub2 * (glob)
1171 @@ -1* +1* @@ (glob)
1172 @@ -1* +1* @@ (glob)
1172 -modified
1173 -modified
1173 +mod
1174 +mod
1174 [1]
1175 [1]
1175
1176
1176 $ cd ..
1177 $ cd ..
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now