##// END OF EJS Templates
merge with stable
Matt Mackall -
r16603:ddd49967 merge default
parent child Browse files
Show More
@@ -1,166 +1,168 b''
1 # Copyright 2011 Fog Creek Software
1 # Copyright 2011 Fog Creek Software
2 #
2 #
3 # This software may be used and distributed according to the terms of the
3 # This software may be used and distributed according to the terms of the
4 # GNU General Public License version 2 or any later version.
4 # GNU General Public License version 2 or any later version.
5
5
6 import os
6 import os
7 import urllib2
7 import urllib2
8
8
9 from mercurial import error, httprepo, util, wireproto
9 from mercurial import error, httprepo, util, wireproto
10 from mercurial.i18n import _
10 from mercurial.i18n import _
11
11
12 import lfutil
12 import lfutil
13
13
14 LARGEFILES_REQUIRED_MSG = ('\nThis repository uses the largefiles extension.'
14 LARGEFILES_REQUIRED_MSG = ('\nThis repository uses the largefiles extension.'
15 '\n\nPlease enable it in your Mercurial config '
15 '\n\nPlease enable it in your Mercurial config '
16 'file.\n')
16 'file.\n')
17
17
18 def putlfile(repo, proto, sha):
18 def putlfile(repo, proto, sha):
19 '''Put a largefile into a repository's local store and into the
19 '''Put a largefile into a repository's local store and into the
20 user cache.'''
20 user cache.'''
21 proto.redirect()
21 proto.redirect()
22
22
23 tmpfp = util.atomictempfile(lfutil.storepath(repo, sha),
23 path = lfutil.storepath(repo, sha)
24 createmode=repo.store.createmode)
24 util.makedirs(os.path.dirname(path))
25 tmpfp = util.atomictempfile(path, createmode=repo.store.createmode)
26
25 try:
27 try:
26 try:
28 try:
27 proto.getfile(tmpfp)
29 proto.getfile(tmpfp)
28 tmpfp._fp.seek(0)
30 tmpfp._fp.seek(0)
29 if sha != lfutil.hexsha1(tmpfp._fp):
31 if sha != lfutil.hexsha1(tmpfp._fp):
30 raise IOError(0, _('largefile contents do not match hash'))
32 raise IOError(0, _('largefile contents do not match hash'))
31 tmpfp.close()
33 tmpfp.close()
32 lfutil.linktousercache(repo, sha)
34 lfutil.linktousercache(repo, sha)
33 except IOError, e:
35 except IOError, e:
34 repo.ui.warn(_('largefiles: failed to put %s into store: %s') %
36 repo.ui.warn(_('largefiles: failed to put %s into store: %s') %
35 (sha, e.strerror))
37 (sha, e.strerror))
36 return wireproto.pushres(1)
38 return wireproto.pushres(1)
37 finally:
39 finally:
38 tmpfp.discard()
40 tmpfp.discard()
39
41
40 return wireproto.pushres(0)
42 return wireproto.pushres(0)
41
43
42 def getlfile(repo, proto, sha):
44 def getlfile(repo, proto, sha):
43 '''Retrieve a largefile from the repository-local cache or system
45 '''Retrieve a largefile from the repository-local cache or system
44 cache.'''
46 cache.'''
45 filename = lfutil.findfile(repo, sha)
47 filename = lfutil.findfile(repo, sha)
46 if not filename:
48 if not filename:
47 raise util.Abort(_('requested largefile %s not present in cache') % sha)
49 raise util.Abort(_('requested largefile %s not present in cache') % sha)
48 f = open(filename, 'rb')
50 f = open(filename, 'rb')
49 length = os.fstat(f.fileno())[6]
51 length = os.fstat(f.fileno())[6]
50
52
51 # Since we can't set an HTTP content-length header here, and
53 # Since we can't set an HTTP content-length header here, and
52 # Mercurial core provides no way to give the length of a streamres
54 # Mercurial core provides no way to give the length of a streamres
53 # (and reading the entire file into RAM would be ill-advised), we
55 # (and reading the entire file into RAM would be ill-advised), we
54 # just send the length on the first line of the response, like the
56 # just send the length on the first line of the response, like the
55 # ssh proto does for string responses.
57 # ssh proto does for string responses.
56 def generator():
58 def generator():
57 yield '%d\n' % length
59 yield '%d\n' % length
58 for chunk in f:
60 for chunk in f:
59 yield chunk
61 yield chunk
60 return wireproto.streamres(generator())
62 return wireproto.streamres(generator())
61
63
62 def statlfile(repo, proto, sha):
64 def statlfile(repo, proto, sha):
63 '''Return '2\n' if the largefile is missing, '1\n' if it has a
65 '''Return '2\n' if the largefile is missing, '1\n' if it has a
64 mismatched checksum, or '0\n' if it is in good condition'''
66 mismatched checksum, or '0\n' if it is in good condition'''
65 filename = lfutil.findfile(repo, sha)
67 filename = lfutil.findfile(repo, sha)
66 if not filename:
68 if not filename:
67 return '2\n'
69 return '2\n'
68 fd = None
70 fd = None
69 try:
71 try:
70 fd = open(filename, 'rb')
72 fd = open(filename, 'rb')
71 return lfutil.hexsha1(fd) == sha and '0\n' or '1\n'
73 return lfutil.hexsha1(fd) == sha and '0\n' or '1\n'
72 finally:
74 finally:
73 if fd:
75 if fd:
74 fd.close()
76 fd.close()
75
77
76 def wirereposetup(ui, repo):
78 def wirereposetup(ui, repo):
77 class lfileswirerepository(repo.__class__):
79 class lfileswirerepository(repo.__class__):
78 def putlfile(self, sha, fd):
80 def putlfile(self, sha, fd):
79 # unfortunately, httprepository._callpush tries to convert its
81 # unfortunately, httprepository._callpush tries to convert its
80 # input file-like into a bundle before sending it, so we can't use
82 # input file-like into a bundle before sending it, so we can't use
81 # it ...
83 # it ...
82 if issubclass(self.__class__, httprepo.httprepository):
84 if issubclass(self.__class__, httprepo.httprepository):
83 res = None
85 res = None
84 try:
86 try:
85 res = self._call('putlfile', data=fd, sha=sha,
87 res = self._call('putlfile', data=fd, sha=sha,
86 headers={'content-type':'application/mercurial-0.1'})
88 headers={'content-type':'application/mercurial-0.1'})
87 d, output = res.split('\n', 1)
89 d, output = res.split('\n', 1)
88 for l in output.splitlines(True):
90 for l in output.splitlines(True):
89 self.ui.warn(_('remote: '), l, '\n')
91 self.ui.warn(_('remote: '), l, '\n')
90 return int(d)
92 return int(d)
91 except (ValueError, urllib2.HTTPError):
93 except (ValueError, urllib2.HTTPError):
92 self.ui.warn(_('unexpected putlfile response: %s') % res)
94 self.ui.warn(_('unexpected putlfile response: %s') % res)
93 return 1
95 return 1
94 # ... but we can't use sshrepository._call because the data=
96 # ... but we can't use sshrepository._call because the data=
95 # argument won't get sent, and _callpush does exactly what we want
97 # argument won't get sent, and _callpush does exactly what we want
96 # in this case: send the data straight through
98 # in this case: send the data straight through
97 else:
99 else:
98 try:
100 try:
99 ret, output = self._callpush("putlfile", fd, sha=sha)
101 ret, output = self._callpush("putlfile", fd, sha=sha)
100 if ret == "":
102 if ret == "":
101 raise error.ResponseError(_('putlfile failed:'),
103 raise error.ResponseError(_('putlfile failed:'),
102 output)
104 output)
103 return int(ret)
105 return int(ret)
104 except IOError:
106 except IOError:
105 return 1
107 return 1
106 except ValueError:
108 except ValueError:
107 raise error.ResponseError(
109 raise error.ResponseError(
108 _('putlfile failed (unexpected response):'), ret)
110 _('putlfile failed (unexpected response):'), ret)
109
111
110 def getlfile(self, sha):
112 def getlfile(self, sha):
111 stream = self._callstream("getlfile", sha=sha)
113 stream = self._callstream("getlfile", sha=sha)
112 length = stream.readline()
114 length = stream.readline()
113 try:
115 try:
114 length = int(length)
116 length = int(length)
115 except ValueError:
117 except ValueError:
116 self._abort(error.ResponseError(_("unexpected response:"),
118 self._abort(error.ResponseError(_("unexpected response:"),
117 length))
119 length))
118 return (length, stream)
120 return (length, stream)
119
121
120 def statlfile(self, sha):
122 def statlfile(self, sha):
121 try:
123 try:
122 return int(self._call("statlfile", sha=sha))
124 return int(self._call("statlfile", sha=sha))
123 except (ValueError, urllib2.HTTPError):
125 except (ValueError, urllib2.HTTPError):
124 # If the server returns anything but an integer followed by a
126 # If the server returns anything but an integer followed by a
125 # newline, newline, it's not speaking our language; if we get
127 # newline, newline, it's not speaking our language; if we get
126 # an HTTP error, we can't be sure the largefile is present;
128 # an HTTP error, we can't be sure the largefile is present;
127 # either way, consider it missing.
129 # either way, consider it missing.
128 return 2
130 return 2
129
131
130 repo.__class__ = lfileswirerepository
132 repo.__class__ = lfileswirerepository
131
133
132 # advertise the largefiles=serve capability
134 # advertise the largefiles=serve capability
133 def capabilities(repo, proto):
135 def capabilities(repo, proto):
134 return capabilitiesorig(repo, proto) + ' largefiles=serve'
136 return capabilitiesorig(repo, proto) + ' largefiles=serve'
135
137
136 # duplicate what Mercurial's new out-of-band errors mechanism does, because
138 # duplicate what Mercurial's new out-of-band errors mechanism does, because
137 # clients old and new alike both handle it well
139 # clients old and new alike both handle it well
138 def webprotorefuseclient(self, message):
140 def webprotorefuseclient(self, message):
139 self.req.header([('Content-Type', 'application/hg-error')])
141 self.req.header([('Content-Type', 'application/hg-error')])
140 return message
142 return message
141
143
142 def sshprotorefuseclient(self, message):
144 def sshprotorefuseclient(self, message):
143 self.ui.write_err('%s\n-\n' % message)
145 self.ui.write_err('%s\n-\n' % message)
144 self.fout.write('\n')
146 self.fout.write('\n')
145 self.fout.flush()
147 self.fout.flush()
146
148
147 return ''
149 return ''
148
150
149 def heads(repo, proto):
151 def heads(repo, proto):
150 if lfutil.islfilesrepo(repo):
152 if lfutil.islfilesrepo(repo):
151 return wireproto.ooberror(LARGEFILES_REQUIRED_MSG)
153 return wireproto.ooberror(LARGEFILES_REQUIRED_MSG)
152 return wireproto.heads(repo, proto)
154 return wireproto.heads(repo, proto)
153
155
154 def sshrepocallstream(self, cmd, **args):
156 def sshrepocallstream(self, cmd, **args):
155 if cmd == 'heads' and self.capable('largefiles'):
157 if cmd == 'heads' and self.capable('largefiles'):
156 cmd = 'lheads'
158 cmd = 'lheads'
157 if cmd == 'batch' and self.capable('largefiles'):
159 if cmd == 'batch' and self.capable('largefiles'):
158 args['cmds'] = args['cmds'].replace('heads ', 'lheads ')
160 args['cmds'] = args['cmds'].replace('heads ', 'lheads ')
159 return ssholdcallstream(self, cmd, **args)
161 return ssholdcallstream(self, cmd, **args)
160
162
161 def httprepocallstream(self, cmd, **args):
163 def httprepocallstream(self, cmd, **args):
162 if cmd == 'heads' and self.capable('largefiles'):
164 if cmd == 'heads' and self.capable('largefiles'):
163 cmd = 'lheads'
165 cmd = 'lheads'
164 if cmd == 'batch' and self.capable('largefiles'):
166 if cmd == 'batch' and self.capable('largefiles'):
165 args['cmds'] = args['cmds'].replace('heads ', 'lheads ')
167 args['cmds'] = args['cmds'].replace('heads ', 'lheads ')
166 return httpoldcallstream(self, cmd, **args)
168 return httpoldcallstream(self, cmd, **args)
@@ -1,5723 +1,5724 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 node import hex, bin, nullid, nullrev, short
8 from node import hex, bin, nullid, nullrev, short
9 from lock import release
9 from lock import release
10 from i18n import _, gettext
10 from i18n import _, gettext
11 import os, re, difflib, time, tempfile, errno
11 import os, re, difflib, time, tempfile, errno
12 import hg, scmutil, util, revlog, extensions, copies, error, bookmarks
12 import hg, scmutil, util, revlog, extensions, copies, error, bookmarks
13 import patch, help, url, encoding, templatekw, discovery
13 import patch, help, url, encoding, templatekw, discovery
14 import archival, changegroup, cmdutil, hbisect
14 import archival, changegroup, cmdutil, hbisect
15 import sshserver, hgweb, hgweb.server, commandserver
15 import sshserver, hgweb, hgweb.server, commandserver
16 import merge as mergemod
16 import merge as mergemod
17 import minirst, revset, fileset
17 import minirst, revset, fileset
18 import dagparser, context, simplemerge
18 import dagparser, context, simplemerge
19 import random, setdiscovery, treediscovery, dagutil, pvec
19 import random, setdiscovery, treediscovery, dagutil, pvec
20 import phases
20 import phases
21
21
22 table = {}
22 table = {}
23
23
24 command = cmdutil.command(table)
24 command = cmdutil.command(table)
25
25
26 # common command options
26 # common command options
27
27
28 globalopts = [
28 globalopts = [
29 ('R', 'repository', '',
29 ('R', 'repository', '',
30 _('repository root directory or name of overlay bundle file'),
30 _('repository root directory or name of overlay bundle file'),
31 _('REPO')),
31 _('REPO')),
32 ('', 'cwd', '',
32 ('', 'cwd', '',
33 _('change working directory'), _('DIR')),
33 _('change working directory'), _('DIR')),
34 ('y', 'noninteractive', None,
34 ('y', 'noninteractive', None,
35 _('do not prompt, automatically pick the first choice for all prompts')),
35 _('do not prompt, automatically pick the first choice for all prompts')),
36 ('q', 'quiet', None, _('suppress output')),
36 ('q', 'quiet', None, _('suppress output')),
37 ('v', 'verbose', None, _('enable additional output')),
37 ('v', 'verbose', None, _('enable additional output')),
38 ('', 'config', [],
38 ('', 'config', [],
39 _('set/override config option (use \'section.name=value\')'),
39 _('set/override config option (use \'section.name=value\')'),
40 _('CONFIG')),
40 _('CONFIG')),
41 ('', 'debug', None, _('enable debugging output')),
41 ('', 'debug', None, _('enable debugging output')),
42 ('', 'debugger', None, _('start debugger')),
42 ('', 'debugger', None, _('start debugger')),
43 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
43 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
44 _('ENCODE')),
44 _('ENCODE')),
45 ('', 'encodingmode', encoding.encodingmode,
45 ('', 'encodingmode', encoding.encodingmode,
46 _('set the charset encoding mode'), _('MODE')),
46 _('set the charset encoding mode'), _('MODE')),
47 ('', 'traceback', None, _('always print a traceback on exception')),
47 ('', 'traceback', None, _('always print a traceback on exception')),
48 ('', 'time', None, _('time how long the command takes')),
48 ('', 'time', None, _('time how long the command takes')),
49 ('', 'profile', None, _('print command execution profile')),
49 ('', 'profile', None, _('print command execution profile')),
50 ('', 'version', None, _('output version information and exit')),
50 ('', 'version', None, _('output version information and exit')),
51 ('h', 'help', None, _('display help and exit')),
51 ('h', 'help', None, _('display help and exit')),
52 ]
52 ]
53
53
54 dryrunopts = [('n', 'dry-run', None,
54 dryrunopts = [('n', 'dry-run', None,
55 _('do not perform actions, just print output'))]
55 _('do not perform actions, just print output'))]
56
56
57 remoteopts = [
57 remoteopts = [
58 ('e', 'ssh', '',
58 ('e', 'ssh', '',
59 _('specify ssh command to use'), _('CMD')),
59 _('specify ssh command to use'), _('CMD')),
60 ('', 'remotecmd', '',
60 ('', 'remotecmd', '',
61 _('specify hg command to run on the remote side'), _('CMD')),
61 _('specify hg command to run on the remote side'), _('CMD')),
62 ('', 'insecure', None,
62 ('', 'insecure', None,
63 _('do not verify server certificate (ignoring web.cacerts config)')),
63 _('do not verify server certificate (ignoring web.cacerts config)')),
64 ]
64 ]
65
65
66 walkopts = [
66 walkopts = [
67 ('I', 'include', [],
67 ('I', 'include', [],
68 _('include names matching the given patterns'), _('PATTERN')),
68 _('include names matching the given patterns'), _('PATTERN')),
69 ('X', 'exclude', [],
69 ('X', 'exclude', [],
70 _('exclude names matching the given patterns'), _('PATTERN')),
70 _('exclude names matching the given patterns'), _('PATTERN')),
71 ]
71 ]
72
72
73 commitopts = [
73 commitopts = [
74 ('m', 'message', '',
74 ('m', 'message', '',
75 _('use text as commit message'), _('TEXT')),
75 _('use text as commit message'), _('TEXT')),
76 ('l', 'logfile', '',
76 ('l', 'logfile', '',
77 _('read commit message from file'), _('FILE')),
77 _('read commit message from file'), _('FILE')),
78 ]
78 ]
79
79
80 commitopts2 = [
80 commitopts2 = [
81 ('d', 'date', '',
81 ('d', 'date', '',
82 _('record the specified date as commit date'), _('DATE')),
82 _('record the specified date as commit date'), _('DATE')),
83 ('u', 'user', '',
83 ('u', 'user', '',
84 _('record the specified user as committer'), _('USER')),
84 _('record the specified user as committer'), _('USER')),
85 ]
85 ]
86
86
87 templateopts = [
87 templateopts = [
88 ('', 'style', '',
88 ('', 'style', '',
89 _('display using template map file'), _('STYLE')),
89 _('display using template map file'), _('STYLE')),
90 ('', 'template', '',
90 ('', 'template', '',
91 _('display with template'), _('TEMPLATE')),
91 _('display with template'), _('TEMPLATE')),
92 ]
92 ]
93
93
94 logopts = [
94 logopts = [
95 ('p', 'patch', None, _('show patch')),
95 ('p', 'patch', None, _('show patch')),
96 ('g', 'git', None, _('use git extended diff format')),
96 ('g', 'git', None, _('use git extended diff format')),
97 ('l', 'limit', '',
97 ('l', 'limit', '',
98 _('limit number of changes displayed'), _('NUM')),
98 _('limit number of changes displayed'), _('NUM')),
99 ('M', 'no-merges', None, _('do not show merges')),
99 ('M', 'no-merges', None, _('do not show merges')),
100 ('', 'stat', None, _('output diffstat-style summary of changes')),
100 ('', 'stat', None, _('output diffstat-style summary of changes')),
101 ] + templateopts
101 ] + templateopts
102
102
103 diffopts = [
103 diffopts = [
104 ('a', 'text', None, _('treat all files as text')),
104 ('a', 'text', None, _('treat all files as text')),
105 ('g', 'git', None, _('use git extended diff format')),
105 ('g', 'git', None, _('use git extended diff format')),
106 ('', 'nodates', None, _('omit dates from diff headers'))
106 ('', 'nodates', None, _('omit dates from diff headers'))
107 ]
107 ]
108
108
109 diffwsopts = [
109 diffwsopts = [
110 ('w', 'ignore-all-space', None,
110 ('w', 'ignore-all-space', None,
111 _('ignore white space when comparing lines')),
111 _('ignore white space when comparing lines')),
112 ('b', 'ignore-space-change', None,
112 ('b', 'ignore-space-change', None,
113 _('ignore changes in the amount of white space')),
113 _('ignore changes in the amount of white space')),
114 ('B', 'ignore-blank-lines', None,
114 ('B', 'ignore-blank-lines', None,
115 _('ignore changes whose lines are all blank')),
115 _('ignore changes whose lines are all blank')),
116 ]
116 ]
117
117
118 diffopts2 = [
118 diffopts2 = [
119 ('p', 'show-function', None, _('show which function each change is in')),
119 ('p', 'show-function', None, _('show which function each change is in')),
120 ('', 'reverse', None, _('produce a diff that undoes the changes')),
120 ('', 'reverse', None, _('produce a diff that undoes the changes')),
121 ] + diffwsopts + [
121 ] + diffwsopts + [
122 ('U', 'unified', '',
122 ('U', 'unified', '',
123 _('number of lines of context to show'), _('NUM')),
123 _('number of lines of context to show'), _('NUM')),
124 ('', 'stat', None, _('output diffstat-style summary of changes')),
124 ('', 'stat', None, _('output diffstat-style summary of changes')),
125 ]
125 ]
126
126
127 mergetoolopts = [
127 mergetoolopts = [
128 ('t', 'tool', '', _('specify merge tool')),
128 ('t', 'tool', '', _('specify merge tool')),
129 ]
129 ]
130
130
131 similarityopts = [
131 similarityopts = [
132 ('s', 'similarity', '',
132 ('s', 'similarity', '',
133 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
133 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
134 ]
134 ]
135
135
136 subrepoopts = [
136 subrepoopts = [
137 ('S', 'subrepos', None,
137 ('S', 'subrepos', None,
138 _('recurse into subrepositories'))
138 _('recurse into subrepositories'))
139 ]
139 ]
140
140
141 # Commands start here, listed alphabetically
141 # Commands start here, listed alphabetically
142
142
143 @command('^add',
143 @command('^add',
144 walkopts + subrepoopts + dryrunopts,
144 walkopts + subrepoopts + dryrunopts,
145 _('[OPTION]... [FILE]...'))
145 _('[OPTION]... [FILE]...'))
146 def add(ui, repo, *pats, **opts):
146 def add(ui, repo, *pats, **opts):
147 """add the specified files on the next commit
147 """add the specified files on the next commit
148
148
149 Schedule files to be version controlled and added to the
149 Schedule files to be version controlled and added to the
150 repository.
150 repository.
151
151
152 The files will be added to the repository at the next commit. To
152 The files will be added to the repository at the next commit. To
153 undo an add before that, see :hg:`forget`.
153 undo an add before that, see :hg:`forget`.
154
154
155 If no names are given, add all files to the repository.
155 If no names are given, add all files to the repository.
156
156
157 .. container:: verbose
157 .. container:: verbose
158
158
159 An example showing how new (unknown) files are added
159 An example showing how new (unknown) files are added
160 automatically by :hg:`add`::
160 automatically by :hg:`add`::
161
161
162 $ ls
162 $ ls
163 foo.c
163 foo.c
164 $ hg status
164 $ hg status
165 ? foo.c
165 ? foo.c
166 $ hg add
166 $ hg add
167 adding foo.c
167 adding foo.c
168 $ hg status
168 $ hg status
169 A foo.c
169 A foo.c
170
170
171 Returns 0 if all files are successfully added.
171 Returns 0 if all files are successfully added.
172 """
172 """
173
173
174 m = scmutil.match(repo[None], pats, opts)
174 m = scmutil.match(repo[None], pats, opts)
175 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
175 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
176 opts.get('subrepos'), prefix="", explicitonly=False)
176 opts.get('subrepos'), prefix="", explicitonly=False)
177 return rejected and 1 or 0
177 return rejected and 1 or 0
178
178
179 @command('addremove',
179 @command('addremove',
180 similarityopts + walkopts + dryrunopts,
180 similarityopts + walkopts + dryrunopts,
181 _('[OPTION]... [FILE]...'))
181 _('[OPTION]... [FILE]...'))
182 def addremove(ui, repo, *pats, **opts):
182 def addremove(ui, repo, *pats, **opts):
183 """add all new files, delete all missing files
183 """add all new files, delete all missing files
184
184
185 Add all new files and remove all missing files from the
185 Add all new files and remove all missing files from the
186 repository.
186 repository.
187
187
188 New files are ignored if they match any of the patterns in
188 New files are ignored if they match any of the patterns in
189 ``.hgignore``. As with add, these changes take effect at the next
189 ``.hgignore``. As with add, these changes take effect at the next
190 commit.
190 commit.
191
191
192 Use the -s/--similarity option to detect renamed files. With a
192 Use the -s/--similarity option to detect renamed files. With a
193 parameter greater than 0, this compares every removed file with
193 parameter greater than 0, this compares every removed file with
194 every added file and records those similar enough as renames. This
194 every added file and records those similar enough as renames. This
195 option takes a percentage between 0 (disabled) and 100 (files must
195 option takes a percentage between 0 (disabled) and 100 (files must
196 be identical) as its parameter. Detecting renamed files this way
196 be identical) as its parameter. Detecting renamed files this way
197 can be expensive. After using this option, :hg:`status -C` can be
197 can be expensive. After using this option, :hg:`status -C` can be
198 used to check which files were identified as moved or renamed.
198 used to check which files were identified as moved or renamed.
199
199
200 Returns 0 if all files are successfully added.
200 Returns 0 if all files are successfully added.
201 """
201 """
202 try:
202 try:
203 sim = float(opts.get('similarity') or 100)
203 sim = float(opts.get('similarity') or 100)
204 except ValueError:
204 except ValueError:
205 raise util.Abort(_('similarity must be a number'))
205 raise util.Abort(_('similarity must be a number'))
206 if sim < 0 or sim > 100:
206 if sim < 0 or sim > 100:
207 raise util.Abort(_('similarity must be between 0 and 100'))
207 raise util.Abort(_('similarity must be between 0 and 100'))
208 return scmutil.addremove(repo, pats, opts, similarity=sim / 100.0)
208 return scmutil.addremove(repo, pats, opts, similarity=sim / 100.0)
209
209
210 @command('^annotate|blame',
210 @command('^annotate|blame',
211 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
211 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
212 ('', 'follow', None,
212 ('', 'follow', None,
213 _('follow copies/renames and list the filename (DEPRECATED)')),
213 _('follow copies/renames and list the filename (DEPRECATED)')),
214 ('', 'no-follow', None, _("don't follow copies and renames")),
214 ('', 'no-follow', None, _("don't follow copies and renames")),
215 ('a', 'text', None, _('treat all files as text')),
215 ('a', 'text', None, _('treat all files as text')),
216 ('u', 'user', None, _('list the author (long with -v)')),
216 ('u', 'user', None, _('list the author (long with -v)')),
217 ('f', 'file', None, _('list the filename')),
217 ('f', 'file', None, _('list the filename')),
218 ('d', 'date', None, _('list the date (short with -q)')),
218 ('d', 'date', None, _('list the date (short with -q)')),
219 ('n', 'number', None, _('list the revision number (default)')),
219 ('n', 'number', None, _('list the revision number (default)')),
220 ('c', 'changeset', None, _('list the changeset')),
220 ('c', 'changeset', None, _('list the changeset')),
221 ('l', 'line-number', None, _('show line number at the first appearance'))
221 ('l', 'line-number', None, _('show line number at the first appearance'))
222 ] + diffwsopts + walkopts,
222 ] + diffwsopts + walkopts,
223 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'))
223 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'))
224 def annotate(ui, repo, *pats, **opts):
224 def annotate(ui, repo, *pats, **opts):
225 """show changeset information by line for each file
225 """show changeset information by line for each file
226
226
227 List changes in files, showing the revision id responsible for
227 List changes in files, showing the revision id responsible for
228 each line
228 each line
229
229
230 This command is useful for discovering when a change was made and
230 This command is useful for discovering when a change was made and
231 by whom.
231 by whom.
232
232
233 Without the -a/--text option, annotate will avoid processing files
233 Without the -a/--text option, annotate will avoid processing files
234 it detects as binary. With -a, annotate will annotate the file
234 it detects as binary. With -a, annotate will annotate the file
235 anyway, although the results will probably be neither useful
235 anyway, although the results will probably be neither useful
236 nor desirable.
236 nor desirable.
237
237
238 Returns 0 on success.
238 Returns 0 on success.
239 """
239 """
240 if opts.get('follow'):
240 if opts.get('follow'):
241 # --follow is deprecated and now just an alias for -f/--file
241 # --follow is deprecated and now just an alias for -f/--file
242 # to mimic the behavior of Mercurial before version 1.5
242 # to mimic the behavior of Mercurial before version 1.5
243 opts['file'] = True
243 opts['file'] = True
244
244
245 datefunc = ui.quiet and util.shortdate or util.datestr
245 datefunc = ui.quiet and util.shortdate or util.datestr
246 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
246 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
247
247
248 if not pats:
248 if not pats:
249 raise util.Abort(_('at least one filename or pattern is required'))
249 raise util.Abort(_('at least one filename or pattern is required'))
250
250
251 hexfn = ui.debugflag and hex or short
251 hexfn = ui.debugflag and hex or short
252
252
253 opmap = [('user', ' ', lambda x: ui.shortuser(x[0].user())),
253 opmap = [('user', ' ', lambda x: ui.shortuser(x[0].user())),
254 ('number', ' ', lambda x: str(x[0].rev())),
254 ('number', ' ', lambda x: str(x[0].rev())),
255 ('changeset', ' ', lambda x: hexfn(x[0].node())),
255 ('changeset', ' ', lambda x: hexfn(x[0].node())),
256 ('date', ' ', getdate),
256 ('date', ' ', getdate),
257 ('file', ' ', lambda x: x[0].path()),
257 ('file', ' ', lambda x: x[0].path()),
258 ('line_number', ':', lambda x: str(x[1])),
258 ('line_number', ':', lambda x: str(x[1])),
259 ]
259 ]
260
260
261 if (not opts.get('user') and not opts.get('changeset')
261 if (not opts.get('user') and not opts.get('changeset')
262 and not opts.get('date') and not opts.get('file')):
262 and not opts.get('date') and not opts.get('file')):
263 opts['number'] = True
263 opts['number'] = True
264
264
265 linenumber = opts.get('line_number') is not None
265 linenumber = opts.get('line_number') is not None
266 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
266 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
267 raise util.Abort(_('at least one of -n/-c is required for -l'))
267 raise util.Abort(_('at least one of -n/-c is required for -l'))
268
268
269 funcmap = [(func, sep) for op, sep, func in opmap if opts.get(op)]
269 funcmap = [(func, sep) for op, sep, func in opmap if opts.get(op)]
270 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
270 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
271
271
272 def bad(x, y):
272 def bad(x, y):
273 raise util.Abort("%s: %s" % (x, y))
273 raise util.Abort("%s: %s" % (x, y))
274
274
275 ctx = scmutil.revsingle(repo, opts.get('rev'))
275 ctx = scmutil.revsingle(repo, opts.get('rev'))
276 m = scmutil.match(ctx, pats, opts)
276 m = scmutil.match(ctx, pats, opts)
277 m.bad = bad
277 m.bad = bad
278 follow = not opts.get('no_follow')
278 follow = not opts.get('no_follow')
279 diffopts = patch.diffopts(ui, opts, section='annotate')
279 diffopts = patch.diffopts(ui, opts, section='annotate')
280 for abs in ctx.walk(m):
280 for abs in ctx.walk(m):
281 fctx = ctx[abs]
281 fctx = ctx[abs]
282 if not opts.get('text') and util.binary(fctx.data()):
282 if not opts.get('text') and util.binary(fctx.data()):
283 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
283 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
284 continue
284 continue
285
285
286 lines = fctx.annotate(follow=follow, linenumber=linenumber,
286 lines = fctx.annotate(follow=follow, linenumber=linenumber,
287 diffopts=diffopts)
287 diffopts=diffopts)
288 pieces = []
288 pieces = []
289
289
290 for f, sep in funcmap:
290 for f, sep in funcmap:
291 l = [f(n) for n, dummy in lines]
291 l = [f(n) for n, dummy in lines]
292 if l:
292 if l:
293 sized = [(x, encoding.colwidth(x)) for x in l]
293 sized = [(x, encoding.colwidth(x)) for x in l]
294 ml = max([w for x, w in sized])
294 ml = max([w for x, w in sized])
295 pieces.append(["%s%s%s" % (sep, ' ' * (ml - w), x)
295 pieces.append(["%s%s%s" % (sep, ' ' * (ml - w), x)
296 for x, w in sized])
296 for x, w in sized])
297
297
298 if pieces:
298 if pieces:
299 for p, l in zip(zip(*pieces), lines):
299 for p, l in zip(zip(*pieces), lines):
300 ui.write("%s: %s" % ("".join(p), l[1]))
300 ui.write("%s: %s" % ("".join(p), l[1]))
301
301
302 if lines and not lines[-1][1].endswith('\n'):
302 if lines and not lines[-1][1].endswith('\n'):
303 ui.write('\n')
303 ui.write('\n')
304
304
305 @command('archive',
305 @command('archive',
306 [('', 'no-decode', None, _('do not pass files through decoders')),
306 [('', 'no-decode', None, _('do not pass files through decoders')),
307 ('p', 'prefix', '', _('directory prefix for files in archive'),
307 ('p', 'prefix', '', _('directory prefix for files in archive'),
308 _('PREFIX')),
308 _('PREFIX')),
309 ('r', 'rev', '', _('revision to distribute'), _('REV')),
309 ('r', 'rev', '', _('revision to distribute'), _('REV')),
310 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
310 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
311 ] + subrepoopts + walkopts,
311 ] + subrepoopts + walkopts,
312 _('[OPTION]... DEST'))
312 _('[OPTION]... DEST'))
313 def archive(ui, repo, dest, **opts):
313 def archive(ui, repo, dest, **opts):
314 '''create an unversioned archive of a repository revision
314 '''create an unversioned archive of a repository revision
315
315
316 By default, the revision used is the parent of the working
316 By default, the revision used is the parent of the working
317 directory; use -r/--rev to specify a different revision.
317 directory; use -r/--rev to specify a different revision.
318
318
319 The archive type is automatically detected based on file
319 The archive type is automatically detected based on file
320 extension (or override using -t/--type).
320 extension (or override using -t/--type).
321
321
322 .. container:: verbose
322 .. container:: verbose
323
323
324 Examples:
324 Examples:
325
325
326 - create a zip file containing the 1.0 release::
326 - create a zip file containing the 1.0 release::
327
327
328 hg archive -r 1.0 project-1.0.zip
328 hg archive -r 1.0 project-1.0.zip
329
329
330 - create a tarball excluding .hg files::
330 - create a tarball excluding .hg files::
331
331
332 hg archive project.tar.gz -X ".hg*"
332 hg archive project.tar.gz -X ".hg*"
333
333
334 Valid types are:
334 Valid types are:
335
335
336 :``files``: a directory full of files (default)
336 :``files``: a directory full of files (default)
337 :``tar``: tar archive, uncompressed
337 :``tar``: tar archive, uncompressed
338 :``tbz2``: tar archive, compressed using bzip2
338 :``tbz2``: tar archive, compressed using bzip2
339 :``tgz``: tar archive, compressed using gzip
339 :``tgz``: tar archive, compressed using gzip
340 :``uzip``: zip archive, uncompressed
340 :``uzip``: zip archive, uncompressed
341 :``zip``: zip archive, compressed using deflate
341 :``zip``: zip archive, compressed using deflate
342
342
343 The exact name of the destination archive or directory is given
343 The exact name of the destination archive or directory is given
344 using a format string; see :hg:`help export` for details.
344 using a format string; see :hg:`help export` for details.
345
345
346 Each member added to an archive file has a directory prefix
346 Each member added to an archive file has a directory prefix
347 prepended. Use -p/--prefix to specify a format string for the
347 prepended. Use -p/--prefix to specify a format string for the
348 prefix. The default is the basename of the archive, with suffixes
348 prefix. The default is the basename of the archive, with suffixes
349 removed.
349 removed.
350
350
351 Returns 0 on success.
351 Returns 0 on success.
352 '''
352 '''
353
353
354 ctx = scmutil.revsingle(repo, opts.get('rev'))
354 ctx = scmutil.revsingle(repo, opts.get('rev'))
355 if not ctx:
355 if not ctx:
356 raise util.Abort(_('no working directory: please specify a revision'))
356 raise util.Abort(_('no working directory: please specify a revision'))
357 node = ctx.node()
357 node = ctx.node()
358 dest = cmdutil.makefilename(repo, dest, node)
358 dest = cmdutil.makefilename(repo, dest, node)
359 if os.path.realpath(dest) == repo.root:
359 if os.path.realpath(dest) == repo.root:
360 raise util.Abort(_('repository root cannot be destination'))
360 raise util.Abort(_('repository root cannot be destination'))
361
361
362 kind = opts.get('type') or archival.guesskind(dest) or 'files'
362 kind = opts.get('type') or archival.guesskind(dest) or 'files'
363 prefix = opts.get('prefix')
363 prefix = opts.get('prefix')
364
364
365 if dest == '-':
365 if dest == '-':
366 if kind == 'files':
366 if kind == 'files':
367 raise util.Abort(_('cannot archive plain files to stdout'))
367 raise util.Abort(_('cannot archive plain files to stdout'))
368 dest = cmdutil.makefileobj(repo, dest)
368 dest = cmdutil.makefileobj(repo, dest)
369 if not prefix:
369 if not prefix:
370 prefix = os.path.basename(repo.root) + '-%h'
370 prefix = os.path.basename(repo.root) + '-%h'
371
371
372 prefix = cmdutil.makefilename(repo, prefix, node)
372 prefix = cmdutil.makefilename(repo, prefix, node)
373 matchfn = scmutil.match(ctx, [], opts)
373 matchfn = scmutil.match(ctx, [], opts)
374 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
374 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
375 matchfn, prefix, subrepos=opts.get('subrepos'))
375 matchfn, prefix, subrepos=opts.get('subrepos'))
376
376
377 @command('backout',
377 @command('backout',
378 [('', 'merge', None, _('merge with old dirstate parent after backout')),
378 [('', 'merge', None, _('merge with old dirstate parent after backout')),
379 ('', 'parent', '',
379 ('', 'parent', '',
380 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
380 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
381 ('r', 'rev', '', _('revision to backout'), _('REV')),
381 ('r', 'rev', '', _('revision to backout'), _('REV')),
382 ] + mergetoolopts + walkopts + commitopts + commitopts2,
382 ] + mergetoolopts + walkopts + commitopts + commitopts2,
383 _('[OPTION]... [-r] REV'))
383 _('[OPTION]... [-r] REV'))
384 def backout(ui, repo, node=None, rev=None, **opts):
384 def backout(ui, repo, node=None, rev=None, **opts):
385 '''reverse effect of earlier changeset
385 '''reverse effect of earlier changeset
386
386
387 Prepare a new changeset with the effect of REV undone in the
387 Prepare a new changeset with the effect of REV undone in the
388 current working directory.
388 current working directory.
389
389
390 If REV is the parent of the working directory, then this new changeset
390 If REV is the parent of the working directory, then this new changeset
391 is committed automatically. Otherwise, hg needs to merge the
391 is committed automatically. Otherwise, hg needs to merge the
392 changes and the merged result is left uncommitted.
392 changes and the merged result is left uncommitted.
393
393
394 .. note::
394 .. note::
395 backout cannot be used to fix either an unwanted or
395 backout cannot be used to fix either an unwanted or
396 incorrect merge.
396 incorrect merge.
397
397
398 .. container:: verbose
398 .. container:: verbose
399
399
400 By default, the pending changeset will have one parent,
400 By default, the pending changeset will have one parent,
401 maintaining a linear history. With --merge, the pending
401 maintaining a linear history. With --merge, the pending
402 changeset will instead have two parents: the old parent of the
402 changeset will instead have two parents: the old parent of the
403 working directory and a new child of REV that simply undoes REV.
403 working directory and a new child of REV that simply undoes REV.
404
404
405 Before version 1.7, the behavior without --merge was equivalent
405 Before version 1.7, the behavior without --merge was equivalent
406 to specifying --merge followed by :hg:`update --clean .` to
406 to specifying --merge followed by :hg:`update --clean .` to
407 cancel the merge and leave the child of REV as a head to be
407 cancel the merge and leave the child of REV as a head to be
408 merged separately.
408 merged separately.
409
409
410 See :hg:`help dates` for a list of formats valid for -d/--date.
410 See :hg:`help dates` for a list of formats valid for -d/--date.
411
411
412 Returns 0 on success.
412 Returns 0 on success.
413 '''
413 '''
414 if rev and node:
414 if rev and node:
415 raise util.Abort(_("please specify just one revision"))
415 raise util.Abort(_("please specify just one revision"))
416
416
417 if not rev:
417 if not rev:
418 rev = node
418 rev = node
419
419
420 if not rev:
420 if not rev:
421 raise util.Abort(_("please specify a revision to backout"))
421 raise util.Abort(_("please specify a revision to backout"))
422
422
423 date = opts.get('date')
423 date = opts.get('date')
424 if date:
424 if date:
425 opts['date'] = util.parsedate(date)
425 opts['date'] = util.parsedate(date)
426
426
427 cmdutil.bailifchanged(repo)
427 cmdutil.bailifchanged(repo)
428 node = scmutil.revsingle(repo, rev).node()
428 node = scmutil.revsingle(repo, rev).node()
429
429
430 op1, op2 = repo.dirstate.parents()
430 op1, op2 = repo.dirstate.parents()
431 a = repo.changelog.ancestor(op1, node)
431 a = repo.changelog.ancestor(op1, node)
432 if a != node:
432 if a != node:
433 raise util.Abort(_('cannot backout change on a different branch'))
433 raise util.Abort(_('cannot backout change on a different branch'))
434
434
435 p1, p2 = repo.changelog.parents(node)
435 p1, p2 = repo.changelog.parents(node)
436 if p1 == nullid:
436 if p1 == nullid:
437 raise util.Abort(_('cannot backout a change with no parents'))
437 raise util.Abort(_('cannot backout a change with no parents'))
438 if p2 != nullid:
438 if p2 != nullid:
439 if not opts.get('parent'):
439 if not opts.get('parent'):
440 raise util.Abort(_('cannot backout a merge changeset'))
440 raise util.Abort(_('cannot backout a merge changeset'))
441 p = repo.lookup(opts['parent'])
441 p = repo.lookup(opts['parent'])
442 if p not in (p1, p2):
442 if p not in (p1, p2):
443 raise util.Abort(_('%s is not a parent of %s') %
443 raise util.Abort(_('%s is not a parent of %s') %
444 (short(p), short(node)))
444 (short(p), short(node)))
445 parent = p
445 parent = p
446 else:
446 else:
447 if opts.get('parent'):
447 if opts.get('parent'):
448 raise util.Abort(_('cannot use --parent on non-merge changeset'))
448 raise util.Abort(_('cannot use --parent on non-merge changeset'))
449 parent = p1
449 parent = p1
450
450
451 # the backout should appear on the same branch
451 # the backout should appear on the same branch
452 wlock = repo.wlock()
452 wlock = repo.wlock()
453 try:
453 try:
454 branch = repo.dirstate.branch()
454 branch = repo.dirstate.branch()
455 hg.clean(repo, node, show_stats=False)
455 hg.clean(repo, node, show_stats=False)
456 repo.dirstate.setbranch(branch)
456 repo.dirstate.setbranch(branch)
457 revert_opts = opts.copy()
457 revert_opts = opts.copy()
458 revert_opts['date'] = None
458 revert_opts['date'] = None
459 revert_opts['all'] = True
459 revert_opts['all'] = True
460 revert_opts['rev'] = hex(parent)
460 revert_opts['rev'] = hex(parent)
461 revert_opts['no_backup'] = None
461 revert_opts['no_backup'] = None
462 revert(ui, repo, **revert_opts)
462 revert(ui, repo, **revert_opts)
463 if not opts.get('merge') and op1 != node:
463 if not opts.get('merge') and op1 != node:
464 try:
464 try:
465 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
465 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
466 return hg.update(repo, op1)
466 return hg.update(repo, op1)
467 finally:
467 finally:
468 ui.setconfig('ui', 'forcemerge', '')
468 ui.setconfig('ui', 'forcemerge', '')
469
469
470 commit_opts = opts.copy()
470 commit_opts = opts.copy()
471 commit_opts['addremove'] = False
471 commit_opts['addremove'] = False
472 if not commit_opts['message'] and not commit_opts['logfile']:
472 if not commit_opts['message'] and not commit_opts['logfile']:
473 # we don't translate commit messages
473 # we don't translate commit messages
474 commit_opts['message'] = "Backed out changeset %s" % short(node)
474 commit_opts['message'] = "Backed out changeset %s" % short(node)
475 commit_opts['force_editor'] = True
475 commit_opts['force_editor'] = True
476 commit(ui, repo, **commit_opts)
476 commit(ui, repo, **commit_opts)
477 def nice(node):
477 def nice(node):
478 return '%d:%s' % (repo.changelog.rev(node), short(node))
478 return '%d:%s' % (repo.changelog.rev(node), short(node))
479 ui.status(_('changeset %s backs out changeset %s\n') %
479 ui.status(_('changeset %s backs out changeset %s\n') %
480 (nice(repo.changelog.tip()), nice(node)))
480 (nice(repo.changelog.tip()), nice(node)))
481 if opts.get('merge') and op1 != node:
481 if opts.get('merge') and op1 != node:
482 hg.clean(repo, op1, show_stats=False)
482 hg.clean(repo, op1, show_stats=False)
483 ui.status(_('merging with changeset %s\n')
483 ui.status(_('merging with changeset %s\n')
484 % nice(repo.changelog.tip()))
484 % nice(repo.changelog.tip()))
485 try:
485 try:
486 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
486 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
487 return hg.merge(repo, hex(repo.changelog.tip()))
487 return hg.merge(repo, hex(repo.changelog.tip()))
488 finally:
488 finally:
489 ui.setconfig('ui', 'forcemerge', '')
489 ui.setconfig('ui', 'forcemerge', '')
490 finally:
490 finally:
491 wlock.release()
491 wlock.release()
492 return 0
492 return 0
493
493
494 @command('bisect',
494 @command('bisect',
495 [('r', 'reset', False, _('reset bisect state')),
495 [('r', 'reset', False, _('reset bisect state')),
496 ('g', 'good', False, _('mark changeset good')),
496 ('g', 'good', False, _('mark changeset good')),
497 ('b', 'bad', False, _('mark changeset bad')),
497 ('b', 'bad', False, _('mark changeset bad')),
498 ('s', 'skip', False, _('skip testing changeset')),
498 ('s', 'skip', False, _('skip testing changeset')),
499 ('e', 'extend', False, _('extend the bisect range')),
499 ('e', 'extend', False, _('extend the bisect range')),
500 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
500 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
501 ('U', 'noupdate', False, _('do not update to target'))],
501 ('U', 'noupdate', False, _('do not update to target'))],
502 _("[-gbsr] [-U] [-c CMD] [REV]"))
502 _("[-gbsr] [-U] [-c CMD] [REV]"))
503 def bisect(ui, repo, rev=None, extra=None, command=None,
503 def bisect(ui, repo, rev=None, extra=None, command=None,
504 reset=None, good=None, bad=None, skip=None, extend=None,
504 reset=None, good=None, bad=None, skip=None, extend=None,
505 noupdate=None):
505 noupdate=None):
506 """subdivision search of changesets
506 """subdivision search of changesets
507
507
508 This command helps to find changesets which introduce problems. To
508 This command helps to find changesets which introduce problems. To
509 use, mark the earliest changeset you know exhibits the problem as
509 use, mark the earliest changeset you know exhibits the problem as
510 bad, then mark the latest changeset which is free from the problem
510 bad, then mark the latest changeset which is free from the problem
511 as good. Bisect will update your working directory to a revision
511 as good. Bisect will update your working directory to a revision
512 for testing (unless the -U/--noupdate option is specified). Once
512 for testing (unless the -U/--noupdate option is specified). Once
513 you have performed tests, mark the working directory as good or
513 you have performed tests, mark the working directory as good or
514 bad, and bisect will either update to another candidate changeset
514 bad, and bisect will either update to another candidate changeset
515 or announce that it has found the bad revision.
515 or announce that it has found the bad revision.
516
516
517 As a shortcut, you can also use the revision argument to mark a
517 As a shortcut, you can also use the revision argument to mark a
518 revision as good or bad without checking it out first.
518 revision as good or bad without checking it out first.
519
519
520 If you supply a command, it will be used for automatic bisection.
520 If you supply a command, it will be used for automatic bisection.
521 Its exit status will be used to mark revisions as good or bad:
521 Its exit status will be used to mark revisions as good or bad:
522 status 0 means good, 125 means to skip the revision, 127
522 status 0 means good, 125 means to skip the revision, 127
523 (command not found) will abort the bisection, and any other
523 (command not found) will abort the bisection, and any other
524 non-zero exit status means the revision is bad.
524 non-zero exit status means the revision is bad.
525
525
526 .. container:: verbose
526 .. container:: verbose
527
527
528 Some examples:
528 Some examples:
529
529
530 - start a bisection with known bad revision 12, and good revision 34::
530 - start a bisection with known bad revision 12, and good revision 34::
531
531
532 hg bisect --bad 34
532 hg bisect --bad 34
533 hg bisect --good 12
533 hg bisect --good 12
534
534
535 - advance the current bisection by marking current revision as good or
535 - advance the current bisection by marking current revision as good or
536 bad::
536 bad::
537
537
538 hg bisect --good
538 hg bisect --good
539 hg bisect --bad
539 hg bisect --bad
540
540
541 - mark the current revision, or a known revision, to be skipped (eg. if
541 - mark the current revision, or a known revision, to be skipped (eg. if
542 that revision is not usable because of another issue)::
542 that revision is not usable because of another issue)::
543
543
544 hg bisect --skip
544 hg bisect --skip
545 hg bisect --skip 23
545 hg bisect --skip 23
546
546
547 - forget the current bisection::
547 - forget the current bisection::
548
548
549 hg bisect --reset
549 hg bisect --reset
550
550
551 - use 'make && make tests' to automatically find the first broken
551 - use 'make && make tests' to automatically find the first broken
552 revision::
552 revision::
553
553
554 hg bisect --reset
554 hg bisect --reset
555 hg bisect --bad 34
555 hg bisect --bad 34
556 hg bisect --good 12
556 hg bisect --good 12
557 hg bisect --command 'make && make tests'
557 hg bisect --command 'make && make tests'
558
558
559 - see all changesets whose states are already known in the current
559 - see all changesets whose states are already known in the current
560 bisection::
560 bisection::
561
561
562 hg log -r "bisect(pruned)"
562 hg log -r "bisect(pruned)"
563
563
564 - see all changesets that took part in the current bisection::
564 - see all changesets that took part in the current bisection::
565
565
566 hg log -r "bisect(range)"
566 hg log -r "bisect(range)"
567
567
568 - with the graphlog extension, you can even get a nice graph::
568 - with the graphlog extension, you can even get a nice graph::
569
569
570 hg log --graph -r "bisect(range)"
570 hg log --graph -r "bisect(range)"
571
571
572 See :hg:`help revsets` for more about the `bisect()` keyword.
572 See :hg:`help revsets` for more about the `bisect()` keyword.
573
573
574 Returns 0 on success.
574 Returns 0 on success.
575 """
575 """
576 def extendbisectrange(nodes, good):
576 def extendbisectrange(nodes, good):
577 # bisect is incomplete when it ends on a merge node and
577 # bisect is incomplete when it ends on a merge node and
578 # one of the parent was not checked.
578 # one of the parent was not checked.
579 parents = repo[nodes[0]].parents()
579 parents = repo[nodes[0]].parents()
580 if len(parents) > 1:
580 if len(parents) > 1:
581 side = good and state['bad'] or state['good']
581 side = good and state['bad'] or state['good']
582 num = len(set(i.node() for i in parents) & set(side))
582 num = len(set(i.node() for i in parents) & set(side))
583 if num == 1:
583 if num == 1:
584 return parents[0].ancestor(parents[1])
584 return parents[0].ancestor(parents[1])
585 return None
585 return None
586
586
587 def print_result(nodes, good):
587 def print_result(nodes, good):
588 displayer = cmdutil.show_changeset(ui, repo, {})
588 displayer = cmdutil.show_changeset(ui, repo, {})
589 if len(nodes) == 1:
589 if len(nodes) == 1:
590 # narrowed it down to a single revision
590 # narrowed it down to a single revision
591 if good:
591 if good:
592 ui.write(_("The first good revision is:\n"))
592 ui.write(_("The first good revision is:\n"))
593 else:
593 else:
594 ui.write(_("The first bad revision is:\n"))
594 ui.write(_("The first bad revision is:\n"))
595 displayer.show(repo[nodes[0]])
595 displayer.show(repo[nodes[0]])
596 extendnode = extendbisectrange(nodes, good)
596 extendnode = extendbisectrange(nodes, good)
597 if extendnode is not None:
597 if extendnode is not None:
598 ui.write(_('Not all ancestors of this changeset have been'
598 ui.write(_('Not all ancestors of this changeset have been'
599 ' checked.\nUse bisect --extend to continue the '
599 ' checked.\nUse bisect --extend to continue the '
600 'bisection from\nthe common ancestor, %s.\n')
600 'bisection from\nthe common ancestor, %s.\n')
601 % extendnode)
601 % extendnode)
602 else:
602 else:
603 # multiple possible revisions
603 # multiple possible revisions
604 if good:
604 if good:
605 ui.write(_("Due to skipped revisions, the first "
605 ui.write(_("Due to skipped revisions, the first "
606 "good revision could be any of:\n"))
606 "good revision could be any of:\n"))
607 else:
607 else:
608 ui.write(_("Due to skipped revisions, the first "
608 ui.write(_("Due to skipped revisions, the first "
609 "bad revision could be any of:\n"))
609 "bad revision could be any of:\n"))
610 for n in nodes:
610 for n in nodes:
611 displayer.show(repo[n])
611 displayer.show(repo[n])
612 displayer.close()
612 displayer.close()
613
613
614 def check_state(state, interactive=True):
614 def check_state(state, interactive=True):
615 if not state['good'] or not state['bad']:
615 if not state['good'] or not state['bad']:
616 if (good or bad or skip or reset) and interactive:
616 if (good or bad or skip or reset) and interactive:
617 return
617 return
618 if not state['good']:
618 if not state['good']:
619 raise util.Abort(_('cannot bisect (no known good revisions)'))
619 raise util.Abort(_('cannot bisect (no known good revisions)'))
620 else:
620 else:
621 raise util.Abort(_('cannot bisect (no known bad revisions)'))
621 raise util.Abort(_('cannot bisect (no known bad revisions)'))
622 return True
622 return True
623
623
624 # backward compatibility
624 # backward compatibility
625 if rev in "good bad reset init".split():
625 if rev in "good bad reset init".split():
626 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
626 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
627 cmd, rev, extra = rev, extra, None
627 cmd, rev, extra = rev, extra, None
628 if cmd == "good":
628 if cmd == "good":
629 good = True
629 good = True
630 elif cmd == "bad":
630 elif cmd == "bad":
631 bad = True
631 bad = True
632 else:
632 else:
633 reset = True
633 reset = True
634 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
634 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
635 raise util.Abort(_('incompatible arguments'))
635 raise util.Abort(_('incompatible arguments'))
636
636
637 if reset:
637 if reset:
638 p = repo.join("bisect.state")
638 p = repo.join("bisect.state")
639 if os.path.exists(p):
639 if os.path.exists(p):
640 os.unlink(p)
640 os.unlink(p)
641 return
641 return
642
642
643 state = hbisect.load_state(repo)
643 state = hbisect.load_state(repo)
644
644
645 if command:
645 if command:
646 changesets = 1
646 changesets = 1
647 try:
647 try:
648 while changesets:
648 while changesets:
649 # update state
649 # update state
650 hbisect.save_state(repo, state)
650 status = util.system(command, out=ui.fout)
651 status = util.system(command, out=ui.fout)
651 if status == 125:
652 if status == 125:
652 transition = "skip"
653 transition = "skip"
653 elif status == 0:
654 elif status == 0:
654 transition = "good"
655 transition = "good"
655 # status < 0 means process was killed
656 # status < 0 means process was killed
656 elif status == 127:
657 elif status == 127:
657 raise util.Abort(_("failed to execute %s") % command)
658 raise util.Abort(_("failed to execute %s") % command)
658 elif status < 0:
659 elif status < 0:
659 raise util.Abort(_("%s killed") % command)
660 raise util.Abort(_("%s killed") % command)
660 else:
661 else:
661 transition = "bad"
662 transition = "bad"
662 ctx = scmutil.revsingle(repo, rev)
663 ctx = scmutil.revsingle(repo, rev)
663 rev = None # clear for future iterations
664 rev = None # clear for future iterations
664 state[transition].append(ctx.node())
665 state[transition].append(ctx.node())
665 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
666 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
666 check_state(state, interactive=False)
667 check_state(state, interactive=False)
667 # bisect
668 # bisect
668 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
669 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
669 # update to next check
670 # update to next check
670 cmdutil.bailifchanged(repo)
671 cmdutil.bailifchanged(repo)
671 hg.clean(repo, nodes[0], show_stats=False)
672 hg.clean(repo, nodes[0], show_stats=False)
672 finally:
673 finally:
673 hbisect.save_state(repo, state)
674 hbisect.save_state(repo, state)
674 print_result(nodes, good)
675 print_result(nodes, good)
675 return
676 return
676
677
677 # update state
678 # update state
678
679
679 if rev:
680 if rev:
680 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
681 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
681 else:
682 else:
682 nodes = [repo.lookup('.')]
683 nodes = [repo.lookup('.')]
683
684
684 if good or bad or skip:
685 if good or bad or skip:
685 if good:
686 if good:
686 state['good'] += nodes
687 state['good'] += nodes
687 elif bad:
688 elif bad:
688 state['bad'] += nodes
689 state['bad'] += nodes
689 elif skip:
690 elif skip:
690 state['skip'] += nodes
691 state['skip'] += nodes
691 hbisect.save_state(repo, state)
692 hbisect.save_state(repo, state)
692
693
693 if not check_state(state):
694 if not check_state(state):
694 return
695 return
695
696
696 # actually bisect
697 # actually bisect
697 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
698 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
698 if extend:
699 if extend:
699 if not changesets:
700 if not changesets:
700 extendnode = extendbisectrange(nodes, good)
701 extendnode = extendbisectrange(nodes, good)
701 if extendnode is not None:
702 if extendnode is not None:
702 ui.write(_("Extending search to changeset %d:%s\n"
703 ui.write(_("Extending search to changeset %d:%s\n"
703 % (extendnode.rev(), extendnode)))
704 % (extendnode.rev(), extendnode)))
704 if noupdate:
705 if noupdate:
705 return
706 return
706 cmdutil.bailifchanged(repo)
707 cmdutil.bailifchanged(repo)
707 return hg.clean(repo, extendnode.node())
708 return hg.clean(repo, extendnode.node())
708 raise util.Abort(_("nothing to extend"))
709 raise util.Abort(_("nothing to extend"))
709
710
710 if changesets == 0:
711 if changesets == 0:
711 print_result(nodes, good)
712 print_result(nodes, good)
712 else:
713 else:
713 assert len(nodes) == 1 # only a single node can be tested next
714 assert len(nodes) == 1 # only a single node can be tested next
714 node = nodes[0]
715 node = nodes[0]
715 # compute the approximate number of remaining tests
716 # compute the approximate number of remaining tests
716 tests, size = 0, 2
717 tests, size = 0, 2
717 while size <= changesets:
718 while size <= changesets:
718 tests, size = tests + 1, size * 2
719 tests, size = tests + 1, size * 2
719 rev = repo.changelog.rev(node)
720 rev = repo.changelog.rev(node)
720 ui.write(_("Testing changeset %d:%s "
721 ui.write(_("Testing changeset %d:%s "
721 "(%d changesets remaining, ~%d tests)\n")
722 "(%d changesets remaining, ~%d tests)\n")
722 % (rev, short(node), changesets, tests))
723 % (rev, short(node), changesets, tests))
723 if not noupdate:
724 if not noupdate:
724 cmdutil.bailifchanged(repo)
725 cmdutil.bailifchanged(repo)
725 return hg.clean(repo, node)
726 return hg.clean(repo, node)
726
727
727 @command('bookmarks',
728 @command('bookmarks',
728 [('f', 'force', False, _('force')),
729 [('f', 'force', False, _('force')),
729 ('r', 'rev', '', _('revision'), _('REV')),
730 ('r', 'rev', '', _('revision'), _('REV')),
730 ('d', 'delete', False, _('delete a given bookmark')),
731 ('d', 'delete', False, _('delete a given bookmark')),
731 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
732 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
732 ('i', 'inactive', False, _('mark a bookmark inactive'))],
733 ('i', 'inactive', False, _('mark a bookmark inactive'))],
733 _('hg bookmarks [-f] [-d] [-i] [-m NAME] [-r REV] [NAME]'))
734 _('hg bookmarks [-f] [-d] [-i] [-m NAME] [-r REV] [NAME]'))
734 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False,
735 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False,
735 rename=None, inactive=False):
736 rename=None, inactive=False):
736 '''track a line of development with movable markers
737 '''track a line of development with movable markers
737
738
738 Bookmarks are pointers to certain commits that move when committing.
739 Bookmarks are pointers to certain commits that move when committing.
739 Bookmarks are local. They can be renamed, copied and deleted. It is
740 Bookmarks are local. They can be renamed, copied and deleted. It is
740 possible to use :hg:`merge NAME` to merge from a given bookmark, and
741 possible to use :hg:`merge NAME` to merge from a given bookmark, and
741 :hg:`update NAME` to update to a given bookmark.
742 :hg:`update NAME` to update to a given bookmark.
742
743
743 You can use :hg:`bookmark NAME` to set a bookmark on the working
744 You can use :hg:`bookmark NAME` to set a bookmark on the working
744 directory's parent revision with the given name. If you specify
745 directory's parent revision with the given name. If you specify
745 a revision using -r REV (where REV may be an existing bookmark),
746 a revision using -r REV (where REV may be an existing bookmark),
746 the bookmark is assigned to that revision.
747 the bookmark is assigned to that revision.
747
748
748 Bookmarks can be pushed and pulled between repositories (see :hg:`help
749 Bookmarks can be pushed and pulled between repositories (see :hg:`help
749 push` and :hg:`help pull`). This requires both the local and remote
750 push` and :hg:`help pull`). This requires both the local and remote
750 repositories to support bookmarks. For versions prior to 1.8, this means
751 repositories to support bookmarks. For versions prior to 1.8, this means
751 the bookmarks extension must be enabled.
752 the bookmarks extension must be enabled.
752
753
753 With -i/--inactive, the new bookmark will not be made the active
754 With -i/--inactive, the new bookmark will not be made the active
754 bookmark. If -r/--rev is given, the new bookmark will not be made
755 bookmark. If -r/--rev is given, the new bookmark will not be made
755 active even if -i/--inactive is not given. If no NAME is given, the
756 active even if -i/--inactive is not given. If no NAME is given, the
756 current active bookmark will be marked inactive.
757 current active bookmark will be marked inactive.
757 '''
758 '''
758 hexfn = ui.debugflag and hex or short
759 hexfn = ui.debugflag and hex or short
759 marks = repo._bookmarks
760 marks = repo._bookmarks
760 cur = repo.changectx('.').node()
761 cur = repo.changectx('.').node()
761
762
762 if delete:
763 if delete:
763 if mark is None:
764 if mark is None:
764 raise util.Abort(_("bookmark name required"))
765 raise util.Abort(_("bookmark name required"))
765 if mark not in marks:
766 if mark not in marks:
766 raise util.Abort(_("bookmark '%s' does not exist") % mark)
767 raise util.Abort(_("bookmark '%s' does not exist") % mark)
767 if mark == repo._bookmarkcurrent:
768 if mark == repo._bookmarkcurrent:
768 bookmarks.setcurrent(repo, None)
769 bookmarks.setcurrent(repo, None)
769 del marks[mark]
770 del marks[mark]
770 bookmarks.write(repo)
771 bookmarks.write(repo)
771 return
772 return
772
773
773 if rename:
774 if rename:
774 if rename not in marks:
775 if rename not in marks:
775 raise util.Abort(_("bookmark '%s' does not exist") % rename)
776 raise util.Abort(_("bookmark '%s' does not exist") % rename)
776 if mark in marks and not force:
777 if mark in marks and not force:
777 raise util.Abort(_("bookmark '%s' already exists "
778 raise util.Abort(_("bookmark '%s' already exists "
778 "(use -f to force)") % mark)
779 "(use -f to force)") % mark)
779 if mark is None:
780 if mark is None:
780 raise util.Abort(_("new bookmark name required"))
781 raise util.Abort(_("new bookmark name required"))
781 marks[mark] = marks[rename]
782 marks[mark] = marks[rename]
782 if repo._bookmarkcurrent == rename and not inactive:
783 if repo._bookmarkcurrent == rename and not inactive:
783 bookmarks.setcurrent(repo, mark)
784 bookmarks.setcurrent(repo, mark)
784 del marks[rename]
785 del marks[rename]
785 bookmarks.write(repo)
786 bookmarks.write(repo)
786 return
787 return
787
788
788 if mark is not None:
789 if mark is not None:
789 if "\n" in mark:
790 if "\n" in mark:
790 raise util.Abort(_("bookmark name cannot contain newlines"))
791 raise util.Abort(_("bookmark name cannot contain newlines"))
791 mark = mark.strip()
792 mark = mark.strip()
792 if not mark:
793 if not mark:
793 raise util.Abort(_("bookmark names cannot consist entirely of "
794 raise util.Abort(_("bookmark names cannot consist entirely of "
794 "whitespace"))
795 "whitespace"))
795 if inactive and mark == repo._bookmarkcurrent:
796 if inactive and mark == repo._bookmarkcurrent:
796 bookmarks.setcurrent(repo, None)
797 bookmarks.setcurrent(repo, None)
797 return
798 return
798 if mark in marks and not force:
799 if mark in marks and not force:
799 raise util.Abort(_("bookmark '%s' already exists "
800 raise util.Abort(_("bookmark '%s' already exists "
800 "(use -f to force)") % mark)
801 "(use -f to force)") % mark)
801 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
802 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
802 and not force):
803 and not force):
803 raise util.Abort(
804 raise util.Abort(
804 _("a bookmark cannot have the name of an existing branch"))
805 _("a bookmark cannot have the name of an existing branch"))
805 if rev:
806 if rev:
806 marks[mark] = repo.lookup(rev)
807 marks[mark] = repo.lookup(rev)
807 else:
808 else:
808 marks[mark] = cur
809 marks[mark] = cur
809 if not inactive and cur == marks[mark]:
810 if not inactive and cur == marks[mark]:
810 bookmarks.setcurrent(repo, mark)
811 bookmarks.setcurrent(repo, mark)
811 bookmarks.write(repo)
812 bookmarks.write(repo)
812 return
813 return
813
814
814 if mark is None:
815 if mark is None:
815 if rev:
816 if rev:
816 raise util.Abort(_("bookmark name required"))
817 raise util.Abort(_("bookmark name required"))
817 if len(marks) == 0:
818 if len(marks) == 0:
818 ui.status(_("no bookmarks set\n"))
819 ui.status(_("no bookmarks set\n"))
819 else:
820 else:
820 for bmark, n in sorted(marks.iteritems()):
821 for bmark, n in sorted(marks.iteritems()):
821 current = repo._bookmarkcurrent
822 current = repo._bookmarkcurrent
822 if bmark == current and n == cur:
823 if bmark == current and n == cur:
823 prefix, label = '*', 'bookmarks.current'
824 prefix, label = '*', 'bookmarks.current'
824 else:
825 else:
825 prefix, label = ' ', ''
826 prefix, label = ' ', ''
826
827
827 if ui.quiet:
828 if ui.quiet:
828 ui.write("%s\n" % bmark, label=label)
829 ui.write("%s\n" % bmark, label=label)
829 else:
830 else:
830 ui.write(" %s %-25s %d:%s\n" % (
831 ui.write(" %s %-25s %d:%s\n" % (
831 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
832 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
832 label=label)
833 label=label)
833 return
834 return
834
835
835 @command('branch',
836 @command('branch',
836 [('f', 'force', None,
837 [('f', 'force', None,
837 _('set branch name even if it shadows an existing branch')),
838 _('set branch name even if it shadows an existing branch')),
838 ('C', 'clean', None, _('reset branch name to parent branch name'))],
839 ('C', 'clean', None, _('reset branch name to parent branch name'))],
839 _('[-fC] [NAME]'))
840 _('[-fC] [NAME]'))
840 def branch(ui, repo, label=None, **opts):
841 def branch(ui, repo, label=None, **opts):
841 """set or show the current branch name
842 """set or show the current branch name
842
843
843 .. note::
844 .. note::
844 Branch names are permanent and global. Use :hg:`bookmark` to create a
845 Branch names are permanent and global. Use :hg:`bookmark` to create a
845 light-weight bookmark instead. See :hg:`help glossary` for more
846 light-weight bookmark instead. See :hg:`help glossary` for more
846 information about named branches and bookmarks.
847 information about named branches and bookmarks.
847
848
848 With no argument, show the current branch name. With one argument,
849 With no argument, show the current branch name. With one argument,
849 set the working directory branch name (the branch will not exist
850 set the working directory branch name (the branch will not exist
850 in the repository until the next commit). Standard practice
851 in the repository until the next commit). Standard practice
851 recommends that primary development take place on the 'default'
852 recommends that primary development take place on the 'default'
852 branch.
853 branch.
853
854
854 Unless -f/--force is specified, branch will not let you set a
855 Unless -f/--force is specified, branch will not let you set a
855 branch name that already exists, even if it's inactive.
856 branch name that already exists, even if it's inactive.
856
857
857 Use -C/--clean to reset the working directory branch to that of
858 Use -C/--clean to reset the working directory branch to that of
858 the parent of the working directory, negating a previous branch
859 the parent of the working directory, negating a previous branch
859 change.
860 change.
860
861
861 Use the command :hg:`update` to switch to an existing branch. Use
862 Use the command :hg:`update` to switch to an existing branch. Use
862 :hg:`commit --close-branch` to mark this branch as closed.
863 :hg:`commit --close-branch` to mark this branch as closed.
863
864
864 Returns 0 on success.
865 Returns 0 on success.
865 """
866 """
866 if not opts.get('clean') and not label:
867 if not opts.get('clean') and not label:
867 ui.write("%s\n" % repo.dirstate.branch())
868 ui.write("%s\n" % repo.dirstate.branch())
868 return
869 return
869
870
870 wlock = repo.wlock()
871 wlock = repo.wlock()
871 try:
872 try:
872 if opts.get('clean'):
873 if opts.get('clean'):
873 label = repo[None].p1().branch()
874 label = repo[None].p1().branch()
874 repo.dirstate.setbranch(label)
875 repo.dirstate.setbranch(label)
875 ui.status(_('reset working directory to branch %s\n') % label)
876 ui.status(_('reset working directory to branch %s\n') % label)
876 elif label:
877 elif label:
877 if not opts.get('force') and label in repo.branchtags():
878 if not opts.get('force') and label in repo.branchtags():
878 if label not in [p.branch() for p in repo.parents()]:
879 if label not in [p.branch() for p in repo.parents()]:
879 raise util.Abort(_('a branch of the same name already'
880 raise util.Abort(_('a branch of the same name already'
880 ' exists'),
881 ' exists'),
881 # i18n: "it" refers to an existing branch
882 # i18n: "it" refers to an existing branch
882 hint=_("use 'hg update' to switch to it"))
883 hint=_("use 'hg update' to switch to it"))
883 repo.dirstate.setbranch(label)
884 repo.dirstate.setbranch(label)
884 ui.status(_('marked working directory as branch %s\n') % label)
885 ui.status(_('marked working directory as branch %s\n') % label)
885 ui.status(_('(branches are permanent and global, '
886 ui.status(_('(branches are permanent and global, '
886 'did you want a bookmark?)\n'))
887 'did you want a bookmark?)\n'))
887 finally:
888 finally:
888 wlock.release()
889 wlock.release()
889
890
890 @command('branches',
891 @command('branches',
891 [('a', 'active', False, _('show only branches that have unmerged heads')),
892 [('a', 'active', False, _('show only branches that have unmerged heads')),
892 ('c', 'closed', False, _('show normal and closed branches'))],
893 ('c', 'closed', False, _('show normal and closed branches'))],
893 _('[-ac]'))
894 _('[-ac]'))
894 def branches(ui, repo, active=False, closed=False):
895 def branches(ui, repo, active=False, closed=False):
895 """list repository named branches
896 """list repository named branches
896
897
897 List the repository's named branches, indicating which ones are
898 List the repository's named branches, indicating which ones are
898 inactive. If -c/--closed is specified, also list branches which have
899 inactive. If -c/--closed is specified, also list branches which have
899 been marked closed (see :hg:`commit --close-branch`).
900 been marked closed (see :hg:`commit --close-branch`).
900
901
901 If -a/--active is specified, only show active branches. A branch
902 If -a/--active is specified, only show active branches. A branch
902 is considered active if it contains repository heads.
903 is considered active if it contains repository heads.
903
904
904 Use the command :hg:`update` to switch to an existing branch.
905 Use the command :hg:`update` to switch to an existing branch.
905
906
906 Returns 0.
907 Returns 0.
907 """
908 """
908
909
909 hexfunc = ui.debugflag and hex or short
910 hexfunc = ui.debugflag and hex or short
910 activebranches = [repo[n].branch() for n in repo.heads()]
911 activebranches = [repo[n].branch() for n in repo.heads()]
911 def testactive(tag, node):
912 def testactive(tag, node):
912 realhead = tag in activebranches
913 realhead = tag in activebranches
913 open = node in repo.branchheads(tag, closed=False)
914 open = node in repo.branchheads(tag, closed=False)
914 return realhead and open
915 return realhead and open
915 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
916 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
916 for tag, node in repo.branchtags().items()],
917 for tag, node in repo.branchtags().items()],
917 reverse=True)
918 reverse=True)
918
919
919 for isactive, node, tag in branches:
920 for isactive, node, tag in branches:
920 if (not active) or isactive:
921 if (not active) or isactive:
921 if ui.quiet:
922 if ui.quiet:
922 ui.write("%s\n" % tag)
923 ui.write("%s\n" % tag)
923 else:
924 else:
924 hn = repo.lookup(node)
925 hn = repo.lookup(node)
925 if isactive:
926 if isactive:
926 label = 'branches.active'
927 label = 'branches.active'
927 notice = ''
928 notice = ''
928 elif hn not in repo.branchheads(tag, closed=False):
929 elif hn not in repo.branchheads(tag, closed=False):
929 if not closed:
930 if not closed:
930 continue
931 continue
931 label = 'branches.closed'
932 label = 'branches.closed'
932 notice = _(' (closed)')
933 notice = _(' (closed)')
933 else:
934 else:
934 label = 'branches.inactive'
935 label = 'branches.inactive'
935 notice = _(' (inactive)')
936 notice = _(' (inactive)')
936 if tag == repo.dirstate.branch():
937 if tag == repo.dirstate.branch():
937 label = 'branches.current'
938 label = 'branches.current'
938 rev = str(node).rjust(31 - encoding.colwidth(tag))
939 rev = str(node).rjust(31 - encoding.colwidth(tag))
939 rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
940 rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
940 tag = ui.label(tag, label)
941 tag = ui.label(tag, label)
941 ui.write("%s %s%s\n" % (tag, rev, notice))
942 ui.write("%s %s%s\n" % (tag, rev, notice))
942
943
943 @command('bundle',
944 @command('bundle',
944 [('f', 'force', None, _('run even when the destination is unrelated')),
945 [('f', 'force', None, _('run even when the destination is unrelated')),
945 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
946 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
946 _('REV')),
947 _('REV')),
947 ('b', 'branch', [], _('a specific branch you would like to bundle'),
948 ('b', 'branch', [], _('a specific branch you would like to bundle'),
948 _('BRANCH')),
949 _('BRANCH')),
949 ('', 'base', [],
950 ('', 'base', [],
950 _('a base changeset assumed to be available at the destination'),
951 _('a base changeset assumed to be available at the destination'),
951 _('REV')),
952 _('REV')),
952 ('a', 'all', None, _('bundle all changesets in the repository')),
953 ('a', 'all', None, _('bundle all changesets in the repository')),
953 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
954 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
954 ] + remoteopts,
955 ] + remoteopts,
955 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
956 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
956 def bundle(ui, repo, fname, dest=None, **opts):
957 def bundle(ui, repo, fname, dest=None, **opts):
957 """create a changegroup file
958 """create a changegroup file
958
959
959 Generate a compressed changegroup file collecting changesets not
960 Generate a compressed changegroup file collecting changesets not
960 known to be in another repository.
961 known to be in another repository.
961
962
962 If you omit the destination repository, then hg assumes the
963 If you omit the destination repository, then hg assumes the
963 destination will have all the nodes you specify with --base
964 destination will have all the nodes you specify with --base
964 parameters. To create a bundle containing all changesets, use
965 parameters. To create a bundle containing all changesets, use
965 -a/--all (or --base null).
966 -a/--all (or --base null).
966
967
967 You can change compression method with the -t/--type option.
968 You can change compression method with the -t/--type option.
968 The available compression methods are: none, bzip2, and
969 The available compression methods are: none, bzip2, and
969 gzip (by default, bundles are compressed using bzip2).
970 gzip (by default, bundles are compressed using bzip2).
970
971
971 The bundle file can then be transferred using conventional means
972 The bundle file can then be transferred using conventional means
972 and applied to another repository with the unbundle or pull
973 and applied to another repository with the unbundle or pull
973 command. This is useful when direct push and pull are not
974 command. This is useful when direct push and pull are not
974 available or when exporting an entire repository is undesirable.
975 available or when exporting an entire repository is undesirable.
975
976
976 Applying bundles preserves all changeset contents including
977 Applying bundles preserves all changeset contents including
977 permissions, copy/rename information, and revision history.
978 permissions, copy/rename information, and revision history.
978
979
979 Returns 0 on success, 1 if no changes found.
980 Returns 0 on success, 1 if no changes found.
980 """
981 """
981 revs = None
982 revs = None
982 if 'rev' in opts:
983 if 'rev' in opts:
983 revs = scmutil.revrange(repo, opts['rev'])
984 revs = scmutil.revrange(repo, opts['rev'])
984
985
985 bundletype = opts.get('type', 'bzip2').lower()
986 bundletype = opts.get('type', 'bzip2').lower()
986 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
987 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
987 bundletype = btypes.get(bundletype)
988 bundletype = btypes.get(bundletype)
988 if bundletype not in changegroup.bundletypes:
989 if bundletype not in changegroup.bundletypes:
989 raise util.Abort(_('unknown bundle type specified with --type'))
990 raise util.Abort(_('unknown bundle type specified with --type'))
990
991
991 if opts.get('all'):
992 if opts.get('all'):
992 base = ['null']
993 base = ['null']
993 else:
994 else:
994 base = scmutil.revrange(repo, opts.get('base'))
995 base = scmutil.revrange(repo, opts.get('base'))
995 if base:
996 if base:
996 if dest:
997 if dest:
997 raise util.Abort(_("--base is incompatible with specifying "
998 raise util.Abort(_("--base is incompatible with specifying "
998 "a destination"))
999 "a destination"))
999 common = [repo.lookup(rev) for rev in base]
1000 common = [repo.lookup(rev) for rev in base]
1000 heads = revs and map(repo.lookup, revs) or revs
1001 heads = revs and map(repo.lookup, revs) or revs
1001 cg = repo.getbundle('bundle', heads=heads, common=common)
1002 cg = repo.getbundle('bundle', heads=heads, common=common)
1002 outgoing = None
1003 outgoing = None
1003 else:
1004 else:
1004 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1005 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1005 dest, branches = hg.parseurl(dest, opts.get('branch'))
1006 dest, branches = hg.parseurl(dest, opts.get('branch'))
1006 other = hg.peer(repo, opts, dest)
1007 other = hg.peer(repo, opts, dest)
1007 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
1008 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
1008 heads = revs and map(repo.lookup, revs) or revs
1009 heads = revs and map(repo.lookup, revs) or revs
1009 outgoing = discovery.findcommonoutgoing(repo, other,
1010 outgoing = discovery.findcommonoutgoing(repo, other,
1010 onlyheads=heads,
1011 onlyheads=heads,
1011 force=opts.get('force'))
1012 force=opts.get('force'))
1012 cg = repo.getlocalbundle('bundle', outgoing)
1013 cg = repo.getlocalbundle('bundle', outgoing)
1013 if not cg:
1014 if not cg:
1014 scmutil.nochangesfound(ui, outgoing and outgoing.excluded)
1015 scmutil.nochangesfound(ui, outgoing and outgoing.excluded)
1015 return 1
1016 return 1
1016
1017
1017 changegroup.writebundle(cg, fname, bundletype)
1018 changegroup.writebundle(cg, fname, bundletype)
1018
1019
1019 @command('cat',
1020 @command('cat',
1020 [('o', 'output', '',
1021 [('o', 'output', '',
1021 _('print output to file with formatted name'), _('FORMAT')),
1022 _('print output to file with formatted name'), _('FORMAT')),
1022 ('r', 'rev', '', _('print the given revision'), _('REV')),
1023 ('r', 'rev', '', _('print the given revision'), _('REV')),
1023 ('', 'decode', None, _('apply any matching decode filter')),
1024 ('', 'decode', None, _('apply any matching decode filter')),
1024 ] + walkopts,
1025 ] + walkopts,
1025 _('[OPTION]... FILE...'))
1026 _('[OPTION]... FILE...'))
1026 def cat(ui, repo, file1, *pats, **opts):
1027 def cat(ui, repo, file1, *pats, **opts):
1027 """output the current or given revision of files
1028 """output the current or given revision of files
1028
1029
1029 Print the specified files as they were at the given revision. If
1030 Print the specified files as they were at the given revision. If
1030 no revision is given, the parent of the working directory is used,
1031 no revision is given, the parent of the working directory is used,
1031 or tip if no revision is checked out.
1032 or tip if no revision is checked out.
1032
1033
1033 Output may be to a file, in which case the name of the file is
1034 Output may be to a file, in which case the name of the file is
1034 given using a format string. The formatting rules are the same as
1035 given using a format string. The formatting rules are the same as
1035 for the export command, with the following additions:
1036 for the export command, with the following additions:
1036
1037
1037 :``%s``: basename of file being printed
1038 :``%s``: basename of file being printed
1038 :``%d``: dirname of file being printed, or '.' if in repository root
1039 :``%d``: dirname of file being printed, or '.' if in repository root
1039 :``%p``: root-relative path name of file being printed
1040 :``%p``: root-relative path name of file being printed
1040
1041
1041 Returns 0 on success.
1042 Returns 0 on success.
1042 """
1043 """
1043 ctx = scmutil.revsingle(repo, opts.get('rev'))
1044 ctx = scmutil.revsingle(repo, opts.get('rev'))
1044 err = 1
1045 err = 1
1045 m = scmutil.match(ctx, (file1,) + pats, opts)
1046 m = scmutil.match(ctx, (file1,) + pats, opts)
1046 for abs in ctx.walk(m):
1047 for abs in ctx.walk(m):
1047 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1048 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1048 pathname=abs)
1049 pathname=abs)
1049 data = ctx[abs].data()
1050 data = ctx[abs].data()
1050 if opts.get('decode'):
1051 if opts.get('decode'):
1051 data = repo.wwritedata(abs, data)
1052 data = repo.wwritedata(abs, data)
1052 fp.write(data)
1053 fp.write(data)
1053 fp.close()
1054 fp.close()
1054 err = 0
1055 err = 0
1055 return err
1056 return err
1056
1057
1057 @command('^clone',
1058 @command('^clone',
1058 [('U', 'noupdate', None,
1059 [('U', 'noupdate', None,
1059 _('the clone will include an empty working copy (only a repository)')),
1060 _('the clone will include an empty working copy (only a repository)')),
1060 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
1061 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
1061 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1062 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1062 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1063 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1063 ('', 'pull', None, _('use pull protocol to copy metadata')),
1064 ('', 'pull', None, _('use pull protocol to copy metadata')),
1064 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1065 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1065 ] + remoteopts,
1066 ] + remoteopts,
1066 _('[OPTION]... SOURCE [DEST]'))
1067 _('[OPTION]... SOURCE [DEST]'))
1067 def clone(ui, source, dest=None, **opts):
1068 def clone(ui, source, dest=None, **opts):
1068 """make a copy of an existing repository
1069 """make a copy of an existing repository
1069
1070
1070 Create a copy of an existing repository in a new directory.
1071 Create a copy of an existing repository in a new directory.
1071
1072
1072 If no destination directory name is specified, it defaults to the
1073 If no destination directory name is specified, it defaults to the
1073 basename of the source.
1074 basename of the source.
1074
1075
1075 The location of the source is added to the new repository's
1076 The location of the source is added to the new repository's
1076 ``.hg/hgrc`` file, as the default to be used for future pulls.
1077 ``.hg/hgrc`` file, as the default to be used for future pulls.
1077
1078
1078 Only local paths and ``ssh://`` URLs are supported as
1079 Only local paths and ``ssh://`` URLs are supported as
1079 destinations. For ``ssh://`` destinations, no working directory or
1080 destinations. For ``ssh://`` destinations, no working directory or
1080 ``.hg/hgrc`` will be created on the remote side.
1081 ``.hg/hgrc`` will be created on the remote side.
1081
1082
1082 To pull only a subset of changesets, specify one or more revisions
1083 To pull only a subset of changesets, specify one or more revisions
1083 identifiers with -r/--rev or branches with -b/--branch. The
1084 identifiers with -r/--rev or branches with -b/--branch. The
1084 resulting clone will contain only the specified changesets and
1085 resulting clone will contain only the specified changesets and
1085 their ancestors. These options (or 'clone src#rev dest') imply
1086 their ancestors. These options (or 'clone src#rev dest') imply
1086 --pull, even for local source repositories. Note that specifying a
1087 --pull, even for local source repositories. Note that specifying a
1087 tag will include the tagged changeset but not the changeset
1088 tag will include the tagged changeset but not the changeset
1088 containing the tag.
1089 containing the tag.
1089
1090
1090 To check out a particular version, use -u/--update, or
1091 To check out a particular version, use -u/--update, or
1091 -U/--noupdate to create a clone with no working directory.
1092 -U/--noupdate to create a clone with no working directory.
1092
1093
1093 .. container:: verbose
1094 .. container:: verbose
1094
1095
1095 For efficiency, hardlinks are used for cloning whenever the
1096 For efficiency, hardlinks are used for cloning whenever the
1096 source and destination are on the same filesystem (note this
1097 source and destination are on the same filesystem (note this
1097 applies only to the repository data, not to the working
1098 applies only to the repository data, not to the working
1098 directory). Some filesystems, such as AFS, implement hardlinking
1099 directory). Some filesystems, such as AFS, implement hardlinking
1099 incorrectly, but do not report errors. In these cases, use the
1100 incorrectly, but do not report errors. In these cases, use the
1100 --pull option to avoid hardlinking.
1101 --pull option to avoid hardlinking.
1101
1102
1102 In some cases, you can clone repositories and the working
1103 In some cases, you can clone repositories and the working
1103 directory using full hardlinks with ::
1104 directory using full hardlinks with ::
1104
1105
1105 $ cp -al REPO REPOCLONE
1106 $ cp -al REPO REPOCLONE
1106
1107
1107 This is the fastest way to clone, but it is not always safe. The
1108 This is the fastest way to clone, but it is not always safe. The
1108 operation is not atomic (making sure REPO is not modified during
1109 operation is not atomic (making sure REPO is not modified during
1109 the operation is up to you) and you have to make sure your
1110 the operation is up to you) and you have to make sure your
1110 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1111 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1111 so). Also, this is not compatible with certain extensions that
1112 so). Also, this is not compatible with certain extensions that
1112 place their metadata under the .hg directory, such as mq.
1113 place their metadata under the .hg directory, such as mq.
1113
1114
1114 Mercurial will update the working directory to the first applicable
1115 Mercurial will update the working directory to the first applicable
1115 revision from this list:
1116 revision from this list:
1116
1117
1117 a) null if -U or the source repository has no changesets
1118 a) null if -U or the source repository has no changesets
1118 b) if -u . and the source repository is local, the first parent of
1119 b) if -u . and the source repository is local, the first parent of
1119 the source repository's working directory
1120 the source repository's working directory
1120 c) the changeset specified with -u (if a branch name, this means the
1121 c) the changeset specified with -u (if a branch name, this means the
1121 latest head of that branch)
1122 latest head of that branch)
1122 d) the changeset specified with -r
1123 d) the changeset specified with -r
1123 e) the tipmost head specified with -b
1124 e) the tipmost head specified with -b
1124 f) the tipmost head specified with the url#branch source syntax
1125 f) the tipmost head specified with the url#branch source syntax
1125 g) the tipmost head of the default branch
1126 g) the tipmost head of the default branch
1126 h) tip
1127 h) tip
1127
1128
1128 Examples:
1129 Examples:
1129
1130
1130 - clone a remote repository to a new directory named hg/::
1131 - clone a remote repository to a new directory named hg/::
1131
1132
1132 hg clone http://selenic.com/hg
1133 hg clone http://selenic.com/hg
1133
1134
1134 - create a lightweight local clone::
1135 - create a lightweight local clone::
1135
1136
1136 hg clone project/ project-feature/
1137 hg clone project/ project-feature/
1137
1138
1138 - clone from an absolute path on an ssh server (note double-slash)::
1139 - clone from an absolute path on an ssh server (note double-slash)::
1139
1140
1140 hg clone ssh://user@server//home/projects/alpha/
1141 hg clone ssh://user@server//home/projects/alpha/
1141
1142
1142 - do a high-speed clone over a LAN while checking out a
1143 - do a high-speed clone over a LAN while checking out a
1143 specified version::
1144 specified version::
1144
1145
1145 hg clone --uncompressed http://server/repo -u 1.5
1146 hg clone --uncompressed http://server/repo -u 1.5
1146
1147
1147 - create a repository without changesets after a particular revision::
1148 - create a repository without changesets after a particular revision::
1148
1149
1149 hg clone -r 04e544 experimental/ good/
1150 hg clone -r 04e544 experimental/ good/
1150
1151
1151 - clone (and track) a particular named branch::
1152 - clone (and track) a particular named branch::
1152
1153
1153 hg clone http://selenic.com/hg#stable
1154 hg clone http://selenic.com/hg#stable
1154
1155
1155 See :hg:`help urls` for details on specifying URLs.
1156 See :hg:`help urls` for details on specifying URLs.
1156
1157
1157 Returns 0 on success.
1158 Returns 0 on success.
1158 """
1159 """
1159 if opts.get('noupdate') and opts.get('updaterev'):
1160 if opts.get('noupdate') and opts.get('updaterev'):
1160 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1161 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1161
1162
1162 r = hg.clone(ui, opts, source, dest,
1163 r = hg.clone(ui, opts, source, dest,
1163 pull=opts.get('pull'),
1164 pull=opts.get('pull'),
1164 stream=opts.get('uncompressed'),
1165 stream=opts.get('uncompressed'),
1165 rev=opts.get('rev'),
1166 rev=opts.get('rev'),
1166 update=opts.get('updaterev') or not opts.get('noupdate'),
1167 update=opts.get('updaterev') or not opts.get('noupdate'),
1167 branch=opts.get('branch'))
1168 branch=opts.get('branch'))
1168
1169
1169 return r is None
1170 return r is None
1170
1171
1171 @command('^commit|ci',
1172 @command('^commit|ci',
1172 [('A', 'addremove', None,
1173 [('A', 'addremove', None,
1173 _('mark new/missing files as added/removed before committing')),
1174 _('mark new/missing files as added/removed before committing')),
1174 ('', 'close-branch', None,
1175 ('', 'close-branch', None,
1175 _('mark a branch as closed, hiding it from the branch list')),
1176 _('mark a branch as closed, hiding it from the branch list')),
1176 ('', 'amend', None, _('amend the parent of the working dir')),
1177 ('', 'amend', None, _('amend the parent of the working dir')),
1177 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1178 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1178 _('[OPTION]... [FILE]...'))
1179 _('[OPTION]... [FILE]...'))
1179 def commit(ui, repo, *pats, **opts):
1180 def commit(ui, repo, *pats, **opts):
1180 """commit the specified files or all outstanding changes
1181 """commit the specified files or all outstanding changes
1181
1182
1182 Commit changes to the given files into the repository. Unlike a
1183 Commit changes to the given files into the repository. Unlike a
1183 centralized SCM, this operation is a local operation. See
1184 centralized SCM, this operation is a local operation. See
1184 :hg:`push` for a way to actively distribute your changes.
1185 :hg:`push` for a way to actively distribute your changes.
1185
1186
1186 If a list of files is omitted, all changes reported by :hg:`status`
1187 If a list of files is omitted, all changes reported by :hg:`status`
1187 will be committed.
1188 will be committed.
1188
1189
1189 If you are committing the result of a merge, do not provide any
1190 If you are committing the result of a merge, do not provide any
1190 filenames or -I/-X filters.
1191 filenames or -I/-X filters.
1191
1192
1192 If no commit message is specified, Mercurial starts your
1193 If no commit message is specified, Mercurial starts your
1193 configured editor where you can enter a message. In case your
1194 configured editor where you can enter a message. In case your
1194 commit fails, you will find a backup of your message in
1195 commit fails, you will find a backup of your message in
1195 ``.hg/last-message.txt``.
1196 ``.hg/last-message.txt``.
1196
1197
1197 The --amend flag can be used to amend the parent of the
1198 The --amend flag can be used to amend the parent of the
1198 working directory with a new commit that contains the changes
1199 working directory with a new commit that contains the changes
1199 in the parent in addition to those currently reported by :hg:`status`,
1200 in the parent in addition to those currently reported by :hg:`status`,
1200 if there are any. The old commit is stored in a backup bundle in
1201 if there are any. The old commit is stored in a backup bundle in
1201 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1202 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1202 on how to restore it).
1203 on how to restore it).
1203
1204
1204 Message, user and date are taken from the amended commit unless
1205 Message, user and date are taken from the amended commit unless
1205 specified. When a message isn't specified on the command line,
1206 specified. When a message isn't specified on the command line,
1206 the editor will open with the message of the amended commit.
1207 the editor will open with the message of the amended commit.
1207
1208
1208 It is not possible to amend public changesets (see :hg:`help phases`)
1209 It is not possible to amend public changesets (see :hg:`help phases`)
1209 or changesets that have children.
1210 or changesets that have children.
1210
1211
1211 See :hg:`help dates` for a list of formats valid for -d/--date.
1212 See :hg:`help dates` for a list of formats valid for -d/--date.
1212
1213
1213 Returns 0 on success, 1 if nothing changed.
1214 Returns 0 on success, 1 if nothing changed.
1214 """
1215 """
1215 if opts.get('subrepos'):
1216 if opts.get('subrepos'):
1216 # Let --subrepos on the command line overide config setting.
1217 # Let --subrepos on the command line overide config setting.
1217 ui.setconfig('ui', 'commitsubrepos', True)
1218 ui.setconfig('ui', 'commitsubrepos', True)
1218
1219
1219 extra = {}
1220 extra = {}
1220 if opts.get('close_branch'):
1221 if opts.get('close_branch'):
1221 if repo['.'].node() not in repo.branchheads():
1222 if repo['.'].node() not in repo.branchheads():
1222 # The topo heads set is included in the branch heads set of the
1223 # The topo heads set is included in the branch heads set of the
1223 # current branch, so it's sufficient to test branchheads
1224 # current branch, so it's sufficient to test branchheads
1224 raise util.Abort(_('can only close branch heads'))
1225 raise util.Abort(_('can only close branch heads'))
1225 extra['close'] = 1
1226 extra['close'] = 1
1226
1227
1227 branch = repo[None].branch()
1228 branch = repo[None].branch()
1228 bheads = repo.branchheads(branch)
1229 bheads = repo.branchheads(branch)
1229
1230
1230 if opts.get('amend'):
1231 if opts.get('amend'):
1231 if ui.configbool('ui', 'commitsubrepos'):
1232 if ui.configbool('ui', 'commitsubrepos'):
1232 raise util.Abort(_('cannot amend recursively'))
1233 raise util.Abort(_('cannot amend recursively'))
1233
1234
1234 old = repo['.']
1235 old = repo['.']
1235 if old.phase() == phases.public:
1236 if old.phase() == phases.public:
1236 raise util.Abort(_('cannot amend public changesets'))
1237 raise util.Abort(_('cannot amend public changesets'))
1237 if len(old.parents()) > 1:
1238 if len(old.parents()) > 1:
1238 raise util.Abort(_('cannot amend merge changesets'))
1239 raise util.Abort(_('cannot amend merge changesets'))
1239 if len(repo[None].parents()) > 1:
1240 if len(repo[None].parents()) > 1:
1240 raise util.Abort(_('cannot amend while merging'))
1241 raise util.Abort(_('cannot amend while merging'))
1241 if old.children():
1242 if old.children():
1242 raise util.Abort(_('cannot amend changeset with children'))
1243 raise util.Abort(_('cannot amend changeset with children'))
1243
1244
1244 e = cmdutil.commiteditor
1245 e = cmdutil.commiteditor
1245 if opts.get('force_editor'):
1246 if opts.get('force_editor'):
1246 e = cmdutil.commitforceeditor
1247 e = cmdutil.commitforceeditor
1247
1248
1248 def commitfunc(ui, repo, message, match, opts):
1249 def commitfunc(ui, repo, message, match, opts):
1249 editor = e
1250 editor = e
1250 # message contains text from -m or -l, if it's empty,
1251 # message contains text from -m or -l, if it's empty,
1251 # open the editor with the old message
1252 # open the editor with the old message
1252 if not message:
1253 if not message:
1253 message = old.description()
1254 message = old.description()
1254 editor = cmdutil.commitforceeditor
1255 editor = cmdutil.commitforceeditor
1255 return repo.commit(message,
1256 return repo.commit(message,
1256 opts.get('user') or old.user(),
1257 opts.get('user') or old.user(),
1257 opts.get('date') or old.date(),
1258 opts.get('date') or old.date(),
1258 match,
1259 match,
1259 editor=editor,
1260 editor=editor,
1260 extra=extra)
1261 extra=extra)
1261
1262
1262 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1263 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1263 if node == old.node():
1264 if node == old.node():
1264 ui.status(_("nothing changed\n"))
1265 ui.status(_("nothing changed\n"))
1265 return 1
1266 return 1
1266 else:
1267 else:
1267 e = cmdutil.commiteditor
1268 e = cmdutil.commiteditor
1268 if opts.get('force_editor'):
1269 if opts.get('force_editor'):
1269 e = cmdutil.commitforceeditor
1270 e = cmdutil.commitforceeditor
1270
1271
1271 def commitfunc(ui, repo, message, match, opts):
1272 def commitfunc(ui, repo, message, match, opts):
1272 return repo.commit(message, opts.get('user'), opts.get('date'),
1273 return repo.commit(message, opts.get('user'), opts.get('date'),
1273 match, editor=e, extra=extra)
1274 match, editor=e, extra=extra)
1274
1275
1275 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1276 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1276
1277
1277 if not node:
1278 if not node:
1278 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1279 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1279 if stat[3]:
1280 if stat[3]:
1280 ui.status(_("nothing changed (%d missing files, see "
1281 ui.status(_("nothing changed (%d missing files, see "
1281 "'hg status')\n") % len(stat[3]))
1282 "'hg status')\n") % len(stat[3]))
1282 else:
1283 else:
1283 ui.status(_("nothing changed\n"))
1284 ui.status(_("nothing changed\n"))
1284 return 1
1285 return 1
1285
1286
1286 ctx = repo[node]
1287 ctx = repo[node]
1287 parents = ctx.parents()
1288 parents = ctx.parents()
1288
1289
1289 if (not opts.get('amend') and bheads and node not in bheads and not
1290 if (not opts.get('amend') and bheads and node not in bheads and not
1290 [x for x in parents if x.node() in bheads and x.branch() == branch]):
1291 [x for x in parents if x.node() in bheads and x.branch() == branch]):
1291 ui.status(_('created new head\n'))
1292 ui.status(_('created new head\n'))
1292 # The message is not printed for initial roots. For the other
1293 # The message is not printed for initial roots. For the other
1293 # changesets, it is printed in the following situations:
1294 # changesets, it is printed in the following situations:
1294 #
1295 #
1295 # Par column: for the 2 parents with ...
1296 # Par column: for the 2 parents with ...
1296 # N: null or no parent
1297 # N: null or no parent
1297 # B: parent is on another named branch
1298 # B: parent is on another named branch
1298 # C: parent is a regular non head changeset
1299 # C: parent is a regular non head changeset
1299 # H: parent was a branch head of the current branch
1300 # H: parent was a branch head of the current branch
1300 # Msg column: whether we print "created new head" message
1301 # Msg column: whether we print "created new head" message
1301 # In the following, it is assumed that there already exists some
1302 # In the following, it is assumed that there already exists some
1302 # initial branch heads of the current branch, otherwise nothing is
1303 # initial branch heads of the current branch, otherwise nothing is
1303 # printed anyway.
1304 # printed anyway.
1304 #
1305 #
1305 # Par Msg Comment
1306 # Par Msg Comment
1306 # NN y additional topo root
1307 # NN y additional topo root
1307 #
1308 #
1308 # BN y additional branch root
1309 # BN y additional branch root
1309 # CN y additional topo head
1310 # CN y additional topo head
1310 # HN n usual case
1311 # HN n usual case
1311 #
1312 #
1312 # BB y weird additional branch root
1313 # BB y weird additional branch root
1313 # CB y branch merge
1314 # CB y branch merge
1314 # HB n merge with named branch
1315 # HB n merge with named branch
1315 #
1316 #
1316 # CC y additional head from merge
1317 # CC y additional head from merge
1317 # CH n merge with a head
1318 # CH n merge with a head
1318 #
1319 #
1319 # HH n head merge: head count decreases
1320 # HH n head merge: head count decreases
1320
1321
1321 if not opts.get('close_branch'):
1322 if not opts.get('close_branch'):
1322 for r in parents:
1323 for r in parents:
1323 if r.extra().get('close') and r.branch() == branch:
1324 if r.extra().get('close') and r.branch() == branch:
1324 ui.status(_('reopening closed branch head %d\n') % r)
1325 ui.status(_('reopening closed branch head %d\n') % r)
1325
1326
1326 if ui.debugflag:
1327 if ui.debugflag:
1327 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
1328 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
1328 elif ui.verbose:
1329 elif ui.verbose:
1329 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
1330 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
1330
1331
1331 @command('copy|cp',
1332 @command('copy|cp',
1332 [('A', 'after', None, _('record a copy that has already occurred')),
1333 [('A', 'after', None, _('record a copy that has already occurred')),
1333 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1334 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1334 ] + walkopts + dryrunopts,
1335 ] + walkopts + dryrunopts,
1335 _('[OPTION]... [SOURCE]... DEST'))
1336 _('[OPTION]... [SOURCE]... DEST'))
1336 def copy(ui, repo, *pats, **opts):
1337 def copy(ui, repo, *pats, **opts):
1337 """mark files as copied for the next commit
1338 """mark files as copied for the next commit
1338
1339
1339 Mark dest as having copies of source files. If dest is a
1340 Mark dest as having copies of source files. If dest is a
1340 directory, copies are put in that directory. If dest is a file,
1341 directory, copies are put in that directory. If dest is a file,
1341 the source must be a single file.
1342 the source must be a single file.
1342
1343
1343 By default, this command copies the contents of files as they
1344 By default, this command copies the contents of files as they
1344 exist in the working directory. If invoked with -A/--after, the
1345 exist in the working directory. If invoked with -A/--after, the
1345 operation is recorded, but no copying is performed.
1346 operation is recorded, but no copying is performed.
1346
1347
1347 This command takes effect with the next commit. To undo a copy
1348 This command takes effect with the next commit. To undo a copy
1348 before that, see :hg:`revert`.
1349 before that, see :hg:`revert`.
1349
1350
1350 Returns 0 on success, 1 if errors are encountered.
1351 Returns 0 on success, 1 if errors are encountered.
1351 """
1352 """
1352 wlock = repo.wlock(False)
1353 wlock = repo.wlock(False)
1353 try:
1354 try:
1354 return cmdutil.copy(ui, repo, pats, opts)
1355 return cmdutil.copy(ui, repo, pats, opts)
1355 finally:
1356 finally:
1356 wlock.release()
1357 wlock.release()
1357
1358
1358 @command('debugancestor', [], _('[INDEX] REV1 REV2'))
1359 @command('debugancestor', [], _('[INDEX] REV1 REV2'))
1359 def debugancestor(ui, repo, *args):
1360 def debugancestor(ui, repo, *args):
1360 """find the ancestor revision of two revisions in a given index"""
1361 """find the ancestor revision of two revisions in a given index"""
1361 if len(args) == 3:
1362 if len(args) == 3:
1362 index, rev1, rev2 = args
1363 index, rev1, rev2 = args
1363 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1364 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1364 lookup = r.lookup
1365 lookup = r.lookup
1365 elif len(args) == 2:
1366 elif len(args) == 2:
1366 if not repo:
1367 if not repo:
1367 raise util.Abort(_("there is no Mercurial repository here "
1368 raise util.Abort(_("there is no Mercurial repository here "
1368 "(.hg not found)"))
1369 "(.hg not found)"))
1369 rev1, rev2 = args
1370 rev1, rev2 = args
1370 r = repo.changelog
1371 r = repo.changelog
1371 lookup = repo.lookup
1372 lookup = repo.lookup
1372 else:
1373 else:
1373 raise util.Abort(_('either two or three arguments required'))
1374 raise util.Abort(_('either two or three arguments required'))
1374 a = r.ancestor(lookup(rev1), lookup(rev2))
1375 a = r.ancestor(lookup(rev1), lookup(rev2))
1375 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1376 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1376
1377
1377 @command('debugbuilddag',
1378 @command('debugbuilddag',
1378 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1379 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1379 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1380 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1380 ('n', 'new-file', None, _('add new file at each rev'))],
1381 ('n', 'new-file', None, _('add new file at each rev'))],
1381 _('[OPTION]... [TEXT]'))
1382 _('[OPTION]... [TEXT]'))
1382 def debugbuilddag(ui, repo, text=None,
1383 def debugbuilddag(ui, repo, text=None,
1383 mergeable_file=False,
1384 mergeable_file=False,
1384 overwritten_file=False,
1385 overwritten_file=False,
1385 new_file=False):
1386 new_file=False):
1386 """builds a repo with a given DAG from scratch in the current empty repo
1387 """builds a repo with a given DAG from scratch in the current empty repo
1387
1388
1388 The description of the DAG is read from stdin if not given on the
1389 The description of the DAG is read from stdin if not given on the
1389 command line.
1390 command line.
1390
1391
1391 Elements:
1392 Elements:
1392
1393
1393 - "+n" is a linear run of n nodes based on the current default parent
1394 - "+n" is a linear run of n nodes based on the current default parent
1394 - "." is a single node based on the current default parent
1395 - "." is a single node based on the current default parent
1395 - "$" resets the default parent to null (implied at the start);
1396 - "$" resets the default parent to null (implied at the start);
1396 otherwise the default parent is always the last node created
1397 otherwise the default parent is always the last node created
1397 - "<p" sets the default parent to the backref p
1398 - "<p" sets the default parent to the backref p
1398 - "*p" is a fork at parent p, which is a backref
1399 - "*p" is a fork at parent p, which is a backref
1399 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1400 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1400 - "/p2" is a merge of the preceding node and p2
1401 - "/p2" is a merge of the preceding node and p2
1401 - ":tag" defines a local tag for the preceding node
1402 - ":tag" defines a local tag for the preceding node
1402 - "@branch" sets the named branch for subsequent nodes
1403 - "@branch" sets the named branch for subsequent nodes
1403 - "#...\\n" is a comment up to the end of the line
1404 - "#...\\n" is a comment up to the end of the line
1404
1405
1405 Whitespace between the above elements is ignored.
1406 Whitespace between the above elements is ignored.
1406
1407
1407 A backref is either
1408 A backref is either
1408
1409
1409 - a number n, which references the node curr-n, where curr is the current
1410 - a number n, which references the node curr-n, where curr is the current
1410 node, or
1411 node, or
1411 - the name of a local tag you placed earlier using ":tag", or
1412 - the name of a local tag you placed earlier using ":tag", or
1412 - empty to denote the default parent.
1413 - empty to denote the default parent.
1413
1414
1414 All string valued-elements are either strictly alphanumeric, or must
1415 All string valued-elements are either strictly alphanumeric, or must
1415 be enclosed in double quotes ("..."), with "\\" as escape character.
1416 be enclosed in double quotes ("..."), with "\\" as escape character.
1416 """
1417 """
1417
1418
1418 if text is None:
1419 if text is None:
1419 ui.status(_("reading DAG from stdin\n"))
1420 ui.status(_("reading DAG from stdin\n"))
1420 text = ui.fin.read()
1421 text = ui.fin.read()
1421
1422
1422 cl = repo.changelog
1423 cl = repo.changelog
1423 if len(cl) > 0:
1424 if len(cl) > 0:
1424 raise util.Abort(_('repository is not empty'))
1425 raise util.Abort(_('repository is not empty'))
1425
1426
1426 # determine number of revs in DAG
1427 # determine number of revs in DAG
1427 total = 0
1428 total = 0
1428 for type, data in dagparser.parsedag(text):
1429 for type, data in dagparser.parsedag(text):
1429 if type == 'n':
1430 if type == 'n':
1430 total += 1
1431 total += 1
1431
1432
1432 if mergeable_file:
1433 if mergeable_file:
1433 linesperrev = 2
1434 linesperrev = 2
1434 # make a file with k lines per rev
1435 # make a file with k lines per rev
1435 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1436 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1436 initialmergedlines.append("")
1437 initialmergedlines.append("")
1437
1438
1438 tags = []
1439 tags = []
1439
1440
1440 lock = tr = None
1441 lock = tr = None
1441 try:
1442 try:
1442 lock = repo.lock()
1443 lock = repo.lock()
1443 tr = repo.transaction("builddag")
1444 tr = repo.transaction("builddag")
1444
1445
1445 at = -1
1446 at = -1
1446 atbranch = 'default'
1447 atbranch = 'default'
1447 nodeids = []
1448 nodeids = []
1448 id = 0
1449 id = 0
1449 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1450 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1450 for type, data in dagparser.parsedag(text):
1451 for type, data in dagparser.parsedag(text):
1451 if type == 'n':
1452 if type == 'n':
1452 ui.note('node %s\n' % str(data))
1453 ui.note('node %s\n' % str(data))
1453 id, ps = data
1454 id, ps = data
1454
1455
1455 files = []
1456 files = []
1456 fctxs = {}
1457 fctxs = {}
1457
1458
1458 p2 = None
1459 p2 = None
1459 if mergeable_file:
1460 if mergeable_file:
1460 fn = "mf"
1461 fn = "mf"
1461 p1 = repo[ps[0]]
1462 p1 = repo[ps[0]]
1462 if len(ps) > 1:
1463 if len(ps) > 1:
1463 p2 = repo[ps[1]]
1464 p2 = repo[ps[1]]
1464 pa = p1.ancestor(p2)
1465 pa = p1.ancestor(p2)
1465 base, local, other = [x[fn].data() for x in pa, p1, p2]
1466 base, local, other = [x[fn].data() for x in pa, p1, p2]
1466 m3 = simplemerge.Merge3Text(base, local, other)
1467 m3 = simplemerge.Merge3Text(base, local, other)
1467 ml = [l.strip() for l in m3.merge_lines()]
1468 ml = [l.strip() for l in m3.merge_lines()]
1468 ml.append("")
1469 ml.append("")
1469 elif at > 0:
1470 elif at > 0:
1470 ml = p1[fn].data().split("\n")
1471 ml = p1[fn].data().split("\n")
1471 else:
1472 else:
1472 ml = initialmergedlines
1473 ml = initialmergedlines
1473 ml[id * linesperrev] += " r%i" % id
1474 ml[id * linesperrev] += " r%i" % id
1474 mergedtext = "\n".join(ml)
1475 mergedtext = "\n".join(ml)
1475 files.append(fn)
1476 files.append(fn)
1476 fctxs[fn] = context.memfilectx(fn, mergedtext)
1477 fctxs[fn] = context.memfilectx(fn, mergedtext)
1477
1478
1478 if overwritten_file:
1479 if overwritten_file:
1479 fn = "of"
1480 fn = "of"
1480 files.append(fn)
1481 files.append(fn)
1481 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1482 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1482
1483
1483 if new_file:
1484 if new_file:
1484 fn = "nf%i" % id
1485 fn = "nf%i" % id
1485 files.append(fn)
1486 files.append(fn)
1486 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1487 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1487 if len(ps) > 1:
1488 if len(ps) > 1:
1488 if not p2:
1489 if not p2:
1489 p2 = repo[ps[1]]
1490 p2 = repo[ps[1]]
1490 for fn in p2:
1491 for fn in p2:
1491 if fn.startswith("nf"):
1492 if fn.startswith("nf"):
1492 files.append(fn)
1493 files.append(fn)
1493 fctxs[fn] = p2[fn]
1494 fctxs[fn] = p2[fn]
1494
1495
1495 def fctxfn(repo, cx, path):
1496 def fctxfn(repo, cx, path):
1496 return fctxs.get(path)
1497 return fctxs.get(path)
1497
1498
1498 if len(ps) == 0 or ps[0] < 0:
1499 if len(ps) == 0 or ps[0] < 0:
1499 pars = [None, None]
1500 pars = [None, None]
1500 elif len(ps) == 1:
1501 elif len(ps) == 1:
1501 pars = [nodeids[ps[0]], None]
1502 pars = [nodeids[ps[0]], None]
1502 else:
1503 else:
1503 pars = [nodeids[p] for p in ps]
1504 pars = [nodeids[p] for p in ps]
1504 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1505 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1505 date=(id, 0),
1506 date=(id, 0),
1506 user="debugbuilddag",
1507 user="debugbuilddag",
1507 extra={'branch': atbranch})
1508 extra={'branch': atbranch})
1508 nodeid = repo.commitctx(cx)
1509 nodeid = repo.commitctx(cx)
1509 nodeids.append(nodeid)
1510 nodeids.append(nodeid)
1510 at = id
1511 at = id
1511 elif type == 'l':
1512 elif type == 'l':
1512 id, name = data
1513 id, name = data
1513 ui.note('tag %s\n' % name)
1514 ui.note('tag %s\n' % name)
1514 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1515 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1515 elif type == 'a':
1516 elif type == 'a':
1516 ui.note('branch %s\n' % data)
1517 ui.note('branch %s\n' % data)
1517 atbranch = data
1518 atbranch = data
1518 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1519 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1519 tr.close()
1520 tr.close()
1520
1521
1521 if tags:
1522 if tags:
1522 repo.opener.write("localtags", "".join(tags))
1523 repo.opener.write("localtags", "".join(tags))
1523 finally:
1524 finally:
1524 ui.progress(_('building'), None)
1525 ui.progress(_('building'), None)
1525 release(tr, lock)
1526 release(tr, lock)
1526
1527
1527 @command('debugbundle', [('a', 'all', None, _('show all details'))], _('FILE'))
1528 @command('debugbundle', [('a', 'all', None, _('show all details'))], _('FILE'))
1528 def debugbundle(ui, bundlepath, all=None, **opts):
1529 def debugbundle(ui, bundlepath, all=None, **opts):
1529 """lists the contents of a bundle"""
1530 """lists the contents of a bundle"""
1530 f = url.open(ui, bundlepath)
1531 f = url.open(ui, bundlepath)
1531 try:
1532 try:
1532 gen = changegroup.readbundle(f, bundlepath)
1533 gen = changegroup.readbundle(f, bundlepath)
1533 if all:
1534 if all:
1534 ui.write("format: id, p1, p2, cset, delta base, len(delta)\n")
1535 ui.write("format: id, p1, p2, cset, delta base, len(delta)\n")
1535
1536
1536 def showchunks(named):
1537 def showchunks(named):
1537 ui.write("\n%s\n" % named)
1538 ui.write("\n%s\n" % named)
1538 chain = None
1539 chain = None
1539 while True:
1540 while True:
1540 chunkdata = gen.deltachunk(chain)
1541 chunkdata = gen.deltachunk(chain)
1541 if not chunkdata:
1542 if not chunkdata:
1542 break
1543 break
1543 node = chunkdata['node']
1544 node = chunkdata['node']
1544 p1 = chunkdata['p1']
1545 p1 = chunkdata['p1']
1545 p2 = chunkdata['p2']
1546 p2 = chunkdata['p2']
1546 cs = chunkdata['cs']
1547 cs = chunkdata['cs']
1547 deltabase = chunkdata['deltabase']
1548 deltabase = chunkdata['deltabase']
1548 delta = chunkdata['delta']
1549 delta = chunkdata['delta']
1549 ui.write("%s %s %s %s %s %s\n" %
1550 ui.write("%s %s %s %s %s %s\n" %
1550 (hex(node), hex(p1), hex(p2),
1551 (hex(node), hex(p1), hex(p2),
1551 hex(cs), hex(deltabase), len(delta)))
1552 hex(cs), hex(deltabase), len(delta)))
1552 chain = node
1553 chain = node
1553
1554
1554 chunkdata = gen.changelogheader()
1555 chunkdata = gen.changelogheader()
1555 showchunks("changelog")
1556 showchunks("changelog")
1556 chunkdata = gen.manifestheader()
1557 chunkdata = gen.manifestheader()
1557 showchunks("manifest")
1558 showchunks("manifest")
1558 while True:
1559 while True:
1559 chunkdata = gen.filelogheader()
1560 chunkdata = gen.filelogheader()
1560 if not chunkdata:
1561 if not chunkdata:
1561 break
1562 break
1562 fname = chunkdata['filename']
1563 fname = chunkdata['filename']
1563 showchunks(fname)
1564 showchunks(fname)
1564 else:
1565 else:
1565 chunkdata = gen.changelogheader()
1566 chunkdata = gen.changelogheader()
1566 chain = None
1567 chain = None
1567 while True:
1568 while True:
1568 chunkdata = gen.deltachunk(chain)
1569 chunkdata = gen.deltachunk(chain)
1569 if not chunkdata:
1570 if not chunkdata:
1570 break
1571 break
1571 node = chunkdata['node']
1572 node = chunkdata['node']
1572 ui.write("%s\n" % hex(node))
1573 ui.write("%s\n" % hex(node))
1573 chain = node
1574 chain = node
1574 finally:
1575 finally:
1575 f.close()
1576 f.close()
1576
1577
1577 @command('debugcheckstate', [], '')
1578 @command('debugcheckstate', [], '')
1578 def debugcheckstate(ui, repo):
1579 def debugcheckstate(ui, repo):
1579 """validate the correctness of the current dirstate"""
1580 """validate the correctness of the current dirstate"""
1580 parent1, parent2 = repo.dirstate.parents()
1581 parent1, parent2 = repo.dirstate.parents()
1581 m1 = repo[parent1].manifest()
1582 m1 = repo[parent1].manifest()
1582 m2 = repo[parent2].manifest()
1583 m2 = repo[parent2].manifest()
1583 errors = 0
1584 errors = 0
1584 for f in repo.dirstate:
1585 for f in repo.dirstate:
1585 state = repo.dirstate[f]
1586 state = repo.dirstate[f]
1586 if state in "nr" and f not in m1:
1587 if state in "nr" and f not in m1:
1587 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1588 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1588 errors += 1
1589 errors += 1
1589 if state in "a" and f in m1:
1590 if state in "a" and f in m1:
1590 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1591 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1591 errors += 1
1592 errors += 1
1592 if state in "m" and f not in m1 and f not in m2:
1593 if state in "m" and f not in m1 and f not in m2:
1593 ui.warn(_("%s in state %s, but not in either manifest\n") %
1594 ui.warn(_("%s in state %s, but not in either manifest\n") %
1594 (f, state))
1595 (f, state))
1595 errors += 1
1596 errors += 1
1596 for f in m1:
1597 for f in m1:
1597 state = repo.dirstate[f]
1598 state = repo.dirstate[f]
1598 if state not in "nrm":
1599 if state not in "nrm":
1599 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1600 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1600 errors += 1
1601 errors += 1
1601 if errors:
1602 if errors:
1602 error = _(".hg/dirstate inconsistent with current parent's manifest")
1603 error = _(".hg/dirstate inconsistent with current parent's manifest")
1603 raise util.Abort(error)
1604 raise util.Abort(error)
1604
1605
1605 @command('debugcommands', [], _('[COMMAND]'))
1606 @command('debugcommands', [], _('[COMMAND]'))
1606 def debugcommands(ui, cmd='', *args):
1607 def debugcommands(ui, cmd='', *args):
1607 """list all available commands and options"""
1608 """list all available commands and options"""
1608 for cmd, vals in sorted(table.iteritems()):
1609 for cmd, vals in sorted(table.iteritems()):
1609 cmd = cmd.split('|')[0].strip('^')
1610 cmd = cmd.split('|')[0].strip('^')
1610 opts = ', '.join([i[1] for i in vals[1]])
1611 opts = ', '.join([i[1] for i in vals[1]])
1611 ui.write('%s: %s\n' % (cmd, opts))
1612 ui.write('%s: %s\n' % (cmd, opts))
1612
1613
1613 @command('debugcomplete',
1614 @command('debugcomplete',
1614 [('o', 'options', None, _('show the command options'))],
1615 [('o', 'options', None, _('show the command options'))],
1615 _('[-o] CMD'))
1616 _('[-o] CMD'))
1616 def debugcomplete(ui, cmd='', **opts):
1617 def debugcomplete(ui, cmd='', **opts):
1617 """returns the completion list associated with the given command"""
1618 """returns the completion list associated with the given command"""
1618
1619
1619 if opts.get('options'):
1620 if opts.get('options'):
1620 options = []
1621 options = []
1621 otables = [globalopts]
1622 otables = [globalopts]
1622 if cmd:
1623 if cmd:
1623 aliases, entry = cmdutil.findcmd(cmd, table, False)
1624 aliases, entry = cmdutil.findcmd(cmd, table, False)
1624 otables.append(entry[1])
1625 otables.append(entry[1])
1625 for t in otables:
1626 for t in otables:
1626 for o in t:
1627 for o in t:
1627 if "(DEPRECATED)" in o[3]:
1628 if "(DEPRECATED)" in o[3]:
1628 continue
1629 continue
1629 if o[0]:
1630 if o[0]:
1630 options.append('-%s' % o[0])
1631 options.append('-%s' % o[0])
1631 options.append('--%s' % o[1])
1632 options.append('--%s' % o[1])
1632 ui.write("%s\n" % "\n".join(options))
1633 ui.write("%s\n" % "\n".join(options))
1633 return
1634 return
1634
1635
1635 cmdlist = cmdutil.findpossible(cmd, table)
1636 cmdlist = cmdutil.findpossible(cmd, table)
1636 if ui.verbose:
1637 if ui.verbose:
1637 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1638 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1638 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1639 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1639
1640
1640 @command('debugdag',
1641 @command('debugdag',
1641 [('t', 'tags', None, _('use tags as labels')),
1642 [('t', 'tags', None, _('use tags as labels')),
1642 ('b', 'branches', None, _('annotate with branch names')),
1643 ('b', 'branches', None, _('annotate with branch names')),
1643 ('', 'dots', None, _('use dots for runs')),
1644 ('', 'dots', None, _('use dots for runs')),
1644 ('s', 'spaces', None, _('separate elements by spaces'))],
1645 ('s', 'spaces', None, _('separate elements by spaces'))],
1645 _('[OPTION]... [FILE [REV]...]'))
1646 _('[OPTION]... [FILE [REV]...]'))
1646 def debugdag(ui, repo, file_=None, *revs, **opts):
1647 def debugdag(ui, repo, file_=None, *revs, **opts):
1647 """format the changelog or an index DAG as a concise textual description
1648 """format the changelog or an index DAG as a concise textual description
1648
1649
1649 If you pass a revlog index, the revlog's DAG is emitted. If you list
1650 If you pass a revlog index, the revlog's DAG is emitted. If you list
1650 revision numbers, they get labelled in the output as rN.
1651 revision numbers, they get labelled in the output as rN.
1651
1652
1652 Otherwise, the changelog DAG of the current repo is emitted.
1653 Otherwise, the changelog DAG of the current repo is emitted.
1653 """
1654 """
1654 spaces = opts.get('spaces')
1655 spaces = opts.get('spaces')
1655 dots = opts.get('dots')
1656 dots = opts.get('dots')
1656 if file_:
1657 if file_:
1657 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1658 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1658 revs = set((int(r) for r in revs))
1659 revs = set((int(r) for r in revs))
1659 def events():
1660 def events():
1660 for r in rlog:
1661 for r in rlog:
1661 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1662 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1662 if r in revs:
1663 if r in revs:
1663 yield 'l', (r, "r%i" % r)
1664 yield 'l', (r, "r%i" % r)
1664 elif repo:
1665 elif repo:
1665 cl = repo.changelog
1666 cl = repo.changelog
1666 tags = opts.get('tags')
1667 tags = opts.get('tags')
1667 branches = opts.get('branches')
1668 branches = opts.get('branches')
1668 if tags:
1669 if tags:
1669 labels = {}
1670 labels = {}
1670 for l, n in repo.tags().items():
1671 for l, n in repo.tags().items():
1671 labels.setdefault(cl.rev(n), []).append(l)
1672 labels.setdefault(cl.rev(n), []).append(l)
1672 def events():
1673 def events():
1673 b = "default"
1674 b = "default"
1674 for r in cl:
1675 for r in cl:
1675 if branches:
1676 if branches:
1676 newb = cl.read(cl.node(r))[5]['branch']
1677 newb = cl.read(cl.node(r))[5]['branch']
1677 if newb != b:
1678 if newb != b:
1678 yield 'a', newb
1679 yield 'a', newb
1679 b = newb
1680 b = newb
1680 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1681 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1681 if tags:
1682 if tags:
1682 ls = labels.get(r)
1683 ls = labels.get(r)
1683 if ls:
1684 if ls:
1684 for l in ls:
1685 for l in ls:
1685 yield 'l', (r, l)
1686 yield 'l', (r, l)
1686 else:
1687 else:
1687 raise util.Abort(_('need repo for changelog dag'))
1688 raise util.Abort(_('need repo for changelog dag'))
1688
1689
1689 for line in dagparser.dagtextlines(events(),
1690 for line in dagparser.dagtextlines(events(),
1690 addspaces=spaces,
1691 addspaces=spaces,
1691 wraplabels=True,
1692 wraplabels=True,
1692 wrapannotations=True,
1693 wrapannotations=True,
1693 wrapnonlinear=dots,
1694 wrapnonlinear=dots,
1694 usedots=dots,
1695 usedots=dots,
1695 maxlinewidth=70):
1696 maxlinewidth=70):
1696 ui.write(line)
1697 ui.write(line)
1697 ui.write("\n")
1698 ui.write("\n")
1698
1699
1699 @command('debugdata',
1700 @command('debugdata',
1700 [('c', 'changelog', False, _('open changelog')),
1701 [('c', 'changelog', False, _('open changelog')),
1701 ('m', 'manifest', False, _('open manifest'))],
1702 ('m', 'manifest', False, _('open manifest'))],
1702 _('-c|-m|FILE REV'))
1703 _('-c|-m|FILE REV'))
1703 def debugdata(ui, repo, file_, rev = None, **opts):
1704 def debugdata(ui, repo, file_, rev = None, **opts):
1704 """dump the contents of a data file revision"""
1705 """dump the contents of a data file revision"""
1705 if opts.get('changelog') or opts.get('manifest'):
1706 if opts.get('changelog') or opts.get('manifest'):
1706 file_, rev = None, file_
1707 file_, rev = None, file_
1707 elif rev is None:
1708 elif rev is None:
1708 raise error.CommandError('debugdata', _('invalid arguments'))
1709 raise error.CommandError('debugdata', _('invalid arguments'))
1709 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
1710 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
1710 try:
1711 try:
1711 ui.write(r.revision(r.lookup(rev)))
1712 ui.write(r.revision(r.lookup(rev)))
1712 except KeyError:
1713 except KeyError:
1713 raise util.Abort(_('invalid revision identifier %s') % rev)
1714 raise util.Abort(_('invalid revision identifier %s') % rev)
1714
1715
1715 @command('debugdate',
1716 @command('debugdate',
1716 [('e', 'extended', None, _('try extended date formats'))],
1717 [('e', 'extended', None, _('try extended date formats'))],
1717 _('[-e] DATE [RANGE]'))
1718 _('[-e] DATE [RANGE]'))
1718 def debugdate(ui, date, range=None, **opts):
1719 def debugdate(ui, date, range=None, **opts):
1719 """parse and display a date"""
1720 """parse and display a date"""
1720 if opts["extended"]:
1721 if opts["extended"]:
1721 d = util.parsedate(date, util.extendeddateformats)
1722 d = util.parsedate(date, util.extendeddateformats)
1722 else:
1723 else:
1723 d = util.parsedate(date)
1724 d = util.parsedate(date)
1724 ui.write("internal: %s %s\n" % d)
1725 ui.write("internal: %s %s\n" % d)
1725 ui.write("standard: %s\n" % util.datestr(d))
1726 ui.write("standard: %s\n" % util.datestr(d))
1726 if range:
1727 if range:
1727 m = util.matchdate(range)
1728 m = util.matchdate(range)
1728 ui.write("match: %s\n" % m(d[0]))
1729 ui.write("match: %s\n" % m(d[0]))
1729
1730
1730 @command('debugdiscovery',
1731 @command('debugdiscovery',
1731 [('', 'old', None, _('use old-style discovery')),
1732 [('', 'old', None, _('use old-style discovery')),
1732 ('', 'nonheads', None,
1733 ('', 'nonheads', None,
1733 _('use old-style discovery with non-heads included')),
1734 _('use old-style discovery with non-heads included')),
1734 ] + remoteopts,
1735 ] + remoteopts,
1735 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
1736 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
1736 def debugdiscovery(ui, repo, remoteurl="default", **opts):
1737 def debugdiscovery(ui, repo, remoteurl="default", **opts):
1737 """runs the changeset discovery protocol in isolation"""
1738 """runs the changeset discovery protocol in isolation"""
1738 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl), opts.get('branch'))
1739 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl), opts.get('branch'))
1739 remote = hg.peer(repo, opts, remoteurl)
1740 remote = hg.peer(repo, opts, remoteurl)
1740 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
1741 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
1741
1742
1742 # make sure tests are repeatable
1743 # make sure tests are repeatable
1743 random.seed(12323)
1744 random.seed(12323)
1744
1745
1745 def doit(localheads, remoteheads):
1746 def doit(localheads, remoteheads):
1746 if opts.get('old'):
1747 if opts.get('old'):
1747 if localheads:
1748 if localheads:
1748 raise util.Abort('cannot use localheads with old style discovery')
1749 raise util.Abort('cannot use localheads with old style discovery')
1749 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
1750 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
1750 force=True)
1751 force=True)
1751 common = set(common)
1752 common = set(common)
1752 if not opts.get('nonheads'):
1753 if not opts.get('nonheads'):
1753 ui.write("unpruned common: %s\n" % " ".join([short(n)
1754 ui.write("unpruned common: %s\n" % " ".join([short(n)
1754 for n in common]))
1755 for n in common]))
1755 dag = dagutil.revlogdag(repo.changelog)
1756 dag = dagutil.revlogdag(repo.changelog)
1756 all = dag.ancestorset(dag.internalizeall(common))
1757 all = dag.ancestorset(dag.internalizeall(common))
1757 common = dag.externalizeall(dag.headsetofconnecteds(all))
1758 common = dag.externalizeall(dag.headsetofconnecteds(all))
1758 else:
1759 else:
1759 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
1760 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
1760 common = set(common)
1761 common = set(common)
1761 rheads = set(hds)
1762 rheads = set(hds)
1762 lheads = set(repo.heads())
1763 lheads = set(repo.heads())
1763 ui.write("common heads: %s\n" % " ".join([short(n) for n in common]))
1764 ui.write("common heads: %s\n" % " ".join([short(n) for n in common]))
1764 if lheads <= common:
1765 if lheads <= common:
1765 ui.write("local is subset\n")
1766 ui.write("local is subset\n")
1766 elif rheads <= common:
1767 elif rheads <= common:
1767 ui.write("remote is subset\n")
1768 ui.write("remote is subset\n")
1768
1769
1769 serverlogs = opts.get('serverlog')
1770 serverlogs = opts.get('serverlog')
1770 if serverlogs:
1771 if serverlogs:
1771 for filename in serverlogs:
1772 for filename in serverlogs:
1772 logfile = open(filename, 'r')
1773 logfile = open(filename, 'r')
1773 try:
1774 try:
1774 line = logfile.readline()
1775 line = logfile.readline()
1775 while line:
1776 while line:
1776 parts = line.strip().split(';')
1777 parts = line.strip().split(';')
1777 op = parts[1]
1778 op = parts[1]
1778 if op == 'cg':
1779 if op == 'cg':
1779 pass
1780 pass
1780 elif op == 'cgss':
1781 elif op == 'cgss':
1781 doit(parts[2].split(' '), parts[3].split(' '))
1782 doit(parts[2].split(' '), parts[3].split(' '))
1782 elif op == 'unb':
1783 elif op == 'unb':
1783 doit(parts[3].split(' '), parts[2].split(' '))
1784 doit(parts[3].split(' '), parts[2].split(' '))
1784 line = logfile.readline()
1785 line = logfile.readline()
1785 finally:
1786 finally:
1786 logfile.close()
1787 logfile.close()
1787
1788
1788 else:
1789 else:
1789 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
1790 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
1790 opts.get('remote_head'))
1791 opts.get('remote_head'))
1791 localrevs = opts.get('local_head')
1792 localrevs = opts.get('local_head')
1792 doit(localrevs, remoterevs)
1793 doit(localrevs, remoterevs)
1793
1794
1794 @command('debugfileset', [], ('REVSPEC'))
1795 @command('debugfileset', [], ('REVSPEC'))
1795 def debugfileset(ui, repo, expr):
1796 def debugfileset(ui, repo, expr):
1796 '''parse and apply a fileset specification'''
1797 '''parse and apply a fileset specification'''
1797 if ui.verbose:
1798 if ui.verbose:
1798 tree = fileset.parse(expr)[0]
1799 tree = fileset.parse(expr)[0]
1799 ui.note(tree, "\n")
1800 ui.note(tree, "\n")
1800
1801
1801 for f in fileset.getfileset(repo[None], expr):
1802 for f in fileset.getfileset(repo[None], expr):
1802 ui.write("%s\n" % f)
1803 ui.write("%s\n" % f)
1803
1804
1804 @command('debugfsinfo', [], _('[PATH]'))
1805 @command('debugfsinfo', [], _('[PATH]'))
1805 def debugfsinfo(ui, path = "."):
1806 def debugfsinfo(ui, path = "."):
1806 """show information detected about current filesystem"""
1807 """show information detected about current filesystem"""
1807 util.writefile('.debugfsinfo', '')
1808 util.writefile('.debugfsinfo', '')
1808 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1809 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1809 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1810 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1810 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1811 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1811 and 'yes' or 'no'))
1812 and 'yes' or 'no'))
1812 os.unlink('.debugfsinfo')
1813 os.unlink('.debugfsinfo')
1813
1814
1814 @command('debuggetbundle',
1815 @command('debuggetbundle',
1815 [('H', 'head', [], _('id of head node'), _('ID')),
1816 [('H', 'head', [], _('id of head node'), _('ID')),
1816 ('C', 'common', [], _('id of common node'), _('ID')),
1817 ('C', 'common', [], _('id of common node'), _('ID')),
1817 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
1818 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
1818 _('REPO FILE [-H|-C ID]...'))
1819 _('REPO FILE [-H|-C ID]...'))
1819 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
1820 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
1820 """retrieves a bundle from a repo
1821 """retrieves a bundle from a repo
1821
1822
1822 Every ID must be a full-length hex node id string. Saves the bundle to the
1823 Every ID must be a full-length hex node id string. Saves the bundle to the
1823 given file.
1824 given file.
1824 """
1825 """
1825 repo = hg.peer(ui, opts, repopath)
1826 repo = hg.peer(ui, opts, repopath)
1826 if not repo.capable('getbundle'):
1827 if not repo.capable('getbundle'):
1827 raise util.Abort("getbundle() not supported by target repository")
1828 raise util.Abort("getbundle() not supported by target repository")
1828 args = {}
1829 args = {}
1829 if common:
1830 if common:
1830 args['common'] = [bin(s) for s in common]
1831 args['common'] = [bin(s) for s in common]
1831 if head:
1832 if head:
1832 args['heads'] = [bin(s) for s in head]
1833 args['heads'] = [bin(s) for s in head]
1833 bundle = repo.getbundle('debug', **args)
1834 bundle = repo.getbundle('debug', **args)
1834
1835
1835 bundletype = opts.get('type', 'bzip2').lower()
1836 bundletype = opts.get('type', 'bzip2').lower()
1836 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1837 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1837 bundletype = btypes.get(bundletype)
1838 bundletype = btypes.get(bundletype)
1838 if bundletype not in changegroup.bundletypes:
1839 if bundletype not in changegroup.bundletypes:
1839 raise util.Abort(_('unknown bundle type specified with --type'))
1840 raise util.Abort(_('unknown bundle type specified with --type'))
1840 changegroup.writebundle(bundle, bundlepath, bundletype)
1841 changegroup.writebundle(bundle, bundlepath, bundletype)
1841
1842
1842 @command('debugignore', [], '')
1843 @command('debugignore', [], '')
1843 def debugignore(ui, repo, *values, **opts):
1844 def debugignore(ui, repo, *values, **opts):
1844 """display the combined ignore pattern"""
1845 """display the combined ignore pattern"""
1845 ignore = repo.dirstate._ignore
1846 ignore = repo.dirstate._ignore
1846 includepat = getattr(ignore, 'includepat', None)
1847 includepat = getattr(ignore, 'includepat', None)
1847 if includepat is not None:
1848 if includepat is not None:
1848 ui.write("%s\n" % includepat)
1849 ui.write("%s\n" % includepat)
1849 else:
1850 else:
1850 raise util.Abort(_("no ignore patterns found"))
1851 raise util.Abort(_("no ignore patterns found"))
1851
1852
1852 @command('debugindex',
1853 @command('debugindex',
1853 [('c', 'changelog', False, _('open changelog')),
1854 [('c', 'changelog', False, _('open changelog')),
1854 ('m', 'manifest', False, _('open manifest')),
1855 ('m', 'manifest', False, _('open manifest')),
1855 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
1856 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
1856 _('[-f FORMAT] -c|-m|FILE'))
1857 _('[-f FORMAT] -c|-m|FILE'))
1857 def debugindex(ui, repo, file_ = None, **opts):
1858 def debugindex(ui, repo, file_ = None, **opts):
1858 """dump the contents of an index file"""
1859 """dump the contents of an index file"""
1859 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
1860 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
1860 format = opts.get('format', 0)
1861 format = opts.get('format', 0)
1861 if format not in (0, 1):
1862 if format not in (0, 1):
1862 raise util.Abort(_("unknown format %d") % format)
1863 raise util.Abort(_("unknown format %d") % format)
1863
1864
1864 generaldelta = r.version & revlog.REVLOGGENERALDELTA
1865 generaldelta = r.version & revlog.REVLOGGENERALDELTA
1865 if generaldelta:
1866 if generaldelta:
1866 basehdr = ' delta'
1867 basehdr = ' delta'
1867 else:
1868 else:
1868 basehdr = ' base'
1869 basehdr = ' base'
1869
1870
1870 if format == 0:
1871 if format == 0:
1871 ui.write(" rev offset length " + basehdr + " linkrev"
1872 ui.write(" rev offset length " + basehdr + " linkrev"
1872 " nodeid p1 p2\n")
1873 " nodeid p1 p2\n")
1873 elif format == 1:
1874 elif format == 1:
1874 ui.write(" rev flag offset length"
1875 ui.write(" rev flag offset length"
1875 " size " + basehdr + " link p1 p2 nodeid\n")
1876 " size " + basehdr + " link p1 p2 nodeid\n")
1876
1877
1877 for i in r:
1878 for i in r:
1878 node = r.node(i)
1879 node = r.node(i)
1879 if generaldelta:
1880 if generaldelta:
1880 base = r.deltaparent(i)
1881 base = r.deltaparent(i)
1881 else:
1882 else:
1882 base = r.chainbase(i)
1883 base = r.chainbase(i)
1883 if format == 0:
1884 if format == 0:
1884 try:
1885 try:
1885 pp = r.parents(node)
1886 pp = r.parents(node)
1886 except:
1887 except:
1887 pp = [nullid, nullid]
1888 pp = [nullid, nullid]
1888 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1889 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1889 i, r.start(i), r.length(i), base, r.linkrev(i),
1890 i, r.start(i), r.length(i), base, r.linkrev(i),
1890 short(node), short(pp[0]), short(pp[1])))
1891 short(node), short(pp[0]), short(pp[1])))
1891 elif format == 1:
1892 elif format == 1:
1892 pr = r.parentrevs(i)
1893 pr = r.parentrevs(i)
1893 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1894 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1894 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1895 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1895 base, r.linkrev(i), pr[0], pr[1], short(node)))
1896 base, r.linkrev(i), pr[0], pr[1], short(node)))
1896
1897
1897 @command('debugindexdot', [], _('FILE'))
1898 @command('debugindexdot', [], _('FILE'))
1898 def debugindexdot(ui, repo, file_):
1899 def debugindexdot(ui, repo, file_):
1899 """dump an index DAG as a graphviz dot file"""
1900 """dump an index DAG as a graphviz dot file"""
1900 r = None
1901 r = None
1901 if repo:
1902 if repo:
1902 filelog = repo.file(file_)
1903 filelog = repo.file(file_)
1903 if len(filelog):
1904 if len(filelog):
1904 r = filelog
1905 r = filelog
1905 if not r:
1906 if not r:
1906 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1907 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1907 ui.write("digraph G {\n")
1908 ui.write("digraph G {\n")
1908 for i in r:
1909 for i in r:
1909 node = r.node(i)
1910 node = r.node(i)
1910 pp = r.parents(node)
1911 pp = r.parents(node)
1911 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1912 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1912 if pp[1] != nullid:
1913 if pp[1] != nullid:
1913 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1914 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1914 ui.write("}\n")
1915 ui.write("}\n")
1915
1916
1916 @command('debuginstall', [], '')
1917 @command('debuginstall', [], '')
1917 def debuginstall(ui):
1918 def debuginstall(ui):
1918 '''test Mercurial installation
1919 '''test Mercurial installation
1919
1920
1920 Returns 0 on success.
1921 Returns 0 on success.
1921 '''
1922 '''
1922
1923
1923 def writetemp(contents):
1924 def writetemp(contents):
1924 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1925 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1925 f = os.fdopen(fd, "wb")
1926 f = os.fdopen(fd, "wb")
1926 f.write(contents)
1927 f.write(contents)
1927 f.close()
1928 f.close()
1928 return name
1929 return name
1929
1930
1930 problems = 0
1931 problems = 0
1931
1932
1932 # encoding
1933 # encoding
1933 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1934 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1934 try:
1935 try:
1935 encoding.fromlocal("test")
1936 encoding.fromlocal("test")
1936 except util.Abort, inst:
1937 except util.Abort, inst:
1937 ui.write(" %s\n" % inst)
1938 ui.write(" %s\n" % inst)
1938 ui.write(_(" (check that your locale is properly set)\n"))
1939 ui.write(_(" (check that your locale is properly set)\n"))
1939 problems += 1
1940 problems += 1
1940
1941
1941 # compiled modules
1942 # compiled modules
1942 ui.status(_("Checking installed modules (%s)...\n")
1943 ui.status(_("Checking installed modules (%s)...\n")
1943 % os.path.dirname(__file__))
1944 % os.path.dirname(__file__))
1944 try:
1945 try:
1945 import bdiff, mpatch, base85, osutil
1946 import bdiff, mpatch, base85, osutil
1946 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
1947 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
1947 except Exception, inst:
1948 except Exception, inst:
1948 ui.write(" %s\n" % inst)
1949 ui.write(" %s\n" % inst)
1949 ui.write(_(" One or more extensions could not be found"))
1950 ui.write(_(" One or more extensions could not be found"))
1950 ui.write(_(" (check that you compiled the extensions)\n"))
1951 ui.write(_(" (check that you compiled the extensions)\n"))
1951 problems += 1
1952 problems += 1
1952
1953
1953 # templates
1954 # templates
1954 import templater
1955 import templater
1955 p = templater.templatepath()
1956 p = templater.templatepath()
1956 ui.status(_("Checking templates (%s)...\n") % ' '.join(p))
1957 ui.status(_("Checking templates (%s)...\n") % ' '.join(p))
1957 try:
1958 try:
1958 templater.templater(templater.templatepath("map-cmdline.default"))
1959 templater.templater(templater.templatepath("map-cmdline.default"))
1959 except Exception, inst:
1960 except Exception, inst:
1960 ui.write(" %s\n" % inst)
1961 ui.write(" %s\n" % inst)
1961 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1962 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1962 problems += 1
1963 problems += 1
1963
1964
1964 # editor
1965 # editor
1965 ui.status(_("Checking commit editor...\n"))
1966 ui.status(_("Checking commit editor...\n"))
1966 editor = ui.geteditor()
1967 editor = ui.geteditor()
1967 cmdpath = util.findexe(editor) or util.findexe(editor.split()[0])
1968 cmdpath = util.findexe(editor) or util.findexe(editor.split()[0])
1968 if not cmdpath:
1969 if not cmdpath:
1969 if editor == 'vi':
1970 if editor == 'vi':
1970 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1971 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1971 ui.write(_(" (specify a commit editor in your configuration"
1972 ui.write(_(" (specify a commit editor in your configuration"
1972 " file)\n"))
1973 " file)\n"))
1973 else:
1974 else:
1974 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1975 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1975 ui.write(_(" (specify a commit editor in your configuration"
1976 ui.write(_(" (specify a commit editor in your configuration"
1976 " file)\n"))
1977 " file)\n"))
1977 problems += 1
1978 problems += 1
1978
1979
1979 # check username
1980 # check username
1980 ui.status(_("Checking username...\n"))
1981 ui.status(_("Checking username...\n"))
1981 try:
1982 try:
1982 ui.username()
1983 ui.username()
1983 except util.Abort, e:
1984 except util.Abort, e:
1984 ui.write(" %s\n" % e)
1985 ui.write(" %s\n" % e)
1985 ui.write(_(" (specify a username in your configuration file)\n"))
1986 ui.write(_(" (specify a username in your configuration file)\n"))
1986 problems += 1
1987 problems += 1
1987
1988
1988 if not problems:
1989 if not problems:
1989 ui.status(_("No problems detected\n"))
1990 ui.status(_("No problems detected\n"))
1990 else:
1991 else:
1991 ui.write(_("%s problems detected,"
1992 ui.write(_("%s problems detected,"
1992 " please check your install!\n") % problems)
1993 " please check your install!\n") % problems)
1993
1994
1994 return problems
1995 return problems
1995
1996
1996 @command('debugknown', [], _('REPO ID...'))
1997 @command('debugknown', [], _('REPO ID...'))
1997 def debugknown(ui, repopath, *ids, **opts):
1998 def debugknown(ui, repopath, *ids, **opts):
1998 """test whether node ids are known to a repo
1999 """test whether node ids are known to a repo
1999
2000
2000 Every ID must be a full-length hex node id string. Returns a list of 0s and 1s
2001 Every ID must be a full-length hex node id string. Returns a list of 0s and 1s
2001 indicating unknown/known.
2002 indicating unknown/known.
2002 """
2003 """
2003 repo = hg.peer(ui, opts, repopath)
2004 repo = hg.peer(ui, opts, repopath)
2004 if not repo.capable('known'):
2005 if not repo.capable('known'):
2005 raise util.Abort("known() not supported by target repository")
2006 raise util.Abort("known() not supported by target repository")
2006 flags = repo.known([bin(s) for s in ids])
2007 flags = repo.known([bin(s) for s in ids])
2007 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
2008 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
2008
2009
2009 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'))
2010 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'))
2010 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
2011 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
2011 '''access the pushkey key/value protocol
2012 '''access the pushkey key/value protocol
2012
2013
2013 With two args, list the keys in the given namespace.
2014 With two args, list the keys in the given namespace.
2014
2015
2015 With five args, set a key to new if it currently is set to old.
2016 With five args, set a key to new if it currently is set to old.
2016 Reports success or failure.
2017 Reports success or failure.
2017 '''
2018 '''
2018
2019
2019 target = hg.peer(ui, {}, repopath)
2020 target = hg.peer(ui, {}, repopath)
2020 if keyinfo:
2021 if keyinfo:
2021 key, old, new = keyinfo
2022 key, old, new = keyinfo
2022 r = target.pushkey(namespace, key, old, new)
2023 r = target.pushkey(namespace, key, old, new)
2023 ui.status(str(r) + '\n')
2024 ui.status(str(r) + '\n')
2024 return not r
2025 return not r
2025 else:
2026 else:
2026 for k, v in target.listkeys(namespace).iteritems():
2027 for k, v in target.listkeys(namespace).iteritems():
2027 ui.write("%s\t%s\n" % (k.encode('string-escape'),
2028 ui.write("%s\t%s\n" % (k.encode('string-escape'),
2028 v.encode('string-escape')))
2029 v.encode('string-escape')))
2029
2030
2030 @command('debugpvec', [], _('A B'))
2031 @command('debugpvec', [], _('A B'))
2031 def debugpvec(ui, repo, a, b=None):
2032 def debugpvec(ui, repo, a, b=None):
2032 ca = scmutil.revsingle(repo, a)
2033 ca = scmutil.revsingle(repo, a)
2033 cb = scmutil.revsingle(repo, b)
2034 cb = scmutil.revsingle(repo, b)
2034 pa = pvec.ctxpvec(ca)
2035 pa = pvec.ctxpvec(ca)
2035 pb = pvec.ctxpvec(cb)
2036 pb = pvec.ctxpvec(cb)
2036 if pa == pb:
2037 if pa == pb:
2037 rel = "="
2038 rel = "="
2038 elif pa > pb:
2039 elif pa > pb:
2039 rel = ">"
2040 rel = ">"
2040 elif pa < pb:
2041 elif pa < pb:
2041 rel = "<"
2042 rel = "<"
2042 elif pa | pb:
2043 elif pa | pb:
2043 rel = "|"
2044 rel = "|"
2044 ui.write(_("a: %s\n") % pa)
2045 ui.write(_("a: %s\n") % pa)
2045 ui.write(_("b: %s\n") % pb)
2046 ui.write(_("b: %s\n") % pb)
2046 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
2047 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
2047 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
2048 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
2048 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
2049 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
2049 pa.distance(pb), rel))
2050 pa.distance(pb), rel))
2050
2051
2051 @command('debugrebuildstate',
2052 @command('debugrebuildstate',
2052 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
2053 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
2053 _('[-r REV] [REV]'))
2054 _('[-r REV] [REV]'))
2054 def debugrebuildstate(ui, repo, rev="tip"):
2055 def debugrebuildstate(ui, repo, rev="tip"):
2055 """rebuild the dirstate as it would look like for the given revision"""
2056 """rebuild the dirstate as it would look like for the given revision"""
2056 ctx = scmutil.revsingle(repo, rev)
2057 ctx = scmutil.revsingle(repo, rev)
2057 wlock = repo.wlock()
2058 wlock = repo.wlock()
2058 try:
2059 try:
2059 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
2060 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
2060 finally:
2061 finally:
2061 wlock.release()
2062 wlock.release()
2062
2063
2063 @command('debugrename',
2064 @command('debugrename',
2064 [('r', 'rev', '', _('revision to debug'), _('REV'))],
2065 [('r', 'rev', '', _('revision to debug'), _('REV'))],
2065 _('[-r REV] FILE'))
2066 _('[-r REV] FILE'))
2066 def debugrename(ui, repo, file1, *pats, **opts):
2067 def debugrename(ui, repo, file1, *pats, **opts):
2067 """dump rename information"""
2068 """dump rename information"""
2068
2069
2069 ctx = scmutil.revsingle(repo, opts.get('rev'))
2070 ctx = scmutil.revsingle(repo, opts.get('rev'))
2070 m = scmutil.match(ctx, (file1,) + pats, opts)
2071 m = scmutil.match(ctx, (file1,) + pats, opts)
2071 for abs in ctx.walk(m):
2072 for abs in ctx.walk(m):
2072 fctx = ctx[abs]
2073 fctx = ctx[abs]
2073 o = fctx.filelog().renamed(fctx.filenode())
2074 o = fctx.filelog().renamed(fctx.filenode())
2074 rel = m.rel(abs)
2075 rel = m.rel(abs)
2075 if o:
2076 if o:
2076 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2077 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2077 else:
2078 else:
2078 ui.write(_("%s not renamed\n") % rel)
2079 ui.write(_("%s not renamed\n") % rel)
2079
2080
2080 @command('debugrevlog',
2081 @command('debugrevlog',
2081 [('c', 'changelog', False, _('open changelog')),
2082 [('c', 'changelog', False, _('open changelog')),
2082 ('m', 'manifest', False, _('open manifest')),
2083 ('m', 'manifest', False, _('open manifest')),
2083 ('d', 'dump', False, _('dump index data'))],
2084 ('d', 'dump', False, _('dump index data'))],
2084 _('-c|-m|FILE'))
2085 _('-c|-m|FILE'))
2085 def debugrevlog(ui, repo, file_ = None, **opts):
2086 def debugrevlog(ui, repo, file_ = None, **opts):
2086 """show data and statistics about a revlog"""
2087 """show data and statistics about a revlog"""
2087 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
2088 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
2088
2089
2089 if opts.get("dump"):
2090 if opts.get("dump"):
2090 numrevs = len(r)
2091 numrevs = len(r)
2091 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
2092 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
2092 " rawsize totalsize compression heads\n")
2093 " rawsize totalsize compression heads\n")
2093 ts = 0
2094 ts = 0
2094 heads = set()
2095 heads = set()
2095 for rev in xrange(numrevs):
2096 for rev in xrange(numrevs):
2096 dbase = r.deltaparent(rev)
2097 dbase = r.deltaparent(rev)
2097 if dbase == -1:
2098 if dbase == -1:
2098 dbase = rev
2099 dbase = rev
2099 cbase = r.chainbase(rev)
2100 cbase = r.chainbase(rev)
2100 p1, p2 = r.parentrevs(rev)
2101 p1, p2 = r.parentrevs(rev)
2101 rs = r.rawsize(rev)
2102 rs = r.rawsize(rev)
2102 ts = ts + rs
2103 ts = ts + rs
2103 heads -= set(r.parentrevs(rev))
2104 heads -= set(r.parentrevs(rev))
2104 heads.add(rev)
2105 heads.add(rev)
2105 ui.write("%d %d %d %d %d %d %d %d %d %d %d %d %d\n" %
2106 ui.write("%d %d %d %d %d %d %d %d %d %d %d %d %d\n" %
2106 (rev, p1, p2, r.start(rev), r.end(rev),
2107 (rev, p1, p2, r.start(rev), r.end(rev),
2107 r.start(dbase), r.start(cbase),
2108 r.start(dbase), r.start(cbase),
2108 r.start(p1), r.start(p2),
2109 r.start(p1), r.start(p2),
2109 rs, ts, ts / r.end(rev), len(heads)))
2110 rs, ts, ts / r.end(rev), len(heads)))
2110 return 0
2111 return 0
2111
2112
2112 v = r.version
2113 v = r.version
2113 format = v & 0xFFFF
2114 format = v & 0xFFFF
2114 flags = []
2115 flags = []
2115 gdelta = False
2116 gdelta = False
2116 if v & revlog.REVLOGNGINLINEDATA:
2117 if v & revlog.REVLOGNGINLINEDATA:
2117 flags.append('inline')
2118 flags.append('inline')
2118 if v & revlog.REVLOGGENERALDELTA:
2119 if v & revlog.REVLOGGENERALDELTA:
2119 gdelta = True
2120 gdelta = True
2120 flags.append('generaldelta')
2121 flags.append('generaldelta')
2121 if not flags:
2122 if not flags:
2122 flags = ['(none)']
2123 flags = ['(none)']
2123
2124
2124 nummerges = 0
2125 nummerges = 0
2125 numfull = 0
2126 numfull = 0
2126 numprev = 0
2127 numprev = 0
2127 nump1 = 0
2128 nump1 = 0
2128 nump2 = 0
2129 nump2 = 0
2129 numother = 0
2130 numother = 0
2130 nump1prev = 0
2131 nump1prev = 0
2131 nump2prev = 0
2132 nump2prev = 0
2132 chainlengths = []
2133 chainlengths = []
2133
2134
2134 datasize = [None, 0, 0L]
2135 datasize = [None, 0, 0L]
2135 fullsize = [None, 0, 0L]
2136 fullsize = [None, 0, 0L]
2136 deltasize = [None, 0, 0L]
2137 deltasize = [None, 0, 0L]
2137
2138
2138 def addsize(size, l):
2139 def addsize(size, l):
2139 if l[0] is None or size < l[0]:
2140 if l[0] is None or size < l[0]:
2140 l[0] = size
2141 l[0] = size
2141 if size > l[1]:
2142 if size > l[1]:
2142 l[1] = size
2143 l[1] = size
2143 l[2] += size
2144 l[2] += size
2144
2145
2145 numrevs = len(r)
2146 numrevs = len(r)
2146 for rev in xrange(numrevs):
2147 for rev in xrange(numrevs):
2147 p1, p2 = r.parentrevs(rev)
2148 p1, p2 = r.parentrevs(rev)
2148 delta = r.deltaparent(rev)
2149 delta = r.deltaparent(rev)
2149 if format > 0:
2150 if format > 0:
2150 addsize(r.rawsize(rev), datasize)
2151 addsize(r.rawsize(rev), datasize)
2151 if p2 != nullrev:
2152 if p2 != nullrev:
2152 nummerges += 1
2153 nummerges += 1
2153 size = r.length(rev)
2154 size = r.length(rev)
2154 if delta == nullrev:
2155 if delta == nullrev:
2155 chainlengths.append(0)
2156 chainlengths.append(0)
2156 numfull += 1
2157 numfull += 1
2157 addsize(size, fullsize)
2158 addsize(size, fullsize)
2158 else:
2159 else:
2159 chainlengths.append(chainlengths[delta] + 1)
2160 chainlengths.append(chainlengths[delta] + 1)
2160 addsize(size, deltasize)
2161 addsize(size, deltasize)
2161 if delta == rev - 1:
2162 if delta == rev - 1:
2162 numprev += 1
2163 numprev += 1
2163 if delta == p1:
2164 if delta == p1:
2164 nump1prev += 1
2165 nump1prev += 1
2165 elif delta == p2:
2166 elif delta == p2:
2166 nump2prev += 1
2167 nump2prev += 1
2167 elif delta == p1:
2168 elif delta == p1:
2168 nump1 += 1
2169 nump1 += 1
2169 elif delta == p2:
2170 elif delta == p2:
2170 nump2 += 1
2171 nump2 += 1
2171 elif delta != nullrev:
2172 elif delta != nullrev:
2172 numother += 1
2173 numother += 1
2173
2174
2174 numdeltas = numrevs - numfull
2175 numdeltas = numrevs - numfull
2175 numoprev = numprev - nump1prev - nump2prev
2176 numoprev = numprev - nump1prev - nump2prev
2176 totalrawsize = datasize[2]
2177 totalrawsize = datasize[2]
2177 datasize[2] /= numrevs
2178 datasize[2] /= numrevs
2178 fulltotal = fullsize[2]
2179 fulltotal = fullsize[2]
2179 fullsize[2] /= numfull
2180 fullsize[2] /= numfull
2180 deltatotal = deltasize[2]
2181 deltatotal = deltasize[2]
2181 deltasize[2] /= numrevs - numfull
2182 deltasize[2] /= numrevs - numfull
2182 totalsize = fulltotal + deltatotal
2183 totalsize = fulltotal + deltatotal
2183 avgchainlen = sum(chainlengths) / numrevs
2184 avgchainlen = sum(chainlengths) / numrevs
2184 compratio = totalrawsize / totalsize
2185 compratio = totalrawsize / totalsize
2185
2186
2186 basedfmtstr = '%%%dd\n'
2187 basedfmtstr = '%%%dd\n'
2187 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2188 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2188
2189
2189 def dfmtstr(max):
2190 def dfmtstr(max):
2190 return basedfmtstr % len(str(max))
2191 return basedfmtstr % len(str(max))
2191 def pcfmtstr(max, padding=0):
2192 def pcfmtstr(max, padding=0):
2192 return basepcfmtstr % (len(str(max)), ' ' * padding)
2193 return basepcfmtstr % (len(str(max)), ' ' * padding)
2193
2194
2194 def pcfmt(value, total):
2195 def pcfmt(value, total):
2195 return (value, 100 * float(value) / total)
2196 return (value, 100 * float(value) / total)
2196
2197
2197 ui.write('format : %d\n' % format)
2198 ui.write('format : %d\n' % format)
2198 ui.write('flags : %s\n' % ', '.join(flags))
2199 ui.write('flags : %s\n' % ', '.join(flags))
2199
2200
2200 ui.write('\n')
2201 ui.write('\n')
2201 fmt = pcfmtstr(totalsize)
2202 fmt = pcfmtstr(totalsize)
2202 fmt2 = dfmtstr(totalsize)
2203 fmt2 = dfmtstr(totalsize)
2203 ui.write('revisions : ' + fmt2 % numrevs)
2204 ui.write('revisions : ' + fmt2 % numrevs)
2204 ui.write(' merges : ' + fmt % pcfmt(nummerges, numrevs))
2205 ui.write(' merges : ' + fmt % pcfmt(nummerges, numrevs))
2205 ui.write(' normal : ' + fmt % pcfmt(numrevs - nummerges, numrevs))
2206 ui.write(' normal : ' + fmt % pcfmt(numrevs - nummerges, numrevs))
2206 ui.write('revisions : ' + fmt2 % numrevs)
2207 ui.write('revisions : ' + fmt2 % numrevs)
2207 ui.write(' full : ' + fmt % pcfmt(numfull, numrevs))
2208 ui.write(' full : ' + fmt % pcfmt(numfull, numrevs))
2208 ui.write(' deltas : ' + fmt % pcfmt(numdeltas, numrevs))
2209 ui.write(' deltas : ' + fmt % pcfmt(numdeltas, numrevs))
2209 ui.write('revision size : ' + fmt2 % totalsize)
2210 ui.write('revision size : ' + fmt2 % totalsize)
2210 ui.write(' full : ' + fmt % pcfmt(fulltotal, totalsize))
2211 ui.write(' full : ' + fmt % pcfmt(fulltotal, totalsize))
2211 ui.write(' deltas : ' + fmt % pcfmt(deltatotal, totalsize))
2212 ui.write(' deltas : ' + fmt % pcfmt(deltatotal, totalsize))
2212
2213
2213 ui.write('\n')
2214 ui.write('\n')
2214 fmt = dfmtstr(max(avgchainlen, compratio))
2215 fmt = dfmtstr(max(avgchainlen, compratio))
2215 ui.write('avg chain length : ' + fmt % avgchainlen)
2216 ui.write('avg chain length : ' + fmt % avgchainlen)
2216 ui.write('compression ratio : ' + fmt % compratio)
2217 ui.write('compression ratio : ' + fmt % compratio)
2217
2218
2218 if format > 0:
2219 if format > 0:
2219 ui.write('\n')
2220 ui.write('\n')
2220 ui.write('uncompressed data size (min/max/avg) : %d / %d / %d\n'
2221 ui.write('uncompressed data size (min/max/avg) : %d / %d / %d\n'
2221 % tuple(datasize))
2222 % tuple(datasize))
2222 ui.write('full revision size (min/max/avg) : %d / %d / %d\n'
2223 ui.write('full revision size (min/max/avg) : %d / %d / %d\n'
2223 % tuple(fullsize))
2224 % tuple(fullsize))
2224 ui.write('delta size (min/max/avg) : %d / %d / %d\n'
2225 ui.write('delta size (min/max/avg) : %d / %d / %d\n'
2225 % tuple(deltasize))
2226 % tuple(deltasize))
2226
2227
2227 if numdeltas > 0:
2228 if numdeltas > 0:
2228 ui.write('\n')
2229 ui.write('\n')
2229 fmt = pcfmtstr(numdeltas)
2230 fmt = pcfmtstr(numdeltas)
2230 fmt2 = pcfmtstr(numdeltas, 4)
2231 fmt2 = pcfmtstr(numdeltas, 4)
2231 ui.write('deltas against prev : ' + fmt % pcfmt(numprev, numdeltas))
2232 ui.write('deltas against prev : ' + fmt % pcfmt(numprev, numdeltas))
2232 if numprev > 0:
2233 if numprev > 0:
2233 ui.write(' where prev = p1 : ' + fmt2 % pcfmt(nump1prev, numprev))
2234 ui.write(' where prev = p1 : ' + fmt2 % pcfmt(nump1prev, numprev))
2234 ui.write(' where prev = p2 : ' + fmt2 % pcfmt(nump2prev, numprev))
2235 ui.write(' where prev = p2 : ' + fmt2 % pcfmt(nump2prev, numprev))
2235 ui.write(' other : ' + fmt2 % pcfmt(numoprev, numprev))
2236 ui.write(' other : ' + fmt2 % pcfmt(numoprev, numprev))
2236 if gdelta:
2237 if gdelta:
2237 ui.write('deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas))
2238 ui.write('deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas))
2238 ui.write('deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas))
2239 ui.write('deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas))
2239 ui.write('deltas against other : ' + fmt % pcfmt(numother, numdeltas))
2240 ui.write('deltas against other : ' + fmt % pcfmt(numother, numdeltas))
2240
2241
2241 @command('debugrevspec', [], ('REVSPEC'))
2242 @command('debugrevspec', [], ('REVSPEC'))
2242 def debugrevspec(ui, repo, expr):
2243 def debugrevspec(ui, repo, expr):
2243 """parse and apply a revision specification
2244 """parse and apply a revision specification
2244
2245
2245 Use --verbose to print the parsed tree before and after aliases
2246 Use --verbose to print the parsed tree before and after aliases
2246 expansion.
2247 expansion.
2247 """
2248 """
2248 if ui.verbose:
2249 if ui.verbose:
2249 tree = revset.parse(expr)[0]
2250 tree = revset.parse(expr)[0]
2250 ui.note(revset.prettyformat(tree), "\n")
2251 ui.note(revset.prettyformat(tree), "\n")
2251 newtree = revset.findaliases(ui, tree)
2252 newtree = revset.findaliases(ui, tree)
2252 if newtree != tree:
2253 if newtree != tree:
2253 ui.note(revset.prettyformat(newtree), "\n")
2254 ui.note(revset.prettyformat(newtree), "\n")
2254 func = revset.match(ui, expr)
2255 func = revset.match(ui, expr)
2255 for c in func(repo, range(len(repo))):
2256 for c in func(repo, range(len(repo))):
2256 ui.write("%s\n" % c)
2257 ui.write("%s\n" % c)
2257
2258
2258 @command('debugsetparents', [], _('REV1 [REV2]'))
2259 @command('debugsetparents', [], _('REV1 [REV2]'))
2259 def debugsetparents(ui, repo, rev1, rev2=None):
2260 def debugsetparents(ui, repo, rev1, rev2=None):
2260 """manually set the parents of the current working directory
2261 """manually set the parents of the current working directory
2261
2262
2262 This is useful for writing repository conversion tools, but should
2263 This is useful for writing repository conversion tools, but should
2263 be used with care.
2264 be used with care.
2264
2265
2265 Returns 0 on success.
2266 Returns 0 on success.
2266 """
2267 """
2267
2268
2268 r1 = scmutil.revsingle(repo, rev1).node()
2269 r1 = scmutil.revsingle(repo, rev1).node()
2269 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2270 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2270
2271
2271 wlock = repo.wlock()
2272 wlock = repo.wlock()
2272 try:
2273 try:
2273 repo.setparents(r1, r2)
2274 repo.setparents(r1, r2)
2274 finally:
2275 finally:
2275 wlock.release()
2276 wlock.release()
2276
2277
2277 @command('debugstate',
2278 @command('debugstate',
2278 [('', 'nodates', None, _('do not display the saved mtime')),
2279 [('', 'nodates', None, _('do not display the saved mtime')),
2279 ('', 'datesort', None, _('sort by saved mtime'))],
2280 ('', 'datesort', None, _('sort by saved mtime'))],
2280 _('[OPTION]...'))
2281 _('[OPTION]...'))
2281 def debugstate(ui, repo, nodates=None, datesort=None):
2282 def debugstate(ui, repo, nodates=None, datesort=None):
2282 """show the contents of the current dirstate"""
2283 """show the contents of the current dirstate"""
2283 timestr = ""
2284 timestr = ""
2284 showdate = not nodates
2285 showdate = not nodates
2285 if datesort:
2286 if datesort:
2286 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2287 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2287 else:
2288 else:
2288 keyfunc = None # sort by filename
2289 keyfunc = None # sort by filename
2289 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2290 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2290 if showdate:
2291 if showdate:
2291 if ent[3] == -1:
2292 if ent[3] == -1:
2292 # Pad or slice to locale representation
2293 # Pad or slice to locale representation
2293 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
2294 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
2294 time.localtime(0)))
2295 time.localtime(0)))
2295 timestr = 'unset'
2296 timestr = 'unset'
2296 timestr = (timestr[:locale_len] +
2297 timestr = (timestr[:locale_len] +
2297 ' ' * (locale_len - len(timestr)))
2298 ' ' * (locale_len - len(timestr)))
2298 else:
2299 else:
2299 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2300 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2300 time.localtime(ent[3]))
2301 time.localtime(ent[3]))
2301 if ent[1] & 020000:
2302 if ent[1] & 020000:
2302 mode = 'lnk'
2303 mode = 'lnk'
2303 else:
2304 else:
2304 mode = '%3o' % (ent[1] & 0777 & ~util.umask)
2305 mode = '%3o' % (ent[1] & 0777 & ~util.umask)
2305 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2306 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2306 for f in repo.dirstate.copies():
2307 for f in repo.dirstate.copies():
2307 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2308 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2308
2309
2309 @command('debugsub',
2310 @command('debugsub',
2310 [('r', 'rev', '',
2311 [('r', 'rev', '',
2311 _('revision to check'), _('REV'))],
2312 _('revision to check'), _('REV'))],
2312 _('[-r REV] [REV]'))
2313 _('[-r REV] [REV]'))
2313 def debugsub(ui, repo, rev=None):
2314 def debugsub(ui, repo, rev=None):
2314 ctx = scmutil.revsingle(repo, rev, None)
2315 ctx = scmutil.revsingle(repo, rev, None)
2315 for k, v in sorted(ctx.substate.items()):
2316 for k, v in sorted(ctx.substate.items()):
2316 ui.write('path %s\n' % k)
2317 ui.write('path %s\n' % k)
2317 ui.write(' source %s\n' % v[0])
2318 ui.write(' source %s\n' % v[0])
2318 ui.write(' revision %s\n' % v[1])
2319 ui.write(' revision %s\n' % v[1])
2319
2320
2320 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'))
2321 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'))
2321 def debugwalk(ui, repo, *pats, **opts):
2322 def debugwalk(ui, repo, *pats, **opts):
2322 """show how files match on given patterns"""
2323 """show how files match on given patterns"""
2323 m = scmutil.match(repo[None], pats, opts)
2324 m = scmutil.match(repo[None], pats, opts)
2324 items = list(repo.walk(m))
2325 items = list(repo.walk(m))
2325 if not items:
2326 if not items:
2326 return
2327 return
2327 fmt = 'f %%-%ds %%-%ds %%s' % (
2328 fmt = 'f %%-%ds %%-%ds %%s' % (
2328 max([len(abs) for abs in items]),
2329 max([len(abs) for abs in items]),
2329 max([len(m.rel(abs)) for abs in items]))
2330 max([len(m.rel(abs)) for abs in items]))
2330 for abs in items:
2331 for abs in items:
2331 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
2332 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
2332 ui.write("%s\n" % line.rstrip())
2333 ui.write("%s\n" % line.rstrip())
2333
2334
2334 @command('debugwireargs',
2335 @command('debugwireargs',
2335 [('', 'three', '', 'three'),
2336 [('', 'three', '', 'three'),
2336 ('', 'four', '', 'four'),
2337 ('', 'four', '', 'four'),
2337 ('', 'five', '', 'five'),
2338 ('', 'five', '', 'five'),
2338 ] + remoteopts,
2339 ] + remoteopts,
2339 _('REPO [OPTIONS]... [ONE [TWO]]'))
2340 _('REPO [OPTIONS]... [ONE [TWO]]'))
2340 def debugwireargs(ui, repopath, *vals, **opts):
2341 def debugwireargs(ui, repopath, *vals, **opts):
2341 repo = hg.peer(ui, opts, repopath)
2342 repo = hg.peer(ui, opts, repopath)
2342 for opt in remoteopts:
2343 for opt in remoteopts:
2343 del opts[opt[1]]
2344 del opts[opt[1]]
2344 args = {}
2345 args = {}
2345 for k, v in opts.iteritems():
2346 for k, v in opts.iteritems():
2346 if v:
2347 if v:
2347 args[k] = v
2348 args[k] = v
2348 # run twice to check that we don't mess up the stream for the next command
2349 # run twice to check that we don't mess up the stream for the next command
2349 res1 = repo.debugwireargs(*vals, **args)
2350 res1 = repo.debugwireargs(*vals, **args)
2350 res2 = repo.debugwireargs(*vals, **args)
2351 res2 = repo.debugwireargs(*vals, **args)
2351 ui.write("%s\n" % res1)
2352 ui.write("%s\n" % res1)
2352 if res1 != res2:
2353 if res1 != res2:
2353 ui.warn("%s\n" % res2)
2354 ui.warn("%s\n" % res2)
2354
2355
2355 @command('^diff',
2356 @command('^diff',
2356 [('r', 'rev', [], _('revision'), _('REV')),
2357 [('r', 'rev', [], _('revision'), _('REV')),
2357 ('c', 'change', '', _('change made by revision'), _('REV'))
2358 ('c', 'change', '', _('change made by revision'), _('REV'))
2358 ] + diffopts + diffopts2 + walkopts + subrepoopts,
2359 ] + diffopts + diffopts2 + walkopts + subrepoopts,
2359 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'))
2360 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'))
2360 def diff(ui, repo, *pats, **opts):
2361 def diff(ui, repo, *pats, **opts):
2361 """diff repository (or selected files)
2362 """diff repository (or selected files)
2362
2363
2363 Show differences between revisions for the specified files.
2364 Show differences between revisions for the specified files.
2364
2365
2365 Differences between files are shown using the unified diff format.
2366 Differences between files are shown using the unified diff format.
2366
2367
2367 .. note::
2368 .. note::
2368 diff may generate unexpected results for merges, as it will
2369 diff may generate unexpected results for merges, as it will
2369 default to comparing against the working directory's first
2370 default to comparing against the working directory's first
2370 parent changeset if no revisions are specified.
2371 parent changeset if no revisions are specified.
2371
2372
2372 When two revision arguments are given, then changes are shown
2373 When two revision arguments are given, then changes are shown
2373 between those revisions. If only one revision is specified then
2374 between those revisions. If only one revision is specified then
2374 that revision is compared to the working directory, and, when no
2375 that revision is compared to the working directory, and, when no
2375 revisions are specified, the working directory files are compared
2376 revisions are specified, the working directory files are compared
2376 to its parent.
2377 to its parent.
2377
2378
2378 Alternatively you can specify -c/--change with a revision to see
2379 Alternatively you can specify -c/--change with a revision to see
2379 the changes in that changeset relative to its first parent.
2380 the changes in that changeset relative to its first parent.
2380
2381
2381 Without the -a/--text option, diff will avoid generating diffs of
2382 Without the -a/--text option, diff will avoid generating diffs of
2382 files it detects as binary. With -a, diff will generate a diff
2383 files it detects as binary. With -a, diff will generate a diff
2383 anyway, probably with undesirable results.
2384 anyway, probably with undesirable results.
2384
2385
2385 Use the -g/--git option to generate diffs in the git extended diff
2386 Use the -g/--git option to generate diffs in the git extended diff
2386 format. For more information, read :hg:`help diffs`.
2387 format. For more information, read :hg:`help diffs`.
2387
2388
2388 .. container:: verbose
2389 .. container:: verbose
2389
2390
2390 Examples:
2391 Examples:
2391
2392
2392 - compare a file in the current working directory to its parent::
2393 - compare a file in the current working directory to its parent::
2393
2394
2394 hg diff foo.c
2395 hg diff foo.c
2395
2396
2396 - compare two historical versions of a directory, with rename info::
2397 - compare two historical versions of a directory, with rename info::
2397
2398
2398 hg diff --git -r 1.0:1.2 lib/
2399 hg diff --git -r 1.0:1.2 lib/
2399
2400
2400 - get change stats relative to the last change on some date::
2401 - get change stats relative to the last change on some date::
2401
2402
2402 hg diff --stat -r "date('may 2')"
2403 hg diff --stat -r "date('may 2')"
2403
2404
2404 - diff all newly-added files that contain a keyword::
2405 - diff all newly-added files that contain a keyword::
2405
2406
2406 hg diff "set:added() and grep(GNU)"
2407 hg diff "set:added() and grep(GNU)"
2407
2408
2408 - compare a revision and its parents::
2409 - compare a revision and its parents::
2409
2410
2410 hg diff -c 9353 # compare against first parent
2411 hg diff -c 9353 # compare against first parent
2411 hg diff -r 9353^:9353 # same using revset syntax
2412 hg diff -r 9353^:9353 # same using revset syntax
2412 hg diff -r 9353^2:9353 # compare against the second parent
2413 hg diff -r 9353^2:9353 # compare against the second parent
2413
2414
2414 Returns 0 on success.
2415 Returns 0 on success.
2415 """
2416 """
2416
2417
2417 revs = opts.get('rev')
2418 revs = opts.get('rev')
2418 change = opts.get('change')
2419 change = opts.get('change')
2419 stat = opts.get('stat')
2420 stat = opts.get('stat')
2420 reverse = opts.get('reverse')
2421 reverse = opts.get('reverse')
2421
2422
2422 if revs and change:
2423 if revs and change:
2423 msg = _('cannot specify --rev and --change at the same time')
2424 msg = _('cannot specify --rev and --change at the same time')
2424 raise util.Abort(msg)
2425 raise util.Abort(msg)
2425 elif change:
2426 elif change:
2426 node2 = scmutil.revsingle(repo, change, None).node()
2427 node2 = scmutil.revsingle(repo, change, None).node()
2427 node1 = repo[node2].p1().node()
2428 node1 = repo[node2].p1().node()
2428 else:
2429 else:
2429 node1, node2 = scmutil.revpair(repo, revs)
2430 node1, node2 = scmutil.revpair(repo, revs)
2430
2431
2431 if reverse:
2432 if reverse:
2432 node1, node2 = node2, node1
2433 node1, node2 = node2, node1
2433
2434
2434 diffopts = patch.diffopts(ui, opts)
2435 diffopts = patch.diffopts(ui, opts)
2435 m = scmutil.match(repo[node2], pats, opts)
2436 m = scmutil.match(repo[node2], pats, opts)
2436 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2437 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2437 listsubrepos=opts.get('subrepos'))
2438 listsubrepos=opts.get('subrepos'))
2438
2439
2439 @command('^export',
2440 @command('^export',
2440 [('o', 'output', '',
2441 [('o', 'output', '',
2441 _('print output to file with formatted name'), _('FORMAT')),
2442 _('print output to file with formatted name'), _('FORMAT')),
2442 ('', 'switch-parent', None, _('diff against the second parent')),
2443 ('', 'switch-parent', None, _('diff against the second parent')),
2443 ('r', 'rev', [], _('revisions to export'), _('REV')),
2444 ('r', 'rev', [], _('revisions to export'), _('REV')),
2444 ] + diffopts,
2445 ] + diffopts,
2445 _('[OPTION]... [-o OUTFILESPEC] REV...'))
2446 _('[OPTION]... [-o OUTFILESPEC] REV...'))
2446 def export(ui, repo, *changesets, **opts):
2447 def export(ui, repo, *changesets, **opts):
2447 """dump the header and diffs for one or more changesets
2448 """dump the header and diffs for one or more changesets
2448
2449
2449 Print the changeset header and diffs for one or more revisions.
2450 Print the changeset header and diffs for one or more revisions.
2450
2451
2451 The information shown in the changeset header is: author, date,
2452 The information shown in the changeset header is: author, date,
2452 branch name (if non-default), changeset hash, parent(s) and commit
2453 branch name (if non-default), changeset hash, parent(s) and commit
2453 comment.
2454 comment.
2454
2455
2455 .. note::
2456 .. note::
2456 export may generate unexpected diff output for merge
2457 export may generate unexpected diff output for merge
2457 changesets, as it will compare the merge changeset against its
2458 changesets, as it will compare the merge changeset against its
2458 first parent only.
2459 first parent only.
2459
2460
2460 Output may be to a file, in which case the name of the file is
2461 Output may be to a file, in which case the name of the file is
2461 given using a format string. The formatting rules are as follows:
2462 given using a format string. The formatting rules are as follows:
2462
2463
2463 :``%%``: literal "%" character
2464 :``%%``: literal "%" character
2464 :``%H``: changeset hash (40 hexadecimal digits)
2465 :``%H``: changeset hash (40 hexadecimal digits)
2465 :``%N``: number of patches being generated
2466 :``%N``: number of patches being generated
2466 :``%R``: changeset revision number
2467 :``%R``: changeset revision number
2467 :``%b``: basename of the exporting repository
2468 :``%b``: basename of the exporting repository
2468 :``%h``: short-form changeset hash (12 hexadecimal digits)
2469 :``%h``: short-form changeset hash (12 hexadecimal digits)
2469 :``%m``: first line of the commit message (only alphanumeric characters)
2470 :``%m``: first line of the commit message (only alphanumeric characters)
2470 :``%n``: zero-padded sequence number, starting at 1
2471 :``%n``: zero-padded sequence number, starting at 1
2471 :``%r``: zero-padded changeset revision number
2472 :``%r``: zero-padded changeset revision number
2472
2473
2473 Without the -a/--text option, export will avoid generating diffs
2474 Without the -a/--text option, export will avoid generating diffs
2474 of files it detects as binary. With -a, export will generate a
2475 of files it detects as binary. With -a, export will generate a
2475 diff anyway, probably with undesirable results.
2476 diff anyway, probably with undesirable results.
2476
2477
2477 Use the -g/--git option to generate diffs in the git extended diff
2478 Use the -g/--git option to generate diffs in the git extended diff
2478 format. See :hg:`help diffs` for more information.
2479 format. See :hg:`help diffs` for more information.
2479
2480
2480 With the --switch-parent option, the diff will be against the
2481 With the --switch-parent option, the diff will be against the
2481 second parent. It can be useful to review a merge.
2482 second parent. It can be useful to review a merge.
2482
2483
2483 .. container:: verbose
2484 .. container:: verbose
2484
2485
2485 Examples:
2486 Examples:
2486
2487
2487 - use export and import to transplant a bugfix to the current
2488 - use export and import to transplant a bugfix to the current
2488 branch::
2489 branch::
2489
2490
2490 hg export -r 9353 | hg import -
2491 hg export -r 9353 | hg import -
2491
2492
2492 - export all the changesets between two revisions to a file with
2493 - export all the changesets between two revisions to a file with
2493 rename information::
2494 rename information::
2494
2495
2495 hg export --git -r 123:150 > changes.txt
2496 hg export --git -r 123:150 > changes.txt
2496
2497
2497 - split outgoing changes into a series of patches with
2498 - split outgoing changes into a series of patches with
2498 descriptive names::
2499 descriptive names::
2499
2500
2500 hg export -r "outgoing()" -o "%n-%m.patch"
2501 hg export -r "outgoing()" -o "%n-%m.patch"
2501
2502
2502 Returns 0 on success.
2503 Returns 0 on success.
2503 """
2504 """
2504 changesets += tuple(opts.get('rev', []))
2505 changesets += tuple(opts.get('rev', []))
2505 revs = scmutil.revrange(repo, changesets)
2506 revs = scmutil.revrange(repo, changesets)
2506 if not revs:
2507 if not revs:
2507 raise util.Abort(_("export requires at least one changeset"))
2508 raise util.Abort(_("export requires at least one changeset"))
2508 if len(revs) > 1:
2509 if len(revs) > 1:
2509 ui.note(_('exporting patches:\n'))
2510 ui.note(_('exporting patches:\n'))
2510 else:
2511 else:
2511 ui.note(_('exporting patch:\n'))
2512 ui.note(_('exporting patch:\n'))
2512 cmdutil.export(repo, revs, template=opts.get('output'),
2513 cmdutil.export(repo, revs, template=opts.get('output'),
2513 switch_parent=opts.get('switch_parent'),
2514 switch_parent=opts.get('switch_parent'),
2514 opts=patch.diffopts(ui, opts))
2515 opts=patch.diffopts(ui, opts))
2515
2516
2516 @command('^forget', walkopts, _('[OPTION]... FILE...'))
2517 @command('^forget', walkopts, _('[OPTION]... FILE...'))
2517 def forget(ui, repo, *pats, **opts):
2518 def forget(ui, repo, *pats, **opts):
2518 """forget the specified files on the next commit
2519 """forget the specified files on the next commit
2519
2520
2520 Mark the specified files so they will no longer be tracked
2521 Mark the specified files so they will no longer be tracked
2521 after the next commit.
2522 after the next commit.
2522
2523
2523 This only removes files from the current branch, not from the
2524 This only removes files from the current branch, not from the
2524 entire project history, and it does not delete them from the
2525 entire project history, and it does not delete them from the
2525 working directory.
2526 working directory.
2526
2527
2527 To undo a forget before the next commit, see :hg:`add`.
2528 To undo a forget before the next commit, see :hg:`add`.
2528
2529
2529 .. container:: verbose
2530 .. container:: verbose
2530
2531
2531 Examples:
2532 Examples:
2532
2533
2533 - forget newly-added binary files::
2534 - forget newly-added binary files::
2534
2535
2535 hg forget "set:added() and binary()"
2536 hg forget "set:added() and binary()"
2536
2537
2537 - forget files that would be excluded by .hgignore::
2538 - forget files that would be excluded by .hgignore::
2538
2539
2539 hg forget "set:hgignore()"
2540 hg forget "set:hgignore()"
2540
2541
2541 Returns 0 on success.
2542 Returns 0 on success.
2542 """
2543 """
2543
2544
2544 if not pats:
2545 if not pats:
2545 raise util.Abort(_('no files specified'))
2546 raise util.Abort(_('no files specified'))
2546
2547
2547 m = scmutil.match(repo[None], pats, opts)
2548 m = scmutil.match(repo[None], pats, opts)
2548 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
2549 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
2549 return rejected and 1 or 0
2550 return rejected and 1 or 0
2550
2551
2551 @command(
2552 @command(
2552 'graft',
2553 'graft',
2553 [('c', 'continue', False, _('resume interrupted graft')),
2554 [('c', 'continue', False, _('resume interrupted graft')),
2554 ('e', 'edit', False, _('invoke editor on commit messages')),
2555 ('e', 'edit', False, _('invoke editor on commit messages')),
2555 ('D', 'currentdate', False,
2556 ('D', 'currentdate', False,
2556 _('record the current date as commit date')),
2557 _('record the current date as commit date')),
2557 ('U', 'currentuser', False,
2558 ('U', 'currentuser', False,
2558 _('record the current user as committer'), _('DATE'))]
2559 _('record the current user as committer'), _('DATE'))]
2559 + commitopts2 + mergetoolopts + dryrunopts,
2560 + commitopts2 + mergetoolopts + dryrunopts,
2560 _('[OPTION]... REVISION...'))
2561 _('[OPTION]... REVISION...'))
2561 def graft(ui, repo, *revs, **opts):
2562 def graft(ui, repo, *revs, **opts):
2562 '''copy changes from other branches onto the current branch
2563 '''copy changes from other branches onto the current branch
2563
2564
2564 This command uses Mercurial's merge logic to copy individual
2565 This command uses Mercurial's merge logic to copy individual
2565 changes from other branches without merging branches in the
2566 changes from other branches without merging branches in the
2566 history graph. This is sometimes known as 'backporting' or
2567 history graph. This is sometimes known as 'backporting' or
2567 'cherry-picking'. By default, graft will copy user, date, and
2568 'cherry-picking'. By default, graft will copy user, date, and
2568 description from the source changesets.
2569 description from the source changesets.
2569
2570
2570 Changesets that are ancestors of the current revision, that have
2571 Changesets that are ancestors of the current revision, that have
2571 already been grafted, or that are merges will be skipped.
2572 already been grafted, or that are merges will be skipped.
2572
2573
2573 If a graft merge results in conflicts, the graft process is
2574 If a graft merge results in conflicts, the graft process is
2574 interrupted so that the current merge can be manually resolved.
2575 interrupted so that the current merge can be manually resolved.
2575 Once all conflicts are addressed, the graft process can be
2576 Once all conflicts are addressed, the graft process can be
2576 continued with the -c/--continue option.
2577 continued with the -c/--continue option.
2577
2578
2578 .. note::
2579 .. note::
2579 The -c/--continue option does not reapply earlier options.
2580 The -c/--continue option does not reapply earlier options.
2580
2581
2581 .. container:: verbose
2582 .. container:: verbose
2582
2583
2583 Examples:
2584 Examples:
2584
2585
2585 - copy a single change to the stable branch and edit its description::
2586 - copy a single change to the stable branch and edit its description::
2586
2587
2587 hg update stable
2588 hg update stable
2588 hg graft --edit 9393
2589 hg graft --edit 9393
2589
2590
2590 - graft a range of changesets with one exception, updating dates::
2591 - graft a range of changesets with one exception, updating dates::
2591
2592
2592 hg graft -D "2085::2093 and not 2091"
2593 hg graft -D "2085::2093 and not 2091"
2593
2594
2594 - continue a graft after resolving conflicts::
2595 - continue a graft after resolving conflicts::
2595
2596
2596 hg graft -c
2597 hg graft -c
2597
2598
2598 - show the source of a grafted changeset::
2599 - show the source of a grafted changeset::
2599
2600
2600 hg log --debug -r tip
2601 hg log --debug -r tip
2601
2602
2602 Returns 0 on successful completion.
2603 Returns 0 on successful completion.
2603 '''
2604 '''
2604
2605
2605 if not opts.get('user') and opts.get('currentuser'):
2606 if not opts.get('user') and opts.get('currentuser'):
2606 opts['user'] = ui.username()
2607 opts['user'] = ui.username()
2607 if not opts.get('date') and opts.get('currentdate'):
2608 if not opts.get('date') and opts.get('currentdate'):
2608 opts['date'] = "%d %d" % util.makedate()
2609 opts['date'] = "%d %d" % util.makedate()
2609
2610
2610 editor = None
2611 editor = None
2611 if opts.get('edit'):
2612 if opts.get('edit'):
2612 editor = cmdutil.commitforceeditor
2613 editor = cmdutil.commitforceeditor
2613
2614
2614 cont = False
2615 cont = False
2615 if opts['continue']:
2616 if opts['continue']:
2616 cont = True
2617 cont = True
2617 if revs:
2618 if revs:
2618 raise util.Abort(_("can't specify --continue and revisions"))
2619 raise util.Abort(_("can't specify --continue and revisions"))
2619 # read in unfinished revisions
2620 # read in unfinished revisions
2620 try:
2621 try:
2621 nodes = repo.opener.read('graftstate').splitlines()
2622 nodes = repo.opener.read('graftstate').splitlines()
2622 revs = [repo[node].rev() for node in nodes]
2623 revs = [repo[node].rev() for node in nodes]
2623 except IOError, inst:
2624 except IOError, inst:
2624 if inst.errno != errno.ENOENT:
2625 if inst.errno != errno.ENOENT:
2625 raise
2626 raise
2626 raise util.Abort(_("no graft state found, can't continue"))
2627 raise util.Abort(_("no graft state found, can't continue"))
2627 else:
2628 else:
2628 cmdutil.bailifchanged(repo)
2629 cmdutil.bailifchanged(repo)
2629 if not revs:
2630 if not revs:
2630 raise util.Abort(_('no revisions specified'))
2631 raise util.Abort(_('no revisions specified'))
2631 revs = scmutil.revrange(repo, revs)
2632 revs = scmutil.revrange(repo, revs)
2632
2633
2633 # check for merges
2634 # check for merges
2634 for rev in repo.revs('%ld and merge()', revs):
2635 for rev in repo.revs('%ld and merge()', revs):
2635 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
2636 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
2636 revs.remove(rev)
2637 revs.remove(rev)
2637 if not revs:
2638 if not revs:
2638 return -1
2639 return -1
2639
2640
2640 # check for ancestors of dest branch
2641 # check for ancestors of dest branch
2641 for rev in repo.revs('::. and %ld', revs):
2642 for rev in repo.revs('::. and %ld', revs):
2642 ui.warn(_('skipping ancestor revision %s\n') % rev)
2643 ui.warn(_('skipping ancestor revision %s\n') % rev)
2643 revs.remove(rev)
2644 revs.remove(rev)
2644 if not revs:
2645 if not revs:
2645 return -1
2646 return -1
2646
2647
2647 # analyze revs for earlier grafts
2648 # analyze revs for earlier grafts
2648 ids = {}
2649 ids = {}
2649 for ctx in repo.set("%ld", revs):
2650 for ctx in repo.set("%ld", revs):
2650 ids[ctx.hex()] = ctx.rev()
2651 ids[ctx.hex()] = ctx.rev()
2651 n = ctx.extra().get('source')
2652 n = ctx.extra().get('source')
2652 if n:
2653 if n:
2653 ids[n] = ctx.rev()
2654 ids[n] = ctx.rev()
2654
2655
2655 # check ancestors for earlier grafts
2656 # check ancestors for earlier grafts
2656 ui.debug('scanning for duplicate grafts\n')
2657 ui.debug('scanning for duplicate grafts\n')
2657 for ctx in repo.set("::. - ::%ld", revs):
2658 for ctx in repo.set("::. - ::%ld", revs):
2658 n = ctx.extra().get('source')
2659 n = ctx.extra().get('source')
2659 if n in ids:
2660 if n in ids:
2660 r = repo[n].rev()
2661 r = repo[n].rev()
2661 if r in revs:
2662 if r in revs:
2662 ui.warn(_('skipping already grafted revision %s\n') % r)
2663 ui.warn(_('skipping already grafted revision %s\n') % r)
2663 revs.remove(r)
2664 revs.remove(r)
2664 elif ids[n] in revs:
2665 elif ids[n] in revs:
2665 ui.warn(_('skipping already grafted revision %s '
2666 ui.warn(_('skipping already grafted revision %s '
2666 '(same origin %d)\n') % (ids[n], r))
2667 '(same origin %d)\n') % (ids[n], r))
2667 revs.remove(ids[n])
2668 revs.remove(ids[n])
2668 elif ctx.hex() in ids:
2669 elif ctx.hex() in ids:
2669 r = ids[ctx.hex()]
2670 r = ids[ctx.hex()]
2670 ui.warn(_('skipping already grafted revision %s '
2671 ui.warn(_('skipping already grafted revision %s '
2671 '(was grafted from %d)\n') % (r, ctx.rev()))
2672 '(was grafted from %d)\n') % (r, ctx.rev()))
2672 revs.remove(r)
2673 revs.remove(r)
2673 if not revs:
2674 if not revs:
2674 return -1
2675 return -1
2675
2676
2676 wlock = repo.wlock()
2677 wlock = repo.wlock()
2677 try:
2678 try:
2678 for pos, ctx in enumerate(repo.set("%ld", revs)):
2679 for pos, ctx in enumerate(repo.set("%ld", revs)):
2679 current = repo['.']
2680 current = repo['.']
2680
2681
2681 ui.status(_('grafting revision %s\n') % ctx.rev())
2682 ui.status(_('grafting revision %s\n') % ctx.rev())
2682 if opts.get('dry_run'):
2683 if opts.get('dry_run'):
2683 continue
2684 continue
2684
2685
2685 # we don't merge the first commit when continuing
2686 # we don't merge the first commit when continuing
2686 if not cont:
2687 if not cont:
2687 # perform the graft merge with p1(rev) as 'ancestor'
2688 # perform the graft merge with p1(rev) as 'ancestor'
2688 try:
2689 try:
2689 # ui.forcemerge is an internal variable, do not document
2690 # ui.forcemerge is an internal variable, do not document
2690 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
2691 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
2691 stats = mergemod.update(repo, ctx.node(), True, True, False,
2692 stats = mergemod.update(repo, ctx.node(), True, True, False,
2692 ctx.p1().node())
2693 ctx.p1().node())
2693 finally:
2694 finally:
2694 ui.setconfig('ui', 'forcemerge', '')
2695 ui.setconfig('ui', 'forcemerge', '')
2695 # drop the second merge parent
2696 # drop the second merge parent
2696 repo.setparents(current.node(), nullid)
2697 repo.setparents(current.node(), nullid)
2697 repo.dirstate.write()
2698 repo.dirstate.write()
2698 # fix up dirstate for copies and renames
2699 # fix up dirstate for copies and renames
2699 cmdutil.duplicatecopies(repo, ctx.rev(), ctx.p1().rev())
2700 cmdutil.duplicatecopies(repo, ctx.rev(), ctx.p1().rev())
2700 # report any conflicts
2701 # report any conflicts
2701 if stats and stats[3] > 0:
2702 if stats and stats[3] > 0:
2702 # write out state for --continue
2703 # write out state for --continue
2703 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
2704 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
2704 repo.opener.write('graftstate', ''.join(nodelines))
2705 repo.opener.write('graftstate', ''.join(nodelines))
2705 raise util.Abort(
2706 raise util.Abort(
2706 _("unresolved conflicts, can't continue"),
2707 _("unresolved conflicts, can't continue"),
2707 hint=_('use hg resolve and hg graft --continue'))
2708 hint=_('use hg resolve and hg graft --continue'))
2708 else:
2709 else:
2709 cont = False
2710 cont = False
2710
2711
2711 # commit
2712 # commit
2712 source = ctx.extra().get('source')
2713 source = ctx.extra().get('source')
2713 if not source:
2714 if not source:
2714 source = ctx.hex()
2715 source = ctx.hex()
2715 extra = {'source': source}
2716 extra = {'source': source}
2716 user = ctx.user()
2717 user = ctx.user()
2717 if opts.get('user'):
2718 if opts.get('user'):
2718 user = opts['user']
2719 user = opts['user']
2719 date = ctx.date()
2720 date = ctx.date()
2720 if opts.get('date'):
2721 if opts.get('date'):
2721 date = opts['date']
2722 date = opts['date']
2722 node = repo.commit(text=ctx.description(), user=user,
2723 node = repo.commit(text=ctx.description(), user=user,
2723 date=date, extra=extra, editor=editor)
2724 date=date, extra=extra, editor=editor)
2724 if node is None:
2725 if node is None:
2725 ui.status(_('graft for revision %s is empty\n') % ctx.rev())
2726 ui.status(_('graft for revision %s is empty\n') % ctx.rev())
2726 finally:
2727 finally:
2727 wlock.release()
2728 wlock.release()
2728
2729
2729 # remove state when we complete successfully
2730 # remove state when we complete successfully
2730 if not opts.get('dry_run') and os.path.exists(repo.join('graftstate')):
2731 if not opts.get('dry_run') and os.path.exists(repo.join('graftstate')):
2731 util.unlinkpath(repo.join('graftstate'))
2732 util.unlinkpath(repo.join('graftstate'))
2732
2733
2733 return 0
2734 return 0
2734
2735
2735 @command('grep',
2736 @command('grep',
2736 [('0', 'print0', None, _('end fields with NUL')),
2737 [('0', 'print0', None, _('end fields with NUL')),
2737 ('', 'all', None, _('print all revisions that match')),
2738 ('', 'all', None, _('print all revisions that match')),
2738 ('a', 'text', None, _('treat all files as text')),
2739 ('a', 'text', None, _('treat all files as text')),
2739 ('f', 'follow', None,
2740 ('f', 'follow', None,
2740 _('follow changeset history,'
2741 _('follow changeset history,'
2741 ' or file history across copies and renames')),
2742 ' or file history across copies and renames')),
2742 ('i', 'ignore-case', None, _('ignore case when matching')),
2743 ('i', 'ignore-case', None, _('ignore case when matching')),
2743 ('l', 'files-with-matches', None,
2744 ('l', 'files-with-matches', None,
2744 _('print only filenames and revisions that match')),
2745 _('print only filenames and revisions that match')),
2745 ('n', 'line-number', None, _('print matching line numbers')),
2746 ('n', 'line-number', None, _('print matching line numbers')),
2746 ('r', 'rev', [],
2747 ('r', 'rev', [],
2747 _('only search files changed within revision range'), _('REV')),
2748 _('only search files changed within revision range'), _('REV')),
2748 ('u', 'user', None, _('list the author (long with -v)')),
2749 ('u', 'user', None, _('list the author (long with -v)')),
2749 ('d', 'date', None, _('list the date (short with -q)')),
2750 ('d', 'date', None, _('list the date (short with -q)')),
2750 ] + walkopts,
2751 ] + walkopts,
2751 _('[OPTION]... PATTERN [FILE]...'))
2752 _('[OPTION]... PATTERN [FILE]...'))
2752 def grep(ui, repo, pattern, *pats, **opts):
2753 def grep(ui, repo, pattern, *pats, **opts):
2753 """search for a pattern in specified files and revisions
2754 """search for a pattern in specified files and revisions
2754
2755
2755 Search revisions of files for a regular expression.
2756 Search revisions of files for a regular expression.
2756
2757
2757 This command behaves differently than Unix grep. It only accepts
2758 This command behaves differently than Unix grep. It only accepts
2758 Python/Perl regexps. It searches repository history, not the
2759 Python/Perl regexps. It searches repository history, not the
2759 working directory. It always prints the revision number in which a
2760 working directory. It always prints the revision number in which a
2760 match appears.
2761 match appears.
2761
2762
2762 By default, grep only prints output for the first revision of a
2763 By default, grep only prints output for the first revision of a
2763 file in which it finds a match. To get it to print every revision
2764 file in which it finds a match. To get it to print every revision
2764 that contains a change in match status ("-" for a match that
2765 that contains a change in match status ("-" for a match that
2765 becomes a non-match, or "+" for a non-match that becomes a match),
2766 becomes a non-match, or "+" for a non-match that becomes a match),
2766 use the --all flag.
2767 use the --all flag.
2767
2768
2768 Returns 0 if a match is found, 1 otherwise.
2769 Returns 0 if a match is found, 1 otherwise.
2769 """
2770 """
2770 reflags = re.M
2771 reflags = re.M
2771 if opts.get('ignore_case'):
2772 if opts.get('ignore_case'):
2772 reflags |= re.I
2773 reflags |= re.I
2773 try:
2774 try:
2774 regexp = re.compile(pattern, reflags)
2775 regexp = re.compile(pattern, reflags)
2775 except re.error, inst:
2776 except re.error, inst:
2776 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2777 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2777 return 1
2778 return 1
2778 sep, eol = ':', '\n'
2779 sep, eol = ':', '\n'
2779 if opts.get('print0'):
2780 if opts.get('print0'):
2780 sep = eol = '\0'
2781 sep = eol = '\0'
2781
2782
2782 getfile = util.lrucachefunc(repo.file)
2783 getfile = util.lrucachefunc(repo.file)
2783
2784
2784 def matchlines(body):
2785 def matchlines(body):
2785 begin = 0
2786 begin = 0
2786 linenum = 0
2787 linenum = 0
2787 while True:
2788 while True:
2788 match = regexp.search(body, begin)
2789 match = regexp.search(body, begin)
2789 if not match:
2790 if not match:
2790 break
2791 break
2791 mstart, mend = match.span()
2792 mstart, mend = match.span()
2792 linenum += body.count('\n', begin, mstart) + 1
2793 linenum += body.count('\n', begin, mstart) + 1
2793 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2794 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2794 begin = body.find('\n', mend) + 1 or len(body) + 1
2795 begin = body.find('\n', mend) + 1 or len(body) + 1
2795 lend = begin - 1
2796 lend = begin - 1
2796 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2797 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2797
2798
2798 class linestate(object):
2799 class linestate(object):
2799 def __init__(self, line, linenum, colstart, colend):
2800 def __init__(self, line, linenum, colstart, colend):
2800 self.line = line
2801 self.line = line
2801 self.linenum = linenum
2802 self.linenum = linenum
2802 self.colstart = colstart
2803 self.colstart = colstart
2803 self.colend = colend
2804 self.colend = colend
2804
2805
2805 def __hash__(self):
2806 def __hash__(self):
2806 return hash((self.linenum, self.line))
2807 return hash((self.linenum, self.line))
2807
2808
2808 def __eq__(self, other):
2809 def __eq__(self, other):
2809 return self.line == other.line
2810 return self.line == other.line
2810
2811
2811 matches = {}
2812 matches = {}
2812 copies = {}
2813 copies = {}
2813 def grepbody(fn, rev, body):
2814 def grepbody(fn, rev, body):
2814 matches[rev].setdefault(fn, [])
2815 matches[rev].setdefault(fn, [])
2815 m = matches[rev][fn]
2816 m = matches[rev][fn]
2816 for lnum, cstart, cend, line in matchlines(body):
2817 for lnum, cstart, cend, line in matchlines(body):
2817 s = linestate(line, lnum, cstart, cend)
2818 s = linestate(line, lnum, cstart, cend)
2818 m.append(s)
2819 m.append(s)
2819
2820
2820 def difflinestates(a, b):
2821 def difflinestates(a, b):
2821 sm = difflib.SequenceMatcher(None, a, b)
2822 sm = difflib.SequenceMatcher(None, a, b)
2822 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2823 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2823 if tag == 'insert':
2824 if tag == 'insert':
2824 for i in xrange(blo, bhi):
2825 for i in xrange(blo, bhi):
2825 yield ('+', b[i])
2826 yield ('+', b[i])
2826 elif tag == 'delete':
2827 elif tag == 'delete':
2827 for i in xrange(alo, ahi):
2828 for i in xrange(alo, ahi):
2828 yield ('-', a[i])
2829 yield ('-', a[i])
2829 elif tag == 'replace':
2830 elif tag == 'replace':
2830 for i in xrange(alo, ahi):
2831 for i in xrange(alo, ahi):
2831 yield ('-', a[i])
2832 yield ('-', a[i])
2832 for i in xrange(blo, bhi):
2833 for i in xrange(blo, bhi):
2833 yield ('+', b[i])
2834 yield ('+', b[i])
2834
2835
2835 def display(fn, ctx, pstates, states):
2836 def display(fn, ctx, pstates, states):
2836 rev = ctx.rev()
2837 rev = ctx.rev()
2837 datefunc = ui.quiet and util.shortdate or util.datestr
2838 datefunc = ui.quiet and util.shortdate or util.datestr
2838 found = False
2839 found = False
2839 filerevmatches = {}
2840 filerevmatches = {}
2840 def binary():
2841 def binary():
2841 flog = getfile(fn)
2842 flog = getfile(fn)
2842 return util.binary(flog.read(ctx.filenode(fn)))
2843 return util.binary(flog.read(ctx.filenode(fn)))
2843
2844
2844 if opts.get('all'):
2845 if opts.get('all'):
2845 iter = difflinestates(pstates, states)
2846 iter = difflinestates(pstates, states)
2846 else:
2847 else:
2847 iter = [('', l) for l in states]
2848 iter = [('', l) for l in states]
2848 for change, l in iter:
2849 for change, l in iter:
2849 cols = [fn, str(rev)]
2850 cols = [fn, str(rev)]
2850 before, match, after = None, None, None
2851 before, match, after = None, None, None
2851 if opts.get('line_number'):
2852 if opts.get('line_number'):
2852 cols.append(str(l.linenum))
2853 cols.append(str(l.linenum))
2853 if opts.get('all'):
2854 if opts.get('all'):
2854 cols.append(change)
2855 cols.append(change)
2855 if opts.get('user'):
2856 if opts.get('user'):
2856 cols.append(ui.shortuser(ctx.user()))
2857 cols.append(ui.shortuser(ctx.user()))
2857 if opts.get('date'):
2858 if opts.get('date'):
2858 cols.append(datefunc(ctx.date()))
2859 cols.append(datefunc(ctx.date()))
2859 if opts.get('files_with_matches'):
2860 if opts.get('files_with_matches'):
2860 c = (fn, rev)
2861 c = (fn, rev)
2861 if c in filerevmatches:
2862 if c in filerevmatches:
2862 continue
2863 continue
2863 filerevmatches[c] = 1
2864 filerevmatches[c] = 1
2864 else:
2865 else:
2865 before = l.line[:l.colstart]
2866 before = l.line[:l.colstart]
2866 match = l.line[l.colstart:l.colend]
2867 match = l.line[l.colstart:l.colend]
2867 after = l.line[l.colend:]
2868 after = l.line[l.colend:]
2868 ui.write(sep.join(cols))
2869 ui.write(sep.join(cols))
2869 if before is not None:
2870 if before is not None:
2870 if not opts.get('text') and binary():
2871 if not opts.get('text') and binary():
2871 ui.write(sep + " Binary file matches")
2872 ui.write(sep + " Binary file matches")
2872 else:
2873 else:
2873 ui.write(sep + before)
2874 ui.write(sep + before)
2874 ui.write(match, label='grep.match')
2875 ui.write(match, label='grep.match')
2875 ui.write(after)
2876 ui.write(after)
2876 ui.write(eol)
2877 ui.write(eol)
2877 found = True
2878 found = True
2878 return found
2879 return found
2879
2880
2880 skip = {}
2881 skip = {}
2881 revfiles = {}
2882 revfiles = {}
2882 matchfn = scmutil.match(repo[None], pats, opts)
2883 matchfn = scmutil.match(repo[None], pats, opts)
2883 found = False
2884 found = False
2884 follow = opts.get('follow')
2885 follow = opts.get('follow')
2885
2886
2886 def prep(ctx, fns):
2887 def prep(ctx, fns):
2887 rev = ctx.rev()
2888 rev = ctx.rev()
2888 pctx = ctx.p1()
2889 pctx = ctx.p1()
2889 parent = pctx.rev()
2890 parent = pctx.rev()
2890 matches.setdefault(rev, {})
2891 matches.setdefault(rev, {})
2891 matches.setdefault(parent, {})
2892 matches.setdefault(parent, {})
2892 files = revfiles.setdefault(rev, [])
2893 files = revfiles.setdefault(rev, [])
2893 for fn in fns:
2894 for fn in fns:
2894 flog = getfile(fn)
2895 flog = getfile(fn)
2895 try:
2896 try:
2896 fnode = ctx.filenode(fn)
2897 fnode = ctx.filenode(fn)
2897 except error.LookupError:
2898 except error.LookupError:
2898 continue
2899 continue
2899
2900
2900 copied = flog.renamed(fnode)
2901 copied = flog.renamed(fnode)
2901 copy = follow and copied and copied[0]
2902 copy = follow and copied and copied[0]
2902 if copy:
2903 if copy:
2903 copies.setdefault(rev, {})[fn] = copy
2904 copies.setdefault(rev, {})[fn] = copy
2904 if fn in skip:
2905 if fn in skip:
2905 if copy:
2906 if copy:
2906 skip[copy] = True
2907 skip[copy] = True
2907 continue
2908 continue
2908 files.append(fn)
2909 files.append(fn)
2909
2910
2910 if fn not in matches[rev]:
2911 if fn not in matches[rev]:
2911 grepbody(fn, rev, flog.read(fnode))
2912 grepbody(fn, rev, flog.read(fnode))
2912
2913
2913 pfn = copy or fn
2914 pfn = copy or fn
2914 if pfn not in matches[parent]:
2915 if pfn not in matches[parent]:
2915 try:
2916 try:
2916 fnode = pctx.filenode(pfn)
2917 fnode = pctx.filenode(pfn)
2917 grepbody(pfn, parent, flog.read(fnode))
2918 grepbody(pfn, parent, flog.read(fnode))
2918 except error.LookupError:
2919 except error.LookupError:
2919 pass
2920 pass
2920
2921
2921 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2922 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2922 rev = ctx.rev()
2923 rev = ctx.rev()
2923 parent = ctx.p1().rev()
2924 parent = ctx.p1().rev()
2924 for fn in sorted(revfiles.get(rev, [])):
2925 for fn in sorted(revfiles.get(rev, [])):
2925 states = matches[rev][fn]
2926 states = matches[rev][fn]
2926 copy = copies.get(rev, {}).get(fn)
2927 copy = copies.get(rev, {}).get(fn)
2927 if fn in skip:
2928 if fn in skip:
2928 if copy:
2929 if copy:
2929 skip[copy] = True
2930 skip[copy] = True
2930 continue
2931 continue
2931 pstates = matches.get(parent, {}).get(copy or fn, [])
2932 pstates = matches.get(parent, {}).get(copy or fn, [])
2932 if pstates or states:
2933 if pstates or states:
2933 r = display(fn, ctx, pstates, states)
2934 r = display(fn, ctx, pstates, states)
2934 found = found or r
2935 found = found or r
2935 if r and not opts.get('all'):
2936 if r and not opts.get('all'):
2936 skip[fn] = True
2937 skip[fn] = True
2937 if copy:
2938 if copy:
2938 skip[copy] = True
2939 skip[copy] = True
2939 del matches[rev]
2940 del matches[rev]
2940 del revfiles[rev]
2941 del revfiles[rev]
2941
2942
2942 return not found
2943 return not found
2943
2944
2944 @command('heads',
2945 @command('heads',
2945 [('r', 'rev', '',
2946 [('r', 'rev', '',
2946 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2947 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2947 ('t', 'topo', False, _('show topological heads only')),
2948 ('t', 'topo', False, _('show topological heads only')),
2948 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2949 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2949 ('c', 'closed', False, _('show normal and closed branch heads')),
2950 ('c', 'closed', False, _('show normal and closed branch heads')),
2950 ] + templateopts,
2951 ] + templateopts,
2951 _('[-ac] [-r STARTREV] [REV]...'))
2952 _('[-ac] [-r STARTREV] [REV]...'))
2952 def heads(ui, repo, *branchrevs, **opts):
2953 def heads(ui, repo, *branchrevs, **opts):
2953 """show current repository heads or show branch heads
2954 """show current repository heads or show branch heads
2954
2955
2955 With no arguments, show all repository branch heads.
2956 With no arguments, show all repository branch heads.
2956
2957
2957 Repository "heads" are changesets with no child changesets. They are
2958 Repository "heads" are changesets with no child changesets. They are
2958 where development generally takes place and are the usual targets
2959 where development generally takes place and are the usual targets
2959 for update and merge operations. Branch heads are changesets that have
2960 for update and merge operations. Branch heads are changesets that have
2960 no child changeset on the same branch.
2961 no child changeset on the same branch.
2961
2962
2962 If one or more REVs are given, only branch heads on the branches
2963 If one or more REVs are given, only branch heads on the branches
2963 associated with the specified changesets are shown. This means
2964 associated with the specified changesets are shown. This means
2964 that you can use :hg:`heads foo` to see the heads on a branch
2965 that you can use :hg:`heads foo` to see the heads on a branch
2965 named ``foo``.
2966 named ``foo``.
2966
2967
2967 If -c/--closed is specified, also show branch heads marked closed
2968 If -c/--closed is specified, also show branch heads marked closed
2968 (see :hg:`commit --close-branch`).
2969 (see :hg:`commit --close-branch`).
2969
2970
2970 If STARTREV is specified, only those heads that are descendants of
2971 If STARTREV is specified, only those heads that are descendants of
2971 STARTREV will be displayed.
2972 STARTREV will be displayed.
2972
2973
2973 If -t/--topo is specified, named branch mechanics will be ignored and only
2974 If -t/--topo is specified, named branch mechanics will be ignored and only
2974 changesets without children will be shown.
2975 changesets without children will be shown.
2975
2976
2976 Returns 0 if matching heads are found, 1 if not.
2977 Returns 0 if matching heads are found, 1 if not.
2977 """
2978 """
2978
2979
2979 start = None
2980 start = None
2980 if 'rev' in opts:
2981 if 'rev' in opts:
2981 start = scmutil.revsingle(repo, opts['rev'], None).node()
2982 start = scmutil.revsingle(repo, opts['rev'], None).node()
2982
2983
2983 if opts.get('topo'):
2984 if opts.get('topo'):
2984 heads = [repo[h] for h in repo.heads(start)]
2985 heads = [repo[h] for h in repo.heads(start)]
2985 else:
2986 else:
2986 heads = []
2987 heads = []
2987 for branch in repo.branchmap():
2988 for branch in repo.branchmap():
2988 heads += repo.branchheads(branch, start, opts.get('closed'))
2989 heads += repo.branchheads(branch, start, opts.get('closed'))
2989 heads = [repo[h] for h in heads]
2990 heads = [repo[h] for h in heads]
2990
2991
2991 if branchrevs:
2992 if branchrevs:
2992 branches = set(repo[br].branch() for br in branchrevs)
2993 branches = set(repo[br].branch() for br in branchrevs)
2993 heads = [h for h in heads if h.branch() in branches]
2994 heads = [h for h in heads if h.branch() in branches]
2994
2995
2995 if opts.get('active') and branchrevs:
2996 if opts.get('active') and branchrevs:
2996 dagheads = repo.heads(start)
2997 dagheads = repo.heads(start)
2997 heads = [h for h in heads if h.node() in dagheads]
2998 heads = [h for h in heads if h.node() in dagheads]
2998
2999
2999 if branchrevs:
3000 if branchrevs:
3000 haveheads = set(h.branch() for h in heads)
3001 haveheads = set(h.branch() for h in heads)
3001 if branches - haveheads:
3002 if branches - haveheads:
3002 headless = ', '.join(b for b in branches - haveheads)
3003 headless = ', '.join(b for b in branches - haveheads)
3003 msg = _('no open branch heads found on branches %s')
3004 msg = _('no open branch heads found on branches %s')
3004 if opts.get('rev'):
3005 if opts.get('rev'):
3005 msg += _(' (started at %s)') % opts['rev']
3006 msg += _(' (started at %s)') % opts['rev']
3006 ui.warn((msg + '\n') % headless)
3007 ui.warn((msg + '\n') % headless)
3007
3008
3008 if not heads:
3009 if not heads:
3009 return 1
3010 return 1
3010
3011
3011 heads = sorted(heads, key=lambda x: -x.rev())
3012 heads = sorted(heads, key=lambda x: -x.rev())
3012 displayer = cmdutil.show_changeset(ui, repo, opts)
3013 displayer = cmdutil.show_changeset(ui, repo, opts)
3013 for ctx in heads:
3014 for ctx in heads:
3014 displayer.show(ctx)
3015 displayer.show(ctx)
3015 displayer.close()
3016 displayer.close()
3016
3017
3017 @command('help',
3018 @command('help',
3018 [('e', 'extension', None, _('show only help for extensions')),
3019 [('e', 'extension', None, _('show only help for extensions')),
3019 ('c', 'command', None, _('show only help for commands'))],
3020 ('c', 'command', None, _('show only help for commands'))],
3020 _('[-ec] [TOPIC]'))
3021 _('[-ec] [TOPIC]'))
3021 def help_(ui, name=None, unknowncmd=False, full=True, **opts):
3022 def help_(ui, name=None, unknowncmd=False, full=True, **opts):
3022 """show help for a given topic or a help overview
3023 """show help for a given topic or a help overview
3023
3024
3024 With no arguments, print a list of commands with short help messages.
3025 With no arguments, print a list of commands with short help messages.
3025
3026
3026 Given a topic, extension, or command name, print help for that
3027 Given a topic, extension, or command name, print help for that
3027 topic.
3028 topic.
3028
3029
3029 Returns 0 if successful.
3030 Returns 0 if successful.
3030 """
3031 """
3031
3032
3032 textwidth = min(ui.termwidth(), 80) - 2
3033 textwidth = min(ui.termwidth(), 80) - 2
3033
3034
3034 def optrst(options):
3035 def optrst(options):
3035 data = []
3036 data = []
3036 multioccur = False
3037 multioccur = False
3037 for option in options:
3038 for option in options:
3038 if len(option) == 5:
3039 if len(option) == 5:
3039 shortopt, longopt, default, desc, optlabel = option
3040 shortopt, longopt, default, desc, optlabel = option
3040 else:
3041 else:
3041 shortopt, longopt, default, desc = option
3042 shortopt, longopt, default, desc = option
3042 optlabel = _("VALUE") # default label
3043 optlabel = _("VALUE") # default label
3043
3044
3044 if _("DEPRECATED") in desc and not ui.verbose:
3045 if _("DEPRECATED") in desc and not ui.verbose:
3045 continue
3046 continue
3046
3047
3047 so = ''
3048 so = ''
3048 if shortopt:
3049 if shortopt:
3049 so = '-' + shortopt
3050 so = '-' + shortopt
3050 lo = '--' + longopt
3051 lo = '--' + longopt
3051 if default:
3052 if default:
3052 desc += _(" (default: %s)") % default
3053 desc += _(" (default: %s)") % default
3053
3054
3054 if isinstance(default, list):
3055 if isinstance(default, list):
3055 lo += " %s [+]" % optlabel
3056 lo += " %s [+]" % optlabel
3056 multioccur = True
3057 multioccur = True
3057 elif (default is not None) and not isinstance(default, bool):
3058 elif (default is not None) and not isinstance(default, bool):
3058 lo += " %s" % optlabel
3059 lo += " %s" % optlabel
3059
3060
3060 data.append((so, lo, desc))
3061 data.append((so, lo, desc))
3061
3062
3062 rst = minirst.maketable(data, 1)
3063 rst = minirst.maketable(data, 1)
3063
3064
3064 if multioccur:
3065 if multioccur:
3065 rst += _("\n[+] marked option can be specified multiple times\n")
3066 rst += _("\n[+] marked option can be specified multiple times\n")
3066
3067
3067 return rst
3068 return rst
3068
3069
3069 # list all option lists
3070 # list all option lists
3070 def opttext(optlist, width):
3071 def opttext(optlist, width):
3071 rst = ''
3072 rst = ''
3072 if not optlist:
3073 if not optlist:
3073 return ''
3074 return ''
3074
3075
3075 for title, options in optlist:
3076 for title, options in optlist:
3076 rst += '\n%s\n' % title
3077 rst += '\n%s\n' % title
3077 if options:
3078 if options:
3078 rst += "\n"
3079 rst += "\n"
3079 rst += optrst(options)
3080 rst += optrst(options)
3080 rst += '\n'
3081 rst += '\n'
3081
3082
3082 return '\n' + minirst.format(rst, width)
3083 return '\n' + minirst.format(rst, width)
3083
3084
3084 def addglobalopts(optlist, aliases):
3085 def addglobalopts(optlist, aliases):
3085 if ui.quiet:
3086 if ui.quiet:
3086 return []
3087 return []
3087
3088
3088 if ui.verbose:
3089 if ui.verbose:
3089 optlist.append((_("global options:"), globalopts))
3090 optlist.append((_("global options:"), globalopts))
3090 if name == 'shortlist':
3091 if name == 'shortlist':
3091 optlist.append((_('use "hg help" for the full list '
3092 optlist.append((_('use "hg help" for the full list '
3092 'of commands'), ()))
3093 'of commands'), ()))
3093 else:
3094 else:
3094 if name == 'shortlist':
3095 if name == 'shortlist':
3095 msg = _('use "hg help" for the full list of commands '
3096 msg = _('use "hg help" for the full list of commands '
3096 'or "hg -v" for details')
3097 'or "hg -v" for details')
3097 elif name and not full:
3098 elif name and not full:
3098 msg = _('use "hg help %s" to show the full help text') % name
3099 msg = _('use "hg help %s" to show the full help text') % name
3099 elif aliases:
3100 elif aliases:
3100 msg = _('use "hg -v help%s" to show builtin aliases and '
3101 msg = _('use "hg -v help%s" to show builtin aliases and '
3101 'global options') % (name and " " + name or "")
3102 'global options') % (name and " " + name or "")
3102 else:
3103 else:
3103 msg = _('use "hg -v help %s" to show more info') % name
3104 msg = _('use "hg -v help %s" to show more info') % name
3104 optlist.append((msg, ()))
3105 optlist.append((msg, ()))
3105
3106
3106 def helpcmd(name):
3107 def helpcmd(name):
3107 try:
3108 try:
3108 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
3109 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
3109 except error.AmbiguousCommand, inst:
3110 except error.AmbiguousCommand, inst:
3110 # py3k fix: except vars can't be used outside the scope of the
3111 # py3k fix: except vars can't be used outside the scope of the
3111 # except block, nor can be used inside a lambda. python issue4617
3112 # except block, nor can be used inside a lambda. python issue4617
3112 prefix = inst.args[0]
3113 prefix = inst.args[0]
3113 select = lambda c: c.lstrip('^').startswith(prefix)
3114 select = lambda c: c.lstrip('^').startswith(prefix)
3114 helplist(select)
3115 helplist(select)
3115 return
3116 return
3116
3117
3117 # check if it's an invalid alias and display its error if it is
3118 # check if it's an invalid alias and display its error if it is
3118 if getattr(entry[0], 'badalias', False):
3119 if getattr(entry[0], 'badalias', False):
3119 if not unknowncmd:
3120 if not unknowncmd:
3120 entry[0](ui)
3121 entry[0](ui)
3121 return
3122 return
3122
3123
3123 rst = ""
3124 rst = ""
3124
3125
3125 # synopsis
3126 # synopsis
3126 if len(entry) > 2:
3127 if len(entry) > 2:
3127 if entry[2].startswith('hg'):
3128 if entry[2].startswith('hg'):
3128 rst += "%s\n" % entry[2]
3129 rst += "%s\n" % entry[2]
3129 else:
3130 else:
3130 rst += 'hg %s %s\n' % (aliases[0], entry[2])
3131 rst += 'hg %s %s\n' % (aliases[0], entry[2])
3131 else:
3132 else:
3132 rst += 'hg %s\n' % aliases[0]
3133 rst += 'hg %s\n' % aliases[0]
3133
3134
3134 # aliases
3135 # aliases
3135 if full and not ui.quiet and len(aliases) > 1:
3136 if full and not ui.quiet and len(aliases) > 1:
3136 rst += _("\naliases: %s\n") % ', '.join(aliases[1:])
3137 rst += _("\naliases: %s\n") % ', '.join(aliases[1:])
3137
3138
3138 # description
3139 # description
3139 doc = gettext(entry[0].__doc__)
3140 doc = gettext(entry[0].__doc__)
3140 if not doc:
3141 if not doc:
3141 doc = _("(no help text available)")
3142 doc = _("(no help text available)")
3142 if util.safehasattr(entry[0], 'definition'): # aliased command
3143 if util.safehasattr(entry[0], 'definition'): # aliased command
3143 if entry[0].definition.startswith('!'): # shell alias
3144 if entry[0].definition.startswith('!'): # shell alias
3144 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
3145 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
3145 else:
3146 else:
3146 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
3147 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
3147 if ui.quiet or not full:
3148 if ui.quiet or not full:
3148 doc = doc.splitlines()[0]
3149 doc = doc.splitlines()[0]
3149 rst += "\n" + doc + "\n"
3150 rst += "\n" + doc + "\n"
3150
3151
3151 # check if this command shadows a non-trivial (multi-line)
3152 # check if this command shadows a non-trivial (multi-line)
3152 # extension help text
3153 # extension help text
3153 try:
3154 try:
3154 mod = extensions.find(name)
3155 mod = extensions.find(name)
3155 doc = gettext(mod.__doc__) or ''
3156 doc = gettext(mod.__doc__) or ''
3156 if '\n' in doc.strip():
3157 if '\n' in doc.strip():
3157 msg = _('use "hg help -e %s" to show help for '
3158 msg = _('use "hg help -e %s" to show help for '
3158 'the %s extension') % (name, name)
3159 'the %s extension') % (name, name)
3159 rst += '\n%s\n' % msg
3160 rst += '\n%s\n' % msg
3160 except KeyError:
3161 except KeyError:
3161 pass
3162 pass
3162
3163
3163 # options
3164 # options
3164 if not ui.quiet and entry[1]:
3165 if not ui.quiet and entry[1]:
3165 rst += '\n'
3166 rst += '\n'
3166 rst += _("options:")
3167 rst += _("options:")
3167 rst += '\n\n'
3168 rst += '\n\n'
3168 rst += optrst(entry[1])
3169 rst += optrst(entry[1])
3169
3170
3170 if ui.verbose:
3171 if ui.verbose:
3171 rst += '\n'
3172 rst += '\n'
3172 rst += _("global options:")
3173 rst += _("global options:")
3173 rst += '\n\n'
3174 rst += '\n\n'
3174 rst += optrst(globalopts)
3175 rst += optrst(globalopts)
3175
3176
3176 keep = ui.verbose and ['verbose'] or []
3177 keep = ui.verbose and ['verbose'] or []
3177 formatted, pruned = minirst.format(rst, textwidth, keep=keep)
3178 formatted, pruned = minirst.format(rst, textwidth, keep=keep)
3178 ui.write(formatted)
3179 ui.write(formatted)
3179
3180
3180 if not ui.verbose:
3181 if not ui.verbose:
3181 if not full:
3182 if not full:
3182 ui.write(_('\nuse "hg help %s" to show the full help text\n')
3183 ui.write(_('\nuse "hg help %s" to show the full help text\n')
3183 % name)
3184 % name)
3184 elif not ui.quiet:
3185 elif not ui.quiet:
3185 ui.write(_('\nuse "hg -v help %s" to show more info\n') % name)
3186 ui.write(_('\nuse "hg -v help %s" to show more info\n') % name)
3186
3187
3187
3188
3188 def helplist(select=None):
3189 def helplist(select=None):
3189 # list of commands
3190 # list of commands
3190 if name == "shortlist":
3191 if name == "shortlist":
3191 header = _('basic commands:\n\n')
3192 header = _('basic commands:\n\n')
3192 else:
3193 else:
3193 header = _('list of commands:\n\n')
3194 header = _('list of commands:\n\n')
3194
3195
3195 h = {}
3196 h = {}
3196 cmds = {}
3197 cmds = {}
3197 for c, e in table.iteritems():
3198 for c, e in table.iteritems():
3198 f = c.split("|", 1)[0]
3199 f = c.split("|", 1)[0]
3199 if select and not select(f):
3200 if select and not select(f):
3200 continue
3201 continue
3201 if (not select and name != 'shortlist' and
3202 if (not select and name != 'shortlist' and
3202 e[0].__module__ != __name__):
3203 e[0].__module__ != __name__):
3203 continue
3204 continue
3204 if name == "shortlist" and not f.startswith("^"):
3205 if name == "shortlist" and not f.startswith("^"):
3205 continue
3206 continue
3206 f = f.lstrip("^")
3207 f = f.lstrip("^")
3207 if not ui.debugflag and f.startswith("debug"):
3208 if not ui.debugflag and f.startswith("debug"):
3208 continue
3209 continue
3209 doc = e[0].__doc__
3210 doc = e[0].__doc__
3210 if doc and 'DEPRECATED' in doc and not ui.verbose:
3211 if doc and 'DEPRECATED' in doc and not ui.verbose:
3211 continue
3212 continue
3212 doc = gettext(doc)
3213 doc = gettext(doc)
3213 if not doc:
3214 if not doc:
3214 doc = _("(no help text available)")
3215 doc = _("(no help text available)")
3215 h[f] = doc.splitlines()[0].rstrip()
3216 h[f] = doc.splitlines()[0].rstrip()
3216 cmds[f] = c.lstrip("^")
3217 cmds[f] = c.lstrip("^")
3217
3218
3218 if not h:
3219 if not h:
3219 ui.status(_('no commands defined\n'))
3220 ui.status(_('no commands defined\n'))
3220 return
3221 return
3221
3222
3222 ui.status(header)
3223 ui.status(header)
3223 fns = sorted(h)
3224 fns = sorted(h)
3224 m = max(map(len, fns))
3225 m = max(map(len, fns))
3225 for f in fns:
3226 for f in fns:
3226 if ui.verbose:
3227 if ui.verbose:
3227 commands = cmds[f].replace("|",", ")
3228 commands = cmds[f].replace("|",", ")
3228 ui.write(" %s:\n %s\n"%(commands, h[f]))
3229 ui.write(" %s:\n %s\n"%(commands, h[f]))
3229 else:
3230 else:
3230 ui.write('%s\n' % (util.wrap(h[f], textwidth,
3231 ui.write('%s\n' % (util.wrap(h[f], textwidth,
3231 initindent=' %-*s ' % (m, f),
3232 initindent=' %-*s ' % (m, f),
3232 hangindent=' ' * (m + 4))))
3233 hangindent=' ' * (m + 4))))
3233
3234
3234 if not name:
3235 if not name:
3235 text = help.listexts(_('enabled extensions:'), extensions.enabled())
3236 text = help.listexts(_('enabled extensions:'), extensions.enabled())
3236 if text:
3237 if text:
3237 ui.write("\n%s" % minirst.format(text, textwidth))
3238 ui.write("\n%s" % minirst.format(text, textwidth))
3238
3239
3239 ui.write(_("\nadditional help topics:\n\n"))
3240 ui.write(_("\nadditional help topics:\n\n"))
3240 topics = []
3241 topics = []
3241 for names, header, doc in help.helptable:
3242 for names, header, doc in help.helptable:
3242 topics.append((sorted(names, key=len, reverse=True)[0], header))
3243 topics.append((sorted(names, key=len, reverse=True)[0], header))
3243 topics_len = max([len(s[0]) for s in topics])
3244 topics_len = max([len(s[0]) for s in topics])
3244 for t, desc in topics:
3245 for t, desc in topics:
3245 ui.write(" %-*s %s\n" % (topics_len, t, desc))
3246 ui.write(" %-*s %s\n" % (topics_len, t, desc))
3246
3247
3247 optlist = []
3248 optlist = []
3248 addglobalopts(optlist, True)
3249 addglobalopts(optlist, True)
3249 ui.write(opttext(optlist, textwidth))
3250 ui.write(opttext(optlist, textwidth))
3250
3251
3251 def helptopic(name):
3252 def helptopic(name):
3252 for names, header, doc in help.helptable:
3253 for names, header, doc in help.helptable:
3253 if name in names:
3254 if name in names:
3254 break
3255 break
3255 else:
3256 else:
3256 raise error.UnknownCommand(name)
3257 raise error.UnknownCommand(name)
3257
3258
3258 # description
3259 # description
3259 if not doc:
3260 if not doc:
3260 doc = _("(no help text available)")
3261 doc = _("(no help text available)")
3261 if util.safehasattr(doc, '__call__'):
3262 if util.safehasattr(doc, '__call__'):
3262 doc = doc()
3263 doc = doc()
3263
3264
3264 ui.write("%s\n\n" % header)
3265 ui.write("%s\n\n" % header)
3265 ui.write("%s" % minirst.format(doc, textwidth, indent=4))
3266 ui.write("%s" % minirst.format(doc, textwidth, indent=4))
3266 try:
3267 try:
3267 cmdutil.findcmd(name, table)
3268 cmdutil.findcmd(name, table)
3268 ui.write(_('\nuse "hg help -c %s" to see help for '
3269 ui.write(_('\nuse "hg help -c %s" to see help for '
3269 'the %s command\n') % (name, name))
3270 'the %s command\n') % (name, name))
3270 except error.UnknownCommand:
3271 except error.UnknownCommand:
3271 pass
3272 pass
3272
3273
3273 def helpext(name):
3274 def helpext(name):
3274 try:
3275 try:
3275 mod = extensions.find(name)
3276 mod = extensions.find(name)
3276 doc = gettext(mod.__doc__) or _('no help text available')
3277 doc = gettext(mod.__doc__) or _('no help text available')
3277 except KeyError:
3278 except KeyError:
3278 mod = None
3279 mod = None
3279 doc = extensions.disabledext(name)
3280 doc = extensions.disabledext(name)
3280 if not doc:
3281 if not doc:
3281 raise error.UnknownCommand(name)
3282 raise error.UnknownCommand(name)
3282
3283
3283 if '\n' not in doc:
3284 if '\n' not in doc:
3284 head, tail = doc, ""
3285 head, tail = doc, ""
3285 else:
3286 else:
3286 head, tail = doc.split('\n', 1)
3287 head, tail = doc.split('\n', 1)
3287 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
3288 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
3288 if tail:
3289 if tail:
3289 ui.write(minirst.format(tail, textwidth))
3290 ui.write(minirst.format(tail, textwidth))
3290 ui.status('\n')
3291 ui.status('\n')
3291
3292
3292 if mod:
3293 if mod:
3293 try:
3294 try:
3294 ct = mod.cmdtable
3295 ct = mod.cmdtable
3295 except AttributeError:
3296 except AttributeError:
3296 ct = {}
3297 ct = {}
3297 modcmds = set([c.split('|', 1)[0] for c in ct])
3298 modcmds = set([c.split('|', 1)[0] for c in ct])
3298 helplist(modcmds.__contains__)
3299 helplist(modcmds.__contains__)
3299 else:
3300 else:
3300 ui.write(_('use "hg help extensions" for information on enabling '
3301 ui.write(_('use "hg help extensions" for information on enabling '
3301 'extensions\n'))
3302 'extensions\n'))
3302
3303
3303 def helpextcmd(name):
3304 def helpextcmd(name):
3304 cmd, ext, mod = extensions.disabledcmd(ui, name,
3305 cmd, ext, mod = extensions.disabledcmd(ui, name,
3305 ui.configbool('ui', 'strict'))
3306 ui.configbool('ui', 'strict'))
3306 doc = gettext(mod.__doc__).splitlines()[0]
3307 doc = gettext(mod.__doc__).splitlines()[0]
3307
3308
3308 msg = help.listexts(_("'%s' is provided by the following "
3309 msg = help.listexts(_("'%s' is provided by the following "
3309 "extension:") % cmd, {ext: doc}, indent=4)
3310 "extension:") % cmd, {ext: doc}, indent=4)
3310 ui.write(minirst.format(msg, textwidth))
3311 ui.write(minirst.format(msg, textwidth))
3311 ui.write('\n')
3312 ui.write('\n')
3312 ui.write(_('use "hg help extensions" for information on enabling '
3313 ui.write(_('use "hg help extensions" for information on enabling '
3313 'extensions\n'))
3314 'extensions\n'))
3314
3315
3315 if name and name != 'shortlist':
3316 if name and name != 'shortlist':
3316 i = None
3317 i = None
3317 if unknowncmd:
3318 if unknowncmd:
3318 queries = (helpextcmd,)
3319 queries = (helpextcmd,)
3319 elif opts.get('extension'):
3320 elif opts.get('extension'):
3320 queries = (helpext,)
3321 queries = (helpext,)
3321 elif opts.get('command'):
3322 elif opts.get('command'):
3322 queries = (helpcmd,)
3323 queries = (helpcmd,)
3323 else:
3324 else:
3324 queries = (helptopic, helpcmd, helpext, helpextcmd)
3325 queries = (helptopic, helpcmd, helpext, helpextcmd)
3325 for f in queries:
3326 for f in queries:
3326 try:
3327 try:
3327 f(name)
3328 f(name)
3328 i = None
3329 i = None
3329 break
3330 break
3330 except error.UnknownCommand, inst:
3331 except error.UnknownCommand, inst:
3331 i = inst
3332 i = inst
3332 if i:
3333 if i:
3333 raise i
3334 raise i
3334 else:
3335 else:
3335 # program name
3336 # program name
3336 ui.status(_("Mercurial Distributed SCM\n"))
3337 ui.status(_("Mercurial Distributed SCM\n"))
3337 ui.status('\n')
3338 ui.status('\n')
3338 helplist()
3339 helplist()
3339
3340
3340
3341
3341 @command('identify|id',
3342 @command('identify|id',
3342 [('r', 'rev', '',
3343 [('r', 'rev', '',
3343 _('identify the specified revision'), _('REV')),
3344 _('identify the specified revision'), _('REV')),
3344 ('n', 'num', None, _('show local revision number')),
3345 ('n', 'num', None, _('show local revision number')),
3345 ('i', 'id', None, _('show global revision id')),
3346 ('i', 'id', None, _('show global revision id')),
3346 ('b', 'branch', None, _('show branch')),
3347 ('b', 'branch', None, _('show branch')),
3347 ('t', 'tags', None, _('show tags')),
3348 ('t', 'tags', None, _('show tags')),
3348 ('B', 'bookmarks', None, _('show bookmarks')),
3349 ('B', 'bookmarks', None, _('show bookmarks')),
3349 ] + remoteopts,
3350 ] + remoteopts,
3350 _('[-nibtB] [-r REV] [SOURCE]'))
3351 _('[-nibtB] [-r REV] [SOURCE]'))
3351 def identify(ui, repo, source=None, rev=None,
3352 def identify(ui, repo, source=None, rev=None,
3352 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3353 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3353 """identify the working copy or specified revision
3354 """identify the working copy or specified revision
3354
3355
3355 Print a summary identifying the repository state at REV using one or
3356 Print a summary identifying the repository state at REV using one or
3356 two parent hash identifiers, followed by a "+" if the working
3357 two parent hash identifiers, followed by a "+" if the working
3357 directory has uncommitted changes, the branch name (if not default),
3358 directory has uncommitted changes, the branch name (if not default),
3358 a list of tags, and a list of bookmarks.
3359 a list of tags, and a list of bookmarks.
3359
3360
3360 When REV is not given, print a summary of the current state of the
3361 When REV is not given, print a summary of the current state of the
3361 repository.
3362 repository.
3362
3363
3363 Specifying a path to a repository root or Mercurial bundle will
3364 Specifying a path to a repository root or Mercurial bundle will
3364 cause lookup to operate on that repository/bundle.
3365 cause lookup to operate on that repository/bundle.
3365
3366
3366 .. container:: verbose
3367 .. container:: verbose
3367
3368
3368 Examples:
3369 Examples:
3369
3370
3370 - generate a build identifier for the working directory::
3371 - generate a build identifier for the working directory::
3371
3372
3372 hg id --id > build-id.dat
3373 hg id --id > build-id.dat
3373
3374
3374 - find the revision corresponding to a tag::
3375 - find the revision corresponding to a tag::
3375
3376
3376 hg id -n -r 1.3
3377 hg id -n -r 1.3
3377
3378
3378 - check the most recent revision of a remote repository::
3379 - check the most recent revision of a remote repository::
3379
3380
3380 hg id -r tip http://selenic.com/hg/
3381 hg id -r tip http://selenic.com/hg/
3381
3382
3382 Returns 0 if successful.
3383 Returns 0 if successful.
3383 """
3384 """
3384
3385
3385 if not repo and not source:
3386 if not repo and not source:
3386 raise util.Abort(_("there is no Mercurial repository here "
3387 raise util.Abort(_("there is no Mercurial repository here "
3387 "(.hg not found)"))
3388 "(.hg not found)"))
3388
3389
3389 hexfunc = ui.debugflag and hex or short
3390 hexfunc = ui.debugflag and hex or short
3390 default = not (num or id or branch or tags or bookmarks)
3391 default = not (num or id or branch or tags or bookmarks)
3391 output = []
3392 output = []
3392 revs = []
3393 revs = []
3393
3394
3394 if source:
3395 if source:
3395 source, branches = hg.parseurl(ui.expandpath(source))
3396 source, branches = hg.parseurl(ui.expandpath(source))
3396 repo = hg.peer(ui, opts, source)
3397 repo = hg.peer(ui, opts, source)
3397 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3398 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3398
3399
3399 if not repo.local():
3400 if not repo.local():
3400 if num or branch or tags:
3401 if num or branch or tags:
3401 raise util.Abort(
3402 raise util.Abort(
3402 _("can't query remote revision number, branch, or tags"))
3403 _("can't query remote revision number, branch, or tags"))
3403 if not rev and revs:
3404 if not rev and revs:
3404 rev = revs[0]
3405 rev = revs[0]
3405 if not rev:
3406 if not rev:
3406 rev = "tip"
3407 rev = "tip"
3407
3408
3408 remoterev = repo.lookup(rev)
3409 remoterev = repo.lookup(rev)
3409 if default or id:
3410 if default or id:
3410 output = [hexfunc(remoterev)]
3411 output = [hexfunc(remoterev)]
3411
3412
3412 def getbms():
3413 def getbms():
3413 bms = []
3414 bms = []
3414
3415
3415 if 'bookmarks' in repo.listkeys('namespaces'):
3416 if 'bookmarks' in repo.listkeys('namespaces'):
3416 hexremoterev = hex(remoterev)
3417 hexremoterev = hex(remoterev)
3417 bms = [bm for bm, bmr in repo.listkeys('bookmarks').iteritems()
3418 bms = [bm for bm, bmr in repo.listkeys('bookmarks').iteritems()
3418 if bmr == hexremoterev]
3419 if bmr == hexremoterev]
3419
3420
3420 return bms
3421 return bms
3421
3422
3422 if bookmarks:
3423 if bookmarks:
3423 output.extend(getbms())
3424 output.extend(getbms())
3424 elif default and not ui.quiet:
3425 elif default and not ui.quiet:
3425 # multiple bookmarks for a single parent separated by '/'
3426 # multiple bookmarks for a single parent separated by '/'
3426 bm = '/'.join(getbms())
3427 bm = '/'.join(getbms())
3427 if bm:
3428 if bm:
3428 output.append(bm)
3429 output.append(bm)
3429 else:
3430 else:
3430 if not rev:
3431 if not rev:
3431 ctx = repo[None]
3432 ctx = repo[None]
3432 parents = ctx.parents()
3433 parents = ctx.parents()
3433 changed = ""
3434 changed = ""
3434 if default or id or num:
3435 if default or id or num:
3435 changed = util.any(repo.status()) and "+" or ""
3436 changed = util.any(repo.status()) and "+" or ""
3436 if default or id:
3437 if default or id:
3437 output = ["%s%s" %
3438 output = ["%s%s" %
3438 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
3439 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
3439 if num:
3440 if num:
3440 output.append("%s%s" %
3441 output.append("%s%s" %
3441 ('+'.join([str(p.rev()) for p in parents]), changed))
3442 ('+'.join([str(p.rev()) for p in parents]), changed))
3442 else:
3443 else:
3443 ctx = scmutil.revsingle(repo, rev)
3444 ctx = scmutil.revsingle(repo, rev)
3444 if default or id:
3445 if default or id:
3445 output = [hexfunc(ctx.node())]
3446 output = [hexfunc(ctx.node())]
3446 if num:
3447 if num:
3447 output.append(str(ctx.rev()))
3448 output.append(str(ctx.rev()))
3448
3449
3449 if default and not ui.quiet:
3450 if default and not ui.quiet:
3450 b = ctx.branch()
3451 b = ctx.branch()
3451 if b != 'default':
3452 if b != 'default':
3452 output.append("(%s)" % b)
3453 output.append("(%s)" % b)
3453
3454
3454 # multiple tags for a single parent separated by '/'
3455 # multiple tags for a single parent separated by '/'
3455 t = '/'.join(ctx.tags())
3456 t = '/'.join(ctx.tags())
3456 if t:
3457 if t:
3457 output.append(t)
3458 output.append(t)
3458
3459
3459 # multiple bookmarks for a single parent separated by '/'
3460 # multiple bookmarks for a single parent separated by '/'
3460 bm = '/'.join(ctx.bookmarks())
3461 bm = '/'.join(ctx.bookmarks())
3461 if bm:
3462 if bm:
3462 output.append(bm)
3463 output.append(bm)
3463 else:
3464 else:
3464 if branch:
3465 if branch:
3465 output.append(ctx.branch())
3466 output.append(ctx.branch())
3466
3467
3467 if tags:
3468 if tags:
3468 output.extend(ctx.tags())
3469 output.extend(ctx.tags())
3469
3470
3470 if bookmarks:
3471 if bookmarks:
3471 output.extend(ctx.bookmarks())
3472 output.extend(ctx.bookmarks())
3472
3473
3473 ui.write("%s\n" % ' '.join(output))
3474 ui.write("%s\n" % ' '.join(output))
3474
3475
3475 @command('import|patch',
3476 @command('import|patch',
3476 [('p', 'strip', 1,
3477 [('p', 'strip', 1,
3477 _('directory strip option for patch. This has the same '
3478 _('directory strip option for patch. This has the same '
3478 'meaning as the corresponding patch option'), _('NUM')),
3479 'meaning as the corresponding patch option'), _('NUM')),
3479 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3480 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3480 ('e', 'edit', False, _('invoke editor on commit messages')),
3481 ('e', 'edit', False, _('invoke editor on commit messages')),
3481 ('f', 'force', None, _('skip check for outstanding uncommitted changes')),
3482 ('f', 'force', None, _('skip check for outstanding uncommitted changes')),
3482 ('', 'no-commit', None,
3483 ('', 'no-commit', None,
3483 _("don't commit, just update the working directory")),
3484 _("don't commit, just update the working directory")),
3484 ('', 'bypass', None,
3485 ('', 'bypass', None,
3485 _("apply patch without touching the working directory")),
3486 _("apply patch without touching the working directory")),
3486 ('', 'exact', None,
3487 ('', 'exact', None,
3487 _('apply patch to the nodes from which it was generated')),
3488 _('apply patch to the nodes from which it was generated')),
3488 ('', 'import-branch', None,
3489 ('', 'import-branch', None,
3489 _('use any branch information in patch (implied by --exact)'))] +
3490 _('use any branch information in patch (implied by --exact)'))] +
3490 commitopts + commitopts2 + similarityopts,
3491 commitopts + commitopts2 + similarityopts,
3491 _('[OPTION]... PATCH...'))
3492 _('[OPTION]... PATCH...'))
3492 def import_(ui, repo, patch1=None, *patches, **opts):
3493 def import_(ui, repo, patch1=None, *patches, **opts):
3493 """import an ordered set of patches
3494 """import an ordered set of patches
3494
3495
3495 Import a list of patches and commit them individually (unless
3496 Import a list of patches and commit them individually (unless
3496 --no-commit is specified).
3497 --no-commit is specified).
3497
3498
3498 If there are outstanding changes in the working directory, import
3499 If there are outstanding changes in the working directory, import
3499 will abort unless given the -f/--force flag.
3500 will abort unless given the -f/--force flag.
3500
3501
3501 You can import a patch straight from a mail message. Even patches
3502 You can import a patch straight from a mail message. Even patches
3502 as attachments work (to use the body part, it must have type
3503 as attachments work (to use the body part, it must have type
3503 text/plain or text/x-patch). From and Subject headers of email
3504 text/plain or text/x-patch). From and Subject headers of email
3504 message are used as default committer and commit message. All
3505 message are used as default committer and commit message. All
3505 text/plain body parts before first diff are added to commit
3506 text/plain body parts before first diff are added to commit
3506 message.
3507 message.
3507
3508
3508 If the imported patch was generated by :hg:`export`, user and
3509 If the imported patch was generated by :hg:`export`, user and
3509 description from patch override values from message headers and
3510 description from patch override values from message headers and
3510 body. Values given on command line with -m/--message and -u/--user
3511 body. Values given on command line with -m/--message and -u/--user
3511 override these.
3512 override these.
3512
3513
3513 If --exact is specified, import will set the working directory to
3514 If --exact is specified, import will set the working directory to
3514 the parent of each patch before applying it, and will abort if the
3515 the parent of each patch before applying it, and will abort if the
3515 resulting changeset has a different ID than the one recorded in
3516 resulting changeset has a different ID than the one recorded in
3516 the patch. This may happen due to character set problems or other
3517 the patch. This may happen due to character set problems or other
3517 deficiencies in the text patch format.
3518 deficiencies in the text patch format.
3518
3519
3519 Use --bypass to apply and commit patches directly to the
3520 Use --bypass to apply and commit patches directly to the
3520 repository, not touching the working directory. Without --exact,
3521 repository, not touching the working directory. Without --exact,
3521 patches will be applied on top of the working directory parent
3522 patches will be applied on top of the working directory parent
3522 revision.
3523 revision.
3523
3524
3524 With -s/--similarity, hg will attempt to discover renames and
3525 With -s/--similarity, hg will attempt to discover renames and
3525 copies in the patch in the same way as :hg:`addremove`.
3526 copies in the patch in the same way as :hg:`addremove`.
3526
3527
3527 To read a patch from standard input, use "-" as the patch name. If
3528 To read a patch from standard input, use "-" as the patch name. If
3528 a URL is specified, the patch will be downloaded from it.
3529 a URL is specified, the patch will be downloaded from it.
3529 See :hg:`help dates` for a list of formats valid for -d/--date.
3530 See :hg:`help dates` for a list of formats valid for -d/--date.
3530
3531
3531 .. container:: verbose
3532 .. container:: verbose
3532
3533
3533 Examples:
3534 Examples:
3534
3535
3535 - import a traditional patch from a website and detect renames::
3536 - import a traditional patch from a website and detect renames::
3536
3537
3537 hg import -s 80 http://example.com/bugfix.patch
3538 hg import -s 80 http://example.com/bugfix.patch
3538
3539
3539 - import a changeset from an hgweb server::
3540 - import a changeset from an hgweb server::
3540
3541
3541 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
3542 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
3542
3543
3543 - import all the patches in an Unix-style mbox::
3544 - import all the patches in an Unix-style mbox::
3544
3545
3545 hg import incoming-patches.mbox
3546 hg import incoming-patches.mbox
3546
3547
3547 - attempt to exactly restore an exported changeset (not always
3548 - attempt to exactly restore an exported changeset (not always
3548 possible)::
3549 possible)::
3549
3550
3550 hg import --exact proposed-fix.patch
3551 hg import --exact proposed-fix.patch
3551
3552
3552 Returns 0 on success.
3553 Returns 0 on success.
3553 """
3554 """
3554
3555
3555 if not patch1:
3556 if not patch1:
3556 raise util.Abort(_('need at least one patch to import'))
3557 raise util.Abort(_('need at least one patch to import'))
3557
3558
3558 patches = (patch1,) + patches
3559 patches = (patch1,) + patches
3559
3560
3560 date = opts.get('date')
3561 date = opts.get('date')
3561 if date:
3562 if date:
3562 opts['date'] = util.parsedate(date)
3563 opts['date'] = util.parsedate(date)
3563
3564
3564 editor = cmdutil.commiteditor
3565 editor = cmdutil.commiteditor
3565 if opts.get('edit'):
3566 if opts.get('edit'):
3566 editor = cmdutil.commitforceeditor
3567 editor = cmdutil.commitforceeditor
3567
3568
3568 update = not opts.get('bypass')
3569 update = not opts.get('bypass')
3569 if not update and opts.get('no_commit'):
3570 if not update and opts.get('no_commit'):
3570 raise util.Abort(_('cannot use --no-commit with --bypass'))
3571 raise util.Abort(_('cannot use --no-commit with --bypass'))
3571 try:
3572 try:
3572 sim = float(opts.get('similarity') or 0)
3573 sim = float(opts.get('similarity') or 0)
3573 except ValueError:
3574 except ValueError:
3574 raise util.Abort(_('similarity must be a number'))
3575 raise util.Abort(_('similarity must be a number'))
3575 if sim < 0 or sim > 100:
3576 if sim < 0 or sim > 100:
3576 raise util.Abort(_('similarity must be between 0 and 100'))
3577 raise util.Abort(_('similarity must be between 0 and 100'))
3577 if sim and not update:
3578 if sim and not update:
3578 raise util.Abort(_('cannot use --similarity with --bypass'))
3579 raise util.Abort(_('cannot use --similarity with --bypass'))
3579
3580
3580 if (opts.get('exact') or not opts.get('force')) and update:
3581 if (opts.get('exact') or not opts.get('force')) and update:
3581 cmdutil.bailifchanged(repo)
3582 cmdutil.bailifchanged(repo)
3582
3583
3583 base = opts["base"]
3584 base = opts["base"]
3584 strip = opts["strip"]
3585 strip = opts["strip"]
3585 wlock = lock = tr = None
3586 wlock = lock = tr = None
3586 msgs = []
3587 msgs = []
3587
3588
3588 def checkexact(repo, n, nodeid):
3589 def checkexact(repo, n, nodeid):
3589 if opts.get('exact') and hex(n) != nodeid:
3590 if opts.get('exact') and hex(n) != nodeid:
3590 repo.rollback()
3591 repo.rollback()
3591 raise util.Abort(_('patch is damaged or loses information'))
3592 raise util.Abort(_('patch is damaged or loses information'))
3592
3593
3593 def tryone(ui, hunk, parents):
3594 def tryone(ui, hunk, parents):
3594 tmpname, message, user, date, branch, nodeid, p1, p2 = \
3595 tmpname, message, user, date, branch, nodeid, p1, p2 = \
3595 patch.extract(ui, hunk)
3596 patch.extract(ui, hunk)
3596
3597
3597 if not tmpname:
3598 if not tmpname:
3598 return (None, None)
3599 return (None, None)
3599 msg = _('applied to working directory')
3600 msg = _('applied to working directory')
3600
3601
3601 try:
3602 try:
3602 cmdline_message = cmdutil.logmessage(ui, opts)
3603 cmdline_message = cmdutil.logmessage(ui, opts)
3603 if cmdline_message:
3604 if cmdline_message:
3604 # pickup the cmdline msg
3605 # pickup the cmdline msg
3605 message = cmdline_message
3606 message = cmdline_message
3606 elif message:
3607 elif message:
3607 # pickup the patch msg
3608 # pickup the patch msg
3608 message = message.strip()
3609 message = message.strip()
3609 else:
3610 else:
3610 # launch the editor
3611 # launch the editor
3611 message = None
3612 message = None
3612 ui.debug('message:\n%s\n' % message)
3613 ui.debug('message:\n%s\n' % message)
3613
3614
3614 if len(parents) == 1:
3615 if len(parents) == 1:
3615 parents.append(repo[nullid])
3616 parents.append(repo[nullid])
3616 if opts.get('exact'):
3617 if opts.get('exact'):
3617 if not nodeid or not p1:
3618 if not nodeid or not p1:
3618 raise util.Abort(_('not a Mercurial patch'))
3619 raise util.Abort(_('not a Mercurial patch'))
3619 p1 = repo[p1]
3620 p1 = repo[p1]
3620 p2 = repo[p2 or nullid]
3621 p2 = repo[p2 or nullid]
3621 elif p2:
3622 elif p2:
3622 try:
3623 try:
3623 p1 = repo[p1]
3624 p1 = repo[p1]
3624 p2 = repo[p2]
3625 p2 = repo[p2]
3625 # Without any options, consider p2 only if the
3626 # Without any options, consider p2 only if the
3626 # patch is being applied on top of the recorded
3627 # patch is being applied on top of the recorded
3627 # first parent.
3628 # first parent.
3628 if p1 != parents[0]:
3629 if p1 != parents[0]:
3629 p1 = parents[0]
3630 p1 = parents[0]
3630 p2 = repo[nullid]
3631 p2 = repo[nullid]
3631 except error.RepoError:
3632 except error.RepoError:
3632 p1, p2 = parents
3633 p1, p2 = parents
3633 else:
3634 else:
3634 p1, p2 = parents
3635 p1, p2 = parents
3635
3636
3636 n = None
3637 n = None
3637 if update:
3638 if update:
3638 if p1 != parents[0]:
3639 if p1 != parents[0]:
3639 hg.clean(repo, p1.node())
3640 hg.clean(repo, p1.node())
3640 if p2 != parents[1]:
3641 if p2 != parents[1]:
3641 repo.setparents(p1.node(), p2.node())
3642 repo.setparents(p1.node(), p2.node())
3642
3643
3643 if opts.get('exact') or opts.get('import_branch'):
3644 if opts.get('exact') or opts.get('import_branch'):
3644 repo.dirstate.setbranch(branch or 'default')
3645 repo.dirstate.setbranch(branch or 'default')
3645
3646
3646 files = set()
3647 files = set()
3647 patch.patch(ui, repo, tmpname, strip=strip, files=files,
3648 patch.patch(ui, repo, tmpname, strip=strip, files=files,
3648 eolmode=None, similarity=sim / 100.0)
3649 eolmode=None, similarity=sim / 100.0)
3649 files = list(files)
3650 files = list(files)
3650 if opts.get('no_commit'):
3651 if opts.get('no_commit'):
3651 if message:
3652 if message:
3652 msgs.append(message)
3653 msgs.append(message)
3653 else:
3654 else:
3654 if opts.get('exact') or p2:
3655 if opts.get('exact') or p2:
3655 # If you got here, you either use --force and know what
3656 # If you got here, you either use --force and know what
3656 # you are doing or used --exact or a merge patch while
3657 # you are doing or used --exact or a merge patch while
3657 # being updated to its first parent.
3658 # being updated to its first parent.
3658 m = None
3659 m = None
3659 else:
3660 else:
3660 m = scmutil.matchfiles(repo, files or [])
3661 m = scmutil.matchfiles(repo, files or [])
3661 n = repo.commit(message, opts.get('user') or user,
3662 n = repo.commit(message, opts.get('user') or user,
3662 opts.get('date') or date, match=m,
3663 opts.get('date') or date, match=m,
3663 editor=editor)
3664 editor=editor)
3664 checkexact(repo, n, nodeid)
3665 checkexact(repo, n, nodeid)
3665 else:
3666 else:
3666 if opts.get('exact') or opts.get('import_branch'):
3667 if opts.get('exact') or opts.get('import_branch'):
3667 branch = branch or 'default'
3668 branch = branch or 'default'
3668 else:
3669 else:
3669 branch = p1.branch()
3670 branch = p1.branch()
3670 store = patch.filestore()
3671 store = patch.filestore()
3671 try:
3672 try:
3672 files = set()
3673 files = set()
3673 try:
3674 try:
3674 patch.patchrepo(ui, repo, p1, store, tmpname, strip,
3675 patch.patchrepo(ui, repo, p1, store, tmpname, strip,
3675 files, eolmode=None)
3676 files, eolmode=None)
3676 except patch.PatchError, e:
3677 except patch.PatchError, e:
3677 raise util.Abort(str(e))
3678 raise util.Abort(str(e))
3678 memctx = patch.makememctx(repo, (p1.node(), p2.node()),
3679 memctx = patch.makememctx(repo, (p1.node(), p2.node()),
3679 message,
3680 message,
3680 opts.get('user') or user,
3681 opts.get('user') or user,
3681 opts.get('date') or date,
3682 opts.get('date') or date,
3682 branch, files, store,
3683 branch, files, store,
3683 editor=cmdutil.commiteditor)
3684 editor=cmdutil.commiteditor)
3684 repo.savecommitmessage(memctx.description())
3685 repo.savecommitmessage(memctx.description())
3685 n = memctx.commit()
3686 n = memctx.commit()
3686 checkexact(repo, n, nodeid)
3687 checkexact(repo, n, nodeid)
3687 finally:
3688 finally:
3688 store.close()
3689 store.close()
3689 if n:
3690 if n:
3690 # i18n: refers to a short changeset id
3691 # i18n: refers to a short changeset id
3691 msg = _('created %s') % short(n)
3692 msg = _('created %s') % short(n)
3692 return (msg, n)
3693 return (msg, n)
3693 finally:
3694 finally:
3694 os.unlink(tmpname)
3695 os.unlink(tmpname)
3695
3696
3696 try:
3697 try:
3697 try:
3698 try:
3698 wlock = repo.wlock()
3699 wlock = repo.wlock()
3699 if not opts.get('no_commit'):
3700 if not opts.get('no_commit'):
3700 lock = repo.lock()
3701 lock = repo.lock()
3701 tr = repo.transaction('import')
3702 tr = repo.transaction('import')
3702 parents = repo.parents()
3703 parents = repo.parents()
3703 for patchurl in patches:
3704 for patchurl in patches:
3704 if patchurl == '-':
3705 if patchurl == '-':
3705 ui.status(_('applying patch from stdin\n'))
3706 ui.status(_('applying patch from stdin\n'))
3706 patchfile = ui.fin
3707 patchfile = ui.fin
3707 patchurl = 'stdin' # for error message
3708 patchurl = 'stdin' # for error message
3708 else:
3709 else:
3709 patchurl = os.path.join(base, patchurl)
3710 patchurl = os.path.join(base, patchurl)
3710 ui.status(_('applying %s\n') % patchurl)
3711 ui.status(_('applying %s\n') % patchurl)
3711 patchfile = url.open(ui, patchurl)
3712 patchfile = url.open(ui, patchurl)
3712
3713
3713 haspatch = False
3714 haspatch = False
3714 for hunk in patch.split(patchfile):
3715 for hunk in patch.split(patchfile):
3715 (msg, node) = tryone(ui, hunk, parents)
3716 (msg, node) = tryone(ui, hunk, parents)
3716 if msg:
3717 if msg:
3717 haspatch = True
3718 haspatch = True
3718 ui.note(msg + '\n')
3719 ui.note(msg + '\n')
3719 if update or opts.get('exact'):
3720 if update or opts.get('exact'):
3720 parents = repo.parents()
3721 parents = repo.parents()
3721 else:
3722 else:
3722 parents = [repo[node]]
3723 parents = [repo[node]]
3723
3724
3724 if not haspatch:
3725 if not haspatch:
3725 raise util.Abort(_('%s: no diffs found') % patchurl)
3726 raise util.Abort(_('%s: no diffs found') % patchurl)
3726
3727
3727 if tr:
3728 if tr:
3728 tr.close()
3729 tr.close()
3729 if msgs:
3730 if msgs:
3730 repo.savecommitmessage('\n* * *\n'.join(msgs))
3731 repo.savecommitmessage('\n* * *\n'.join(msgs))
3731 except:
3732 except:
3732 # wlock.release() indirectly calls dirstate.write(): since
3733 # wlock.release() indirectly calls dirstate.write(): since
3733 # we're crashing, we do not want to change the working dir
3734 # we're crashing, we do not want to change the working dir
3734 # parent after all, so make sure it writes nothing
3735 # parent after all, so make sure it writes nothing
3735 repo.dirstate.invalidate()
3736 repo.dirstate.invalidate()
3736 raise
3737 raise
3737 finally:
3738 finally:
3738 if tr:
3739 if tr:
3739 tr.release()
3740 tr.release()
3740 release(lock, wlock)
3741 release(lock, wlock)
3741
3742
3742 @command('incoming|in',
3743 @command('incoming|in',
3743 [('f', 'force', None,
3744 [('f', 'force', None,
3744 _('run even if remote repository is unrelated')),
3745 _('run even if remote repository is unrelated')),
3745 ('n', 'newest-first', None, _('show newest record first')),
3746 ('n', 'newest-first', None, _('show newest record first')),
3746 ('', 'bundle', '',
3747 ('', 'bundle', '',
3747 _('file to store the bundles into'), _('FILE')),
3748 _('file to store the bundles into'), _('FILE')),
3748 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3749 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3749 ('B', 'bookmarks', False, _("compare bookmarks")),
3750 ('B', 'bookmarks', False, _("compare bookmarks")),
3750 ('b', 'branch', [],
3751 ('b', 'branch', [],
3751 _('a specific branch you would like to pull'), _('BRANCH')),
3752 _('a specific branch you would like to pull'), _('BRANCH')),
3752 ] + logopts + remoteopts + subrepoopts,
3753 ] + logopts + remoteopts + subrepoopts,
3753 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3754 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3754 def incoming(ui, repo, source="default", **opts):
3755 def incoming(ui, repo, source="default", **opts):
3755 """show new changesets found in source
3756 """show new changesets found in source
3756
3757
3757 Show new changesets found in the specified path/URL or the default
3758 Show new changesets found in the specified path/URL or the default
3758 pull location. These are the changesets that would have been pulled
3759 pull location. These are the changesets that would have been pulled
3759 if a pull at the time you issued this command.
3760 if a pull at the time you issued this command.
3760
3761
3761 For remote repository, using --bundle avoids downloading the
3762 For remote repository, using --bundle avoids downloading the
3762 changesets twice if the incoming is followed by a pull.
3763 changesets twice if the incoming is followed by a pull.
3763
3764
3764 See pull for valid source format details.
3765 See pull for valid source format details.
3765
3766
3766 Returns 0 if there are incoming changes, 1 otherwise.
3767 Returns 0 if there are incoming changes, 1 otherwise.
3767 """
3768 """
3768 if opts.get('bundle') and opts.get('subrepos'):
3769 if opts.get('bundle') and opts.get('subrepos'):
3769 raise util.Abort(_('cannot combine --bundle and --subrepos'))
3770 raise util.Abort(_('cannot combine --bundle and --subrepos'))
3770
3771
3771 if opts.get('bookmarks'):
3772 if opts.get('bookmarks'):
3772 source, branches = hg.parseurl(ui.expandpath(source),
3773 source, branches = hg.parseurl(ui.expandpath(source),
3773 opts.get('branch'))
3774 opts.get('branch'))
3774 other = hg.peer(repo, opts, source)
3775 other = hg.peer(repo, opts, source)
3775 if 'bookmarks' not in other.listkeys('namespaces'):
3776 if 'bookmarks' not in other.listkeys('namespaces'):
3776 ui.warn(_("remote doesn't support bookmarks\n"))
3777 ui.warn(_("remote doesn't support bookmarks\n"))
3777 return 0
3778 return 0
3778 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3779 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3779 return bookmarks.diff(ui, repo, other)
3780 return bookmarks.diff(ui, repo, other)
3780
3781
3781 repo._subtoppath = ui.expandpath(source)
3782 repo._subtoppath = ui.expandpath(source)
3782 try:
3783 try:
3783 return hg.incoming(ui, repo, source, opts)
3784 return hg.incoming(ui, repo, source, opts)
3784 finally:
3785 finally:
3785 del repo._subtoppath
3786 del repo._subtoppath
3786
3787
3787
3788
3788 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'))
3789 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'))
3789 def init(ui, dest=".", **opts):
3790 def init(ui, dest=".", **opts):
3790 """create a new repository in the given directory
3791 """create a new repository in the given directory
3791
3792
3792 Initialize a new repository in the given directory. If the given
3793 Initialize a new repository in the given directory. If the given
3793 directory does not exist, it will be created.
3794 directory does not exist, it will be created.
3794
3795
3795 If no directory is given, the current directory is used.
3796 If no directory is given, the current directory is used.
3796
3797
3797 It is possible to specify an ``ssh://`` URL as the destination.
3798 It is possible to specify an ``ssh://`` URL as the destination.
3798 See :hg:`help urls` for more information.
3799 See :hg:`help urls` for more information.
3799
3800
3800 Returns 0 on success.
3801 Returns 0 on success.
3801 """
3802 """
3802 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3803 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3803
3804
3804 @command('locate',
3805 @command('locate',
3805 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3806 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3806 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3807 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3807 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3808 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3808 ] + walkopts,
3809 ] + walkopts,
3809 _('[OPTION]... [PATTERN]...'))
3810 _('[OPTION]... [PATTERN]...'))
3810 def locate(ui, repo, *pats, **opts):
3811 def locate(ui, repo, *pats, **opts):
3811 """locate files matching specific patterns
3812 """locate files matching specific patterns
3812
3813
3813 Print files under Mercurial control in the working directory whose
3814 Print files under Mercurial control in the working directory whose
3814 names match the given patterns.
3815 names match the given patterns.
3815
3816
3816 By default, this command searches all directories in the working
3817 By default, this command searches all directories in the working
3817 directory. To search just the current directory and its
3818 directory. To search just the current directory and its
3818 subdirectories, use "--include .".
3819 subdirectories, use "--include .".
3819
3820
3820 If no patterns are given to match, this command prints the names
3821 If no patterns are given to match, this command prints the names
3821 of all files under Mercurial control in the working directory.
3822 of all files under Mercurial control in the working directory.
3822
3823
3823 If you want to feed the output of this command into the "xargs"
3824 If you want to feed the output of this command into the "xargs"
3824 command, use the -0 option to both this command and "xargs". This
3825 command, use the -0 option to both this command and "xargs". This
3825 will avoid the problem of "xargs" treating single filenames that
3826 will avoid the problem of "xargs" treating single filenames that
3826 contain whitespace as multiple filenames.
3827 contain whitespace as multiple filenames.
3827
3828
3828 Returns 0 if a match is found, 1 otherwise.
3829 Returns 0 if a match is found, 1 otherwise.
3829 """
3830 """
3830 end = opts.get('print0') and '\0' or '\n'
3831 end = opts.get('print0') and '\0' or '\n'
3831 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3832 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3832
3833
3833 ret = 1
3834 ret = 1
3834 m = scmutil.match(repo[rev], pats, opts, default='relglob')
3835 m = scmutil.match(repo[rev], pats, opts, default='relglob')
3835 m.bad = lambda x, y: False
3836 m.bad = lambda x, y: False
3836 for abs in repo[rev].walk(m):
3837 for abs in repo[rev].walk(m):
3837 if not rev and abs not in repo.dirstate:
3838 if not rev and abs not in repo.dirstate:
3838 continue
3839 continue
3839 if opts.get('fullpath'):
3840 if opts.get('fullpath'):
3840 ui.write(repo.wjoin(abs), end)
3841 ui.write(repo.wjoin(abs), end)
3841 else:
3842 else:
3842 ui.write(((pats and m.rel(abs)) or abs), end)
3843 ui.write(((pats and m.rel(abs)) or abs), end)
3843 ret = 0
3844 ret = 0
3844
3845
3845 return ret
3846 return ret
3846
3847
3847 @command('^log|history',
3848 @command('^log|history',
3848 [('f', 'follow', None,
3849 [('f', 'follow', None,
3849 _('follow changeset history, or file history across copies and renames')),
3850 _('follow changeset history, or file history across copies and renames')),
3850 ('', 'follow-first', None,
3851 ('', 'follow-first', None,
3851 _('only follow the first parent of merge changesets (DEPRECATED)')),
3852 _('only follow the first parent of merge changesets (DEPRECATED)')),
3852 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3853 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3853 ('C', 'copies', None, _('show copied files')),
3854 ('C', 'copies', None, _('show copied files')),
3854 ('k', 'keyword', [],
3855 ('k', 'keyword', [],
3855 _('do case-insensitive search for a given text'), _('TEXT')),
3856 _('do case-insensitive search for a given text'), _('TEXT')),
3856 ('r', 'rev', [], _('show the specified revision or range'), _('REV')),
3857 ('r', 'rev', [], _('show the specified revision or range'), _('REV')),
3857 ('', 'removed', None, _('include revisions where files were removed')),
3858 ('', 'removed', None, _('include revisions where files were removed')),
3858 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3859 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3859 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3860 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3860 ('', 'only-branch', [],
3861 ('', 'only-branch', [],
3861 _('show only changesets within the given named branch (DEPRECATED)'),
3862 _('show only changesets within the given named branch (DEPRECATED)'),
3862 _('BRANCH')),
3863 _('BRANCH')),
3863 ('b', 'branch', [],
3864 ('b', 'branch', [],
3864 _('show changesets within the given named branch'), _('BRANCH')),
3865 _('show changesets within the given named branch'), _('BRANCH')),
3865 ('P', 'prune', [],
3866 ('P', 'prune', [],
3866 _('do not display revision or any of its ancestors'), _('REV')),
3867 _('do not display revision or any of its ancestors'), _('REV')),
3867 ('', 'hidden', False, _('show hidden changesets (DEPRECATED)')),
3868 ('', 'hidden', False, _('show hidden changesets (DEPRECATED)')),
3868 ] + logopts + walkopts,
3869 ] + logopts + walkopts,
3869 _('[OPTION]... [FILE]'))
3870 _('[OPTION]... [FILE]'))
3870 def log(ui, repo, *pats, **opts):
3871 def log(ui, repo, *pats, **opts):
3871 """show revision history of entire repository or files
3872 """show revision history of entire repository or files
3872
3873
3873 Print the revision history of the specified files or the entire
3874 Print the revision history of the specified files or the entire
3874 project.
3875 project.
3875
3876
3876 If no revision range is specified, the default is ``tip:0`` unless
3877 If no revision range is specified, the default is ``tip:0`` unless
3877 --follow is set, in which case the working directory parent is
3878 --follow is set, in which case the working directory parent is
3878 used as the starting revision.
3879 used as the starting revision.
3879
3880
3880 File history is shown without following rename or copy history of
3881 File history is shown without following rename or copy history of
3881 files. Use -f/--follow with a filename to follow history across
3882 files. Use -f/--follow with a filename to follow history across
3882 renames and copies. --follow without a filename will only show
3883 renames and copies. --follow without a filename will only show
3883 ancestors or descendants of the starting revision.
3884 ancestors or descendants of the starting revision.
3884
3885
3885 By default this command prints revision number and changeset id,
3886 By default this command prints revision number and changeset id,
3886 tags, non-trivial parents, user, date and time, and a summary for
3887 tags, non-trivial parents, user, date and time, and a summary for
3887 each commit. When the -v/--verbose switch is used, the list of
3888 each commit. When the -v/--verbose switch is used, the list of
3888 changed files and full commit message are shown.
3889 changed files and full commit message are shown.
3889
3890
3890 .. note::
3891 .. note::
3891 log -p/--patch may generate unexpected diff output for merge
3892 log -p/--patch may generate unexpected diff output for merge
3892 changesets, as it will only compare the merge changeset against
3893 changesets, as it will only compare the merge changeset against
3893 its first parent. Also, only files different from BOTH parents
3894 its first parent. Also, only files different from BOTH parents
3894 will appear in files:.
3895 will appear in files:.
3895
3896
3896 .. note::
3897 .. note::
3897 for performance reasons, log FILE may omit duplicate changes
3898 for performance reasons, log FILE may omit duplicate changes
3898 made on branches and will not show deletions. To see all
3899 made on branches and will not show deletions. To see all
3899 changes including duplicates and deletions, use the --removed
3900 changes including duplicates and deletions, use the --removed
3900 switch.
3901 switch.
3901
3902
3902 .. container:: verbose
3903 .. container:: verbose
3903
3904
3904 Some examples:
3905 Some examples:
3905
3906
3906 - changesets with full descriptions and file lists::
3907 - changesets with full descriptions and file lists::
3907
3908
3908 hg log -v
3909 hg log -v
3909
3910
3910 - changesets ancestral to the working directory::
3911 - changesets ancestral to the working directory::
3911
3912
3912 hg log -f
3913 hg log -f
3913
3914
3914 - last 10 commits on the current branch::
3915 - last 10 commits on the current branch::
3915
3916
3916 hg log -l 10 -b .
3917 hg log -l 10 -b .
3917
3918
3918 - changesets showing all modifications of a file, including removals::
3919 - changesets showing all modifications of a file, including removals::
3919
3920
3920 hg log --removed file.c
3921 hg log --removed file.c
3921
3922
3922 - all changesets that touch a directory, with diffs, excluding merges::
3923 - all changesets that touch a directory, with diffs, excluding merges::
3923
3924
3924 hg log -Mp lib/
3925 hg log -Mp lib/
3925
3926
3926 - all revision numbers that match a keyword::
3927 - all revision numbers that match a keyword::
3927
3928
3928 hg log -k bug --template "{rev}\\n"
3929 hg log -k bug --template "{rev}\\n"
3929
3930
3930 - check if a given changeset is included is a tagged release::
3931 - check if a given changeset is included is a tagged release::
3931
3932
3932 hg log -r "a21ccf and ancestor(1.9)"
3933 hg log -r "a21ccf and ancestor(1.9)"
3933
3934
3934 - find all changesets by some user in a date range::
3935 - find all changesets by some user in a date range::
3935
3936
3936 hg log -k alice -d "may 2008 to jul 2008"
3937 hg log -k alice -d "may 2008 to jul 2008"
3937
3938
3938 - summary of all changesets after the last tag::
3939 - summary of all changesets after the last tag::
3939
3940
3940 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3941 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3941
3942
3942 See :hg:`help dates` for a list of formats valid for -d/--date.
3943 See :hg:`help dates` for a list of formats valid for -d/--date.
3943
3944
3944 See :hg:`help revisions` and :hg:`help revsets` for more about
3945 See :hg:`help revisions` and :hg:`help revsets` for more about
3945 specifying revisions.
3946 specifying revisions.
3946
3947
3947 See :hg:`help templates` for more about pre-packaged styles and
3948 See :hg:`help templates` for more about pre-packaged styles and
3948 specifying custom templates.
3949 specifying custom templates.
3949
3950
3950 Returns 0 on success.
3951 Returns 0 on success.
3951 """
3952 """
3952
3953
3953 matchfn = scmutil.match(repo[None], pats, opts)
3954 matchfn = scmutil.match(repo[None], pats, opts)
3954 limit = cmdutil.loglimit(opts)
3955 limit = cmdutil.loglimit(opts)
3955 count = 0
3956 count = 0
3956
3957
3957 getrenamed, endrev = None, None
3958 getrenamed, endrev = None, None
3958 if opts.get('copies'):
3959 if opts.get('copies'):
3959 if opts.get('rev'):
3960 if opts.get('rev'):
3960 endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1
3961 endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1
3961 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3962 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3962
3963
3963 df = False
3964 df = False
3964 if opts["date"]:
3965 if opts["date"]:
3965 df = util.matchdate(opts["date"])
3966 df = util.matchdate(opts["date"])
3966
3967
3967 branches = opts.get('branch', []) + opts.get('only_branch', [])
3968 branches = opts.get('branch', []) + opts.get('only_branch', [])
3968 opts['branch'] = [repo.lookupbranch(b) for b in branches]
3969 opts['branch'] = [repo.lookupbranch(b) for b in branches]
3969
3970
3970 displayer = cmdutil.show_changeset(ui, repo, opts, True)
3971 displayer = cmdutil.show_changeset(ui, repo, opts, True)
3971 def prep(ctx, fns):
3972 def prep(ctx, fns):
3972 rev = ctx.rev()
3973 rev = ctx.rev()
3973 parents = [p for p in repo.changelog.parentrevs(rev)
3974 parents = [p for p in repo.changelog.parentrevs(rev)
3974 if p != nullrev]
3975 if p != nullrev]
3975 if opts.get('no_merges') and len(parents) == 2:
3976 if opts.get('no_merges') and len(parents) == 2:
3976 return
3977 return
3977 if opts.get('only_merges') and len(parents) != 2:
3978 if opts.get('only_merges') and len(parents) != 2:
3978 return
3979 return
3979 if opts.get('branch') and ctx.branch() not in opts['branch']:
3980 if opts.get('branch') and ctx.branch() not in opts['branch']:
3980 return
3981 return
3981 if not opts.get('hidden') and ctx.hidden():
3982 if not opts.get('hidden') and ctx.hidden():
3982 return
3983 return
3983 if df and not df(ctx.date()[0]):
3984 if df and not df(ctx.date()[0]):
3984 return
3985 return
3985
3986
3986 lower = encoding.lower
3987 lower = encoding.lower
3987 if opts.get('user'):
3988 if opts.get('user'):
3988 luser = lower(ctx.user())
3989 luser = lower(ctx.user())
3989 for k in [lower(x) for x in opts['user']]:
3990 for k in [lower(x) for x in opts['user']]:
3990 if (k in luser):
3991 if (k in luser):
3991 break
3992 break
3992 else:
3993 else:
3993 return
3994 return
3994 if opts.get('keyword'):
3995 if opts.get('keyword'):
3995 luser = lower(ctx.user())
3996 luser = lower(ctx.user())
3996 ldesc = lower(ctx.description())
3997 ldesc = lower(ctx.description())
3997 lfiles = lower(" ".join(ctx.files()))
3998 lfiles = lower(" ".join(ctx.files()))
3998 for k in [lower(x) for x in opts['keyword']]:
3999 for k in [lower(x) for x in opts['keyword']]:
3999 if (k in luser or k in ldesc or k in lfiles):
4000 if (k in luser or k in ldesc or k in lfiles):
4000 break
4001 break
4001 else:
4002 else:
4002 return
4003 return
4003
4004
4004 copies = None
4005 copies = None
4005 if getrenamed is not None and rev:
4006 if getrenamed is not None and rev:
4006 copies = []
4007 copies = []
4007 for fn in ctx.files():
4008 for fn in ctx.files():
4008 rename = getrenamed(fn, rev)
4009 rename = getrenamed(fn, rev)
4009 if rename:
4010 if rename:
4010 copies.append((fn, rename[0]))
4011 copies.append((fn, rename[0]))
4011
4012
4012 revmatchfn = None
4013 revmatchfn = None
4013 if opts.get('patch') or opts.get('stat'):
4014 if opts.get('patch') or opts.get('stat'):
4014 if opts.get('follow') or opts.get('follow_first'):
4015 if opts.get('follow') or opts.get('follow_first'):
4015 # note: this might be wrong when following through merges
4016 # note: this might be wrong when following through merges
4016 revmatchfn = scmutil.match(repo[None], fns, default='path')
4017 revmatchfn = scmutil.match(repo[None], fns, default='path')
4017 else:
4018 else:
4018 revmatchfn = matchfn
4019 revmatchfn = matchfn
4019
4020
4020 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
4021 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
4021
4022
4022 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
4023 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
4023 if count == limit:
4024 if count == limit:
4024 break
4025 break
4025 if displayer.flush(ctx.rev()):
4026 if displayer.flush(ctx.rev()):
4026 count += 1
4027 count += 1
4027 displayer.close()
4028 displayer.close()
4028
4029
4029 @command('manifest',
4030 @command('manifest',
4030 [('r', 'rev', '', _('revision to display'), _('REV')),
4031 [('r', 'rev', '', _('revision to display'), _('REV')),
4031 ('', 'all', False, _("list files from all revisions"))],
4032 ('', 'all', False, _("list files from all revisions"))],
4032 _('[-r REV]'))
4033 _('[-r REV]'))
4033 def manifest(ui, repo, node=None, rev=None, **opts):
4034 def manifest(ui, repo, node=None, rev=None, **opts):
4034 """output the current or given revision of the project manifest
4035 """output the current or given revision of the project manifest
4035
4036
4036 Print a list of version controlled files for the given revision.
4037 Print a list of version controlled files for the given revision.
4037 If no revision is given, the first parent of the working directory
4038 If no revision is given, the first parent of the working directory
4038 is used, or the null revision if no revision is checked out.
4039 is used, or the null revision if no revision is checked out.
4039
4040
4040 With -v, print file permissions, symlink and executable bits.
4041 With -v, print file permissions, symlink and executable bits.
4041 With --debug, print file revision hashes.
4042 With --debug, print file revision hashes.
4042
4043
4043 If option --all is specified, the list of all files from all revisions
4044 If option --all is specified, the list of all files from all revisions
4044 is printed. This includes deleted and renamed files.
4045 is printed. This includes deleted and renamed files.
4045
4046
4046 Returns 0 on success.
4047 Returns 0 on success.
4047 """
4048 """
4048 if opts.get('all'):
4049 if opts.get('all'):
4049 if rev or node:
4050 if rev or node:
4050 raise util.Abort(_("can't specify a revision with --all"))
4051 raise util.Abort(_("can't specify a revision with --all"))
4051
4052
4052 res = []
4053 res = []
4053 prefix = "data/"
4054 prefix = "data/"
4054 suffix = ".i"
4055 suffix = ".i"
4055 plen = len(prefix)
4056 plen = len(prefix)
4056 slen = len(suffix)
4057 slen = len(suffix)
4057 lock = repo.lock()
4058 lock = repo.lock()
4058 try:
4059 try:
4059 for fn, b, size in repo.store.datafiles():
4060 for fn, b, size in repo.store.datafiles():
4060 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
4061 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
4061 res.append(fn[plen:-slen])
4062 res.append(fn[plen:-slen])
4062 finally:
4063 finally:
4063 lock.release()
4064 lock.release()
4064 for f in sorted(res):
4065 for f in sorted(res):
4065 ui.write("%s\n" % f)
4066 ui.write("%s\n" % f)
4066 return
4067 return
4067
4068
4068 if rev and node:
4069 if rev and node:
4069 raise util.Abort(_("please specify just one revision"))
4070 raise util.Abort(_("please specify just one revision"))
4070
4071
4071 if not node:
4072 if not node:
4072 node = rev
4073 node = rev
4073
4074
4074 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
4075 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
4075 ctx = scmutil.revsingle(repo, node)
4076 ctx = scmutil.revsingle(repo, node)
4076 for f in ctx:
4077 for f in ctx:
4077 if ui.debugflag:
4078 if ui.debugflag:
4078 ui.write("%40s " % hex(ctx.manifest()[f]))
4079 ui.write("%40s " % hex(ctx.manifest()[f]))
4079 if ui.verbose:
4080 if ui.verbose:
4080 ui.write(decor[ctx.flags(f)])
4081 ui.write(decor[ctx.flags(f)])
4081 ui.write("%s\n" % f)
4082 ui.write("%s\n" % f)
4082
4083
4083 @command('^merge',
4084 @command('^merge',
4084 [('f', 'force', None, _('force a merge with outstanding changes')),
4085 [('f', 'force', None, _('force a merge with outstanding changes')),
4085 ('r', 'rev', '', _('revision to merge'), _('REV')),
4086 ('r', 'rev', '', _('revision to merge'), _('REV')),
4086 ('P', 'preview', None,
4087 ('P', 'preview', None,
4087 _('review revisions to merge (no merge is performed)'))
4088 _('review revisions to merge (no merge is performed)'))
4088 ] + mergetoolopts,
4089 ] + mergetoolopts,
4089 _('[-P] [-f] [[-r] REV]'))
4090 _('[-P] [-f] [[-r] REV]'))
4090 def merge(ui, repo, node=None, **opts):
4091 def merge(ui, repo, node=None, **opts):
4091 """merge working directory with another revision
4092 """merge working directory with another revision
4092
4093
4093 The current working directory is updated with all changes made in
4094 The current working directory is updated with all changes made in
4094 the requested revision since the last common predecessor revision.
4095 the requested revision since the last common predecessor revision.
4095
4096
4096 Files that changed between either parent are marked as changed for
4097 Files that changed between either parent are marked as changed for
4097 the next commit and a commit must be performed before any further
4098 the next commit and a commit must be performed before any further
4098 updates to the repository are allowed. The next commit will have
4099 updates to the repository are allowed. The next commit will have
4099 two parents.
4100 two parents.
4100
4101
4101 ``--tool`` can be used to specify the merge tool used for file
4102 ``--tool`` can be used to specify the merge tool used for file
4102 merges. It overrides the HGMERGE environment variable and your
4103 merges. It overrides the HGMERGE environment variable and your
4103 configuration files. See :hg:`help merge-tools` for options.
4104 configuration files. See :hg:`help merge-tools` for options.
4104
4105
4105 If no revision is specified, the working directory's parent is a
4106 If no revision is specified, the working directory's parent is a
4106 head revision, and the current branch contains exactly one other
4107 head revision, and the current branch contains exactly one other
4107 head, the other head is merged with by default. Otherwise, an
4108 head, the other head is merged with by default. Otherwise, an
4108 explicit revision with which to merge with must be provided.
4109 explicit revision with which to merge with must be provided.
4109
4110
4110 :hg:`resolve` must be used to resolve unresolved files.
4111 :hg:`resolve` must be used to resolve unresolved files.
4111
4112
4112 To undo an uncommitted merge, use :hg:`update --clean .` which
4113 To undo an uncommitted merge, use :hg:`update --clean .` which
4113 will check out a clean copy of the original merge parent, losing
4114 will check out a clean copy of the original merge parent, losing
4114 all changes.
4115 all changes.
4115
4116
4116 Returns 0 on success, 1 if there are unresolved files.
4117 Returns 0 on success, 1 if there are unresolved files.
4117 """
4118 """
4118
4119
4119 if opts.get('rev') and node:
4120 if opts.get('rev') and node:
4120 raise util.Abort(_("please specify just one revision"))
4121 raise util.Abort(_("please specify just one revision"))
4121 if not node:
4122 if not node:
4122 node = opts.get('rev')
4123 node = opts.get('rev')
4123
4124
4124 if not node:
4125 if not node:
4125 branch = repo[None].branch()
4126 branch = repo[None].branch()
4126 bheads = repo.branchheads(branch)
4127 bheads = repo.branchheads(branch)
4127 if len(bheads) > 2:
4128 if len(bheads) > 2:
4128 raise util.Abort(_("branch '%s' has %d heads - "
4129 raise util.Abort(_("branch '%s' has %d heads - "
4129 "please merge with an explicit rev")
4130 "please merge with an explicit rev")
4130 % (branch, len(bheads)),
4131 % (branch, len(bheads)),
4131 hint=_("run 'hg heads .' to see heads"))
4132 hint=_("run 'hg heads .' to see heads"))
4132
4133
4133 parent = repo.dirstate.p1()
4134 parent = repo.dirstate.p1()
4134 if len(bheads) == 1:
4135 if len(bheads) == 1:
4135 if len(repo.heads()) > 1:
4136 if len(repo.heads()) > 1:
4136 raise util.Abort(_("branch '%s' has one head - "
4137 raise util.Abort(_("branch '%s' has one head - "
4137 "please merge with an explicit rev")
4138 "please merge with an explicit rev")
4138 % branch,
4139 % branch,
4139 hint=_("run 'hg heads' to see all heads"))
4140 hint=_("run 'hg heads' to see all heads"))
4140 msg, hint = _('nothing to merge'), None
4141 msg, hint = _('nothing to merge'), None
4141 if parent != repo.lookup(branch):
4142 if parent != repo.lookup(branch):
4142 hint = _("use 'hg update' instead")
4143 hint = _("use 'hg update' instead")
4143 raise util.Abort(msg, hint=hint)
4144 raise util.Abort(msg, hint=hint)
4144
4145
4145 if parent not in bheads:
4146 if parent not in bheads:
4146 raise util.Abort(_('working directory not at a head revision'),
4147 raise util.Abort(_('working directory not at a head revision'),
4147 hint=_("use 'hg update' or merge with an "
4148 hint=_("use 'hg update' or merge with an "
4148 "explicit revision"))
4149 "explicit revision"))
4149 node = parent == bheads[0] and bheads[-1] or bheads[0]
4150 node = parent == bheads[0] and bheads[-1] or bheads[0]
4150 else:
4151 else:
4151 node = scmutil.revsingle(repo, node).node()
4152 node = scmutil.revsingle(repo, node).node()
4152
4153
4153 if opts.get('preview'):
4154 if opts.get('preview'):
4154 # find nodes that are ancestors of p2 but not of p1
4155 # find nodes that are ancestors of p2 but not of p1
4155 p1 = repo.lookup('.')
4156 p1 = repo.lookup('.')
4156 p2 = repo.lookup(node)
4157 p2 = repo.lookup(node)
4157 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4158 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4158
4159
4159 displayer = cmdutil.show_changeset(ui, repo, opts)
4160 displayer = cmdutil.show_changeset(ui, repo, opts)
4160 for node in nodes:
4161 for node in nodes:
4161 displayer.show(repo[node])
4162 displayer.show(repo[node])
4162 displayer.close()
4163 displayer.close()
4163 return 0
4164 return 0
4164
4165
4165 try:
4166 try:
4166 # ui.forcemerge is an internal variable, do not document
4167 # ui.forcemerge is an internal variable, do not document
4167 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4168 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4168 return hg.merge(repo, node, force=opts.get('force'))
4169 return hg.merge(repo, node, force=opts.get('force'))
4169 finally:
4170 finally:
4170 ui.setconfig('ui', 'forcemerge', '')
4171 ui.setconfig('ui', 'forcemerge', '')
4171
4172
4172 @command('outgoing|out',
4173 @command('outgoing|out',
4173 [('f', 'force', None, _('run even when the destination is unrelated')),
4174 [('f', 'force', None, _('run even when the destination is unrelated')),
4174 ('r', 'rev', [],
4175 ('r', 'rev', [],
4175 _('a changeset intended to be included in the destination'), _('REV')),
4176 _('a changeset intended to be included in the destination'), _('REV')),
4176 ('n', 'newest-first', None, _('show newest record first')),
4177 ('n', 'newest-first', None, _('show newest record first')),
4177 ('B', 'bookmarks', False, _('compare bookmarks')),
4178 ('B', 'bookmarks', False, _('compare bookmarks')),
4178 ('b', 'branch', [], _('a specific branch you would like to push'),
4179 ('b', 'branch', [], _('a specific branch you would like to push'),
4179 _('BRANCH')),
4180 _('BRANCH')),
4180 ] + logopts + remoteopts + subrepoopts,
4181 ] + logopts + remoteopts + subrepoopts,
4181 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4182 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4182 def outgoing(ui, repo, dest=None, **opts):
4183 def outgoing(ui, repo, dest=None, **opts):
4183 """show changesets not found in the destination
4184 """show changesets not found in the destination
4184
4185
4185 Show changesets not found in the specified destination repository
4186 Show changesets not found in the specified destination repository
4186 or the default push location. These are the changesets that would
4187 or the default push location. These are the changesets that would
4187 be pushed if a push was requested.
4188 be pushed if a push was requested.
4188
4189
4189 See pull for details of valid destination formats.
4190 See pull for details of valid destination formats.
4190
4191
4191 Returns 0 if there are outgoing changes, 1 otherwise.
4192 Returns 0 if there are outgoing changes, 1 otherwise.
4192 """
4193 """
4193
4194
4194 if opts.get('bookmarks'):
4195 if opts.get('bookmarks'):
4195 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4196 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4196 dest, branches = hg.parseurl(dest, opts.get('branch'))
4197 dest, branches = hg.parseurl(dest, opts.get('branch'))
4197 other = hg.peer(repo, opts, dest)
4198 other = hg.peer(repo, opts, dest)
4198 if 'bookmarks' not in other.listkeys('namespaces'):
4199 if 'bookmarks' not in other.listkeys('namespaces'):
4199 ui.warn(_("remote doesn't support bookmarks\n"))
4200 ui.warn(_("remote doesn't support bookmarks\n"))
4200 return 0
4201 return 0
4201 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4202 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4202 return bookmarks.diff(ui, other, repo)
4203 return bookmarks.diff(ui, other, repo)
4203
4204
4204 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4205 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4205 try:
4206 try:
4206 return hg.outgoing(ui, repo, dest, opts)
4207 return hg.outgoing(ui, repo, dest, opts)
4207 finally:
4208 finally:
4208 del repo._subtoppath
4209 del repo._subtoppath
4209
4210
4210 @command('parents',
4211 @command('parents',
4211 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4212 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4212 ] + templateopts,
4213 ] + templateopts,
4213 _('[-r REV] [FILE]'))
4214 _('[-r REV] [FILE]'))
4214 def parents(ui, repo, file_=None, **opts):
4215 def parents(ui, repo, file_=None, **opts):
4215 """show the parents of the working directory or revision
4216 """show the parents of the working directory or revision
4216
4217
4217 Print the working directory's parent revisions. If a revision is
4218 Print the working directory's parent revisions. If a revision is
4218 given via -r/--rev, the parent of that revision will be printed.
4219 given via -r/--rev, the parent of that revision will be printed.
4219 If a file argument is given, the revision in which the file was
4220 If a file argument is given, the revision in which the file was
4220 last changed (before the working directory revision or the
4221 last changed (before the working directory revision or the
4221 argument to --rev if given) is printed.
4222 argument to --rev if given) is printed.
4222
4223
4223 Returns 0 on success.
4224 Returns 0 on success.
4224 """
4225 """
4225
4226
4226 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4227 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4227
4228
4228 if file_:
4229 if file_:
4229 m = scmutil.match(ctx, (file_,), opts)
4230 m = scmutil.match(ctx, (file_,), opts)
4230 if m.anypats() or len(m.files()) != 1:
4231 if m.anypats() or len(m.files()) != 1:
4231 raise util.Abort(_('can only specify an explicit filename'))
4232 raise util.Abort(_('can only specify an explicit filename'))
4232 file_ = m.files()[0]
4233 file_ = m.files()[0]
4233 filenodes = []
4234 filenodes = []
4234 for cp in ctx.parents():
4235 for cp in ctx.parents():
4235 if not cp:
4236 if not cp:
4236 continue
4237 continue
4237 try:
4238 try:
4238 filenodes.append(cp.filenode(file_))
4239 filenodes.append(cp.filenode(file_))
4239 except error.LookupError:
4240 except error.LookupError:
4240 pass
4241 pass
4241 if not filenodes:
4242 if not filenodes:
4242 raise util.Abort(_("'%s' not found in manifest!") % file_)
4243 raise util.Abort(_("'%s' not found in manifest!") % file_)
4243 fl = repo.file(file_)
4244 fl = repo.file(file_)
4244 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
4245 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
4245 else:
4246 else:
4246 p = [cp.node() for cp in ctx.parents()]
4247 p = [cp.node() for cp in ctx.parents()]
4247
4248
4248 displayer = cmdutil.show_changeset(ui, repo, opts)
4249 displayer = cmdutil.show_changeset(ui, repo, opts)
4249 for n in p:
4250 for n in p:
4250 if n != nullid:
4251 if n != nullid:
4251 displayer.show(repo[n])
4252 displayer.show(repo[n])
4252 displayer.close()
4253 displayer.close()
4253
4254
4254 @command('paths', [], _('[NAME]'))
4255 @command('paths', [], _('[NAME]'))
4255 def paths(ui, repo, search=None):
4256 def paths(ui, repo, search=None):
4256 """show aliases for remote repositories
4257 """show aliases for remote repositories
4257
4258
4258 Show definition of symbolic path name NAME. If no name is given,
4259 Show definition of symbolic path name NAME. If no name is given,
4259 show definition of all available names.
4260 show definition of all available names.
4260
4261
4261 Option -q/--quiet suppresses all output when searching for NAME
4262 Option -q/--quiet suppresses all output when searching for NAME
4262 and shows only the path names when listing all definitions.
4263 and shows only the path names when listing all definitions.
4263
4264
4264 Path names are defined in the [paths] section of your
4265 Path names are defined in the [paths] section of your
4265 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4266 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4266 repository, ``.hg/hgrc`` is used, too.
4267 repository, ``.hg/hgrc`` is used, too.
4267
4268
4268 The path names ``default`` and ``default-push`` have a special
4269 The path names ``default`` and ``default-push`` have a special
4269 meaning. When performing a push or pull operation, they are used
4270 meaning. When performing a push or pull operation, they are used
4270 as fallbacks if no location is specified on the command-line.
4271 as fallbacks if no location is specified on the command-line.
4271 When ``default-push`` is set, it will be used for push and
4272 When ``default-push`` is set, it will be used for push and
4272 ``default`` will be used for pull; otherwise ``default`` is used
4273 ``default`` will be used for pull; otherwise ``default`` is used
4273 as the fallback for both. When cloning a repository, the clone
4274 as the fallback for both. When cloning a repository, the clone
4274 source is written as ``default`` in ``.hg/hgrc``. Note that
4275 source is written as ``default`` in ``.hg/hgrc``. Note that
4275 ``default`` and ``default-push`` apply to all inbound (e.g.
4276 ``default`` and ``default-push`` apply to all inbound (e.g.
4276 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
4277 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
4277 :hg:`bundle`) operations.
4278 :hg:`bundle`) operations.
4278
4279
4279 See :hg:`help urls` for more information.
4280 See :hg:`help urls` for more information.
4280
4281
4281 Returns 0 on success.
4282 Returns 0 on success.
4282 """
4283 """
4283 if search:
4284 if search:
4284 for name, path in ui.configitems("paths"):
4285 for name, path in ui.configitems("paths"):
4285 if name == search:
4286 if name == search:
4286 ui.status("%s\n" % util.hidepassword(path))
4287 ui.status("%s\n" % util.hidepassword(path))
4287 return
4288 return
4288 if not ui.quiet:
4289 if not ui.quiet:
4289 ui.warn(_("not found!\n"))
4290 ui.warn(_("not found!\n"))
4290 return 1
4291 return 1
4291 else:
4292 else:
4292 for name, path in ui.configitems("paths"):
4293 for name, path in ui.configitems("paths"):
4293 if ui.quiet:
4294 if ui.quiet:
4294 ui.write("%s\n" % name)
4295 ui.write("%s\n" % name)
4295 else:
4296 else:
4296 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
4297 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
4297
4298
4298 @command('^phase',
4299 @command('^phase',
4299 [('p', 'public', False, _('set changeset phase to public')),
4300 [('p', 'public', False, _('set changeset phase to public')),
4300 ('d', 'draft', False, _('set changeset phase to draft')),
4301 ('d', 'draft', False, _('set changeset phase to draft')),
4301 ('s', 'secret', False, _('set changeset phase to secret')),
4302 ('s', 'secret', False, _('set changeset phase to secret')),
4302 ('f', 'force', False, _('allow to move boundary backward')),
4303 ('f', 'force', False, _('allow to move boundary backward')),
4303 ('r', 'rev', [], _('target revision'), _('REV')),
4304 ('r', 'rev', [], _('target revision'), _('REV')),
4304 ],
4305 ],
4305 _('[-p|-d|-s] [-f] [-r] REV...'))
4306 _('[-p|-d|-s] [-f] [-r] REV...'))
4306 def phase(ui, repo, *revs, **opts):
4307 def phase(ui, repo, *revs, **opts):
4307 """set or show the current phase name
4308 """set or show the current phase name
4308
4309
4309 With no argument, show the phase name of specified revisions.
4310 With no argument, show the phase name of specified revisions.
4310
4311
4311 With one of -p/--public, -d/--draft or -s/--secret, change the
4312 With one of -p/--public, -d/--draft or -s/--secret, change the
4312 phase value of the specified revisions.
4313 phase value of the specified revisions.
4313
4314
4314 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
4315 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
4315 lower phase to an higher phase. Phases are ordered as follows::
4316 lower phase to an higher phase. Phases are ordered as follows::
4316
4317
4317 public < draft < secret
4318 public < draft < secret
4318
4319
4319 Return 0 on success, 1 if no phases were changed or some could not
4320 Return 0 on success, 1 if no phases were changed or some could not
4320 be changed.
4321 be changed.
4321 """
4322 """
4322 # search for a unique phase argument
4323 # search for a unique phase argument
4323 targetphase = None
4324 targetphase = None
4324 for idx, name in enumerate(phases.phasenames):
4325 for idx, name in enumerate(phases.phasenames):
4325 if opts[name]:
4326 if opts[name]:
4326 if targetphase is not None:
4327 if targetphase is not None:
4327 raise util.Abort(_('only one phase can be specified'))
4328 raise util.Abort(_('only one phase can be specified'))
4328 targetphase = idx
4329 targetphase = idx
4329
4330
4330 # look for specified revision
4331 # look for specified revision
4331 revs = list(revs)
4332 revs = list(revs)
4332 revs.extend(opts['rev'])
4333 revs.extend(opts['rev'])
4333 if not revs:
4334 if not revs:
4334 raise util.Abort(_('no revisions specified'))
4335 raise util.Abort(_('no revisions specified'))
4335
4336
4336 revs = scmutil.revrange(repo, revs)
4337 revs = scmutil.revrange(repo, revs)
4337
4338
4338 lock = None
4339 lock = None
4339 ret = 0
4340 ret = 0
4340 if targetphase is None:
4341 if targetphase is None:
4341 # display
4342 # display
4342 for r in revs:
4343 for r in revs:
4343 ctx = repo[r]
4344 ctx = repo[r]
4344 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4345 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4345 else:
4346 else:
4346 lock = repo.lock()
4347 lock = repo.lock()
4347 try:
4348 try:
4348 # set phase
4349 # set phase
4349 nodes = [ctx.node() for ctx in repo.set('%ld', revs)]
4350 nodes = [ctx.node() for ctx in repo.set('%ld', revs)]
4350 if not nodes:
4351 if not nodes:
4351 raise util.Abort(_('empty revision set'))
4352 raise util.Abort(_('empty revision set'))
4352 olddata = repo._phaserev[:]
4353 olddata = repo._phaserev[:]
4353 phases.advanceboundary(repo, targetphase, nodes)
4354 phases.advanceboundary(repo, targetphase, nodes)
4354 if opts['force']:
4355 if opts['force']:
4355 phases.retractboundary(repo, targetphase, nodes)
4356 phases.retractboundary(repo, targetphase, nodes)
4356 finally:
4357 finally:
4357 lock.release()
4358 lock.release()
4358 if olddata is not None:
4359 if olddata is not None:
4359 changes = 0
4360 changes = 0
4360 newdata = repo._phaserev
4361 newdata = repo._phaserev
4361 changes = sum(o != newdata[i] for i, o in enumerate(olddata))
4362 changes = sum(o != newdata[i] for i, o in enumerate(olddata))
4362 rejected = [n for n in nodes
4363 rejected = [n for n in nodes
4363 if newdata[repo[n].rev()] < targetphase]
4364 if newdata[repo[n].rev()] < targetphase]
4364 if rejected:
4365 if rejected:
4365 ui.warn(_('cannot move %i changesets to a more permissive '
4366 ui.warn(_('cannot move %i changesets to a more permissive '
4366 'phase, use --force\n') % len(rejected))
4367 'phase, use --force\n') % len(rejected))
4367 ret = 1
4368 ret = 1
4368 if changes:
4369 if changes:
4369 msg = _('phase changed for %i changesets\n') % changes
4370 msg = _('phase changed for %i changesets\n') % changes
4370 if ret:
4371 if ret:
4371 ui.status(msg)
4372 ui.status(msg)
4372 else:
4373 else:
4373 ui.note(msg)
4374 ui.note(msg)
4374 else:
4375 else:
4375 ui.warn(_('no phases changed\n'))
4376 ui.warn(_('no phases changed\n'))
4376 ret = 1
4377 ret = 1
4377 return ret
4378 return ret
4378
4379
4379 def postincoming(ui, repo, modheads, optupdate, checkout):
4380 def postincoming(ui, repo, modheads, optupdate, checkout):
4380 if modheads == 0:
4381 if modheads == 0:
4381 return
4382 return
4382 if optupdate:
4383 if optupdate:
4383 movemarkfrom = repo['.'].node()
4384 movemarkfrom = repo['.'].node()
4384 try:
4385 try:
4385 ret = hg.update(repo, checkout)
4386 ret = hg.update(repo, checkout)
4386 except util.Abort, inst:
4387 except util.Abort, inst:
4387 ui.warn(_("not updating: %s\n") % str(inst))
4388 ui.warn(_("not updating: %s\n") % str(inst))
4388 return 0
4389 return 0
4389 if not ret and not checkout:
4390 if not ret and not checkout:
4390 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
4391 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
4391 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
4392 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
4392 return ret
4393 return ret
4393 if modheads > 1:
4394 if modheads > 1:
4394 currentbranchheads = len(repo.branchheads())
4395 currentbranchheads = len(repo.branchheads())
4395 if currentbranchheads == modheads:
4396 if currentbranchheads == modheads:
4396 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4397 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4397 elif currentbranchheads > 1:
4398 elif currentbranchheads > 1:
4398 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to merge)\n"))
4399 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to merge)\n"))
4399 else:
4400 else:
4400 ui.status(_("(run 'hg heads' to see heads)\n"))
4401 ui.status(_("(run 'hg heads' to see heads)\n"))
4401 else:
4402 else:
4402 ui.status(_("(run 'hg update' to get a working copy)\n"))
4403 ui.status(_("(run 'hg update' to get a working copy)\n"))
4403
4404
4404 @command('^pull',
4405 @command('^pull',
4405 [('u', 'update', None,
4406 [('u', 'update', None,
4406 _('update to new branch head if changesets were pulled')),
4407 _('update to new branch head if changesets were pulled')),
4407 ('f', 'force', None, _('run even when remote repository is unrelated')),
4408 ('f', 'force', None, _('run even when remote repository is unrelated')),
4408 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4409 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4409 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4410 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4410 ('b', 'branch', [], _('a specific branch you would like to pull'),
4411 ('b', 'branch', [], _('a specific branch you would like to pull'),
4411 _('BRANCH')),
4412 _('BRANCH')),
4412 ] + remoteopts,
4413 ] + remoteopts,
4413 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
4414 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
4414 def pull(ui, repo, source="default", **opts):
4415 def pull(ui, repo, source="default", **opts):
4415 """pull changes from the specified source
4416 """pull changes from the specified source
4416
4417
4417 Pull changes from a remote repository to a local one.
4418 Pull changes from a remote repository to a local one.
4418
4419
4419 This finds all changes from the repository at the specified path
4420 This finds all changes from the repository at the specified path
4420 or URL and adds them to a local repository (the current one unless
4421 or URL and adds them to a local repository (the current one unless
4421 -R is specified). By default, this does not update the copy of the
4422 -R is specified). By default, this does not update the copy of the
4422 project in the working directory.
4423 project in the working directory.
4423
4424
4424 Use :hg:`incoming` if you want to see what would have been added
4425 Use :hg:`incoming` if you want to see what would have been added
4425 by a pull at the time you issued this command. If you then decide
4426 by a pull at the time you issued this command. If you then decide
4426 to add those changes to the repository, you should use :hg:`pull
4427 to add those changes to the repository, you should use :hg:`pull
4427 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4428 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4428
4429
4429 If SOURCE is omitted, the 'default' path will be used.
4430 If SOURCE is omitted, the 'default' path will be used.
4430 See :hg:`help urls` for more information.
4431 See :hg:`help urls` for more information.
4431
4432
4432 Returns 0 on success, 1 if an update had unresolved files.
4433 Returns 0 on success, 1 if an update had unresolved files.
4433 """
4434 """
4434 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4435 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4435 other = hg.peer(repo, opts, source)
4436 other = hg.peer(repo, opts, source)
4436 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4437 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4437 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
4438 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
4438
4439
4439 if opts.get('bookmark'):
4440 if opts.get('bookmark'):
4440 if not revs:
4441 if not revs:
4441 revs = []
4442 revs = []
4442 rb = other.listkeys('bookmarks')
4443 rb = other.listkeys('bookmarks')
4443 for b in opts['bookmark']:
4444 for b in opts['bookmark']:
4444 if b not in rb:
4445 if b not in rb:
4445 raise util.Abort(_('remote bookmark %s not found!') % b)
4446 raise util.Abort(_('remote bookmark %s not found!') % b)
4446 revs.append(rb[b])
4447 revs.append(rb[b])
4447
4448
4448 if revs:
4449 if revs:
4449 try:
4450 try:
4450 revs = [other.lookup(rev) for rev in revs]
4451 revs = [other.lookup(rev) for rev in revs]
4451 except error.CapabilityError:
4452 except error.CapabilityError:
4452 err = _("other repository doesn't support revision lookup, "
4453 err = _("other repository doesn't support revision lookup, "
4453 "so a rev cannot be specified.")
4454 "so a rev cannot be specified.")
4454 raise util.Abort(err)
4455 raise util.Abort(err)
4455
4456
4456 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
4457 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
4457 bookmarks.updatefromremote(ui, repo, other, source)
4458 bookmarks.updatefromremote(ui, repo, other, source)
4458 if checkout:
4459 if checkout:
4459 checkout = str(repo.changelog.rev(other.lookup(checkout)))
4460 checkout = str(repo.changelog.rev(other.lookup(checkout)))
4460 repo._subtoppath = source
4461 repo._subtoppath = source
4461 try:
4462 try:
4462 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
4463 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
4463
4464
4464 finally:
4465 finally:
4465 del repo._subtoppath
4466 del repo._subtoppath
4466
4467
4467 # update specified bookmarks
4468 # update specified bookmarks
4468 if opts.get('bookmark'):
4469 if opts.get('bookmark'):
4469 for b in opts['bookmark']:
4470 for b in opts['bookmark']:
4470 # explicit pull overrides local bookmark if any
4471 # explicit pull overrides local bookmark if any
4471 ui.status(_("importing bookmark %s\n") % b)
4472 ui.status(_("importing bookmark %s\n") % b)
4472 repo._bookmarks[b] = repo[rb[b]].node()
4473 repo._bookmarks[b] = repo[rb[b]].node()
4473 bookmarks.write(repo)
4474 bookmarks.write(repo)
4474
4475
4475 return ret
4476 return ret
4476
4477
4477 @command('^push',
4478 @command('^push',
4478 [('f', 'force', None, _('force push')),
4479 [('f', 'force', None, _('force push')),
4479 ('r', 'rev', [],
4480 ('r', 'rev', [],
4480 _('a changeset intended to be included in the destination'),
4481 _('a changeset intended to be included in the destination'),
4481 _('REV')),
4482 _('REV')),
4482 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4483 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4483 ('b', 'branch', [],
4484 ('b', 'branch', [],
4484 _('a specific branch you would like to push'), _('BRANCH')),
4485 _('a specific branch you would like to push'), _('BRANCH')),
4485 ('', 'new-branch', False, _('allow pushing a new branch')),
4486 ('', 'new-branch', False, _('allow pushing a new branch')),
4486 ] + remoteopts,
4487 ] + remoteopts,
4487 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4488 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4488 def push(ui, repo, dest=None, **opts):
4489 def push(ui, repo, dest=None, **opts):
4489 """push changes to the specified destination
4490 """push changes to the specified destination
4490
4491
4491 Push changesets from the local repository to the specified
4492 Push changesets from the local repository to the specified
4492 destination.
4493 destination.
4493
4494
4494 This operation is symmetrical to pull: it is identical to a pull
4495 This operation is symmetrical to pull: it is identical to a pull
4495 in the destination repository from the current one.
4496 in the destination repository from the current one.
4496
4497
4497 By default, push will not allow creation of new heads at the
4498 By default, push will not allow creation of new heads at the
4498 destination, since multiple heads would make it unclear which head
4499 destination, since multiple heads would make it unclear which head
4499 to use. In this situation, it is recommended to pull and merge
4500 to use. In this situation, it is recommended to pull and merge
4500 before pushing.
4501 before pushing.
4501
4502
4502 Use --new-branch if you want to allow push to create a new named
4503 Use --new-branch if you want to allow push to create a new named
4503 branch that is not present at the destination. This allows you to
4504 branch that is not present at the destination. This allows you to
4504 only create a new branch without forcing other changes.
4505 only create a new branch without forcing other changes.
4505
4506
4506 Use -f/--force to override the default behavior and push all
4507 Use -f/--force to override the default behavior and push all
4507 changesets on all branches.
4508 changesets on all branches.
4508
4509
4509 If -r/--rev is used, the specified revision and all its ancestors
4510 If -r/--rev is used, the specified revision and all its ancestors
4510 will be pushed to the remote repository.
4511 will be pushed to the remote repository.
4511
4512
4512 Please see :hg:`help urls` for important details about ``ssh://``
4513 Please see :hg:`help urls` for important details about ``ssh://``
4513 URLs. If DESTINATION is omitted, a default path will be used.
4514 URLs. If DESTINATION is omitted, a default path will be used.
4514
4515
4515 Returns 0 if push was successful, 1 if nothing to push.
4516 Returns 0 if push was successful, 1 if nothing to push.
4516 """
4517 """
4517
4518
4518 if opts.get('bookmark'):
4519 if opts.get('bookmark'):
4519 for b in opts['bookmark']:
4520 for b in opts['bookmark']:
4520 # translate -B options to -r so changesets get pushed
4521 # translate -B options to -r so changesets get pushed
4521 if b in repo._bookmarks:
4522 if b in repo._bookmarks:
4522 opts.setdefault('rev', []).append(b)
4523 opts.setdefault('rev', []).append(b)
4523 else:
4524 else:
4524 # if we try to push a deleted bookmark, translate it to null
4525 # if we try to push a deleted bookmark, translate it to null
4525 # this lets simultaneous -r, -b options continue working
4526 # this lets simultaneous -r, -b options continue working
4526 opts.setdefault('rev', []).append("null")
4527 opts.setdefault('rev', []).append("null")
4527
4528
4528 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4529 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4529 dest, branches = hg.parseurl(dest, opts.get('branch'))
4530 dest, branches = hg.parseurl(dest, opts.get('branch'))
4530 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4531 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4531 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4532 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4532 other = hg.peer(repo, opts, dest)
4533 other = hg.peer(repo, opts, dest)
4533 if revs:
4534 if revs:
4534 revs = [repo.lookup(rev) for rev in revs]
4535 revs = [repo.lookup(rev) for rev in revs]
4535
4536
4536 repo._subtoppath = dest
4537 repo._subtoppath = dest
4537 try:
4538 try:
4538 # push subrepos depth-first for coherent ordering
4539 # push subrepos depth-first for coherent ordering
4539 c = repo['']
4540 c = repo['']
4540 subs = c.substate # only repos that are committed
4541 subs = c.substate # only repos that are committed
4541 for s in sorted(subs):
4542 for s in sorted(subs):
4542 if c.sub(s).push(opts) == 0:
4543 if c.sub(s).push(opts) == 0:
4543 return False
4544 return False
4544 finally:
4545 finally:
4545 del repo._subtoppath
4546 del repo._subtoppath
4546 result = repo.push(other, opts.get('force'), revs=revs,
4547 result = repo.push(other, opts.get('force'), revs=revs,
4547 newbranch=opts.get('new_branch'))
4548 newbranch=opts.get('new_branch'))
4548
4549
4549 result = not result
4550 result = not result
4550
4551
4551 if opts.get('bookmark'):
4552 if opts.get('bookmark'):
4552 rb = other.listkeys('bookmarks')
4553 rb = other.listkeys('bookmarks')
4553 for b in opts['bookmark']:
4554 for b in opts['bookmark']:
4554 # explicit push overrides remote bookmark if any
4555 # explicit push overrides remote bookmark if any
4555 if b in repo._bookmarks:
4556 if b in repo._bookmarks:
4556 ui.status(_("exporting bookmark %s\n") % b)
4557 ui.status(_("exporting bookmark %s\n") % b)
4557 new = repo[b].hex()
4558 new = repo[b].hex()
4558 elif b in rb:
4559 elif b in rb:
4559 ui.status(_("deleting remote bookmark %s\n") % b)
4560 ui.status(_("deleting remote bookmark %s\n") % b)
4560 new = '' # delete
4561 new = '' # delete
4561 else:
4562 else:
4562 ui.warn(_('bookmark %s does not exist on the local '
4563 ui.warn(_('bookmark %s does not exist on the local '
4563 'or remote repository!\n') % b)
4564 'or remote repository!\n') % b)
4564 return 2
4565 return 2
4565 old = rb.get(b, '')
4566 old = rb.get(b, '')
4566 r = other.pushkey('bookmarks', b, old, new)
4567 r = other.pushkey('bookmarks', b, old, new)
4567 if not r:
4568 if not r:
4568 ui.warn(_('updating bookmark %s failed!\n') % b)
4569 ui.warn(_('updating bookmark %s failed!\n') % b)
4569 if not result:
4570 if not result:
4570 result = 2
4571 result = 2
4571
4572
4572 return result
4573 return result
4573
4574
4574 @command('recover', [])
4575 @command('recover', [])
4575 def recover(ui, repo):
4576 def recover(ui, repo):
4576 """roll back an interrupted transaction
4577 """roll back an interrupted transaction
4577
4578
4578 Recover from an interrupted commit or pull.
4579 Recover from an interrupted commit or pull.
4579
4580
4580 This command tries to fix the repository status after an
4581 This command tries to fix the repository status after an
4581 interrupted operation. It should only be necessary when Mercurial
4582 interrupted operation. It should only be necessary when Mercurial
4582 suggests it.
4583 suggests it.
4583
4584
4584 Returns 0 if successful, 1 if nothing to recover or verify fails.
4585 Returns 0 if successful, 1 if nothing to recover or verify fails.
4585 """
4586 """
4586 if repo.recover():
4587 if repo.recover():
4587 return hg.verify(repo)
4588 return hg.verify(repo)
4588 return 1
4589 return 1
4589
4590
4590 @command('^remove|rm',
4591 @command('^remove|rm',
4591 [('A', 'after', None, _('record delete for missing files')),
4592 [('A', 'after', None, _('record delete for missing files')),
4592 ('f', 'force', None,
4593 ('f', 'force', None,
4593 _('remove (and delete) file even if added or modified')),
4594 _('remove (and delete) file even if added or modified')),
4594 ] + walkopts,
4595 ] + walkopts,
4595 _('[OPTION]... FILE...'))
4596 _('[OPTION]... FILE...'))
4596 def remove(ui, repo, *pats, **opts):
4597 def remove(ui, repo, *pats, **opts):
4597 """remove the specified files on the next commit
4598 """remove the specified files on the next commit
4598
4599
4599 Schedule the indicated files for removal from the current branch.
4600 Schedule the indicated files for removal from the current branch.
4600
4601
4601 This command schedules the files to be removed at the next commit.
4602 This command schedules the files to be removed at the next commit.
4602 To undo a remove before that, see :hg:`revert`. To undo added
4603 To undo a remove before that, see :hg:`revert`. To undo added
4603 files, see :hg:`forget`.
4604 files, see :hg:`forget`.
4604
4605
4605 .. container:: verbose
4606 .. container:: verbose
4606
4607
4607 -A/--after can be used to remove only files that have already
4608 -A/--after can be used to remove only files that have already
4608 been deleted, -f/--force can be used to force deletion, and -Af
4609 been deleted, -f/--force can be used to force deletion, and -Af
4609 can be used to remove files from the next revision without
4610 can be used to remove files from the next revision without
4610 deleting them from the working directory.
4611 deleting them from the working directory.
4611
4612
4612 The following table details the behavior of remove for different
4613 The following table details the behavior of remove for different
4613 file states (columns) and option combinations (rows). The file
4614 file states (columns) and option combinations (rows). The file
4614 states are Added [A], Clean [C], Modified [M] and Missing [!]
4615 states are Added [A], Clean [C], Modified [M] and Missing [!]
4615 (as reported by :hg:`status`). The actions are Warn, Remove
4616 (as reported by :hg:`status`). The actions are Warn, Remove
4616 (from branch) and Delete (from disk):
4617 (from branch) and Delete (from disk):
4617
4618
4618 ======= == == == ==
4619 ======= == == == ==
4619 A C M !
4620 A C M !
4620 ======= == == == ==
4621 ======= == == == ==
4621 none W RD W R
4622 none W RD W R
4622 -f R RD RD R
4623 -f R RD RD R
4623 -A W W W R
4624 -A W W W R
4624 -Af R R R R
4625 -Af R R R R
4625 ======= == == == ==
4626 ======= == == == ==
4626
4627
4627 Note that remove never deletes files in Added [A] state from the
4628 Note that remove never deletes files in Added [A] state from the
4628 working directory, not even if option --force is specified.
4629 working directory, not even if option --force is specified.
4629
4630
4630 Returns 0 on success, 1 if any warnings encountered.
4631 Returns 0 on success, 1 if any warnings encountered.
4631 """
4632 """
4632
4633
4633 ret = 0
4634 ret = 0
4634 after, force = opts.get('after'), opts.get('force')
4635 after, force = opts.get('after'), opts.get('force')
4635 if not pats and not after:
4636 if not pats and not after:
4636 raise util.Abort(_('no files specified'))
4637 raise util.Abort(_('no files specified'))
4637
4638
4638 m = scmutil.match(repo[None], pats, opts)
4639 m = scmutil.match(repo[None], pats, opts)
4639 s = repo.status(match=m, clean=True)
4640 s = repo.status(match=m, clean=True)
4640 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
4641 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
4641
4642
4642 for f in m.files():
4643 for f in m.files():
4643 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
4644 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
4644 if os.path.exists(m.rel(f)):
4645 if os.path.exists(m.rel(f)):
4645 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
4646 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
4646 ret = 1
4647 ret = 1
4647
4648
4648 if force:
4649 if force:
4649 list = modified + deleted + clean + added
4650 list = modified + deleted + clean + added
4650 elif after:
4651 elif after:
4651 list = deleted
4652 list = deleted
4652 for f in modified + added + clean:
4653 for f in modified + added + clean:
4653 ui.warn(_('not removing %s: file still exists (use -f'
4654 ui.warn(_('not removing %s: file still exists (use -f'
4654 ' to force removal)\n') % m.rel(f))
4655 ' to force removal)\n') % m.rel(f))
4655 ret = 1
4656 ret = 1
4656 else:
4657 else:
4657 list = deleted + clean
4658 list = deleted + clean
4658 for f in modified:
4659 for f in modified:
4659 ui.warn(_('not removing %s: file is modified (use -f'
4660 ui.warn(_('not removing %s: file is modified (use -f'
4660 ' to force removal)\n') % m.rel(f))
4661 ' to force removal)\n') % m.rel(f))
4661 ret = 1
4662 ret = 1
4662 for f in added:
4663 for f in added:
4663 ui.warn(_('not removing %s: file has been marked for add'
4664 ui.warn(_('not removing %s: file has been marked for add'
4664 ' (use forget to undo)\n') % m.rel(f))
4665 ' (use forget to undo)\n') % m.rel(f))
4665 ret = 1
4666 ret = 1
4666
4667
4667 for f in sorted(list):
4668 for f in sorted(list):
4668 if ui.verbose or not m.exact(f):
4669 if ui.verbose or not m.exact(f):
4669 ui.status(_('removing %s\n') % m.rel(f))
4670 ui.status(_('removing %s\n') % m.rel(f))
4670
4671
4671 wlock = repo.wlock()
4672 wlock = repo.wlock()
4672 try:
4673 try:
4673 if not after:
4674 if not after:
4674 for f in list:
4675 for f in list:
4675 if f in added:
4676 if f in added:
4676 continue # we never unlink added files on remove
4677 continue # we never unlink added files on remove
4677 try:
4678 try:
4678 util.unlinkpath(repo.wjoin(f))
4679 util.unlinkpath(repo.wjoin(f))
4679 except OSError, inst:
4680 except OSError, inst:
4680 if inst.errno != errno.ENOENT:
4681 if inst.errno != errno.ENOENT:
4681 raise
4682 raise
4682 repo[None].forget(list)
4683 repo[None].forget(list)
4683 finally:
4684 finally:
4684 wlock.release()
4685 wlock.release()
4685
4686
4686 return ret
4687 return ret
4687
4688
4688 @command('rename|move|mv',
4689 @command('rename|move|mv',
4689 [('A', 'after', None, _('record a rename that has already occurred')),
4690 [('A', 'after', None, _('record a rename that has already occurred')),
4690 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4691 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4691 ] + walkopts + dryrunopts,
4692 ] + walkopts + dryrunopts,
4692 _('[OPTION]... SOURCE... DEST'))
4693 _('[OPTION]... SOURCE... DEST'))
4693 def rename(ui, repo, *pats, **opts):
4694 def rename(ui, repo, *pats, **opts):
4694 """rename files; equivalent of copy + remove
4695 """rename files; equivalent of copy + remove
4695
4696
4696 Mark dest as copies of sources; mark sources for deletion. If dest
4697 Mark dest as copies of sources; mark sources for deletion. If dest
4697 is a directory, copies are put in that directory. If dest is a
4698 is a directory, copies are put in that directory. If dest is a
4698 file, there can only be one source.
4699 file, there can only be one source.
4699
4700
4700 By default, this command copies the contents of files as they
4701 By default, this command copies the contents of files as they
4701 exist in the working directory. If invoked with -A/--after, the
4702 exist in the working directory. If invoked with -A/--after, the
4702 operation is recorded, but no copying is performed.
4703 operation is recorded, but no copying is performed.
4703
4704
4704 This command takes effect at the next commit. To undo a rename
4705 This command takes effect at the next commit. To undo a rename
4705 before that, see :hg:`revert`.
4706 before that, see :hg:`revert`.
4706
4707
4707 Returns 0 on success, 1 if errors are encountered.
4708 Returns 0 on success, 1 if errors are encountered.
4708 """
4709 """
4709 wlock = repo.wlock(False)
4710 wlock = repo.wlock(False)
4710 try:
4711 try:
4711 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4712 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4712 finally:
4713 finally:
4713 wlock.release()
4714 wlock.release()
4714
4715
4715 @command('resolve',
4716 @command('resolve',
4716 [('a', 'all', None, _('select all unresolved files')),
4717 [('a', 'all', None, _('select all unresolved files')),
4717 ('l', 'list', None, _('list state of files needing merge')),
4718 ('l', 'list', None, _('list state of files needing merge')),
4718 ('m', 'mark', None, _('mark files as resolved')),
4719 ('m', 'mark', None, _('mark files as resolved')),
4719 ('u', 'unmark', None, _('mark files as unresolved')),
4720 ('u', 'unmark', None, _('mark files as unresolved')),
4720 ('n', 'no-status', None, _('hide status prefix'))]
4721 ('n', 'no-status', None, _('hide status prefix'))]
4721 + mergetoolopts + walkopts,
4722 + mergetoolopts + walkopts,
4722 _('[OPTION]... [FILE]...'))
4723 _('[OPTION]... [FILE]...'))
4723 def resolve(ui, repo, *pats, **opts):
4724 def resolve(ui, repo, *pats, **opts):
4724 """redo merges or set/view the merge status of files
4725 """redo merges or set/view the merge status of files
4725
4726
4726 Merges with unresolved conflicts are often the result of
4727 Merges with unresolved conflicts are often the result of
4727 non-interactive merging using the ``internal:merge`` configuration
4728 non-interactive merging using the ``internal:merge`` configuration
4728 setting, or a command-line merge tool like ``diff3``. The resolve
4729 setting, or a command-line merge tool like ``diff3``. The resolve
4729 command is used to manage the files involved in a merge, after
4730 command is used to manage the files involved in a merge, after
4730 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4731 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4731 working directory must have two parents). See :hg:`help
4732 working directory must have two parents). See :hg:`help
4732 merge-tools` for information on configuring merge tools.
4733 merge-tools` for information on configuring merge tools.
4733
4734
4734 The resolve command can be used in the following ways:
4735 The resolve command can be used in the following ways:
4735
4736
4736 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4737 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4737 files, discarding any previous merge attempts. Re-merging is not
4738 files, discarding any previous merge attempts. Re-merging is not
4738 performed for files already marked as resolved. Use ``--all/-a``
4739 performed for files already marked as resolved. Use ``--all/-a``
4739 to select all unresolved files. ``--tool`` can be used to specify
4740 to select all unresolved files. ``--tool`` can be used to specify
4740 the merge tool used for the given files. It overrides the HGMERGE
4741 the merge tool used for the given files. It overrides the HGMERGE
4741 environment variable and your configuration files. Previous file
4742 environment variable and your configuration files. Previous file
4742 contents are saved with a ``.orig`` suffix.
4743 contents are saved with a ``.orig`` suffix.
4743
4744
4744 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4745 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4745 (e.g. after having manually fixed-up the files). The default is
4746 (e.g. after having manually fixed-up the files). The default is
4746 to mark all unresolved files.
4747 to mark all unresolved files.
4747
4748
4748 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4749 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4749 default is to mark all resolved files.
4750 default is to mark all resolved files.
4750
4751
4751 - :hg:`resolve -l`: list files which had or still have conflicts.
4752 - :hg:`resolve -l`: list files which had or still have conflicts.
4752 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4753 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4753
4754
4754 Note that Mercurial will not let you commit files with unresolved
4755 Note that Mercurial will not let you commit files with unresolved
4755 merge conflicts. You must use :hg:`resolve -m ...` before you can
4756 merge conflicts. You must use :hg:`resolve -m ...` before you can
4756 commit after a conflicting merge.
4757 commit after a conflicting merge.
4757
4758
4758 Returns 0 on success, 1 if any files fail a resolve attempt.
4759 Returns 0 on success, 1 if any files fail a resolve attempt.
4759 """
4760 """
4760
4761
4761 all, mark, unmark, show, nostatus = \
4762 all, mark, unmark, show, nostatus = \
4762 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
4763 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
4763
4764
4764 if (show and (mark or unmark)) or (mark and unmark):
4765 if (show and (mark or unmark)) or (mark and unmark):
4765 raise util.Abort(_("too many options specified"))
4766 raise util.Abort(_("too many options specified"))
4766 if pats and all:
4767 if pats and all:
4767 raise util.Abort(_("can't specify --all and patterns"))
4768 raise util.Abort(_("can't specify --all and patterns"))
4768 if not (all or pats or show or mark or unmark):
4769 if not (all or pats or show or mark or unmark):
4769 raise util.Abort(_('no files or directories specified; '
4770 raise util.Abort(_('no files or directories specified; '
4770 'use --all to remerge all files'))
4771 'use --all to remerge all files'))
4771
4772
4772 ms = mergemod.mergestate(repo)
4773 ms = mergemod.mergestate(repo)
4773 m = scmutil.match(repo[None], pats, opts)
4774 m = scmutil.match(repo[None], pats, opts)
4774 ret = 0
4775 ret = 0
4775
4776
4776 for f in ms:
4777 for f in ms:
4777 if m(f):
4778 if m(f):
4778 if show:
4779 if show:
4779 if nostatus:
4780 if nostatus:
4780 ui.write("%s\n" % f)
4781 ui.write("%s\n" % f)
4781 else:
4782 else:
4782 ui.write("%s %s\n" % (ms[f].upper(), f),
4783 ui.write("%s %s\n" % (ms[f].upper(), f),
4783 label='resolve.' +
4784 label='resolve.' +
4784 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
4785 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
4785 elif mark:
4786 elif mark:
4786 ms.mark(f, "r")
4787 ms.mark(f, "r")
4787 elif unmark:
4788 elif unmark:
4788 ms.mark(f, "u")
4789 ms.mark(f, "u")
4789 else:
4790 else:
4790 wctx = repo[None]
4791 wctx = repo[None]
4791 mctx = wctx.parents()[-1]
4792 mctx = wctx.parents()[-1]
4792
4793
4793 # backup pre-resolve (merge uses .orig for its own purposes)
4794 # backup pre-resolve (merge uses .orig for its own purposes)
4794 a = repo.wjoin(f)
4795 a = repo.wjoin(f)
4795 util.copyfile(a, a + ".resolve")
4796 util.copyfile(a, a + ".resolve")
4796
4797
4797 try:
4798 try:
4798 # resolve file
4799 # resolve file
4799 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4800 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4800 if ms.resolve(f, wctx, mctx):
4801 if ms.resolve(f, wctx, mctx):
4801 ret = 1
4802 ret = 1
4802 finally:
4803 finally:
4803 ui.setconfig('ui', 'forcemerge', '')
4804 ui.setconfig('ui', 'forcemerge', '')
4804
4805
4805 # replace filemerge's .orig file with our resolve file
4806 # replace filemerge's .orig file with our resolve file
4806 util.rename(a + ".resolve", a + ".orig")
4807 util.rename(a + ".resolve", a + ".orig")
4807
4808
4808 ms.commit()
4809 ms.commit()
4809 return ret
4810 return ret
4810
4811
4811 @command('revert',
4812 @command('revert',
4812 [('a', 'all', None, _('revert all changes when no arguments given')),
4813 [('a', 'all', None, _('revert all changes when no arguments given')),
4813 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4814 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4814 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4815 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4815 ('C', 'no-backup', None, _('do not save backup copies of files')),
4816 ('C', 'no-backup', None, _('do not save backup copies of files')),
4816 ] + walkopts + dryrunopts,
4817 ] + walkopts + dryrunopts,
4817 _('[OPTION]... [-r REV] [NAME]...'))
4818 _('[OPTION]... [-r REV] [NAME]...'))
4818 def revert(ui, repo, *pats, **opts):
4819 def revert(ui, repo, *pats, **opts):
4819 """restore files to their checkout state
4820 """restore files to their checkout state
4820
4821
4821 .. note::
4822 .. note::
4822 To check out earlier revisions, you should use :hg:`update REV`.
4823 To check out earlier revisions, you should use :hg:`update REV`.
4823 To cancel a merge (and lose your changes), use :hg:`update --clean .`.
4824 To cancel a merge (and lose your changes), use :hg:`update --clean .`.
4824
4825
4825 With no revision specified, revert the specified files or directories
4826 With no revision specified, revert the specified files or directories
4826 to the contents they had in the parent of the working directory.
4827 to the contents they had in the parent of the working directory.
4827 This restores the contents of files to an unmodified
4828 This restores the contents of files to an unmodified
4828 state and unschedules adds, removes, copies, and renames. If the
4829 state and unschedules adds, removes, copies, and renames. If the
4829 working directory has two parents, you must explicitly specify a
4830 working directory has two parents, you must explicitly specify a
4830 revision.
4831 revision.
4831
4832
4832 Using the -r/--rev or -d/--date options, revert the given files or
4833 Using the -r/--rev or -d/--date options, revert the given files or
4833 directories to their states as of a specific revision. Because
4834 directories to their states as of a specific revision. Because
4834 revert does not change the working directory parents, this will
4835 revert does not change the working directory parents, this will
4835 cause these files to appear modified. This can be helpful to "back
4836 cause these files to appear modified. This can be helpful to "back
4836 out" some or all of an earlier change. See :hg:`backout` for a
4837 out" some or all of an earlier change. See :hg:`backout` for a
4837 related method.
4838 related method.
4838
4839
4839 Modified files are saved with a .orig suffix before reverting.
4840 Modified files are saved with a .orig suffix before reverting.
4840 To disable these backups, use --no-backup.
4841 To disable these backups, use --no-backup.
4841
4842
4842 See :hg:`help dates` for a list of formats valid for -d/--date.
4843 See :hg:`help dates` for a list of formats valid for -d/--date.
4843
4844
4844 Returns 0 on success.
4845 Returns 0 on success.
4845 """
4846 """
4846
4847
4847 if opts.get("date"):
4848 if opts.get("date"):
4848 if opts.get("rev"):
4849 if opts.get("rev"):
4849 raise util.Abort(_("you can't specify a revision and a date"))
4850 raise util.Abort(_("you can't specify a revision and a date"))
4850 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4851 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4851
4852
4852 parent, p2 = repo.dirstate.parents()
4853 parent, p2 = repo.dirstate.parents()
4853 if not opts.get('rev') and p2 != nullid:
4854 if not opts.get('rev') and p2 != nullid:
4854 # revert after merge is a trap for new users (issue2915)
4855 # revert after merge is a trap for new users (issue2915)
4855 raise util.Abort(_('uncommitted merge with no revision specified'),
4856 raise util.Abort(_('uncommitted merge with no revision specified'),
4856 hint=_('use "hg update" or see "hg help revert"'))
4857 hint=_('use "hg update" or see "hg help revert"'))
4857
4858
4858 ctx = scmutil.revsingle(repo, opts.get('rev'))
4859 ctx = scmutil.revsingle(repo, opts.get('rev'))
4859
4860
4860 if not pats and not opts.get('all'):
4861 if not pats and not opts.get('all'):
4861 msg = _("no files or directories specified")
4862 msg = _("no files or directories specified")
4862 if p2 != nullid:
4863 if p2 != nullid:
4863 hint = _("uncommitted merge, use --all to discard all changes,"
4864 hint = _("uncommitted merge, use --all to discard all changes,"
4864 " or 'hg update -C .' to abort the merge")
4865 " or 'hg update -C .' to abort the merge")
4865 raise util.Abort(msg, hint=hint)
4866 raise util.Abort(msg, hint=hint)
4866 dirty = util.any(repo.status())
4867 dirty = util.any(repo.status())
4867 node = ctx.node()
4868 node = ctx.node()
4868 if node != parent:
4869 if node != parent:
4869 if dirty:
4870 if dirty:
4870 hint = _("uncommitted changes, use --all to discard all"
4871 hint = _("uncommitted changes, use --all to discard all"
4871 " changes, or 'hg update %s' to update") % ctx.rev()
4872 " changes, or 'hg update %s' to update") % ctx.rev()
4872 else:
4873 else:
4873 hint = _("use --all to revert all files,"
4874 hint = _("use --all to revert all files,"
4874 " or 'hg update %s' to update") % ctx.rev()
4875 " or 'hg update %s' to update") % ctx.rev()
4875 elif dirty:
4876 elif dirty:
4876 hint = _("uncommitted changes, use --all to discard all changes")
4877 hint = _("uncommitted changes, use --all to discard all changes")
4877 else:
4878 else:
4878 hint = _("use --all to revert all files")
4879 hint = _("use --all to revert all files")
4879 raise util.Abort(msg, hint=hint)
4880 raise util.Abort(msg, hint=hint)
4880
4881
4881 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
4882 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
4882
4883
4883 @command('rollback', dryrunopts +
4884 @command('rollback', dryrunopts +
4884 [('f', 'force', False, _('ignore safety measures'))])
4885 [('f', 'force', False, _('ignore safety measures'))])
4885 def rollback(ui, repo, **opts):
4886 def rollback(ui, repo, **opts):
4886 """roll back the last transaction (dangerous)
4887 """roll back the last transaction (dangerous)
4887
4888
4888 This command should be used with care. There is only one level of
4889 This command should be used with care. There is only one level of
4889 rollback, and there is no way to undo a rollback. It will also
4890 rollback, and there is no way to undo a rollback. It will also
4890 restore the dirstate at the time of the last transaction, losing
4891 restore the dirstate at the time of the last transaction, losing
4891 any dirstate changes since that time. This command does not alter
4892 any dirstate changes since that time. This command does not alter
4892 the working directory.
4893 the working directory.
4893
4894
4894 Transactions are used to encapsulate the effects of all commands
4895 Transactions are used to encapsulate the effects of all commands
4895 that create new changesets or propagate existing changesets into a
4896 that create new changesets or propagate existing changesets into a
4896 repository. For example, the following commands are transactional,
4897 repository. For example, the following commands are transactional,
4897 and their effects can be rolled back:
4898 and their effects can be rolled back:
4898
4899
4899 - commit
4900 - commit
4900 - import
4901 - import
4901 - pull
4902 - pull
4902 - push (with this repository as the destination)
4903 - push (with this repository as the destination)
4903 - unbundle
4904 - unbundle
4904
4905
4905 To avoid permanent data loss, rollback will refuse to rollback a
4906 To avoid permanent data loss, rollback will refuse to rollback a
4906 commit transaction if it isn't checked out. Use --force to
4907 commit transaction if it isn't checked out. Use --force to
4907 override this protection.
4908 override this protection.
4908
4909
4909 This command is not intended for use on public repositories. Once
4910 This command is not intended for use on public repositories. Once
4910 changes are visible for pull by other users, rolling a transaction
4911 changes are visible for pull by other users, rolling a transaction
4911 back locally is ineffective (someone else may already have pulled
4912 back locally is ineffective (someone else may already have pulled
4912 the changes). Furthermore, a race is possible with readers of the
4913 the changes). Furthermore, a race is possible with readers of the
4913 repository; for example an in-progress pull from the repository
4914 repository; for example an in-progress pull from the repository
4914 may fail if a rollback is performed.
4915 may fail if a rollback is performed.
4915
4916
4916 Returns 0 on success, 1 if no rollback data is available.
4917 Returns 0 on success, 1 if no rollback data is available.
4917 """
4918 """
4918 return repo.rollback(dryrun=opts.get('dry_run'),
4919 return repo.rollback(dryrun=opts.get('dry_run'),
4919 force=opts.get('force'))
4920 force=opts.get('force'))
4920
4921
4921 @command('root', [])
4922 @command('root', [])
4922 def root(ui, repo):
4923 def root(ui, repo):
4923 """print the root (top) of the current working directory
4924 """print the root (top) of the current working directory
4924
4925
4925 Print the root directory of the current repository.
4926 Print the root directory of the current repository.
4926
4927
4927 Returns 0 on success.
4928 Returns 0 on success.
4928 """
4929 """
4929 ui.write(repo.root + "\n")
4930 ui.write(repo.root + "\n")
4930
4931
4931 @command('^serve',
4932 @command('^serve',
4932 [('A', 'accesslog', '', _('name of access log file to write to'),
4933 [('A', 'accesslog', '', _('name of access log file to write to'),
4933 _('FILE')),
4934 _('FILE')),
4934 ('d', 'daemon', None, _('run server in background')),
4935 ('d', 'daemon', None, _('run server in background')),
4935 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('NUM')),
4936 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('NUM')),
4936 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4937 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4937 # use string type, then we can check if something was passed
4938 # use string type, then we can check if something was passed
4938 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4939 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4939 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4940 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4940 _('ADDR')),
4941 _('ADDR')),
4941 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4942 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4942 _('PREFIX')),
4943 _('PREFIX')),
4943 ('n', 'name', '',
4944 ('n', 'name', '',
4944 _('name to show in web pages (default: working directory)'), _('NAME')),
4945 _('name to show in web pages (default: working directory)'), _('NAME')),
4945 ('', 'web-conf', '',
4946 ('', 'web-conf', '',
4946 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
4947 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
4947 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4948 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4948 _('FILE')),
4949 _('FILE')),
4949 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4950 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4950 ('', 'stdio', None, _('for remote clients')),
4951 ('', 'stdio', None, _('for remote clients')),
4951 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
4952 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
4952 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4953 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4953 ('', 'style', '', _('template style to use'), _('STYLE')),
4954 ('', 'style', '', _('template style to use'), _('STYLE')),
4954 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4955 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4955 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
4956 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
4956 _('[OPTION]...'))
4957 _('[OPTION]...'))
4957 def serve(ui, repo, **opts):
4958 def serve(ui, repo, **opts):
4958 """start stand-alone webserver
4959 """start stand-alone webserver
4959
4960
4960 Start a local HTTP repository browser and pull server. You can use
4961 Start a local HTTP repository browser and pull server. You can use
4961 this for ad-hoc sharing and browsing of repositories. It is
4962 this for ad-hoc sharing and browsing of repositories. It is
4962 recommended to use a real web server to serve a repository for
4963 recommended to use a real web server to serve a repository for
4963 longer periods of time.
4964 longer periods of time.
4964
4965
4965 Please note that the server does not implement access control.
4966 Please note that the server does not implement access control.
4966 This means that, by default, anybody can read from the server and
4967 This means that, by default, anybody can read from the server and
4967 nobody can write to it by default. Set the ``web.allow_push``
4968 nobody can write to it by default. Set the ``web.allow_push``
4968 option to ``*`` to allow everybody to push to the server. You
4969 option to ``*`` to allow everybody to push to the server. You
4969 should use a real web server if you need to authenticate users.
4970 should use a real web server if you need to authenticate users.
4970
4971
4971 By default, the server logs accesses to stdout and errors to
4972 By default, the server logs accesses to stdout and errors to
4972 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4973 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4973 files.
4974 files.
4974
4975
4975 To have the server choose a free port number to listen on, specify
4976 To have the server choose a free port number to listen on, specify
4976 a port number of 0; in this case, the server will print the port
4977 a port number of 0; in this case, the server will print the port
4977 number it uses.
4978 number it uses.
4978
4979
4979 Returns 0 on success.
4980 Returns 0 on success.
4980 """
4981 """
4981
4982
4982 if opts["stdio"] and opts["cmdserver"]:
4983 if opts["stdio"] and opts["cmdserver"]:
4983 raise util.Abort(_("cannot use --stdio with --cmdserver"))
4984 raise util.Abort(_("cannot use --stdio with --cmdserver"))
4984
4985
4985 def checkrepo():
4986 def checkrepo():
4986 if repo is None:
4987 if repo is None:
4987 raise error.RepoError(_("There is no Mercurial repository here"
4988 raise error.RepoError(_("There is no Mercurial repository here"
4988 " (.hg not found)"))
4989 " (.hg not found)"))
4989
4990
4990 if opts["stdio"]:
4991 if opts["stdio"]:
4991 checkrepo()
4992 checkrepo()
4992 s = sshserver.sshserver(ui, repo)
4993 s = sshserver.sshserver(ui, repo)
4993 s.serve_forever()
4994 s.serve_forever()
4994
4995
4995 if opts["cmdserver"]:
4996 if opts["cmdserver"]:
4996 checkrepo()
4997 checkrepo()
4997 s = commandserver.server(ui, repo, opts["cmdserver"])
4998 s = commandserver.server(ui, repo, opts["cmdserver"])
4998 return s.serve()
4999 return s.serve()
4999
5000
5000 # this way we can check if something was given in the command-line
5001 # this way we can check if something was given in the command-line
5001 if opts.get('port'):
5002 if opts.get('port'):
5002 opts['port'] = util.getport(opts.get('port'))
5003 opts['port'] = util.getport(opts.get('port'))
5003
5004
5004 baseui = repo and repo.baseui or ui
5005 baseui = repo and repo.baseui or ui
5005 optlist = ("name templates style address port prefix ipv6"
5006 optlist = ("name templates style address port prefix ipv6"
5006 " accesslog errorlog certificate encoding")
5007 " accesslog errorlog certificate encoding")
5007 for o in optlist.split():
5008 for o in optlist.split():
5008 val = opts.get(o, '')
5009 val = opts.get(o, '')
5009 if val in (None, ''): # should check against default options instead
5010 if val in (None, ''): # should check against default options instead
5010 continue
5011 continue
5011 baseui.setconfig("web", o, val)
5012 baseui.setconfig("web", o, val)
5012 if repo and repo.ui != baseui:
5013 if repo and repo.ui != baseui:
5013 repo.ui.setconfig("web", o, val)
5014 repo.ui.setconfig("web", o, val)
5014
5015
5015 o = opts.get('web_conf') or opts.get('webdir_conf')
5016 o = opts.get('web_conf') or opts.get('webdir_conf')
5016 if not o:
5017 if not o:
5017 if not repo:
5018 if not repo:
5018 raise error.RepoError(_("There is no Mercurial repository"
5019 raise error.RepoError(_("There is no Mercurial repository"
5019 " here (.hg not found)"))
5020 " here (.hg not found)"))
5020 o = repo.root
5021 o = repo.root
5021
5022
5022 app = hgweb.hgweb(o, baseui=ui)
5023 app = hgweb.hgweb(o, baseui=ui)
5023
5024
5024 class service(object):
5025 class service(object):
5025 def init(self):
5026 def init(self):
5026 util.setsignalhandler()
5027 util.setsignalhandler()
5027 self.httpd = hgweb.server.create_server(ui, app)
5028 self.httpd = hgweb.server.create_server(ui, app)
5028
5029
5029 if opts['port'] and not ui.verbose:
5030 if opts['port'] and not ui.verbose:
5030 return
5031 return
5031
5032
5032 if self.httpd.prefix:
5033 if self.httpd.prefix:
5033 prefix = self.httpd.prefix.strip('/') + '/'
5034 prefix = self.httpd.prefix.strip('/') + '/'
5034 else:
5035 else:
5035 prefix = ''
5036 prefix = ''
5036
5037
5037 port = ':%d' % self.httpd.port
5038 port = ':%d' % self.httpd.port
5038 if port == ':80':
5039 if port == ':80':
5039 port = ''
5040 port = ''
5040
5041
5041 bindaddr = self.httpd.addr
5042 bindaddr = self.httpd.addr
5042 if bindaddr == '0.0.0.0':
5043 if bindaddr == '0.0.0.0':
5043 bindaddr = '*'
5044 bindaddr = '*'
5044 elif ':' in bindaddr: # IPv6
5045 elif ':' in bindaddr: # IPv6
5045 bindaddr = '[%s]' % bindaddr
5046 bindaddr = '[%s]' % bindaddr
5046
5047
5047 fqaddr = self.httpd.fqaddr
5048 fqaddr = self.httpd.fqaddr
5048 if ':' in fqaddr:
5049 if ':' in fqaddr:
5049 fqaddr = '[%s]' % fqaddr
5050 fqaddr = '[%s]' % fqaddr
5050 if opts['port']:
5051 if opts['port']:
5051 write = ui.status
5052 write = ui.status
5052 else:
5053 else:
5053 write = ui.write
5054 write = ui.write
5054 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
5055 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
5055 (fqaddr, port, prefix, bindaddr, self.httpd.port))
5056 (fqaddr, port, prefix, bindaddr, self.httpd.port))
5056
5057
5057 def run(self):
5058 def run(self):
5058 self.httpd.serve_forever()
5059 self.httpd.serve_forever()
5059
5060
5060 service = service()
5061 service = service()
5061
5062
5062 cmdutil.service(opts, initfn=service.init, runfn=service.run)
5063 cmdutil.service(opts, initfn=service.init, runfn=service.run)
5063
5064
5064 @command('showconfig|debugconfig',
5065 @command('showconfig|debugconfig',
5065 [('u', 'untrusted', None, _('show untrusted configuration options'))],
5066 [('u', 'untrusted', None, _('show untrusted configuration options'))],
5066 _('[-u] [NAME]...'))
5067 _('[-u] [NAME]...'))
5067 def showconfig(ui, repo, *values, **opts):
5068 def showconfig(ui, repo, *values, **opts):
5068 """show combined config settings from all hgrc files
5069 """show combined config settings from all hgrc files
5069
5070
5070 With no arguments, print names and values of all config items.
5071 With no arguments, print names and values of all config items.
5071
5072
5072 With one argument of the form section.name, print just the value
5073 With one argument of the form section.name, print just the value
5073 of that config item.
5074 of that config item.
5074
5075
5075 With multiple arguments, print names and values of all config
5076 With multiple arguments, print names and values of all config
5076 items with matching section names.
5077 items with matching section names.
5077
5078
5078 With --debug, the source (filename and line number) is printed
5079 With --debug, the source (filename and line number) is printed
5079 for each config item.
5080 for each config item.
5080
5081
5081 Returns 0 on success.
5082 Returns 0 on success.
5082 """
5083 """
5083
5084
5084 for f in scmutil.rcpath():
5085 for f in scmutil.rcpath():
5085 ui.debug('read config from: %s\n' % f)
5086 ui.debug('read config from: %s\n' % f)
5086 untrusted = bool(opts.get('untrusted'))
5087 untrusted = bool(opts.get('untrusted'))
5087 if values:
5088 if values:
5088 sections = [v for v in values if '.' not in v]
5089 sections = [v for v in values if '.' not in v]
5089 items = [v for v in values if '.' in v]
5090 items = [v for v in values if '.' in v]
5090 if len(items) > 1 or items and sections:
5091 if len(items) > 1 or items and sections:
5091 raise util.Abort(_('only one config item permitted'))
5092 raise util.Abort(_('only one config item permitted'))
5092 for section, name, value in ui.walkconfig(untrusted=untrusted):
5093 for section, name, value in ui.walkconfig(untrusted=untrusted):
5093 value = str(value).replace('\n', '\\n')
5094 value = str(value).replace('\n', '\\n')
5094 sectname = section + '.' + name
5095 sectname = section + '.' + name
5095 if values:
5096 if values:
5096 for v in values:
5097 for v in values:
5097 if v == section:
5098 if v == section:
5098 ui.debug('%s: ' %
5099 ui.debug('%s: ' %
5099 ui.configsource(section, name, untrusted))
5100 ui.configsource(section, name, untrusted))
5100 ui.write('%s=%s\n' % (sectname, value))
5101 ui.write('%s=%s\n' % (sectname, value))
5101 elif v == sectname:
5102 elif v == sectname:
5102 ui.debug('%s: ' %
5103 ui.debug('%s: ' %
5103 ui.configsource(section, name, untrusted))
5104 ui.configsource(section, name, untrusted))
5104 ui.write(value, '\n')
5105 ui.write(value, '\n')
5105 else:
5106 else:
5106 ui.debug('%s: ' %
5107 ui.debug('%s: ' %
5107 ui.configsource(section, name, untrusted))
5108 ui.configsource(section, name, untrusted))
5108 ui.write('%s=%s\n' % (sectname, value))
5109 ui.write('%s=%s\n' % (sectname, value))
5109
5110
5110 @command('^status|st',
5111 @command('^status|st',
5111 [('A', 'all', None, _('show status of all files')),
5112 [('A', 'all', None, _('show status of all files')),
5112 ('m', 'modified', None, _('show only modified files')),
5113 ('m', 'modified', None, _('show only modified files')),
5113 ('a', 'added', None, _('show only added files')),
5114 ('a', 'added', None, _('show only added files')),
5114 ('r', 'removed', None, _('show only removed files')),
5115 ('r', 'removed', None, _('show only removed files')),
5115 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5116 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5116 ('c', 'clean', None, _('show only files without changes')),
5117 ('c', 'clean', None, _('show only files without changes')),
5117 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5118 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5118 ('i', 'ignored', None, _('show only ignored files')),
5119 ('i', 'ignored', None, _('show only ignored files')),
5119 ('n', 'no-status', None, _('hide status prefix')),
5120 ('n', 'no-status', None, _('hide status prefix')),
5120 ('C', 'copies', None, _('show source of copied files')),
5121 ('C', 'copies', None, _('show source of copied files')),
5121 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5122 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5122 ('', 'rev', [], _('show difference from revision'), _('REV')),
5123 ('', 'rev', [], _('show difference from revision'), _('REV')),
5123 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5124 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5124 ] + walkopts + subrepoopts,
5125 ] + walkopts + subrepoopts,
5125 _('[OPTION]... [FILE]...'))
5126 _('[OPTION]... [FILE]...'))
5126 def status(ui, repo, *pats, **opts):
5127 def status(ui, repo, *pats, **opts):
5127 """show changed files in the working directory
5128 """show changed files in the working directory
5128
5129
5129 Show status of files in the repository. If names are given, only
5130 Show status of files in the repository. If names are given, only
5130 files that match are shown. Files that are clean or ignored or
5131 files that match are shown. Files that are clean or ignored or
5131 the source of a copy/move operation, are not listed unless
5132 the source of a copy/move operation, are not listed unless
5132 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5133 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5133 Unless options described with "show only ..." are given, the
5134 Unless options described with "show only ..." are given, the
5134 options -mardu are used.
5135 options -mardu are used.
5135
5136
5136 Option -q/--quiet hides untracked (unknown and ignored) files
5137 Option -q/--quiet hides untracked (unknown and ignored) files
5137 unless explicitly requested with -u/--unknown or -i/--ignored.
5138 unless explicitly requested with -u/--unknown or -i/--ignored.
5138
5139
5139 .. note::
5140 .. note::
5140 status may appear to disagree with diff if permissions have
5141 status may appear to disagree with diff if permissions have
5141 changed or a merge has occurred. The standard diff format does
5142 changed or a merge has occurred. The standard diff format does
5142 not report permission changes and diff only reports changes
5143 not report permission changes and diff only reports changes
5143 relative to one merge parent.
5144 relative to one merge parent.
5144
5145
5145 If one revision is given, it is used as the base revision.
5146 If one revision is given, it is used as the base revision.
5146 If two revisions are given, the differences between them are
5147 If two revisions are given, the differences between them are
5147 shown. The --change option can also be used as a shortcut to list
5148 shown. The --change option can also be used as a shortcut to list
5148 the changed files of a revision from its first parent.
5149 the changed files of a revision from its first parent.
5149
5150
5150 The codes used to show the status of files are::
5151 The codes used to show the status of files are::
5151
5152
5152 M = modified
5153 M = modified
5153 A = added
5154 A = added
5154 R = removed
5155 R = removed
5155 C = clean
5156 C = clean
5156 ! = missing (deleted by non-hg command, but still tracked)
5157 ! = missing (deleted by non-hg command, but still tracked)
5157 ? = not tracked
5158 ? = not tracked
5158 I = ignored
5159 I = ignored
5159 = origin of the previous file listed as A (added)
5160 = origin of the previous file listed as A (added)
5160
5161
5161 .. container:: verbose
5162 .. container:: verbose
5162
5163
5163 Examples:
5164 Examples:
5164
5165
5165 - show changes in the working directory relative to a
5166 - show changes in the working directory relative to a
5166 changeset::
5167 changeset::
5167
5168
5168 hg status --rev 9353
5169 hg status --rev 9353
5169
5170
5170 - show all changes including copies in an existing changeset::
5171 - show all changes including copies in an existing changeset::
5171
5172
5172 hg status --copies --change 9353
5173 hg status --copies --change 9353
5173
5174
5174 - get a NUL separated list of added files, suitable for xargs::
5175 - get a NUL separated list of added files, suitable for xargs::
5175
5176
5176 hg status -an0
5177 hg status -an0
5177
5178
5178 Returns 0 on success.
5179 Returns 0 on success.
5179 """
5180 """
5180
5181
5181 revs = opts.get('rev')
5182 revs = opts.get('rev')
5182 change = opts.get('change')
5183 change = opts.get('change')
5183
5184
5184 if revs and change:
5185 if revs and change:
5185 msg = _('cannot specify --rev and --change at the same time')
5186 msg = _('cannot specify --rev and --change at the same time')
5186 raise util.Abort(msg)
5187 raise util.Abort(msg)
5187 elif change:
5188 elif change:
5188 node2 = scmutil.revsingle(repo, change, None).node()
5189 node2 = scmutil.revsingle(repo, change, None).node()
5189 node1 = repo[node2].p1().node()
5190 node1 = repo[node2].p1().node()
5190 else:
5191 else:
5191 node1, node2 = scmutil.revpair(repo, revs)
5192 node1, node2 = scmutil.revpair(repo, revs)
5192
5193
5193 cwd = (pats and repo.getcwd()) or ''
5194 cwd = (pats and repo.getcwd()) or ''
5194 end = opts.get('print0') and '\0' or '\n'
5195 end = opts.get('print0') and '\0' or '\n'
5195 copy = {}
5196 copy = {}
5196 states = 'modified added removed deleted unknown ignored clean'.split()
5197 states = 'modified added removed deleted unknown ignored clean'.split()
5197 show = [k for k in states if opts.get(k)]
5198 show = [k for k in states if opts.get(k)]
5198 if opts.get('all'):
5199 if opts.get('all'):
5199 show += ui.quiet and (states[:4] + ['clean']) or states
5200 show += ui.quiet and (states[:4] + ['clean']) or states
5200 if not show:
5201 if not show:
5201 show = ui.quiet and states[:4] or states[:5]
5202 show = ui.quiet and states[:4] or states[:5]
5202
5203
5203 stat = repo.status(node1, node2, scmutil.match(repo[node2], pats, opts),
5204 stat = repo.status(node1, node2, scmutil.match(repo[node2], pats, opts),
5204 'ignored' in show, 'clean' in show, 'unknown' in show,
5205 'ignored' in show, 'clean' in show, 'unknown' in show,
5205 opts.get('subrepos'))
5206 opts.get('subrepos'))
5206 changestates = zip(states, 'MAR!?IC', stat)
5207 changestates = zip(states, 'MAR!?IC', stat)
5207
5208
5208 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
5209 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
5209 copy = copies.pathcopies(repo[node1], repo[node2])
5210 copy = copies.pathcopies(repo[node1], repo[node2])
5210
5211
5211 fm = ui.formatter('status', opts)
5212 fm = ui.formatter('status', opts)
5212 format = '%s %s' + end
5213 format = '%s %s' + end
5213 if opts.get('no_status'):
5214 if opts.get('no_status'):
5214 format = '%.0s%s' + end
5215 format = '%.0s%s' + end
5215
5216
5216 for state, char, files in changestates:
5217 for state, char, files in changestates:
5217 if state in show:
5218 if state in show:
5218 label = 'status.' + state
5219 label = 'status.' + state
5219 for f in files:
5220 for f in files:
5220 fm.startitem()
5221 fm.startitem()
5221 fm.write("status path", format, char,
5222 fm.write("status path", format, char,
5222 repo.pathto(f, cwd), label=label)
5223 repo.pathto(f, cwd), label=label)
5223 if f in copy:
5224 if f in copy:
5224 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
5225 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
5225 label='status.copied')
5226 label='status.copied')
5226 fm.end()
5227 fm.end()
5227
5228
5228 @command('^summary|sum',
5229 @command('^summary|sum',
5229 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5230 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5230 def summary(ui, repo, **opts):
5231 def summary(ui, repo, **opts):
5231 """summarize working directory state
5232 """summarize working directory state
5232
5233
5233 This generates a brief summary of the working directory state,
5234 This generates a brief summary of the working directory state,
5234 including parents, branch, commit status, and available updates.
5235 including parents, branch, commit status, and available updates.
5235
5236
5236 With the --remote option, this will check the default paths for
5237 With the --remote option, this will check the default paths for
5237 incoming and outgoing changes. This can be time-consuming.
5238 incoming and outgoing changes. This can be time-consuming.
5238
5239
5239 Returns 0 on success.
5240 Returns 0 on success.
5240 """
5241 """
5241
5242
5242 ctx = repo[None]
5243 ctx = repo[None]
5243 parents = ctx.parents()
5244 parents = ctx.parents()
5244 pnode = parents[0].node()
5245 pnode = parents[0].node()
5245 marks = []
5246 marks = []
5246
5247
5247 for p in parents:
5248 for p in parents:
5248 # label with log.changeset (instead of log.parent) since this
5249 # label with log.changeset (instead of log.parent) since this
5249 # shows a working directory parent *changeset*:
5250 # shows a working directory parent *changeset*:
5250 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5251 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5251 label='log.changeset')
5252 label='log.changeset')
5252 ui.write(' '.join(p.tags()), label='log.tag')
5253 ui.write(' '.join(p.tags()), label='log.tag')
5253 if p.bookmarks():
5254 if p.bookmarks():
5254 marks.extend(p.bookmarks())
5255 marks.extend(p.bookmarks())
5255 if p.rev() == -1:
5256 if p.rev() == -1:
5256 if not len(repo):
5257 if not len(repo):
5257 ui.write(_(' (empty repository)'))
5258 ui.write(_(' (empty repository)'))
5258 else:
5259 else:
5259 ui.write(_(' (no revision checked out)'))
5260 ui.write(_(' (no revision checked out)'))
5260 ui.write('\n')
5261 ui.write('\n')
5261 if p.description():
5262 if p.description():
5262 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5263 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5263 label='log.summary')
5264 label='log.summary')
5264
5265
5265 branch = ctx.branch()
5266 branch = ctx.branch()
5266 bheads = repo.branchheads(branch)
5267 bheads = repo.branchheads(branch)
5267 m = _('branch: %s\n') % branch
5268 m = _('branch: %s\n') % branch
5268 if branch != 'default':
5269 if branch != 'default':
5269 ui.write(m, label='log.branch')
5270 ui.write(m, label='log.branch')
5270 else:
5271 else:
5271 ui.status(m, label='log.branch')
5272 ui.status(m, label='log.branch')
5272
5273
5273 if marks:
5274 if marks:
5274 current = repo._bookmarkcurrent
5275 current = repo._bookmarkcurrent
5275 ui.write(_('bookmarks:'), label='log.bookmark')
5276 ui.write(_('bookmarks:'), label='log.bookmark')
5276 if current is not None:
5277 if current is not None:
5277 try:
5278 try:
5278 marks.remove(current)
5279 marks.remove(current)
5279 ui.write(' *' + current, label='bookmarks.current')
5280 ui.write(' *' + current, label='bookmarks.current')
5280 except ValueError:
5281 except ValueError:
5281 # current bookmark not in parent ctx marks
5282 # current bookmark not in parent ctx marks
5282 pass
5283 pass
5283 for m in marks:
5284 for m in marks:
5284 ui.write(' ' + m, label='log.bookmark')
5285 ui.write(' ' + m, label='log.bookmark')
5285 ui.write('\n', label='log.bookmark')
5286 ui.write('\n', label='log.bookmark')
5286
5287
5287 st = list(repo.status(unknown=True))[:6]
5288 st = list(repo.status(unknown=True))[:6]
5288
5289
5289 c = repo.dirstate.copies()
5290 c = repo.dirstate.copies()
5290 copied, renamed = [], []
5291 copied, renamed = [], []
5291 for d, s in c.iteritems():
5292 for d, s in c.iteritems():
5292 if s in st[2]:
5293 if s in st[2]:
5293 st[2].remove(s)
5294 st[2].remove(s)
5294 renamed.append(d)
5295 renamed.append(d)
5295 else:
5296 else:
5296 copied.append(d)
5297 copied.append(d)
5297 if d in st[1]:
5298 if d in st[1]:
5298 st[1].remove(d)
5299 st[1].remove(d)
5299 st.insert(3, renamed)
5300 st.insert(3, renamed)
5300 st.insert(4, copied)
5301 st.insert(4, copied)
5301
5302
5302 ms = mergemod.mergestate(repo)
5303 ms = mergemod.mergestate(repo)
5303 st.append([f for f in ms if ms[f] == 'u'])
5304 st.append([f for f in ms if ms[f] == 'u'])
5304
5305
5305 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5306 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5306 st.append(subs)
5307 st.append(subs)
5307
5308
5308 labels = [ui.label(_('%d modified'), 'status.modified'),
5309 labels = [ui.label(_('%d modified'), 'status.modified'),
5309 ui.label(_('%d added'), 'status.added'),
5310 ui.label(_('%d added'), 'status.added'),
5310 ui.label(_('%d removed'), 'status.removed'),
5311 ui.label(_('%d removed'), 'status.removed'),
5311 ui.label(_('%d renamed'), 'status.copied'),
5312 ui.label(_('%d renamed'), 'status.copied'),
5312 ui.label(_('%d copied'), 'status.copied'),
5313 ui.label(_('%d copied'), 'status.copied'),
5313 ui.label(_('%d deleted'), 'status.deleted'),
5314 ui.label(_('%d deleted'), 'status.deleted'),
5314 ui.label(_('%d unknown'), 'status.unknown'),
5315 ui.label(_('%d unknown'), 'status.unknown'),
5315 ui.label(_('%d ignored'), 'status.ignored'),
5316 ui.label(_('%d ignored'), 'status.ignored'),
5316 ui.label(_('%d unresolved'), 'resolve.unresolved'),
5317 ui.label(_('%d unresolved'), 'resolve.unresolved'),
5317 ui.label(_('%d subrepos'), 'status.modified')]
5318 ui.label(_('%d subrepos'), 'status.modified')]
5318 t = []
5319 t = []
5319 for s, l in zip(st, labels):
5320 for s, l in zip(st, labels):
5320 if s:
5321 if s:
5321 t.append(l % len(s))
5322 t.append(l % len(s))
5322
5323
5323 t = ', '.join(t)
5324 t = ', '.join(t)
5324 cleanworkdir = False
5325 cleanworkdir = False
5325
5326
5326 if len(parents) > 1:
5327 if len(parents) > 1:
5327 t += _(' (merge)')
5328 t += _(' (merge)')
5328 elif branch != parents[0].branch():
5329 elif branch != parents[0].branch():
5329 t += _(' (new branch)')
5330 t += _(' (new branch)')
5330 elif (parents[0].extra().get('close') and
5331 elif (parents[0].extra().get('close') and
5331 pnode in repo.branchheads(branch, closed=True)):
5332 pnode in repo.branchheads(branch, closed=True)):
5332 t += _(' (head closed)')
5333 t += _(' (head closed)')
5333 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
5334 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
5334 t += _(' (clean)')
5335 t += _(' (clean)')
5335 cleanworkdir = True
5336 cleanworkdir = True
5336 elif pnode not in bheads:
5337 elif pnode not in bheads:
5337 t += _(' (new branch head)')
5338 t += _(' (new branch head)')
5338
5339
5339 if cleanworkdir:
5340 if cleanworkdir:
5340 ui.status(_('commit: %s\n') % t.strip())
5341 ui.status(_('commit: %s\n') % t.strip())
5341 else:
5342 else:
5342 ui.write(_('commit: %s\n') % t.strip())
5343 ui.write(_('commit: %s\n') % t.strip())
5343
5344
5344 # all ancestors of branch heads - all ancestors of parent = new csets
5345 # all ancestors of branch heads - all ancestors of parent = new csets
5345 new = [0] * len(repo)
5346 new = [0] * len(repo)
5346 cl = repo.changelog
5347 cl = repo.changelog
5347 for a in [cl.rev(n) for n in bheads]:
5348 for a in [cl.rev(n) for n in bheads]:
5348 new[a] = 1
5349 new[a] = 1
5349 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
5350 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
5350 new[a] = 1
5351 new[a] = 1
5351 for a in [p.rev() for p in parents]:
5352 for a in [p.rev() for p in parents]:
5352 if a >= 0:
5353 if a >= 0:
5353 new[a] = 0
5354 new[a] = 0
5354 for a in cl.ancestors(*[p.rev() for p in parents]):
5355 for a in cl.ancestors(*[p.rev() for p in parents]):
5355 new[a] = 0
5356 new[a] = 0
5356 new = sum(new)
5357 new = sum(new)
5357
5358
5358 if new == 0:
5359 if new == 0:
5359 ui.status(_('update: (current)\n'))
5360 ui.status(_('update: (current)\n'))
5360 elif pnode not in bheads:
5361 elif pnode not in bheads:
5361 ui.write(_('update: %d new changesets (update)\n') % new)
5362 ui.write(_('update: %d new changesets (update)\n') % new)
5362 else:
5363 else:
5363 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5364 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5364 (new, len(bheads)))
5365 (new, len(bheads)))
5365
5366
5366 if opts.get('remote'):
5367 if opts.get('remote'):
5367 t = []
5368 t = []
5368 source, branches = hg.parseurl(ui.expandpath('default'))
5369 source, branches = hg.parseurl(ui.expandpath('default'))
5369 other = hg.peer(repo, {}, source)
5370 other = hg.peer(repo, {}, source)
5370 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
5371 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
5371 ui.debug('comparing with %s\n' % util.hidepassword(source))
5372 ui.debug('comparing with %s\n' % util.hidepassword(source))
5372 repo.ui.pushbuffer()
5373 repo.ui.pushbuffer()
5373 commoninc = discovery.findcommonincoming(repo, other)
5374 commoninc = discovery.findcommonincoming(repo, other)
5374 _common, incoming, _rheads = commoninc
5375 _common, incoming, _rheads = commoninc
5375 repo.ui.popbuffer()
5376 repo.ui.popbuffer()
5376 if incoming:
5377 if incoming:
5377 t.append(_('1 or more incoming'))
5378 t.append(_('1 or more incoming'))
5378
5379
5379 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5380 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5380 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5381 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5381 if source != dest:
5382 if source != dest:
5382 other = hg.peer(repo, {}, dest)
5383 other = hg.peer(repo, {}, dest)
5383 commoninc = None
5384 commoninc = None
5384 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5385 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5385 repo.ui.pushbuffer()
5386 repo.ui.pushbuffer()
5386 outgoing = discovery.findcommonoutgoing(repo, other,
5387 outgoing = discovery.findcommonoutgoing(repo, other,
5387 commoninc=commoninc)
5388 commoninc=commoninc)
5388 repo.ui.popbuffer()
5389 repo.ui.popbuffer()
5389 o = outgoing.missing
5390 o = outgoing.missing
5390 if o:
5391 if o:
5391 t.append(_('%d outgoing') % len(o))
5392 t.append(_('%d outgoing') % len(o))
5392 if 'bookmarks' in other.listkeys('namespaces'):
5393 if 'bookmarks' in other.listkeys('namespaces'):
5393 lmarks = repo.listkeys('bookmarks')
5394 lmarks = repo.listkeys('bookmarks')
5394 rmarks = other.listkeys('bookmarks')
5395 rmarks = other.listkeys('bookmarks')
5395 diff = set(rmarks) - set(lmarks)
5396 diff = set(rmarks) - set(lmarks)
5396 if len(diff) > 0:
5397 if len(diff) > 0:
5397 t.append(_('%d incoming bookmarks') % len(diff))
5398 t.append(_('%d incoming bookmarks') % len(diff))
5398 diff = set(lmarks) - set(rmarks)
5399 diff = set(lmarks) - set(rmarks)
5399 if len(diff) > 0:
5400 if len(diff) > 0:
5400 t.append(_('%d outgoing bookmarks') % len(diff))
5401 t.append(_('%d outgoing bookmarks') % len(diff))
5401
5402
5402 if t:
5403 if t:
5403 ui.write(_('remote: %s\n') % (', '.join(t)))
5404 ui.write(_('remote: %s\n') % (', '.join(t)))
5404 else:
5405 else:
5405 ui.status(_('remote: (synced)\n'))
5406 ui.status(_('remote: (synced)\n'))
5406
5407
5407 @command('tag',
5408 @command('tag',
5408 [('f', 'force', None, _('force tag')),
5409 [('f', 'force', None, _('force tag')),
5409 ('l', 'local', None, _('make the tag local')),
5410 ('l', 'local', None, _('make the tag local')),
5410 ('r', 'rev', '', _('revision to tag'), _('REV')),
5411 ('r', 'rev', '', _('revision to tag'), _('REV')),
5411 ('', 'remove', None, _('remove a tag')),
5412 ('', 'remove', None, _('remove a tag')),
5412 # -l/--local is already there, commitopts cannot be used
5413 # -l/--local is already there, commitopts cannot be used
5413 ('e', 'edit', None, _('edit commit message')),
5414 ('e', 'edit', None, _('edit commit message')),
5414 ('m', 'message', '', _('use <text> as commit message'), _('TEXT')),
5415 ('m', 'message', '', _('use <text> as commit message'), _('TEXT')),
5415 ] + commitopts2,
5416 ] + commitopts2,
5416 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5417 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5417 def tag(ui, repo, name1, *names, **opts):
5418 def tag(ui, repo, name1, *names, **opts):
5418 """add one or more tags for the current or given revision
5419 """add one or more tags for the current or given revision
5419
5420
5420 Name a particular revision using <name>.
5421 Name a particular revision using <name>.
5421
5422
5422 Tags are used to name particular revisions of the repository and are
5423 Tags are used to name particular revisions of the repository and are
5423 very useful to compare different revisions, to go back to significant
5424 very useful to compare different revisions, to go back to significant
5424 earlier versions or to mark branch points as releases, etc. Changing
5425 earlier versions or to mark branch points as releases, etc. Changing
5425 an existing tag is normally disallowed; use -f/--force to override.
5426 an existing tag is normally disallowed; use -f/--force to override.
5426
5427
5427 If no revision is given, the parent of the working directory is
5428 If no revision is given, the parent of the working directory is
5428 used, or tip if no revision is checked out.
5429 used, or tip if no revision is checked out.
5429
5430
5430 To facilitate version control, distribution, and merging of tags,
5431 To facilitate version control, distribution, and merging of tags,
5431 they are stored as a file named ".hgtags" which is managed similarly
5432 they are stored as a file named ".hgtags" which is managed similarly
5432 to other project files and can be hand-edited if necessary. This
5433 to other project files and can be hand-edited if necessary. This
5433 also means that tagging creates a new commit. The file
5434 also means that tagging creates a new commit. The file
5434 ".hg/localtags" is used for local tags (not shared among
5435 ".hg/localtags" is used for local tags (not shared among
5435 repositories).
5436 repositories).
5436
5437
5437 Tag commits are usually made at the head of a branch. If the parent
5438 Tag commits are usually made at the head of a branch. If the parent
5438 of the working directory is not a branch head, :hg:`tag` aborts; use
5439 of the working directory is not a branch head, :hg:`tag` aborts; use
5439 -f/--force to force the tag commit to be based on a non-head
5440 -f/--force to force the tag commit to be based on a non-head
5440 changeset.
5441 changeset.
5441
5442
5442 See :hg:`help dates` for a list of formats valid for -d/--date.
5443 See :hg:`help dates` for a list of formats valid for -d/--date.
5443
5444
5444 Since tag names have priority over branch names during revision
5445 Since tag names have priority over branch names during revision
5445 lookup, using an existing branch name as a tag name is discouraged.
5446 lookup, using an existing branch name as a tag name is discouraged.
5446
5447
5447 Returns 0 on success.
5448 Returns 0 on success.
5448 """
5449 """
5449 wlock = lock = None
5450 wlock = lock = None
5450 try:
5451 try:
5451 wlock = repo.wlock()
5452 wlock = repo.wlock()
5452 lock = repo.lock()
5453 lock = repo.lock()
5453 rev_ = "."
5454 rev_ = "."
5454 names = [t.strip() for t in (name1,) + names]
5455 names = [t.strip() for t in (name1,) + names]
5455 if len(names) != len(set(names)):
5456 if len(names) != len(set(names)):
5456 raise util.Abort(_('tag names must be unique'))
5457 raise util.Abort(_('tag names must be unique'))
5457 for n in names:
5458 for n in names:
5458 if n in ['tip', '.', 'null']:
5459 if n in ['tip', '.', 'null']:
5459 raise util.Abort(_("the name '%s' is reserved") % n)
5460 raise util.Abort(_("the name '%s' is reserved") % n)
5460 if not n:
5461 if not n:
5461 raise util.Abort(_('tag names cannot consist entirely of '
5462 raise util.Abort(_('tag names cannot consist entirely of '
5462 'whitespace'))
5463 'whitespace'))
5463 if opts.get('rev') and opts.get('remove'):
5464 if opts.get('rev') and opts.get('remove'):
5464 raise util.Abort(_("--rev and --remove are incompatible"))
5465 raise util.Abort(_("--rev and --remove are incompatible"))
5465 if opts.get('rev'):
5466 if opts.get('rev'):
5466 rev_ = opts['rev']
5467 rev_ = opts['rev']
5467 message = opts.get('message')
5468 message = opts.get('message')
5468 if opts.get('remove'):
5469 if opts.get('remove'):
5469 expectedtype = opts.get('local') and 'local' or 'global'
5470 expectedtype = opts.get('local') and 'local' or 'global'
5470 for n in names:
5471 for n in names:
5471 if not repo.tagtype(n):
5472 if not repo.tagtype(n):
5472 raise util.Abort(_("tag '%s' does not exist") % n)
5473 raise util.Abort(_("tag '%s' does not exist") % n)
5473 if repo.tagtype(n) != expectedtype:
5474 if repo.tagtype(n) != expectedtype:
5474 if expectedtype == 'global':
5475 if expectedtype == 'global':
5475 raise util.Abort(_("tag '%s' is not a global tag") % n)
5476 raise util.Abort(_("tag '%s' is not a global tag") % n)
5476 else:
5477 else:
5477 raise util.Abort(_("tag '%s' is not a local tag") % n)
5478 raise util.Abort(_("tag '%s' is not a local tag") % n)
5478 rev_ = nullid
5479 rev_ = nullid
5479 if not message:
5480 if not message:
5480 # we don't translate commit messages
5481 # we don't translate commit messages
5481 message = 'Removed tag %s' % ', '.join(names)
5482 message = 'Removed tag %s' % ', '.join(names)
5482 elif not opts.get('force'):
5483 elif not opts.get('force'):
5483 for n in names:
5484 for n in names:
5484 if n in repo.tags():
5485 if n in repo.tags():
5485 raise util.Abort(_("tag '%s' already exists "
5486 raise util.Abort(_("tag '%s' already exists "
5486 "(use -f to force)") % n)
5487 "(use -f to force)") % n)
5487 if not opts.get('local'):
5488 if not opts.get('local'):
5488 p1, p2 = repo.dirstate.parents()
5489 p1, p2 = repo.dirstate.parents()
5489 if p2 != nullid:
5490 if p2 != nullid:
5490 raise util.Abort(_('uncommitted merge'))
5491 raise util.Abort(_('uncommitted merge'))
5491 bheads = repo.branchheads()
5492 bheads = repo.branchheads()
5492 if not opts.get('force') and bheads and p1 not in bheads:
5493 if not opts.get('force') and bheads and p1 not in bheads:
5493 raise util.Abort(_('not at a branch head (use -f to force)'))
5494 raise util.Abort(_('not at a branch head (use -f to force)'))
5494 r = scmutil.revsingle(repo, rev_).node()
5495 r = scmutil.revsingle(repo, rev_).node()
5495
5496
5496 if not message:
5497 if not message:
5497 # we don't translate commit messages
5498 # we don't translate commit messages
5498 message = ('Added tag %s for changeset %s' %
5499 message = ('Added tag %s for changeset %s' %
5499 (', '.join(names), short(r)))
5500 (', '.join(names), short(r)))
5500
5501
5501 date = opts.get('date')
5502 date = opts.get('date')
5502 if date:
5503 if date:
5503 date = util.parsedate(date)
5504 date = util.parsedate(date)
5504
5505
5505 if opts.get('edit'):
5506 if opts.get('edit'):
5506 message = ui.edit(message, ui.username())
5507 message = ui.edit(message, ui.username())
5507
5508
5508 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
5509 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
5509 finally:
5510 finally:
5510 release(lock, wlock)
5511 release(lock, wlock)
5511
5512
5512 @command('tags', [], '')
5513 @command('tags', [], '')
5513 def tags(ui, repo):
5514 def tags(ui, repo):
5514 """list repository tags
5515 """list repository tags
5515
5516
5516 This lists both regular and local tags. When the -v/--verbose
5517 This lists both regular and local tags. When the -v/--verbose
5517 switch is used, a third column "local" is printed for local tags.
5518 switch is used, a third column "local" is printed for local tags.
5518
5519
5519 Returns 0 on success.
5520 Returns 0 on success.
5520 """
5521 """
5521
5522
5522 hexfunc = ui.debugflag and hex or short
5523 hexfunc = ui.debugflag and hex or short
5523 tagtype = ""
5524 tagtype = ""
5524
5525
5525 for t, n in reversed(repo.tagslist()):
5526 for t, n in reversed(repo.tagslist()):
5526 if ui.quiet:
5527 if ui.quiet:
5527 ui.write("%s\n" % t, label='tags.normal')
5528 ui.write("%s\n" % t, label='tags.normal')
5528 continue
5529 continue
5529
5530
5530 hn = hexfunc(n)
5531 hn = hexfunc(n)
5531 r = "%5d:%s" % (repo.changelog.rev(n), hn)
5532 r = "%5d:%s" % (repo.changelog.rev(n), hn)
5532 rev = ui.label(r, 'log.changeset')
5533 rev = ui.label(r, 'log.changeset')
5533 spaces = " " * (30 - encoding.colwidth(t))
5534 spaces = " " * (30 - encoding.colwidth(t))
5534
5535
5535 tag = ui.label(t, 'tags.normal')
5536 tag = ui.label(t, 'tags.normal')
5536 if ui.verbose:
5537 if ui.verbose:
5537 if repo.tagtype(t) == 'local':
5538 if repo.tagtype(t) == 'local':
5538 tagtype = " local"
5539 tagtype = " local"
5539 tag = ui.label(t, 'tags.local')
5540 tag = ui.label(t, 'tags.local')
5540 else:
5541 else:
5541 tagtype = ""
5542 tagtype = ""
5542 ui.write("%s%s %s%s\n" % (tag, spaces, rev, tagtype))
5543 ui.write("%s%s %s%s\n" % (tag, spaces, rev, tagtype))
5543
5544
5544 @command('tip',
5545 @command('tip',
5545 [('p', 'patch', None, _('show patch')),
5546 [('p', 'patch', None, _('show patch')),
5546 ('g', 'git', None, _('use git extended diff format')),
5547 ('g', 'git', None, _('use git extended diff format')),
5547 ] + templateopts,
5548 ] + templateopts,
5548 _('[-p] [-g]'))
5549 _('[-p] [-g]'))
5549 def tip(ui, repo, **opts):
5550 def tip(ui, repo, **opts):
5550 """show the tip revision
5551 """show the tip revision
5551
5552
5552 The tip revision (usually just called the tip) is the changeset
5553 The tip revision (usually just called the tip) is the changeset
5553 most recently added to the repository (and therefore the most
5554 most recently added to the repository (and therefore the most
5554 recently changed head).
5555 recently changed head).
5555
5556
5556 If you have just made a commit, that commit will be the tip. If
5557 If you have just made a commit, that commit will be the tip. If
5557 you have just pulled changes from another repository, the tip of
5558 you have just pulled changes from another repository, the tip of
5558 that repository becomes the current tip. The "tip" tag is special
5559 that repository becomes the current tip. The "tip" tag is special
5559 and cannot be renamed or assigned to a different changeset.
5560 and cannot be renamed or assigned to a different changeset.
5560
5561
5561 Returns 0 on success.
5562 Returns 0 on success.
5562 """
5563 """
5563 displayer = cmdutil.show_changeset(ui, repo, opts)
5564 displayer = cmdutil.show_changeset(ui, repo, opts)
5564 displayer.show(repo[len(repo) - 1])
5565 displayer.show(repo[len(repo) - 1])
5565 displayer.close()
5566 displayer.close()
5566
5567
5567 @command('unbundle',
5568 @command('unbundle',
5568 [('u', 'update', None,
5569 [('u', 'update', None,
5569 _('update to new branch head if changesets were unbundled'))],
5570 _('update to new branch head if changesets were unbundled'))],
5570 _('[-u] FILE...'))
5571 _('[-u] FILE...'))
5571 def unbundle(ui, repo, fname1, *fnames, **opts):
5572 def unbundle(ui, repo, fname1, *fnames, **opts):
5572 """apply one or more changegroup files
5573 """apply one or more changegroup files
5573
5574
5574 Apply one or more compressed changegroup files generated by the
5575 Apply one or more compressed changegroup files generated by the
5575 bundle command.
5576 bundle command.
5576
5577
5577 Returns 0 on success, 1 if an update has unresolved files.
5578 Returns 0 on success, 1 if an update has unresolved files.
5578 """
5579 """
5579 fnames = (fname1,) + fnames
5580 fnames = (fname1,) + fnames
5580
5581
5581 lock = repo.lock()
5582 lock = repo.lock()
5582 wc = repo['.']
5583 wc = repo['.']
5583 try:
5584 try:
5584 for fname in fnames:
5585 for fname in fnames:
5585 f = url.open(ui, fname)
5586 f = url.open(ui, fname)
5586 gen = changegroup.readbundle(f, fname)
5587 gen = changegroup.readbundle(f, fname)
5587 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
5588 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
5588 bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
5589 bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
5589 finally:
5590 finally:
5590 lock.release()
5591 lock.release()
5591 return postincoming(ui, repo, modheads, opts.get('update'), None)
5592 return postincoming(ui, repo, modheads, opts.get('update'), None)
5592
5593
5593 @command('^update|up|checkout|co',
5594 @command('^update|up|checkout|co',
5594 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5595 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5595 ('c', 'check', None,
5596 ('c', 'check', None,
5596 _('update across branches if no uncommitted changes')),
5597 _('update across branches if no uncommitted changes')),
5597 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5598 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5598 ('r', 'rev', '', _('revision'), _('REV'))],
5599 ('r', 'rev', '', _('revision'), _('REV'))],
5599 _('[-c] [-C] [-d DATE] [[-r] REV]'))
5600 _('[-c] [-C] [-d DATE] [[-r] REV]'))
5600 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
5601 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
5601 """update working directory (or switch revisions)
5602 """update working directory (or switch revisions)
5602
5603
5603 Update the repository's working directory to the specified
5604 Update the repository's working directory to the specified
5604 changeset. If no changeset is specified, update to the tip of the
5605 changeset. If no changeset is specified, update to the tip of the
5605 current named branch and move the current bookmark (see :hg:`help
5606 current named branch and move the current bookmark (see :hg:`help
5606 bookmarks`).
5607 bookmarks`).
5607
5608
5608 If the changeset is not a descendant of the working directory's
5609 If the changeset is not a descendant of the working directory's
5609 parent, the update is aborted. With the -c/--check option, the
5610 parent, the update is aborted. With the -c/--check option, the
5610 working directory is checked for uncommitted changes; if none are
5611 working directory is checked for uncommitted changes; if none are
5611 found, the working directory is updated to the specified
5612 found, the working directory is updated to the specified
5612 changeset.
5613 changeset.
5613
5614
5614 Update sets the working directory's parent revison to the specified
5615 Update sets the working directory's parent revison to the specified
5615 changeset (see :hg:`help parents`).
5616 changeset (see :hg:`help parents`).
5616
5617
5617 The following rules apply when the working directory contains
5618 The following rules apply when the working directory contains
5618 uncommitted changes:
5619 uncommitted changes:
5619
5620
5620 1. If neither -c/--check nor -C/--clean is specified, and if
5621 1. If neither -c/--check nor -C/--clean is specified, and if
5621 the requested changeset is an ancestor or descendant of
5622 the requested changeset is an ancestor or descendant of
5622 the working directory's parent, the uncommitted changes
5623 the working directory's parent, the uncommitted changes
5623 are merged into the requested changeset and the merged
5624 are merged into the requested changeset and the merged
5624 result is left uncommitted. If the requested changeset is
5625 result is left uncommitted. If the requested changeset is
5625 not an ancestor or descendant (that is, it is on another
5626 not an ancestor or descendant (that is, it is on another
5626 branch), the update is aborted and the uncommitted changes
5627 branch), the update is aborted and the uncommitted changes
5627 are preserved.
5628 are preserved.
5628
5629
5629 2. With the -c/--check option, the update is aborted and the
5630 2. With the -c/--check option, the update is aborted and the
5630 uncommitted changes are preserved.
5631 uncommitted changes are preserved.
5631
5632
5632 3. With the -C/--clean option, uncommitted changes are discarded and
5633 3. With the -C/--clean option, uncommitted changes are discarded and
5633 the working directory is updated to the requested changeset.
5634 the working directory is updated to the requested changeset.
5634
5635
5635 Use null as the changeset to remove the working directory (like
5636 Use null as the changeset to remove the working directory (like
5636 :hg:`clone -U`).
5637 :hg:`clone -U`).
5637
5638
5638 If you want to revert just one file to an older revision, use
5639 If you want to revert just one file to an older revision, use
5639 :hg:`revert [-r REV] NAME`.
5640 :hg:`revert [-r REV] NAME`.
5640
5641
5641 See :hg:`help dates` for a list of formats valid for -d/--date.
5642 See :hg:`help dates` for a list of formats valid for -d/--date.
5642
5643
5643 Returns 0 on success, 1 if there are unresolved files.
5644 Returns 0 on success, 1 if there are unresolved files.
5644 """
5645 """
5645 if rev and node:
5646 if rev and node:
5646 raise util.Abort(_("please specify just one revision"))
5647 raise util.Abort(_("please specify just one revision"))
5647
5648
5648 if rev is None or rev == '':
5649 if rev is None or rev == '':
5649 rev = node
5650 rev = node
5650
5651
5651 # with no argument, we also move the current bookmark, if any
5652 # with no argument, we also move the current bookmark, if any
5652 movemarkfrom = None
5653 movemarkfrom = None
5653 if rev is None or node == '':
5654 if rev is None or node == '':
5654 movemarkfrom = repo['.'].node()
5655 movemarkfrom = repo['.'].node()
5655
5656
5656 # if we defined a bookmark, we have to remember the original bookmark name
5657 # if we defined a bookmark, we have to remember the original bookmark name
5657 brev = rev
5658 brev = rev
5658 rev = scmutil.revsingle(repo, rev, rev).rev()
5659 rev = scmutil.revsingle(repo, rev, rev).rev()
5659
5660
5660 if check and clean:
5661 if check and clean:
5661 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
5662 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
5662
5663
5663 if date:
5664 if date:
5664 if rev is not None:
5665 if rev is not None:
5665 raise util.Abort(_("you can't specify a revision and a date"))
5666 raise util.Abort(_("you can't specify a revision and a date"))
5666 rev = cmdutil.finddate(ui, repo, date)
5667 rev = cmdutil.finddate(ui, repo, date)
5667
5668
5668 if check:
5669 if check:
5669 c = repo[None]
5670 c = repo[None]
5670 if c.dirty(merge=False, branch=False):
5671 if c.dirty(merge=False, branch=False):
5671 raise util.Abort(_("uncommitted local changes"))
5672 raise util.Abort(_("uncommitted local changes"))
5672 if rev is None:
5673 if rev is None:
5673 rev = repo[repo[None].branch()].rev()
5674 rev = repo[repo[None].branch()].rev()
5674 mergemod._checkunknown(repo, repo[None], repo[rev])
5675 mergemod._checkunknown(repo, repo[None], repo[rev])
5675
5676
5676 if clean:
5677 if clean:
5677 ret = hg.clean(repo, rev)
5678 ret = hg.clean(repo, rev)
5678 else:
5679 else:
5679 ret = hg.update(repo, rev)
5680 ret = hg.update(repo, rev)
5680
5681
5681 if not ret and movemarkfrom:
5682 if not ret and movemarkfrom:
5682 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
5683 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
5683 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
5684 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
5684 elif brev in repo._bookmarks:
5685 elif brev in repo._bookmarks:
5685 bookmarks.setcurrent(repo, brev)
5686 bookmarks.setcurrent(repo, brev)
5686 elif brev:
5687 elif brev:
5687 bookmarks.unsetcurrent(repo)
5688 bookmarks.unsetcurrent(repo)
5688
5689
5689 return ret
5690 return ret
5690
5691
5691 @command('verify', [])
5692 @command('verify', [])
5692 def verify(ui, repo):
5693 def verify(ui, repo):
5693 """verify the integrity of the repository
5694 """verify the integrity of the repository
5694
5695
5695 Verify the integrity of the current repository.
5696 Verify the integrity of the current repository.
5696
5697
5697 This will perform an extensive check of the repository's
5698 This will perform an extensive check of the repository's
5698 integrity, validating the hashes and checksums of each entry in
5699 integrity, validating the hashes and checksums of each entry in
5699 the changelog, manifest, and tracked files, as well as the
5700 the changelog, manifest, and tracked files, as well as the
5700 integrity of their crosslinks and indices.
5701 integrity of their crosslinks and indices.
5701
5702
5702 Returns 0 on success, 1 if errors are encountered.
5703 Returns 0 on success, 1 if errors are encountered.
5703 """
5704 """
5704 return hg.verify(repo)
5705 return hg.verify(repo)
5705
5706
5706 @command('version', [])
5707 @command('version', [])
5707 def version_(ui):
5708 def version_(ui):
5708 """output version and copyright information"""
5709 """output version and copyright information"""
5709 ui.write(_("Mercurial Distributed SCM (version %s)\n")
5710 ui.write(_("Mercurial Distributed SCM (version %s)\n")
5710 % util.version())
5711 % util.version())
5711 ui.status(_(
5712 ui.status(_(
5712 "(see http://mercurial.selenic.com for more information)\n"
5713 "(see http://mercurial.selenic.com for more information)\n"
5713 "\nCopyright (C) 2005-2012 Matt Mackall and others\n"
5714 "\nCopyright (C) 2005-2012 Matt Mackall and others\n"
5714 "This is free software; see the source for copying conditions. "
5715 "This is free software; see the source for copying conditions. "
5715 "There is NO\nwarranty; "
5716 "There is NO\nwarranty; "
5716 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5717 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5717 ))
5718 ))
5718
5719
5719 norepo = ("clone init version help debugcommands debugcomplete"
5720 norepo = ("clone init version help debugcommands debugcomplete"
5720 " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
5721 " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
5721 " debugknown debuggetbundle debugbundle")
5722 " debugknown debuggetbundle debugbundle")
5722 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
5723 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
5723 " debugdata debugindex debugindexdot debugrevlog")
5724 " debugdata debugindex debugindexdot debugrevlog")
@@ -1,1186 +1,1186 b''
1 /*
1 /*
2 parsers.c - efficient content parsing
2 parsers.c - efficient content parsing
3
3
4 Copyright 2008 Matt Mackall <mpm@selenic.com> and others
4 Copyright 2008 Matt Mackall <mpm@selenic.com> and others
5
5
6 This software may be used and distributed according to the terms of
6 This software may be used and distributed according to the terms of
7 the GNU General Public License, incorporated herein by reference.
7 the GNU General Public License, incorporated herein by reference.
8 */
8 */
9
9
10 #include <Python.h>
10 #include <Python.h>
11 #include <ctype.h>
11 #include <ctype.h>
12 #include <string.h>
12 #include <string.h>
13
13
14 #include "util.h"
14 #include "util.h"
15
15
16 static int hexdigit(char c)
16 static int hexdigit(char c)
17 {
17 {
18 if (c >= '0' && c <= '9')
18 if (c >= '0' && c <= '9')
19 return c - '0';
19 return c - '0';
20 if (c >= 'a' && c <= 'f')
20 if (c >= 'a' && c <= 'f')
21 return c - 'a' + 10;
21 return c - 'a' + 10;
22 if (c >= 'A' && c <= 'F')
22 if (c >= 'A' && c <= 'F')
23 return c - 'A' + 10;
23 return c - 'A' + 10;
24
24
25 PyErr_SetString(PyExc_ValueError, "input contains non-hex character");
25 PyErr_SetString(PyExc_ValueError, "input contains non-hex character");
26 return 0;
26 return 0;
27 }
27 }
28
28
29 /*
29 /*
30 * Turn a hex-encoded string into binary.
30 * Turn a hex-encoded string into binary.
31 */
31 */
32 static PyObject *unhexlify(const char *str, int len)
32 static PyObject *unhexlify(const char *str, int len)
33 {
33 {
34 PyObject *ret;
34 PyObject *ret;
35 const char *c;
35 const char *c;
36 char *d;
36 char *d;
37
37
38 ret = PyBytes_FromStringAndSize(NULL, len / 2);
38 ret = PyBytes_FromStringAndSize(NULL, len / 2);
39
39
40 if (!ret)
40 if (!ret)
41 return NULL;
41 return NULL;
42
42
43 d = PyBytes_AsString(ret);
43 d = PyBytes_AsString(ret);
44
44
45 for (c = str; c < str + len;) {
45 for (c = str; c < str + len;) {
46 int hi = hexdigit(*c++);
46 int hi = hexdigit(*c++);
47 int lo = hexdigit(*c++);
47 int lo = hexdigit(*c++);
48 *d++ = (hi << 4) | lo;
48 *d++ = (hi << 4) | lo;
49 }
49 }
50
50
51 return ret;
51 return ret;
52 }
52 }
53
53
54 /*
54 /*
55 * This code assumes that a manifest is stitched together with newline
55 * This code assumes that a manifest is stitched together with newline
56 * ('\n') characters.
56 * ('\n') characters.
57 */
57 */
58 static PyObject *parse_manifest(PyObject *self, PyObject *args)
58 static PyObject *parse_manifest(PyObject *self, PyObject *args)
59 {
59 {
60 PyObject *mfdict, *fdict;
60 PyObject *mfdict, *fdict;
61 char *str, *cur, *start, *zero;
61 char *str, *cur, *start, *zero;
62 int len;
62 int len;
63
63
64 if (!PyArg_ParseTuple(args, "O!O!s#:parse_manifest",
64 if (!PyArg_ParseTuple(args, "O!O!s#:parse_manifest",
65 &PyDict_Type, &mfdict,
65 &PyDict_Type, &mfdict,
66 &PyDict_Type, &fdict,
66 &PyDict_Type, &fdict,
67 &str, &len))
67 &str, &len))
68 goto quit;
68 goto quit;
69
69
70 for (start = cur = str, zero = NULL; cur < str + len; cur++) {
70 for (start = cur = str, zero = NULL; cur < str + len; cur++) {
71 PyObject *file = NULL, *node = NULL;
71 PyObject *file = NULL, *node = NULL;
72 PyObject *flags = NULL;
72 PyObject *flags = NULL;
73 int nlen;
73 int nlen;
74
74
75 if (!*cur) {
75 if (!*cur) {
76 zero = cur;
76 zero = cur;
77 continue;
77 continue;
78 }
78 }
79 else if (*cur != '\n')
79 else if (*cur != '\n')
80 continue;
80 continue;
81
81
82 if (!zero) {
82 if (!zero) {
83 PyErr_SetString(PyExc_ValueError,
83 PyErr_SetString(PyExc_ValueError,
84 "manifest entry has no separator");
84 "manifest entry has no separator");
85 goto quit;
85 goto quit;
86 }
86 }
87
87
88 file = PyBytes_FromStringAndSize(start, zero - start);
88 file = PyBytes_FromStringAndSize(start, zero - start);
89
89
90 if (!file)
90 if (!file)
91 goto bail;
91 goto bail;
92
92
93 nlen = cur - zero - 1;
93 nlen = cur - zero - 1;
94
94
95 node = unhexlify(zero + 1, nlen > 40 ? 40 : nlen);
95 node = unhexlify(zero + 1, nlen > 40 ? 40 : nlen);
96 if (!node)
96 if (!node)
97 goto bail;
97 goto bail;
98
98
99 if (nlen > 40) {
99 if (nlen > 40) {
100 flags = PyBytes_FromStringAndSize(zero + 41,
100 flags = PyBytes_FromStringAndSize(zero + 41,
101 nlen - 40);
101 nlen - 40);
102 if (!flags)
102 if (!flags)
103 goto bail;
103 goto bail;
104
104
105 if (PyDict_SetItem(fdict, file, flags) == -1)
105 if (PyDict_SetItem(fdict, file, flags) == -1)
106 goto bail;
106 goto bail;
107 }
107 }
108
108
109 if (PyDict_SetItem(mfdict, file, node) == -1)
109 if (PyDict_SetItem(mfdict, file, node) == -1)
110 goto bail;
110 goto bail;
111
111
112 start = cur + 1;
112 start = cur + 1;
113 zero = NULL;
113 zero = NULL;
114
114
115 Py_XDECREF(flags);
115 Py_XDECREF(flags);
116 Py_XDECREF(node);
116 Py_XDECREF(node);
117 Py_XDECREF(file);
117 Py_XDECREF(file);
118 continue;
118 continue;
119 bail:
119 bail:
120 Py_XDECREF(flags);
120 Py_XDECREF(flags);
121 Py_XDECREF(node);
121 Py_XDECREF(node);
122 Py_XDECREF(file);
122 Py_XDECREF(file);
123 goto quit;
123 goto quit;
124 }
124 }
125
125
126 if (len > 0 && *(cur - 1) != '\n') {
126 if (len > 0 && *(cur - 1) != '\n') {
127 PyErr_SetString(PyExc_ValueError,
127 PyErr_SetString(PyExc_ValueError,
128 "manifest contains trailing garbage");
128 "manifest contains trailing garbage");
129 goto quit;
129 goto quit;
130 }
130 }
131
131
132 Py_INCREF(Py_None);
132 Py_INCREF(Py_None);
133 return Py_None;
133 return Py_None;
134 quit:
134 quit:
135 return NULL;
135 return NULL;
136 }
136 }
137
137
138 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
138 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
139 {
139 {
140 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
140 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
141 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
141 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
142 char *str, *cur, *end, *cpos;
142 char *str, *cur, *end, *cpos;
143 int state, mode, size, mtime;
143 int state, mode, size, mtime;
144 unsigned int flen;
144 unsigned int flen;
145 int len;
145 int len;
146
146
147 if (!PyArg_ParseTuple(args, "O!O!s#:parse_dirstate",
147 if (!PyArg_ParseTuple(args, "O!O!s#:parse_dirstate",
148 &PyDict_Type, &dmap,
148 &PyDict_Type, &dmap,
149 &PyDict_Type, &cmap,
149 &PyDict_Type, &cmap,
150 &str, &len))
150 &str, &len))
151 goto quit;
151 goto quit;
152
152
153 /* read parents */
153 /* read parents */
154 if (len < 40)
154 if (len < 40)
155 goto quit;
155 goto quit;
156
156
157 parents = Py_BuildValue("s#s#", str, 20, str + 20, 20);
157 parents = Py_BuildValue("s#s#", str, 20, str + 20, 20);
158 if (!parents)
158 if (!parents)
159 goto quit;
159 goto quit;
160
160
161 /* read filenames */
161 /* read filenames */
162 cur = str + 40;
162 cur = str + 40;
163 end = str + len;
163 end = str + len;
164
164
165 while (cur < end - 17) {
165 while (cur < end - 17) {
166 /* unpack header */
166 /* unpack header */
167 state = *cur;
167 state = *cur;
168 mode = getbe32(cur + 1);
168 mode = getbe32(cur + 1);
169 size = getbe32(cur + 5);
169 size = getbe32(cur + 5);
170 mtime = getbe32(cur + 9);
170 mtime = getbe32(cur + 9);
171 flen = getbe32(cur + 13);
171 flen = getbe32(cur + 13);
172 cur += 17;
172 cur += 17;
173 if (cur + flen > end || cur + flen < cur) {
173 if (cur + flen > end || cur + flen < cur) {
174 PyErr_SetString(PyExc_ValueError, "overflow in dirstate");
174 PyErr_SetString(PyExc_ValueError, "overflow in dirstate");
175 goto quit;
175 goto quit;
176 }
176 }
177
177
178 entry = Py_BuildValue("ciii", state, mode, size, mtime);
178 entry = Py_BuildValue("ciii", state, mode, size, mtime);
179 if (!entry)
179 if (!entry)
180 goto quit;
180 goto quit;
181 PyObject_GC_UnTrack(entry); /* don't waste time with this */
181 PyObject_GC_UnTrack(entry); /* don't waste time with this */
182
182
183 cpos = memchr(cur, 0, flen);
183 cpos = memchr(cur, 0, flen);
184 if (cpos) {
184 if (cpos) {
185 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
185 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
186 cname = PyBytes_FromStringAndSize(cpos + 1,
186 cname = PyBytes_FromStringAndSize(cpos + 1,
187 flen - (cpos - cur) - 1);
187 flen - (cpos - cur) - 1);
188 if (!fname || !cname ||
188 if (!fname || !cname ||
189 PyDict_SetItem(cmap, fname, cname) == -1 ||
189 PyDict_SetItem(cmap, fname, cname) == -1 ||
190 PyDict_SetItem(dmap, fname, entry) == -1)
190 PyDict_SetItem(dmap, fname, entry) == -1)
191 goto quit;
191 goto quit;
192 Py_DECREF(cname);
192 Py_DECREF(cname);
193 } else {
193 } else {
194 fname = PyBytes_FromStringAndSize(cur, flen);
194 fname = PyBytes_FromStringAndSize(cur, flen);
195 if (!fname ||
195 if (!fname ||
196 PyDict_SetItem(dmap, fname, entry) == -1)
196 PyDict_SetItem(dmap, fname, entry) == -1)
197 goto quit;
197 goto quit;
198 }
198 }
199 cur += flen;
199 cur += flen;
200 Py_DECREF(fname);
200 Py_DECREF(fname);
201 Py_DECREF(entry);
201 Py_DECREF(entry);
202 fname = cname = entry = NULL;
202 fname = cname = entry = NULL;
203 }
203 }
204
204
205 ret = parents;
205 ret = parents;
206 Py_INCREF(ret);
206 Py_INCREF(ret);
207 quit:
207 quit:
208 Py_XDECREF(fname);
208 Py_XDECREF(fname);
209 Py_XDECREF(cname);
209 Py_XDECREF(cname);
210 Py_XDECREF(entry);
210 Py_XDECREF(entry);
211 Py_XDECREF(parents);
211 Py_XDECREF(parents);
212 return ret;
212 return ret;
213 }
213 }
214
214
215 /*
215 /*
216 * A base-16 trie for fast node->rev mapping.
216 * A base-16 trie for fast node->rev mapping.
217 *
217 *
218 * Positive value is index of the next node in the trie
218 * Positive value is index of the next node in the trie
219 * Negative value is a leaf: -(rev + 1)
219 * Negative value is a leaf: -(rev + 1)
220 * Zero is empty
220 * Zero is empty
221 */
221 */
222 typedef struct {
222 typedef struct {
223 int children[16];
223 int children[16];
224 } nodetree;
224 } nodetree;
225
225
226 /*
226 /*
227 * This class has two behaviours.
227 * This class has two behaviours.
228 *
228 *
229 * When used in a list-like way (with integer keys), we decode an
229 * When used in a list-like way (with integer keys), we decode an
230 * entry in a RevlogNG index file on demand. Our last entry is a
230 * entry in a RevlogNG index file on demand. Our last entry is a
231 * sentinel, always a nullid. We have limited support for
231 * sentinel, always a nullid. We have limited support for
232 * integer-keyed insert and delete, only at elements right before the
232 * integer-keyed insert and delete, only at elements right before the
233 * sentinel.
233 * sentinel.
234 *
234 *
235 * With string keys, we lazily perform a reverse mapping from node to
235 * With string keys, we lazily perform a reverse mapping from node to
236 * rev, using a base-16 trie.
236 * rev, using a base-16 trie.
237 */
237 */
238 typedef struct {
238 typedef struct {
239 PyObject_HEAD
239 PyObject_HEAD
240 /* Type-specific fields go here. */
240 /* Type-specific fields go here. */
241 PyObject *data; /* raw bytes of index */
241 PyObject *data; /* raw bytes of index */
242 PyObject **cache; /* cached tuples */
242 PyObject **cache; /* cached tuples */
243 const char **offsets; /* populated on demand */
243 const char **offsets; /* populated on demand */
244 Py_ssize_t raw_length; /* original number of elements */
244 Py_ssize_t raw_length; /* original number of elements */
245 Py_ssize_t length; /* current number of elements */
245 Py_ssize_t length; /* current number of elements */
246 PyObject *added; /* populated on demand */
246 PyObject *added; /* populated on demand */
247 nodetree *nt; /* base-16 trie */
247 nodetree *nt; /* base-16 trie */
248 int ntlength; /* # nodes in use */
248 int ntlength; /* # nodes in use */
249 int ntcapacity; /* # nodes allocated */
249 int ntcapacity; /* # nodes allocated */
250 int ntdepth; /* maximum depth of tree */
250 int ntdepth; /* maximum depth of tree */
251 int ntsplits; /* # splits performed */
251 int ntsplits; /* # splits performed */
252 int ntrev; /* last rev scanned */
252 int ntrev; /* last rev scanned */
253 int ntlookups; /* # lookups */
253 int ntlookups; /* # lookups */
254 int ntmisses; /* # lookups that miss the cache */
254 int ntmisses; /* # lookups that miss the cache */
255 int inlined;
255 int inlined;
256 } indexObject;
256 } indexObject;
257
257
258 static Py_ssize_t index_length(const indexObject *self)
258 static Py_ssize_t index_length(const indexObject *self)
259 {
259 {
260 if (self->added == NULL)
260 if (self->added == NULL)
261 return self->length;
261 return self->length;
262 return self->length + PyList_GET_SIZE(self->added);
262 return self->length + PyList_GET_SIZE(self->added);
263 }
263 }
264
264
265 static PyObject *nullentry;
265 static PyObject *nullentry;
266 static const char nullid[20];
266 static const char nullid[20];
267
267
268 static long inline_scan(indexObject *self, const char **offsets);
268 static long inline_scan(indexObject *self, const char **offsets);
269
269
270 #if LONG_MAX == 0x7fffffffL
270 #if LONG_MAX == 0x7fffffffL
271 static char *tuple_format = "Kiiiiiis#";
271 static char *tuple_format = "Kiiiiiis#";
272 #else
272 #else
273 static char *tuple_format = "kiiiiiis#";
273 static char *tuple_format = "kiiiiiis#";
274 #endif
274 #endif
275
275
276 /*
276 /*
277 * Return a pointer to the beginning of a RevlogNG record.
277 * Return a pointer to the beginning of a RevlogNG record.
278 */
278 */
279 static const char *index_deref(indexObject *self, Py_ssize_t pos)
279 static const char *index_deref(indexObject *self, Py_ssize_t pos)
280 {
280 {
281 if (self->inlined && pos > 0) {
281 if (self->inlined && pos > 0) {
282 if (self->offsets == NULL) {
282 if (self->offsets == NULL) {
283 self->offsets = malloc(self->raw_length *
283 self->offsets = malloc(self->raw_length *
284 sizeof(*self->offsets));
284 sizeof(*self->offsets));
285 if (self->offsets == NULL)
285 if (self->offsets == NULL)
286 return (const char *)PyErr_NoMemory();
286 return (const char *)PyErr_NoMemory();
287 inline_scan(self, self->offsets);
287 inline_scan(self, self->offsets);
288 }
288 }
289 return self->offsets[pos];
289 return self->offsets[pos];
290 }
290 }
291
291
292 return PyString_AS_STRING(self->data) + pos * 64;
292 return PyString_AS_STRING(self->data) + pos * 64;
293 }
293 }
294
294
295 /*
295 /*
296 * RevlogNG format (all in big endian, data may be inlined):
296 * RevlogNG format (all in big endian, data may be inlined):
297 * 6 bytes: offset
297 * 6 bytes: offset
298 * 2 bytes: flags
298 * 2 bytes: flags
299 * 4 bytes: compressed length
299 * 4 bytes: compressed length
300 * 4 bytes: uncompressed length
300 * 4 bytes: uncompressed length
301 * 4 bytes: base revision
301 * 4 bytes: base revision
302 * 4 bytes: link revision
302 * 4 bytes: link revision
303 * 4 bytes: parent 1 revision
303 * 4 bytes: parent 1 revision
304 * 4 bytes: parent 2 revision
304 * 4 bytes: parent 2 revision
305 * 32 bytes: nodeid (only 20 bytes used)
305 * 32 bytes: nodeid (only 20 bytes used)
306 */
306 */
307 static PyObject *index_get(indexObject *self, Py_ssize_t pos)
307 static PyObject *index_get(indexObject *self, Py_ssize_t pos)
308 {
308 {
309 uint64_t offset_flags;
309 uint64_t offset_flags;
310 int comp_len, uncomp_len, base_rev, link_rev, parent_1, parent_2;
310 int comp_len, uncomp_len, base_rev, link_rev, parent_1, parent_2;
311 const char *c_node_id;
311 const char *c_node_id;
312 const char *data;
312 const char *data;
313 Py_ssize_t length = index_length(self);
313 Py_ssize_t length = index_length(self);
314 PyObject *entry;
314 PyObject *entry;
315
315
316 if (pos < 0)
316 if (pos < 0)
317 pos += length;
317 pos += length;
318
318
319 if (pos < 0 || pos >= length) {
319 if (pos < 0 || pos >= length) {
320 PyErr_SetString(PyExc_IndexError, "revlog index out of range");
320 PyErr_SetString(PyExc_IndexError, "revlog index out of range");
321 return NULL;
321 return NULL;
322 }
322 }
323
323
324 if (pos == length - 1) {
324 if (pos == length - 1) {
325 Py_INCREF(nullentry);
325 Py_INCREF(nullentry);
326 return nullentry;
326 return nullentry;
327 }
327 }
328
328
329 if (pos >= self->length - 1) {
329 if (pos >= self->length - 1) {
330 PyObject *obj;
330 PyObject *obj;
331 obj = PyList_GET_ITEM(self->added, pos - self->length + 1);
331 obj = PyList_GET_ITEM(self->added, pos - self->length + 1);
332 Py_INCREF(obj);
332 Py_INCREF(obj);
333 return obj;
333 return obj;
334 }
334 }
335
335
336 if (self->cache) {
336 if (self->cache) {
337 if (self->cache[pos]) {
337 if (self->cache[pos]) {
338 Py_INCREF(self->cache[pos]);
338 Py_INCREF(self->cache[pos]);
339 return self->cache[pos];
339 return self->cache[pos];
340 }
340 }
341 } else {
341 } else {
342 self->cache = calloc(self->raw_length, sizeof(PyObject *));
342 self->cache = calloc(self->raw_length, sizeof(PyObject *));
343 if (self->cache == NULL)
343 if (self->cache == NULL)
344 return PyErr_NoMemory();
344 return PyErr_NoMemory();
345 }
345 }
346
346
347 data = index_deref(self, pos);
347 data = index_deref(self, pos);
348 if (data == NULL)
348 if (data == NULL)
349 return NULL;
349 return NULL;
350
350
351 offset_flags = getbe32(data + 4);
351 offset_flags = getbe32(data + 4);
352 if (pos == 0) /* mask out version number for the first entry */
352 if (pos == 0) /* mask out version number for the first entry */
353 offset_flags &= 0xFFFF;
353 offset_flags &= 0xFFFF;
354 else {
354 else {
355 uint32_t offset_high = getbe32(data);
355 uint32_t offset_high = getbe32(data);
356 offset_flags |= ((uint64_t)offset_high) << 32;
356 offset_flags |= ((uint64_t)offset_high) << 32;
357 }
357 }
358
358
359 comp_len = getbe32(data + 8);
359 comp_len = getbe32(data + 8);
360 uncomp_len = getbe32(data + 12);
360 uncomp_len = getbe32(data + 12);
361 base_rev = getbe32(data + 16);
361 base_rev = getbe32(data + 16);
362 link_rev = getbe32(data + 20);
362 link_rev = getbe32(data + 20);
363 parent_1 = getbe32(data + 24);
363 parent_1 = getbe32(data + 24);
364 parent_2 = getbe32(data + 28);
364 parent_2 = getbe32(data + 28);
365 c_node_id = data + 32;
365 c_node_id = data + 32;
366
366
367 entry = Py_BuildValue(tuple_format, offset_flags, comp_len,
367 entry = Py_BuildValue(tuple_format, offset_flags, comp_len,
368 uncomp_len, base_rev, link_rev,
368 uncomp_len, base_rev, link_rev,
369 parent_1, parent_2, c_node_id, 20);
369 parent_1, parent_2, c_node_id, 20);
370
370
371 if (entry)
371 if (entry)
372 PyObject_GC_UnTrack(entry);
372 PyObject_GC_UnTrack(entry);
373
373
374 self->cache[pos] = entry;
374 self->cache[pos] = entry;
375 Py_INCREF(entry);
375 Py_INCREF(entry);
376
376
377 return entry;
377 return entry;
378 }
378 }
379
379
380 /*
380 /*
381 * Return the 20-byte SHA of the node corresponding to the given rev.
381 * Return the 20-byte SHA of the node corresponding to the given rev.
382 */
382 */
383 static const char *index_node(indexObject *self, Py_ssize_t pos)
383 static const char *index_node(indexObject *self, Py_ssize_t pos)
384 {
384 {
385 Py_ssize_t length = index_length(self);
385 Py_ssize_t length = index_length(self);
386 const char *data;
386 const char *data;
387
387
388 if (pos == length - 1)
388 if (pos == length - 1)
389 return nullid;
389 return nullid;
390
390
391 if (pos >= length)
391 if (pos >= length)
392 return NULL;
392 return NULL;
393
393
394 if (pos >= self->length - 1) {
394 if (pos >= self->length - 1) {
395 PyObject *tuple, *str;
395 PyObject *tuple, *str;
396 tuple = PyList_GET_ITEM(self->added, pos - self->length + 1);
396 tuple = PyList_GET_ITEM(self->added, pos - self->length + 1);
397 str = PyTuple_GetItem(tuple, 7);
397 str = PyTuple_GetItem(tuple, 7);
398 return str ? PyString_AS_STRING(str) : NULL;
398 return str ? PyString_AS_STRING(str) : NULL;
399 }
399 }
400
400
401 data = index_deref(self, pos);
401 data = index_deref(self, pos);
402 return data ? data + 32 : NULL;
402 return data ? data + 32 : NULL;
403 }
403 }
404
404
405 static int nt_insert(indexObject *self, const char *node, int rev);
405 static int nt_insert(indexObject *self, const char *node, int rev);
406
406
407 static int node_check(PyObject *obj, char **node, Py_ssize_t *nodelen)
407 static int node_check(PyObject *obj, char **node, Py_ssize_t *nodelen)
408 {
408 {
409 if (PyString_AsStringAndSize(obj, node, nodelen) == -1)
409 if (PyString_AsStringAndSize(obj, node, nodelen) == -1)
410 return -1;
410 return -1;
411 if (*nodelen == 20)
411 if (*nodelen == 20)
412 return 0;
412 return 0;
413 PyErr_SetString(PyExc_ValueError, "20-byte hash required");
413 PyErr_SetString(PyExc_ValueError, "20-byte hash required");
414 return -1;
414 return -1;
415 }
415 }
416
416
417 static PyObject *index_insert(indexObject *self, PyObject *args)
417 static PyObject *index_insert(indexObject *self, PyObject *args)
418 {
418 {
419 PyObject *obj;
419 PyObject *obj;
420 char *node;
420 char *node;
421 long offset;
421 long offset;
422 Py_ssize_t len, nodelen;
422 Py_ssize_t len, nodelen;
423
423
424 if (!PyArg_ParseTuple(args, "lO", &offset, &obj))
424 if (!PyArg_ParseTuple(args, "lO", &offset, &obj))
425 return NULL;
425 return NULL;
426
426
427 if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 8) {
427 if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 8) {
428 PyErr_SetString(PyExc_TypeError, "8-tuple required");
428 PyErr_SetString(PyExc_TypeError, "8-tuple required");
429 return NULL;
429 return NULL;
430 }
430 }
431
431
432 if (node_check(PyTuple_GET_ITEM(obj, 7), &node, &nodelen) == -1)
432 if (node_check(PyTuple_GET_ITEM(obj, 7), &node, &nodelen) == -1)
433 return NULL;
433 return NULL;
434
434
435 len = index_length(self);
435 len = index_length(self);
436
436
437 if (offset < 0)
437 if (offset < 0)
438 offset += len;
438 offset += len;
439
439
440 if (offset != len - 1) {
440 if (offset != len - 1) {
441 PyErr_SetString(PyExc_IndexError,
441 PyErr_SetString(PyExc_IndexError,
442 "insert only supported at index -1");
442 "insert only supported at index -1");
443 return NULL;
443 return NULL;
444 }
444 }
445
445
446 if (offset > INT_MAX) {
446 if (offset > INT_MAX) {
447 PyErr_SetString(PyExc_ValueError,
447 PyErr_SetString(PyExc_ValueError,
448 "currently only 2**31 revs supported");
448 "currently only 2**31 revs supported");
449 return NULL;
449 return NULL;
450 }
450 }
451
451
452 if (self->added == NULL) {
452 if (self->added == NULL) {
453 self->added = PyList_New(0);
453 self->added = PyList_New(0);
454 if (self->added == NULL)
454 if (self->added == NULL)
455 return NULL;
455 return NULL;
456 }
456 }
457
457
458 if (PyList_Append(self->added, obj) == -1)
458 if (PyList_Append(self->added, obj) == -1)
459 return NULL;
459 return NULL;
460
460
461 if (self->nt)
461 if (self->nt)
462 nt_insert(self, node, (int)offset);
462 nt_insert(self, node, (int)offset);
463
463
464 Py_RETURN_NONE;
464 Py_RETURN_NONE;
465 }
465 }
466
466
467 static void _index_clearcaches(indexObject *self)
467 static void _index_clearcaches(indexObject *self)
468 {
468 {
469 if (self->cache) {
469 if (self->cache) {
470 Py_ssize_t i;
470 Py_ssize_t i;
471
471
472 for (i = 0; i < self->raw_length; i++) {
472 for (i = 0; i < self->raw_length; i++) {
473 if (self->cache[i]) {
473 if (self->cache[i]) {
474 Py_DECREF(self->cache[i]);
474 Py_DECREF(self->cache[i]);
475 self->cache[i] = NULL;
475 self->cache[i] = NULL;
476 }
476 }
477 }
477 }
478 free(self->cache);
478 free(self->cache);
479 self->cache = NULL;
479 self->cache = NULL;
480 }
480 }
481 if (self->offsets) {
481 if (self->offsets) {
482 free(self->offsets);
482 free(self->offsets);
483 self->offsets = NULL;
483 self->offsets = NULL;
484 }
484 }
485 if (self->nt) {
485 if (self->nt) {
486 free(self->nt);
486 free(self->nt);
487 self->nt = NULL;
487 self->nt = NULL;
488 }
488 }
489 }
489 }
490
490
491 static PyObject *index_clearcaches(indexObject *self)
491 static PyObject *index_clearcaches(indexObject *self)
492 {
492 {
493 _index_clearcaches(self);
493 _index_clearcaches(self);
494 self->ntlength = self->ntcapacity = 0;
494 self->ntlength = self->ntcapacity = 0;
495 self->ntdepth = self->ntsplits = 0;
495 self->ntdepth = self->ntsplits = 0;
496 self->ntrev = -1;
496 self->ntrev = -1;
497 self->ntlookups = self->ntmisses = 0;
497 self->ntlookups = self->ntmisses = 0;
498 Py_RETURN_NONE;
498 Py_RETURN_NONE;
499 }
499 }
500
500
501 static PyObject *index_stats(indexObject *self)
501 static PyObject *index_stats(indexObject *self)
502 {
502 {
503 PyObject *obj = PyDict_New();
503 PyObject *obj = PyDict_New();
504
504
505 if (obj == NULL)
505 if (obj == NULL)
506 return NULL;
506 return NULL;
507
507
508 #define istat(__n, __d) \
508 #define istat(__n, __d) \
509 if (PyDict_SetItemString(obj, __d, PyInt_FromLong(self->__n)) == -1) \
509 if (PyDict_SetItemString(obj, __d, PyInt_FromLong(self->__n)) == -1) \
510 goto bail;
510 goto bail;
511
511
512 if (self->added) {
512 if (self->added) {
513 Py_ssize_t len = PyList_GET_SIZE(self->added);
513 Py_ssize_t len = PyList_GET_SIZE(self->added);
514 if (PyDict_SetItemString(obj, "index entries added",
514 if (PyDict_SetItemString(obj, "index entries added",
515 PyInt_FromLong(len)) == -1)
515 PyInt_FromLong(len)) == -1)
516 goto bail;
516 goto bail;
517 }
517 }
518
518
519 if (self->raw_length != self->length - 1)
519 if (self->raw_length != self->length - 1)
520 istat(raw_length, "revs on disk");
520 istat(raw_length, "revs on disk");
521 istat(length, "revs in memory");
521 istat(length, "revs in memory");
522 istat(ntcapacity, "node trie capacity");
522 istat(ntcapacity, "node trie capacity");
523 istat(ntdepth, "node trie depth");
523 istat(ntdepth, "node trie depth");
524 istat(ntlength, "node trie count");
524 istat(ntlength, "node trie count");
525 istat(ntlookups, "node trie lookups");
525 istat(ntlookups, "node trie lookups");
526 istat(ntmisses, "node trie misses");
526 istat(ntmisses, "node trie misses");
527 istat(ntrev, "node trie last rev scanned");
527 istat(ntrev, "node trie last rev scanned");
528 istat(ntsplits, "node trie splits");
528 istat(ntsplits, "node trie splits");
529
529
530 #undef istat
530 #undef istat
531
531
532 return obj;
532 return obj;
533
533
534 bail:
534 bail:
535 Py_XDECREF(obj);
535 Py_XDECREF(obj);
536 return NULL;
536 return NULL;
537 }
537 }
538
538
539 static inline int nt_level(const char *node, int level)
539 static inline int nt_level(const char *node, int level)
540 {
540 {
541 int v = node[level>>1];
541 int v = node[level>>1];
542 if (!(level & 1))
542 if (!(level & 1))
543 v >>= 4;
543 v >>= 4;
544 return v & 0xf;
544 return v & 0xf;
545 }
545 }
546
546
547 static int nt_find(indexObject *self, const char *node, Py_ssize_t nodelen)
547 static int nt_find(indexObject *self, const char *node, Py_ssize_t nodelen)
548 {
548 {
549 int level, off;
549 int level, off;
550
550
551 if (nodelen == 20 && node[0] == '\0' && memcmp(node, nullid, 20) == 0)
551 if (nodelen == 20 && node[0] == '\0' && memcmp(node, nullid, 20) == 0)
552 return -1;
552 return -1;
553
553
554 if (self->nt == NULL)
554 if (self->nt == NULL)
555 return -2;
555 return -2;
556
556
557 for (level = off = 0; level < nodelen; level++) {
557 for (level = off = 0; level < nodelen; level++) {
558 int k = nt_level(node, level);
558 int k = nt_level(node, level);
559 nodetree *n = &self->nt[off];
559 nodetree *n = &self->nt[off];
560 int v = n->children[k];
560 int v = n->children[k];
561
561
562 if (v < 0) {
562 if (v < 0) {
563 const char *n;
563 const char *n;
564 v = -v - 1;
564 v = -v - 1;
565 n = index_node(self, v);
565 n = index_node(self, v);
566 if (n == NULL)
566 if (n == NULL)
567 return -2;
567 return -2;
568 return memcmp(node, n, nodelen > 20 ? 20 : nodelen)
568 return memcmp(node, n, nodelen > 20 ? 20 : nodelen)
569 ? -2 : v;
569 ? -2 : v;
570 }
570 }
571 if (v == 0)
571 if (v == 0)
572 return -2;
572 return -2;
573 off = v;
573 off = v;
574 }
574 }
575 return -2;
575 return -2;
576 }
576 }
577
577
578 static int nt_new(indexObject *self)
578 static int nt_new(indexObject *self)
579 {
579 {
580 if (self->ntlength == self->ntcapacity) {
580 if (self->ntlength == self->ntcapacity) {
581 self->ntcapacity *= 2;
581 self->ntcapacity *= 2;
582 self->nt = realloc(self->nt,
582 self->nt = realloc(self->nt,
583 self->ntcapacity * sizeof(nodetree));
583 self->ntcapacity * sizeof(nodetree));
584 if (self->nt == NULL) {
584 if (self->nt == NULL) {
585 PyErr_SetString(PyExc_MemoryError, "out of memory");
585 PyErr_SetString(PyExc_MemoryError, "out of memory");
586 return -1;
586 return -1;
587 }
587 }
588 memset(&self->nt[self->ntlength], 0,
588 memset(&self->nt[self->ntlength], 0,
589 sizeof(nodetree) * (self->ntcapacity - self->ntlength));
589 sizeof(nodetree) * (self->ntcapacity - self->ntlength));
590 }
590 }
591 return self->ntlength++;
591 return self->ntlength++;
592 }
592 }
593
593
594 static int nt_insert(indexObject *self, const char *node, int rev)
594 static int nt_insert(indexObject *self, const char *node, int rev)
595 {
595 {
596 int level = 0;
596 int level = 0;
597 int off = 0;
597 int off = 0;
598
598
599 while (level < 20) {
599 while (level < 20) {
600 int k = nt_level(node, level);
600 int k = nt_level(node, level);
601 nodetree *n;
601 nodetree *n;
602 int v;
602 int v;
603
603
604 n = &self->nt[off];
604 n = &self->nt[off];
605 v = n->children[k];
605 v = n->children[k];
606
606
607 if (v == 0) {
607 if (v == 0) {
608 n->children[k] = -rev - 1;
608 n->children[k] = -rev - 1;
609 return 0;
609 return 0;
610 }
610 }
611 if (v < 0) {
611 if (v < 0) {
612 const char *oldnode = index_node(self, -v - 1);
612 const char *oldnode = index_node(self, -v - 1);
613 int noff;
613 int noff;
614
614
615 if (!oldnode || !memcmp(oldnode, node, 20)) {
615 if (!oldnode || !memcmp(oldnode, node, 20)) {
616 n->children[k] = -rev - 1;
616 n->children[k] = -rev - 1;
617 return 0;
617 return 0;
618 }
618 }
619 noff = nt_new(self);
619 noff = nt_new(self);
620 if (noff == -1)
620 if (noff == -1)
621 return -1;
621 return -1;
622 /* self->nt may have been changed by realloc */
622 /* self->nt may have been changed by realloc */
623 self->nt[off].children[k] = noff;
623 self->nt[off].children[k] = noff;
624 off = noff;
624 off = noff;
625 n = &self->nt[off];
625 n = &self->nt[off];
626 n->children[nt_level(oldnode, ++level)] = v;
626 n->children[nt_level(oldnode, ++level)] = v;
627 if (level > self->ntdepth)
627 if (level > self->ntdepth)
628 self->ntdepth = level;
628 self->ntdepth = level;
629 self->ntsplits += 1;
629 self->ntsplits += 1;
630 } else {
630 } else {
631 level += 1;
631 level += 1;
632 off = v;
632 off = v;
633 }
633 }
634 }
634 }
635
635
636 return -1;
636 return -1;
637 }
637 }
638
638
639 /*
639 /*
640 * Return values:
640 * Return values:
641 *
641 *
642 * -3: error (exception set)
642 * -3: error (exception set)
643 * -2: not found (no exception set)
643 * -2: not found (no exception set)
644 * rest: valid rev
644 * rest: valid rev
645 */
645 */
646 static int index_find_node(indexObject *self,
646 static int index_find_node(indexObject *self,
647 const char *node, Py_ssize_t nodelen)
647 const char *node, Py_ssize_t nodelen)
648 {
648 {
649 int rev;
649 int rev;
650
650
651 self->ntlookups++;
651 self->ntlookups++;
652 rev = nt_find(self, node, nodelen);
652 rev = nt_find(self, node, nodelen);
653 if (rev >= -1)
653 if (rev >= -1)
654 return rev;
654 return rev;
655
655
656 if (self->nt == NULL) {
656 if (self->nt == NULL) {
657 self->ntcapacity = self->raw_length < 4
657 self->ntcapacity = self->raw_length < 4
658 ? 4 : self->raw_length / 2;
658 ? 4 : self->raw_length / 2;
659 self->nt = calloc(self->ntcapacity, sizeof(nodetree));
659 self->nt = calloc(self->ntcapacity, sizeof(nodetree));
660 if (self->nt == NULL) {
660 if (self->nt == NULL) {
661 PyErr_SetString(PyExc_MemoryError, "out of memory");
661 PyErr_SetString(PyExc_MemoryError, "out of memory");
662 return -3;
662 return -3;
663 }
663 }
664 self->ntlength = 1;
664 self->ntlength = 1;
665 self->ntrev = (int)index_length(self) - 1;
665 self->ntrev = (int)index_length(self) - 1;
666 self->ntlookups = 1;
666 self->ntlookups = 1;
667 self->ntmisses = 0;
667 self->ntmisses = 0;
668 }
668 }
669
669
670 /*
670 /*
671 * For the first handful of lookups, we scan the entire index,
671 * For the first handful of lookups, we scan the entire index,
672 * and cache only the matching nodes. This optimizes for cases
672 * and cache only the matching nodes. This optimizes for cases
673 * like "hg tip", where only a few nodes are accessed.
673 * like "hg tip", where only a few nodes are accessed.
674 *
674 *
675 * After that, we cache every node we visit, using a single
675 * After that, we cache every node we visit, using a single
676 * scan amortized over multiple lookups. This gives the best
676 * scan amortized over multiple lookups. This gives the best
677 * bulk performance, e.g. for "hg log".
677 * bulk performance, e.g. for "hg log".
678 */
678 */
679 if (self->ntmisses++ < 4) {
679 if (self->ntmisses++ < 4) {
680 for (rev = self->ntrev - 1; rev >= 0; rev--) {
680 for (rev = self->ntrev - 1; rev >= 0; rev--) {
681 const char *n = index_node(self, rev);
681 const char *n = index_node(self, rev);
682 if (n == NULL)
682 if (n == NULL)
683 return -2;
683 return -2;
684 if (memcmp(node, n, nodelen > 20 ? 20 : nodelen) == 0) {
684 if (memcmp(node, n, nodelen > 20 ? 20 : nodelen) == 0) {
685 if (nt_insert(self, n, rev) == -1)
685 if (nt_insert(self, n, rev) == -1)
686 return -3;
686 return -3;
687 break;
687 break;
688 }
688 }
689 }
689 }
690 } else {
690 } else {
691 for (rev = self->ntrev - 1; rev >= 0; rev--) {
691 for (rev = self->ntrev - 1; rev >= 0; rev--) {
692 const char *n = index_node(self, rev);
692 const char *n = index_node(self, rev);
693 if (n == NULL)
693 if (n == NULL)
694 return -2;
694 return -2;
695 if (nt_insert(self, n, rev) == -1)
695 if (nt_insert(self, n, rev) == -1)
696 return -3;
696 return -3;
697 if (memcmp(node, n, nodelen > 20 ? 20 : nodelen) == 0) {
697 if (memcmp(node, n, nodelen > 20 ? 20 : nodelen) == 0) {
698 break;
698 break;
699 }
699 }
700 }
700 }
701 self->ntrev = rev;
701 self->ntrev = rev;
702 }
702 }
703
703
704 if (rev >= 0)
704 if (rev >= 0)
705 return rev;
705 return rev;
706 return -2;
706 return -2;
707 }
707 }
708
708
709 static PyObject *raise_revlog_error(void)
709 static PyObject *raise_revlog_error(void)
710 {
710 {
711 static PyObject *errclass;
711 static PyObject *errclass;
712 PyObject *mod = NULL, *errobj;
712 PyObject *mod = NULL, *errobj;
713
713
714 if (errclass == NULL) {
714 if (errclass == NULL) {
715 PyObject *dict;
715 PyObject *dict;
716
716
717 mod = PyImport_ImportModule("mercurial.error");
717 mod = PyImport_ImportModule("mercurial.error");
718 if (mod == NULL)
718 if (mod == NULL)
719 goto classfail;
719 goto classfail;
720
720
721 dict = PyModule_GetDict(mod);
721 dict = PyModule_GetDict(mod);
722 if (dict == NULL)
722 if (dict == NULL)
723 goto classfail;
723 goto classfail;
724
724
725 errclass = PyDict_GetItemString(dict, "RevlogError");
725 errclass = PyDict_GetItemString(dict, "RevlogError");
726 if (errclass == NULL) {
726 if (errclass == NULL) {
727 PyErr_SetString(PyExc_SystemError,
727 PyErr_SetString(PyExc_SystemError,
728 "could not find RevlogError");
728 "could not find RevlogError");
729 goto classfail;
729 goto classfail;
730 }
730 }
731 Py_INCREF(errclass);
731 Py_INCREF(errclass);
732 }
732 }
733
733
734 errobj = PyObject_CallFunction(errclass, NULL);
734 errobj = PyObject_CallFunction(errclass, NULL);
735 if (errobj == NULL)
735 if (errobj == NULL)
736 return NULL;
736 return NULL;
737 PyErr_SetObject(errclass, errobj);
737 PyErr_SetObject(errclass, errobj);
738 return errobj;
738 return errobj;
739
739
740 classfail:
740 classfail:
741 Py_XDECREF(mod);
741 Py_XDECREF(mod);
742 return NULL;
742 return NULL;
743 }
743 }
744
744
745 static PyObject *index_getitem(indexObject *self, PyObject *value)
745 static PyObject *index_getitem(indexObject *self, PyObject *value)
746 {
746 {
747 char *node;
747 char *node;
748 Py_ssize_t nodelen;
748 Py_ssize_t nodelen;
749 int rev;
749 int rev;
750
750
751 if (PyInt_Check(value))
751 if (PyInt_Check(value))
752 return index_get(self, PyInt_AS_LONG(value));
752 return index_get(self, PyInt_AS_LONG(value));
753
753
754 if (PyString_AsStringAndSize(value, &node, &nodelen) == -1)
754 if (PyString_AsStringAndSize(value, &node, &nodelen) == -1)
755 return NULL;
755 return NULL;
756 rev = index_find_node(self, node, nodelen);
756 rev = index_find_node(self, node, nodelen);
757 if (rev >= -1)
757 if (rev >= -1)
758 return PyInt_FromLong(rev);
758 return PyInt_FromLong(rev);
759 if (rev == -2)
759 if (rev == -2)
760 raise_revlog_error();
760 raise_revlog_error();
761 return NULL;
761 return NULL;
762 }
762 }
763
763
764 static PyObject *index_m_get(indexObject *self, PyObject *args)
764 static PyObject *index_m_get(indexObject *self, PyObject *args)
765 {
765 {
766 char *node;
766 char *node;
767 int nodelen, rev;
767 int nodelen, rev;
768
768
769 if (!PyArg_ParseTuple(args, "s#", &node, &nodelen))
769 if (!PyArg_ParseTuple(args, "s#", &node, &nodelen))
770 return NULL;
770 return NULL;
771
771
772 rev = index_find_node(self, node, nodelen);
772 rev = index_find_node(self, node, nodelen);
773 if (rev == -3)
773 if (rev == -3)
774 return NULL;
774 return NULL;
775 if (rev == -2)
775 if (rev == -2)
776 Py_RETURN_NONE;
776 Py_RETURN_NONE;
777 return PyInt_FromLong(rev);
777 return PyInt_FromLong(rev);
778 }
778 }
779
779
780 static int index_contains(indexObject *self, PyObject *value)
780 static int index_contains(indexObject *self, PyObject *value)
781 {
781 {
782 char *node;
782 char *node;
783 Py_ssize_t nodelen;
783 Py_ssize_t nodelen;
784
784
785 if (PyInt_Check(value)) {
785 if (PyInt_Check(value)) {
786 long rev = PyInt_AS_LONG(value);
786 long rev = PyInt_AS_LONG(value);
787 return rev >= -1 && rev < index_length(self);
787 return rev >= -1 && rev < index_length(self);
788 }
788 }
789
789
790 if (!PyString_Check(value))
790 if (!PyString_Check(value))
791 return 0;
791 return 0;
792
792
793 node = PyString_AS_STRING(value);
793 node = PyString_AS_STRING(value);
794 nodelen = PyString_GET_SIZE(value);
794 nodelen = PyString_GET_SIZE(value);
795
795
796 switch (index_find_node(self, node, nodelen)) {
796 switch (index_find_node(self, node, nodelen)) {
797 case -3:
797 case -3:
798 return -1;
798 return -1;
799 case -2:
799 case -2:
800 return 0;
800 return 0;
801 default:
801 default:
802 return 1;
802 return 1;
803 }
803 }
804 }
804 }
805
805
806 /*
806 /*
807 * Invalidate any trie entries introduced by added revs.
807 * Invalidate any trie entries introduced by added revs.
808 */
808 */
809 static void nt_invalidate_added(indexObject *self, Py_ssize_t start)
809 static void nt_invalidate_added(indexObject *self, Py_ssize_t start)
810 {
810 {
811 Py_ssize_t i, len = PyList_GET_SIZE(self->added);
811 Py_ssize_t i, len = PyList_GET_SIZE(self->added);
812
812
813 for (i = start; i < len; i++) {
813 for (i = start; i < len; i++) {
814 PyObject *tuple = PyList_GET_ITEM(self->added, i);
814 PyObject *tuple = PyList_GET_ITEM(self->added, i);
815 PyObject *node = PyTuple_GET_ITEM(tuple, 7);
815 PyObject *node = PyTuple_GET_ITEM(tuple, 7);
816
816
817 nt_insert(self, PyString_AS_STRING(node), -1);
817 nt_insert(self, PyString_AS_STRING(node), -1);
818 }
818 }
819
819
820 if (start == 0) {
820 if (start == 0) {
821 Py_DECREF(self->added);
821 Py_DECREF(self->added);
822 self->added = NULL;
822 self->added = NULL;
823 }
823 }
824 }
824 }
825
825
826 /*
826 /*
827 * Delete a numeric range of revs, which must be at the end of the
827 * Delete a numeric range of revs, which must be at the end of the
828 * range, but exclude the sentinel nullid entry.
828 * range, but exclude the sentinel nullid entry.
829 */
829 */
830 static int index_slice_del(indexObject *self, PyObject *item)
830 static int index_slice_del(indexObject *self, PyObject *item)
831 {
831 {
832 Py_ssize_t start, stop, step, slicelength;
832 Py_ssize_t start, stop, step, slicelength;
833 Py_ssize_t length = index_length(self);
833 Py_ssize_t length = index_length(self);
834
834
835 if (PySlice_GetIndicesEx((PySliceObject*)item, length,
835 if (PySlice_GetIndicesEx((PySliceObject*)item, length,
836 &start, &stop, &step, &slicelength) < 0)
836 &start, &stop, &step, &slicelength) < 0)
837 return -1;
837 return -1;
838
838
839 if (slicelength <= 0)
839 if (slicelength <= 0)
840 return 0;
840 return 0;
841
841
842 if ((step < 0 && start < stop) || (step > 0 && start > stop))
842 if ((step < 0 && start < stop) || (step > 0 && start > stop))
843 stop = start;
843 stop = start;
844
844
845 if (step < 0) {
845 if (step < 0) {
846 stop = start + 1;
846 stop = start + 1;
847 start = stop + step*(slicelength - 1) - 1;
847 start = stop + step*(slicelength - 1) - 1;
848 step = -step;
848 step = -step;
849 }
849 }
850
850
851 if (step != 1) {
851 if (step != 1) {
852 PyErr_SetString(PyExc_ValueError,
852 PyErr_SetString(PyExc_ValueError,
853 "revlog index delete requires step size of 1");
853 "revlog index delete requires step size of 1");
854 return -1;
854 return -1;
855 }
855 }
856
856
857 if (stop != length - 1) {
857 if (stop != length - 1) {
858 PyErr_SetString(PyExc_IndexError,
858 PyErr_SetString(PyExc_IndexError,
859 "revlog index deletion indices are invalid");
859 "revlog index deletion indices are invalid");
860 return -1;
860 return -1;
861 }
861 }
862
862
863 if (start < self->length - 1) {
863 if (start < self->length - 1) {
864 if (self->nt) {
864 if (self->nt) {
865 Py_ssize_t i;
865 Py_ssize_t i;
866
866
867 for (i = start + 1; i < self->length - 1; i++) {
867 for (i = start + 1; i < self->length - 1; i++) {
868 const char *node = index_node(self, i);
868 const char *node = index_node(self, i);
869
869
870 if (node)
870 if (node)
871 nt_insert(self, node, -1);
871 nt_insert(self, node, -1);
872 }
872 }
873 if (self->added)
873 if (self->added)
874 nt_invalidate_added(self, 0);
874 nt_invalidate_added(self, 0);
875 if (self->ntrev > start)
875 if (self->ntrev > start)
876 self->ntrev = (int)start;
876 self->ntrev = (int)start;
877 }
877 }
878 self->length = start + 1;
878 self->length = start + 1;
879 return 0;
879 return 0;
880 }
880 }
881
881
882 if (self->nt) {
882 if (self->nt) {
883 nt_invalidate_added(self, start - self->length + 1);
883 nt_invalidate_added(self, start - self->length + 1);
884 if (self->ntrev > start)
884 if (self->ntrev > start)
885 self->ntrev = (int)start;
885 self->ntrev = (int)start;
886 }
886 }
887 return self->added
887 return self->added
888 ? PyList_SetSlice(self->added, start - self->length + 1,
888 ? PyList_SetSlice(self->added, start - self->length + 1,
889 PyList_GET_SIZE(self->added), NULL)
889 PyList_GET_SIZE(self->added), NULL)
890 : 0;
890 : 0;
891 }
891 }
892
892
893 /*
893 /*
894 * Supported ops:
894 * Supported ops:
895 *
895 *
896 * slice deletion
896 * slice deletion
897 * string assignment (extend node->rev mapping)
897 * string assignment (extend node->rev mapping)
898 * string deletion (shrink node->rev mapping)
898 * string deletion (shrink node->rev mapping)
899 */
899 */
900 static int index_assign_subscript(indexObject *self, PyObject *item,
900 static int index_assign_subscript(indexObject *self, PyObject *item,
901 PyObject *value)
901 PyObject *value)
902 {
902 {
903 char *node;
903 char *node;
904 Py_ssize_t nodelen;
904 Py_ssize_t nodelen;
905 long rev;
905 long rev;
906
906
907 if (PySlice_Check(item) && value == NULL)
907 if (PySlice_Check(item) && value == NULL)
908 return index_slice_del(self, item);
908 return index_slice_del(self, item);
909
909
910 if (node_check(item, &node, &nodelen) == -1)
910 if (node_check(item, &node, &nodelen) == -1)
911 return -1;
911 return -1;
912
912
913 if (value == NULL)
913 if (value == NULL)
914 return self->nt ? nt_insert(self, node, -1) : 0;
914 return self->nt ? nt_insert(self, node, -1) : 0;
915 rev = PyInt_AsLong(value);
915 rev = PyInt_AsLong(value);
916 if (rev > INT_MAX || rev < 0) {
916 if (rev > INT_MAX || rev < 0) {
917 if (!PyErr_Occurred())
917 if (!PyErr_Occurred())
918 PyErr_SetString(PyExc_ValueError, "rev out of range");
918 PyErr_SetString(PyExc_ValueError, "rev out of range");
919 return -1;
919 return -1;
920 }
920 }
921 return nt_insert(self, node, (int)rev);
921 return nt_insert(self, node, (int)rev);
922 }
922 }
923
923
924 /*
924 /*
925 * Find all RevlogNG entries in an index that has inline data. Update
925 * Find all RevlogNG entries in an index that has inline data. Update
926 * the optional "offsets" table with those entries.
926 * the optional "offsets" table with those entries.
927 */
927 */
928 static long inline_scan(indexObject *self, const char **offsets)
928 static long inline_scan(indexObject *self, const char **offsets)
929 {
929 {
930 const char *data = PyString_AS_STRING(self->data);
930 const char *data = PyString_AS_STRING(self->data);
931 const char *end = data + PyString_GET_SIZE(self->data);
931 const char *end = data + PyString_GET_SIZE(self->data);
932 const long hdrsize = 64;
932 const long hdrsize = 64;
933 long incr = hdrsize;
933 long incr = hdrsize;
934 Py_ssize_t len = 0;
934 Py_ssize_t len = 0;
935
935
936 while (data + hdrsize <= end) {
936 while (data + hdrsize <= end) {
937 uint32_t comp_len;
937 uint32_t comp_len;
938 const char *old_data;
938 const char *old_data;
939 /* 3rd element of header is length of compressed inline data */
939 /* 3rd element of header is length of compressed inline data */
940 comp_len = getbe32(data + 8);
940 comp_len = getbe32(data + 8);
941 incr = hdrsize + comp_len;
941 incr = hdrsize + comp_len;
942 if (incr < hdrsize)
942 if (incr < hdrsize)
943 break;
943 break;
944 if (offsets)
944 if (offsets)
945 offsets[len] = data;
945 offsets[len] = data;
946 len++;
946 len++;
947 old_data = data;
947 old_data = data;
948 data += incr;
948 data += incr;
949 if (data <= old_data)
949 if (data <= old_data)
950 break;
950 break;
951 }
951 }
952
952
953 if (data != end && data + hdrsize != end) {
953 if (data != end && data + hdrsize != end) {
954 if (!PyErr_Occurred())
954 if (!PyErr_Occurred())
955 PyErr_SetString(PyExc_ValueError, "corrupt index file");
955 PyErr_SetString(PyExc_ValueError, "corrupt index file");
956 return -1;
956 return -1;
957 }
957 }
958
958
959 return len;
959 return len;
960 }
960 }
961
961
962 static int index_init(indexObject *self, PyObject *args)
962 static int index_init(indexObject *self, PyObject *args)
963 {
963 {
964 PyObject *data_obj, *inlined_obj;
964 PyObject *data_obj, *inlined_obj;
965 Py_ssize_t size;
965 Py_ssize_t size;
966
966
967 if (!PyArg_ParseTuple(args, "OO", &data_obj, &inlined_obj))
967 if (!PyArg_ParseTuple(args, "OO", &data_obj, &inlined_obj))
968 return -1;
968 return -1;
969 if (!PyString_Check(data_obj)) {
969 if (!PyString_Check(data_obj)) {
970 PyErr_SetString(PyExc_TypeError, "data is not a string");
970 PyErr_SetString(PyExc_TypeError, "data is not a string");
971 return -1;
971 return -1;
972 }
972 }
973 size = PyString_GET_SIZE(data_obj);
973 size = PyString_GET_SIZE(data_obj);
974
974
975 self->inlined = inlined_obj && PyObject_IsTrue(inlined_obj);
975 self->inlined = inlined_obj && PyObject_IsTrue(inlined_obj);
976 self->data = data_obj;
976 self->data = data_obj;
977 self->cache = NULL;
977 self->cache = NULL;
978
978
979 self->added = NULL;
979 self->added = NULL;
980 self->offsets = NULL;
980 self->offsets = NULL;
981 self->nt = NULL;
981 self->nt = NULL;
982 self->ntlength = self->ntcapacity = 0;
982 self->ntlength = self->ntcapacity = 0;
983 self->ntdepth = self->ntsplits = 0;
983 self->ntdepth = self->ntsplits = 0;
984 self->ntlookups = self->ntmisses = 0;
984 self->ntlookups = self->ntmisses = 0;
985 self->ntrev = -1;
985 self->ntrev = -1;
986 Py_INCREF(self->data);
986
987
987 if (self->inlined) {
988 if (self->inlined) {
988 long len = inline_scan(self, NULL);
989 long len = inline_scan(self, NULL);
989 if (len == -1)
990 if (len == -1)
990 goto bail;
991 goto bail;
991 self->raw_length = len;
992 self->raw_length = len;
992 self->length = len + 1;
993 self->length = len + 1;
993 } else {
994 } else {
994 if (size % 64) {
995 if (size % 64) {
995 PyErr_SetString(PyExc_ValueError, "corrupt index file");
996 PyErr_SetString(PyExc_ValueError, "corrupt index file");
996 goto bail;
997 goto bail;
997 }
998 }
998 self->raw_length = size / 64;
999 self->raw_length = size / 64;
999 self->length = self->raw_length + 1;
1000 self->length = self->raw_length + 1;
1000 }
1001 }
1001 Py_INCREF(self->data);
1002
1002
1003 return 0;
1003 return 0;
1004 bail:
1004 bail:
1005 return -1;
1005 return -1;
1006 }
1006 }
1007
1007
1008 static PyObject *index_nodemap(indexObject *self)
1008 static PyObject *index_nodemap(indexObject *self)
1009 {
1009 {
1010 Py_INCREF(self);
1010 Py_INCREF(self);
1011 return (PyObject *)self;
1011 return (PyObject *)self;
1012 }
1012 }
1013
1013
1014 static void index_dealloc(indexObject *self)
1014 static void index_dealloc(indexObject *self)
1015 {
1015 {
1016 _index_clearcaches(self);
1016 _index_clearcaches(self);
1017 Py_DECREF(self->data);
1017 Py_DECREF(self->data);
1018 Py_XDECREF(self->added);
1018 Py_XDECREF(self->added);
1019 PyObject_Del(self);
1019 PyObject_Del(self);
1020 }
1020 }
1021
1021
1022 static PySequenceMethods index_sequence_methods = {
1022 static PySequenceMethods index_sequence_methods = {
1023 (lenfunc)index_length, /* sq_length */
1023 (lenfunc)index_length, /* sq_length */
1024 0, /* sq_concat */
1024 0, /* sq_concat */
1025 0, /* sq_repeat */
1025 0, /* sq_repeat */
1026 (ssizeargfunc)index_get, /* sq_item */
1026 (ssizeargfunc)index_get, /* sq_item */
1027 0, /* sq_slice */
1027 0, /* sq_slice */
1028 0, /* sq_ass_item */
1028 0, /* sq_ass_item */
1029 0, /* sq_ass_slice */
1029 0, /* sq_ass_slice */
1030 (objobjproc)index_contains, /* sq_contains */
1030 (objobjproc)index_contains, /* sq_contains */
1031 };
1031 };
1032
1032
1033 static PyMappingMethods index_mapping_methods = {
1033 static PyMappingMethods index_mapping_methods = {
1034 (lenfunc)index_length, /* mp_length */
1034 (lenfunc)index_length, /* mp_length */
1035 (binaryfunc)index_getitem, /* mp_subscript */
1035 (binaryfunc)index_getitem, /* mp_subscript */
1036 (objobjargproc)index_assign_subscript, /* mp_ass_subscript */
1036 (objobjargproc)index_assign_subscript, /* mp_ass_subscript */
1037 };
1037 };
1038
1038
1039 static PyMethodDef index_methods[] = {
1039 static PyMethodDef index_methods[] = {
1040 {"clearcaches", (PyCFunction)index_clearcaches, METH_NOARGS,
1040 {"clearcaches", (PyCFunction)index_clearcaches, METH_NOARGS,
1041 "clear the index caches"},
1041 "clear the index caches"},
1042 {"get", (PyCFunction)index_m_get, METH_VARARGS,
1042 {"get", (PyCFunction)index_m_get, METH_VARARGS,
1043 "get an index entry"},
1043 "get an index entry"},
1044 {"insert", (PyCFunction)index_insert, METH_VARARGS,
1044 {"insert", (PyCFunction)index_insert, METH_VARARGS,
1045 "insert an index entry"},
1045 "insert an index entry"},
1046 {"stats", (PyCFunction)index_stats, METH_NOARGS,
1046 {"stats", (PyCFunction)index_stats, METH_NOARGS,
1047 "stats for the index"},
1047 "stats for the index"},
1048 {NULL} /* Sentinel */
1048 {NULL} /* Sentinel */
1049 };
1049 };
1050
1050
1051 static PyGetSetDef index_getset[] = {
1051 static PyGetSetDef index_getset[] = {
1052 {"nodemap", (getter)index_nodemap, NULL, "nodemap", NULL},
1052 {"nodemap", (getter)index_nodemap, NULL, "nodemap", NULL},
1053 {NULL} /* Sentinel */
1053 {NULL} /* Sentinel */
1054 };
1054 };
1055
1055
1056 static PyTypeObject indexType = {
1056 static PyTypeObject indexType = {
1057 PyObject_HEAD_INIT(NULL)
1057 PyObject_HEAD_INIT(NULL)
1058 0, /* ob_size */
1058 0, /* ob_size */
1059 "parsers.index", /* tp_name */
1059 "parsers.index", /* tp_name */
1060 sizeof(indexObject), /* tp_basicsize */
1060 sizeof(indexObject), /* tp_basicsize */
1061 0, /* tp_itemsize */
1061 0, /* tp_itemsize */
1062 (destructor)index_dealloc, /* tp_dealloc */
1062 (destructor)index_dealloc, /* tp_dealloc */
1063 0, /* tp_print */
1063 0, /* tp_print */
1064 0, /* tp_getattr */
1064 0, /* tp_getattr */
1065 0, /* tp_setattr */
1065 0, /* tp_setattr */
1066 0, /* tp_compare */
1066 0, /* tp_compare */
1067 0, /* tp_repr */
1067 0, /* tp_repr */
1068 0, /* tp_as_number */
1068 0, /* tp_as_number */
1069 &index_sequence_methods, /* tp_as_sequence */
1069 &index_sequence_methods, /* tp_as_sequence */
1070 &index_mapping_methods, /* tp_as_mapping */
1070 &index_mapping_methods, /* tp_as_mapping */
1071 0, /* tp_hash */
1071 0, /* tp_hash */
1072 0, /* tp_call */
1072 0, /* tp_call */
1073 0, /* tp_str */
1073 0, /* tp_str */
1074 0, /* tp_getattro */
1074 0, /* tp_getattro */
1075 0, /* tp_setattro */
1075 0, /* tp_setattro */
1076 0, /* tp_as_buffer */
1076 0, /* tp_as_buffer */
1077 Py_TPFLAGS_DEFAULT, /* tp_flags */
1077 Py_TPFLAGS_DEFAULT, /* tp_flags */
1078 "revlog index", /* tp_doc */
1078 "revlog index", /* tp_doc */
1079 0, /* tp_traverse */
1079 0, /* tp_traverse */
1080 0, /* tp_clear */
1080 0, /* tp_clear */
1081 0, /* tp_richcompare */
1081 0, /* tp_richcompare */
1082 0, /* tp_weaklistoffset */
1082 0, /* tp_weaklistoffset */
1083 0, /* tp_iter */
1083 0, /* tp_iter */
1084 0, /* tp_iternext */
1084 0, /* tp_iternext */
1085 index_methods, /* tp_methods */
1085 index_methods, /* tp_methods */
1086 0, /* tp_members */
1086 0, /* tp_members */
1087 index_getset, /* tp_getset */
1087 index_getset, /* tp_getset */
1088 0, /* tp_base */
1088 0, /* tp_base */
1089 0, /* tp_dict */
1089 0, /* tp_dict */
1090 0, /* tp_descr_get */
1090 0, /* tp_descr_get */
1091 0, /* tp_descr_set */
1091 0, /* tp_descr_set */
1092 0, /* tp_dictoffset */
1092 0, /* tp_dictoffset */
1093 (initproc)index_init, /* tp_init */
1093 (initproc)index_init, /* tp_init */
1094 0, /* tp_alloc */
1094 0, /* tp_alloc */
1095 PyType_GenericNew, /* tp_new */
1095 PyType_GenericNew, /* tp_new */
1096 };
1096 };
1097
1097
1098 /*
1098 /*
1099 * returns a tuple of the form (index, index, cache) with elements as
1099 * returns a tuple of the form (index, index, cache) with elements as
1100 * follows:
1100 * follows:
1101 *
1101 *
1102 * index: an index object that lazily parses RevlogNG records
1102 * index: an index object that lazily parses RevlogNG records
1103 * cache: if data is inlined, a tuple (index_file_content, 0), else None
1103 * cache: if data is inlined, a tuple (index_file_content, 0), else None
1104 *
1104 *
1105 * added complications are for backwards compatibility
1105 * added complications are for backwards compatibility
1106 */
1106 */
1107 static PyObject *parse_index2(PyObject *self, PyObject *args)
1107 static PyObject *parse_index2(PyObject *self, PyObject *args)
1108 {
1108 {
1109 PyObject *tuple = NULL, *cache = NULL;
1109 PyObject *tuple = NULL, *cache = NULL;
1110 indexObject *idx;
1110 indexObject *idx;
1111 int ret;
1111 int ret;
1112
1112
1113 idx = PyObject_New(indexObject, &indexType);
1113 idx = PyObject_New(indexObject, &indexType);
1114 if (idx == NULL)
1114 if (idx == NULL)
1115 goto bail;
1115 goto bail;
1116
1116
1117 ret = index_init(idx, args);
1117 ret = index_init(idx, args);
1118 if (ret == -1)
1118 if (ret == -1)
1119 goto bail;
1119 goto bail;
1120
1120
1121 if (idx->inlined) {
1121 if (idx->inlined) {
1122 cache = Py_BuildValue("iO", 0, idx->data);
1122 cache = Py_BuildValue("iO", 0, idx->data);
1123 if (cache == NULL)
1123 if (cache == NULL)
1124 goto bail;
1124 goto bail;
1125 } else {
1125 } else {
1126 cache = Py_None;
1126 cache = Py_None;
1127 Py_INCREF(cache);
1127 Py_INCREF(cache);
1128 }
1128 }
1129
1129
1130 tuple = Py_BuildValue("NN", idx, cache);
1130 tuple = Py_BuildValue("NN", idx, cache);
1131 if (!tuple)
1131 if (!tuple)
1132 goto bail;
1132 goto bail;
1133 return tuple;
1133 return tuple;
1134
1134
1135 bail:
1135 bail:
1136 Py_XDECREF(idx);
1136 Py_XDECREF(idx);
1137 Py_XDECREF(cache);
1137 Py_XDECREF(cache);
1138 Py_XDECREF(tuple);
1138 Py_XDECREF(tuple);
1139 return NULL;
1139 return NULL;
1140 }
1140 }
1141
1141
1142 static char parsers_doc[] = "Efficient content parsing.";
1142 static char parsers_doc[] = "Efficient content parsing.";
1143
1143
1144 static PyMethodDef methods[] = {
1144 static PyMethodDef methods[] = {
1145 {"parse_manifest", parse_manifest, METH_VARARGS, "parse a manifest\n"},
1145 {"parse_manifest", parse_manifest, METH_VARARGS, "parse a manifest\n"},
1146 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
1146 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
1147 {"parse_index2", parse_index2, METH_VARARGS, "parse a revlog index\n"},
1147 {"parse_index2", parse_index2, METH_VARARGS, "parse a revlog index\n"},
1148 {NULL, NULL}
1148 {NULL, NULL}
1149 };
1149 };
1150
1150
1151 static void module_init(PyObject *mod)
1151 static void module_init(PyObject *mod)
1152 {
1152 {
1153 if (PyType_Ready(&indexType) < 0)
1153 if (PyType_Ready(&indexType) < 0)
1154 return;
1154 return;
1155 Py_INCREF(&indexType);
1155 Py_INCREF(&indexType);
1156
1156
1157 PyModule_AddObject(mod, "index", (PyObject *)&indexType);
1157 PyModule_AddObject(mod, "index", (PyObject *)&indexType);
1158
1158
1159 nullentry = Py_BuildValue("iiiiiiis#", 0, 0, 0,
1159 nullentry = Py_BuildValue("iiiiiiis#", 0, 0, 0,
1160 -1, -1, -1, -1, nullid, 20);
1160 -1, -1, -1, -1, nullid, 20);
1161 if (nullentry)
1161 if (nullentry)
1162 PyObject_GC_UnTrack(nullentry);
1162 PyObject_GC_UnTrack(nullentry);
1163 }
1163 }
1164
1164
1165 #ifdef IS_PY3K
1165 #ifdef IS_PY3K
1166 static struct PyModuleDef parsers_module = {
1166 static struct PyModuleDef parsers_module = {
1167 PyModuleDef_HEAD_INIT,
1167 PyModuleDef_HEAD_INIT,
1168 "parsers",
1168 "parsers",
1169 parsers_doc,
1169 parsers_doc,
1170 -1,
1170 -1,
1171 methods
1171 methods
1172 };
1172 };
1173
1173
1174 PyMODINIT_FUNC PyInit_parsers(void)
1174 PyMODINIT_FUNC PyInit_parsers(void)
1175 {
1175 {
1176 PyObject *mod = PyModule_Create(&parsers_module);
1176 PyObject *mod = PyModule_Create(&parsers_module);
1177 module_init(mod);
1177 module_init(mod);
1178 return mod;
1178 return mod;
1179 }
1179 }
1180 #else
1180 #else
1181 PyMODINIT_FUNC initparsers(void)
1181 PyMODINIT_FUNC initparsers(void)
1182 {
1182 {
1183 PyObject *mod = Py_InitModule3("parsers", methods, parsers_doc);
1183 PyObject *mod = Py_InitModule3("parsers", methods, parsers_doc);
1184 module_init(mod);
1184 module_init(mod);
1185 }
1185 }
1186 #endif
1186 #endif
@@ -1,74 +1,75 b''
1 # base85.py: pure python base85 codec
1 # base85.py: pure python base85 codec
2 #
2 #
3 # Copyright (C) 2009 Brendan Cully <brendan@kublai.com>
3 # Copyright (C) 2009 Brendan Cully <brendan@kublai.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 import struct
8 import struct
9
9
10 _b85chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
10 _b85chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
11 "abcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~"
11 "abcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~"
12 _b85chars2 = [(a + b) for a in _b85chars for b in _b85chars]
12 _b85chars2 = [(a + b) for a in _b85chars for b in _b85chars]
13 _b85dec = {}
13 _b85dec = {}
14
14
15 def _mkb85dec():
15 def _mkb85dec():
16 for i, c in enumerate(_b85chars):
16 for i, c in enumerate(_b85chars):
17 _b85dec[c] = i
17 _b85dec[c] = i
18
18
19 def b85encode(text, pad=False):
19 def b85encode(text, pad=False):
20 """encode text in base85 format"""
20 """encode text in base85 format"""
21 l = len(text)
21 l = len(text)
22 r = l % 4
22 r = l % 4
23 if r:
23 if r:
24 text += '\0' * (4 - r)
24 text += '\0' * (4 - r)
25 longs = len(text) >> 2
25 longs = len(text) >> 2
26 words = struct.unpack('>%dL' % (longs), text)
26 words = struct.unpack('>%dL' % (longs), text)
27
27
28 out = ''.join(_b85chars[(word // 52200625) % 85] +
28 out = ''.join(_b85chars[(word // 52200625) % 85] +
29 _b85chars2[(word // 7225) % 7225] +
29 _b85chars2[(word // 7225) % 7225] +
30 _b85chars2[word % 7225]
30 _b85chars2[word % 7225]
31 for word in words)
31 for word in words)
32
32
33 if pad:
33 if pad:
34 return out
34 return out
35
35
36 # Trim padding
36 # Trim padding
37 olen = l % 4
37 olen = l % 4
38 if olen:
38 if olen:
39 olen += 1
39 olen += 1
40 olen += l // 4 * 5
40 olen += l // 4 * 5
41 return out[:olen]
41 return out[:olen]
42
42
43 def b85decode(text):
43 def b85decode(text):
44 """decode base85-encoded text"""
44 """decode base85-encoded text"""
45 if not _b85dec:
45 if not _b85dec:
46 _mkb85dec()
46 _mkb85dec()
47
47
48 l = len(text)
48 l = len(text)
49 out = []
49 out = []
50 for i in range(0, len(text), 5):
50 for i in range(0, len(text), 5):
51 chunk = text[i:i + 5]
51 chunk = text[i:i + 5]
52 acc = 0
52 acc = 0
53 for j, c in enumerate(chunk):
53 for j, c in enumerate(chunk):
54 try:
54 try:
55 acc = acc * 85 + _b85dec[c]
55 acc = acc * 85 + _b85dec[c]
56 except KeyError:
56 except KeyError:
57 raise TypeError('Bad base85 character at byte %d' % (i + j))
57 raise ValueError('bad base85 character at position %d'
58 % (i + j))
58 if acc > 4294967295:
59 if acc > 4294967295:
59 raise OverflowError('Base85 overflow in hunk starting at byte %d' % i)
60 raise ValueError('Base85 overflow in hunk starting at byte %d' % i)
60 out.append(acc)
61 out.append(acc)
61
62
62 # Pad final chunk if necessary
63 # Pad final chunk if necessary
63 cl = l % 5
64 cl = l % 5
64 if cl:
65 if cl:
65 acc *= 85 ** (5 - cl)
66 acc *= 85 ** (5 - cl)
66 if cl > 1:
67 if cl > 1:
67 acc += 0xffffff >> (cl - 2) * 8
68 acc += 0xffffff >> (cl - 2) * 8
68 out[-1] = acc
69 out[-1] = acc
69
70
70 out = struct.pack('>%dL' % (len(out)), *out)
71 out = struct.pack('>%dL' % (len(out)), *out)
71 if cl:
72 if cl:
72 out = out[:-(5 - cl)]
73 out = out[:-(5 - cl)]
73
74
74 return out
75 return out
@@ -1,1234 +1,1242 b''
1 # subrepo.py - sub-repository handling for Mercurial
1 # subrepo.py - sub-repository handling for Mercurial
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 import errno, os, re, xml.dom.minidom, shutil, posixpath
8 import errno, os, re, xml.dom.minidom, shutil, posixpath
9 import stat, subprocess, tarfile
9 import stat, subprocess, tarfile
10 from i18n import _
10 from i18n import _
11 import config, scmutil, util, node, error, cmdutil, bookmarks
11 import config, scmutil, util, node, error, cmdutil, bookmarks
12 hg = None
12 hg = None
13 propertycache = util.propertycache
13 propertycache = util.propertycache
14
14
15 nullstate = ('', '', 'empty')
15 nullstate = ('', '', 'empty')
16
16
17 def state(ctx, ui):
17 def state(ctx, ui):
18 """return a state dict, mapping subrepo paths configured in .hgsub
18 """return a state dict, mapping subrepo paths configured in .hgsub
19 to tuple: (source from .hgsub, revision from .hgsubstate, kind
19 to tuple: (source from .hgsub, revision from .hgsubstate, kind
20 (key in types dict))
20 (key in types dict))
21 """
21 """
22 p = config.config()
22 p = config.config()
23 def read(f, sections=None, remap=None):
23 def read(f, sections=None, remap=None):
24 if f in ctx:
24 if f in ctx:
25 try:
25 try:
26 data = ctx[f].data()
26 data = ctx[f].data()
27 except IOError, err:
27 except IOError, err:
28 if err.errno != errno.ENOENT:
28 if err.errno != errno.ENOENT:
29 raise
29 raise
30 # handle missing subrepo spec files as removed
30 # handle missing subrepo spec files as removed
31 ui.warn(_("warning: subrepo spec file %s not found\n") % f)
31 ui.warn(_("warning: subrepo spec file %s not found\n") % f)
32 return
32 return
33 p.parse(f, data, sections, remap, read)
33 p.parse(f, data, sections, remap, read)
34 else:
34 else:
35 raise util.Abort(_("subrepo spec file %s not found") % f)
35 raise util.Abort(_("subrepo spec file %s not found") % f)
36
36
37 if '.hgsub' in ctx:
37 if '.hgsub' in ctx:
38 read('.hgsub')
38 read('.hgsub')
39
39
40 for path, src in ui.configitems('subpaths'):
40 for path, src in ui.configitems('subpaths'):
41 p.set('subpaths', path, src, ui.configsource('subpaths', path))
41 p.set('subpaths', path, src, ui.configsource('subpaths', path))
42
42
43 rev = {}
43 rev = {}
44 if '.hgsubstate' in ctx:
44 if '.hgsubstate' in ctx:
45 try:
45 try:
46 for l in ctx['.hgsubstate'].data().splitlines():
46 for i, l in enumerate(ctx['.hgsubstate'].data().splitlines()):
47 l = l.lstrip()
48 if not l:
49 continue
50 try:
47 revision, path = l.split(" ", 1)
51 revision, path = l.split(" ", 1)
52 except ValueError:
53 raise util.Abort(_("invalid subrepository revision "
54 "specifier in .hgsubstate line %d")
55 % (i + 1))
48 rev[path] = revision
56 rev[path] = revision
49 except IOError, err:
57 except IOError, err:
50 if err.errno != errno.ENOENT:
58 if err.errno != errno.ENOENT:
51 raise
59 raise
52
60
53 def remap(src):
61 def remap(src):
54 for pattern, repl in p.items('subpaths'):
62 for pattern, repl in p.items('subpaths'):
55 # Turn r'C:\foo\bar' into r'C:\\foo\\bar' since re.sub
63 # Turn r'C:\foo\bar' into r'C:\\foo\\bar' since re.sub
56 # does a string decode.
64 # does a string decode.
57 repl = repl.encode('string-escape')
65 repl = repl.encode('string-escape')
58 # However, we still want to allow back references to go
66 # However, we still want to allow back references to go
59 # through unharmed, so we turn r'\\1' into r'\1'. Again,
67 # through unharmed, so we turn r'\\1' into r'\1'. Again,
60 # extra escapes are needed because re.sub string decodes.
68 # extra escapes are needed because re.sub string decodes.
61 repl = re.sub(r'\\\\([0-9]+)', r'\\\1', repl)
69 repl = re.sub(r'\\\\([0-9]+)', r'\\\1', repl)
62 try:
70 try:
63 src = re.sub(pattern, repl, src, 1)
71 src = re.sub(pattern, repl, src, 1)
64 except re.error, e:
72 except re.error, e:
65 raise util.Abort(_("bad subrepository pattern in %s: %s")
73 raise util.Abort(_("bad subrepository pattern in %s: %s")
66 % (p.source('subpaths', pattern), e))
74 % (p.source('subpaths', pattern), e))
67 return src
75 return src
68
76
69 state = {}
77 state = {}
70 for path, src in p[''].items():
78 for path, src in p[''].items():
71 kind = 'hg'
79 kind = 'hg'
72 if src.startswith('['):
80 if src.startswith('['):
73 if ']' not in src:
81 if ']' not in src:
74 raise util.Abort(_('missing ] in subrepo source'))
82 raise util.Abort(_('missing ] in subrepo source'))
75 kind, src = src.split(']', 1)
83 kind, src = src.split(']', 1)
76 kind = kind[1:]
84 kind = kind[1:]
77 src = src.lstrip() # strip any extra whitespace after ']'
85 src = src.lstrip() # strip any extra whitespace after ']'
78
86
79 if not util.url(src).isabs():
87 if not util.url(src).isabs():
80 parent = _abssource(ctx._repo, abort=False)
88 parent = _abssource(ctx._repo, abort=False)
81 if parent:
89 if parent:
82 parent = util.url(parent)
90 parent = util.url(parent)
83 parent.path = posixpath.join(parent.path or '', src)
91 parent.path = posixpath.join(parent.path or '', src)
84 parent.path = posixpath.normpath(parent.path)
92 parent.path = posixpath.normpath(parent.path)
85 joined = str(parent)
93 joined = str(parent)
86 # Remap the full joined path and use it if it changes,
94 # Remap the full joined path and use it if it changes,
87 # else remap the original source.
95 # else remap the original source.
88 remapped = remap(joined)
96 remapped = remap(joined)
89 if remapped == joined:
97 if remapped == joined:
90 src = remap(src)
98 src = remap(src)
91 else:
99 else:
92 src = remapped
100 src = remapped
93
101
94 src = remap(src)
102 src = remap(src)
95 state[util.pconvert(path)] = (src.strip(), rev.get(path, ''), kind)
103 state[util.pconvert(path)] = (src.strip(), rev.get(path, ''), kind)
96
104
97 return state
105 return state
98
106
99 def writestate(repo, state):
107 def writestate(repo, state):
100 """rewrite .hgsubstate in (outer) repo with these subrepo states"""
108 """rewrite .hgsubstate in (outer) repo with these subrepo states"""
101 lines = ['%s %s\n' % (state[s][1], s) for s in sorted(state)]
109 lines = ['%s %s\n' % (state[s][1], s) for s in sorted(state)]
102 repo.wwrite('.hgsubstate', ''.join(lines), '')
110 repo.wwrite('.hgsubstate', ''.join(lines), '')
103
111
104 def submerge(repo, wctx, mctx, actx, overwrite):
112 def submerge(repo, wctx, mctx, actx, overwrite):
105 """delegated from merge.applyupdates: merging of .hgsubstate file
113 """delegated from merge.applyupdates: merging of .hgsubstate file
106 in working context, merging context and ancestor context"""
114 in working context, merging context and ancestor context"""
107 if mctx == actx: # backwards?
115 if mctx == actx: # backwards?
108 actx = wctx.p1()
116 actx = wctx.p1()
109 s1 = wctx.substate
117 s1 = wctx.substate
110 s2 = mctx.substate
118 s2 = mctx.substate
111 sa = actx.substate
119 sa = actx.substate
112 sm = {}
120 sm = {}
113
121
114 repo.ui.debug("subrepo merge %s %s %s\n" % (wctx, mctx, actx))
122 repo.ui.debug("subrepo merge %s %s %s\n" % (wctx, mctx, actx))
115
123
116 def debug(s, msg, r=""):
124 def debug(s, msg, r=""):
117 if r:
125 if r:
118 r = "%s:%s:%s" % r
126 r = "%s:%s:%s" % r
119 repo.ui.debug(" subrepo %s: %s %s\n" % (s, msg, r))
127 repo.ui.debug(" subrepo %s: %s %s\n" % (s, msg, r))
120
128
121 for s, l in s1.items():
129 for s, l in s1.items():
122 a = sa.get(s, nullstate)
130 a = sa.get(s, nullstate)
123 ld = l # local state with possible dirty flag for compares
131 ld = l # local state with possible dirty flag for compares
124 if wctx.sub(s).dirty():
132 if wctx.sub(s).dirty():
125 ld = (l[0], l[1] + "+")
133 ld = (l[0], l[1] + "+")
126 if wctx == actx: # overwrite
134 if wctx == actx: # overwrite
127 a = ld
135 a = ld
128
136
129 if s in s2:
137 if s in s2:
130 r = s2[s]
138 r = s2[s]
131 if ld == r or r == a: # no change or local is newer
139 if ld == r or r == a: # no change or local is newer
132 sm[s] = l
140 sm[s] = l
133 continue
141 continue
134 elif ld == a: # other side changed
142 elif ld == a: # other side changed
135 debug(s, "other changed, get", r)
143 debug(s, "other changed, get", r)
136 wctx.sub(s).get(r, overwrite)
144 wctx.sub(s).get(r, overwrite)
137 sm[s] = r
145 sm[s] = r
138 elif ld[0] != r[0]: # sources differ
146 elif ld[0] != r[0]: # sources differ
139 if repo.ui.promptchoice(
147 if repo.ui.promptchoice(
140 _(' subrepository sources for %s differ\n'
148 _(' subrepository sources for %s differ\n'
141 'use (l)ocal source (%s) or (r)emote source (%s)?')
149 'use (l)ocal source (%s) or (r)emote source (%s)?')
142 % (s, l[0], r[0]),
150 % (s, l[0], r[0]),
143 (_('&Local'), _('&Remote')), 0):
151 (_('&Local'), _('&Remote')), 0):
144 debug(s, "prompt changed, get", r)
152 debug(s, "prompt changed, get", r)
145 wctx.sub(s).get(r, overwrite)
153 wctx.sub(s).get(r, overwrite)
146 sm[s] = r
154 sm[s] = r
147 elif ld[1] == a[1]: # local side is unchanged
155 elif ld[1] == a[1]: # local side is unchanged
148 debug(s, "other side changed, get", r)
156 debug(s, "other side changed, get", r)
149 wctx.sub(s).get(r, overwrite)
157 wctx.sub(s).get(r, overwrite)
150 sm[s] = r
158 sm[s] = r
151 else:
159 else:
152 debug(s, "both sides changed, merge with", r)
160 debug(s, "both sides changed, merge with", r)
153 wctx.sub(s).merge(r)
161 wctx.sub(s).merge(r)
154 sm[s] = l
162 sm[s] = l
155 elif ld == a: # remote removed, local unchanged
163 elif ld == a: # remote removed, local unchanged
156 debug(s, "remote removed, remove")
164 debug(s, "remote removed, remove")
157 wctx.sub(s).remove()
165 wctx.sub(s).remove()
158 elif a == nullstate: # not present in remote or ancestor
166 elif a == nullstate: # not present in remote or ancestor
159 debug(s, "local added, keep")
167 debug(s, "local added, keep")
160 sm[s] = l
168 sm[s] = l
161 continue
169 continue
162 else:
170 else:
163 if repo.ui.promptchoice(
171 if repo.ui.promptchoice(
164 _(' local changed subrepository %s which remote removed\n'
172 _(' local changed subrepository %s which remote removed\n'
165 'use (c)hanged version or (d)elete?') % s,
173 'use (c)hanged version or (d)elete?') % s,
166 (_('&Changed'), _('&Delete')), 0):
174 (_('&Changed'), _('&Delete')), 0):
167 debug(s, "prompt remove")
175 debug(s, "prompt remove")
168 wctx.sub(s).remove()
176 wctx.sub(s).remove()
169
177
170 for s, r in sorted(s2.items()):
178 for s, r in sorted(s2.items()):
171 if s in s1:
179 if s in s1:
172 continue
180 continue
173 elif s not in sa:
181 elif s not in sa:
174 debug(s, "remote added, get", r)
182 debug(s, "remote added, get", r)
175 mctx.sub(s).get(r)
183 mctx.sub(s).get(r)
176 sm[s] = r
184 sm[s] = r
177 elif r != sa[s]:
185 elif r != sa[s]:
178 if repo.ui.promptchoice(
186 if repo.ui.promptchoice(
179 _(' remote changed subrepository %s which local removed\n'
187 _(' remote changed subrepository %s which local removed\n'
180 'use (c)hanged version or (d)elete?') % s,
188 'use (c)hanged version or (d)elete?') % s,
181 (_('&Changed'), _('&Delete')), 0) == 0:
189 (_('&Changed'), _('&Delete')), 0) == 0:
182 debug(s, "prompt recreate", r)
190 debug(s, "prompt recreate", r)
183 wctx.sub(s).get(r)
191 wctx.sub(s).get(r)
184 sm[s] = r
192 sm[s] = r
185
193
186 # record merged .hgsubstate
194 # record merged .hgsubstate
187 writestate(repo, sm)
195 writestate(repo, sm)
188
196
189 def _updateprompt(ui, sub, dirty, local, remote):
197 def _updateprompt(ui, sub, dirty, local, remote):
190 if dirty:
198 if dirty:
191 msg = (_(' subrepository sources for %s differ\n'
199 msg = (_(' subrepository sources for %s differ\n'
192 'use (l)ocal source (%s) or (r)emote source (%s)?\n')
200 'use (l)ocal source (%s) or (r)emote source (%s)?\n')
193 % (subrelpath(sub), local, remote))
201 % (subrelpath(sub), local, remote))
194 else:
202 else:
195 msg = (_(' subrepository sources for %s differ (in checked out version)\n'
203 msg = (_(' subrepository sources for %s differ (in checked out version)\n'
196 'use (l)ocal source (%s) or (r)emote source (%s)?\n')
204 'use (l)ocal source (%s) or (r)emote source (%s)?\n')
197 % (subrelpath(sub), local, remote))
205 % (subrelpath(sub), local, remote))
198 return ui.promptchoice(msg, (_('&Local'), _('&Remote')), 0)
206 return ui.promptchoice(msg, (_('&Local'), _('&Remote')), 0)
199
207
200 def reporelpath(repo):
208 def reporelpath(repo):
201 """return path to this (sub)repo as seen from outermost repo"""
209 """return path to this (sub)repo as seen from outermost repo"""
202 parent = repo
210 parent = repo
203 while util.safehasattr(parent, '_subparent'):
211 while util.safehasattr(parent, '_subparent'):
204 parent = parent._subparent
212 parent = parent._subparent
205 p = parent.root.rstrip(os.sep)
213 p = parent.root.rstrip(os.sep)
206 return repo.root[len(p) + 1:]
214 return repo.root[len(p) + 1:]
207
215
208 def subrelpath(sub):
216 def subrelpath(sub):
209 """return path to this subrepo as seen from outermost repo"""
217 """return path to this subrepo as seen from outermost repo"""
210 if util.safehasattr(sub, '_relpath'):
218 if util.safehasattr(sub, '_relpath'):
211 return sub._relpath
219 return sub._relpath
212 if not util.safehasattr(sub, '_repo'):
220 if not util.safehasattr(sub, '_repo'):
213 return sub._path
221 return sub._path
214 return reporelpath(sub._repo)
222 return reporelpath(sub._repo)
215
223
216 def _abssource(repo, push=False, abort=True):
224 def _abssource(repo, push=False, abort=True):
217 """return pull/push path of repo - either based on parent repo .hgsub info
225 """return pull/push path of repo - either based on parent repo .hgsub info
218 or on the top repo config. Abort or return None if no source found."""
226 or on the top repo config. Abort or return None if no source found."""
219 if util.safehasattr(repo, '_subparent'):
227 if util.safehasattr(repo, '_subparent'):
220 source = util.url(repo._subsource)
228 source = util.url(repo._subsource)
221 if source.isabs():
229 if source.isabs():
222 return str(source)
230 return str(source)
223 source.path = posixpath.normpath(source.path)
231 source.path = posixpath.normpath(source.path)
224 parent = _abssource(repo._subparent, push, abort=False)
232 parent = _abssource(repo._subparent, push, abort=False)
225 if parent:
233 if parent:
226 parent = util.url(util.pconvert(parent))
234 parent = util.url(util.pconvert(parent))
227 parent.path = posixpath.join(parent.path or '', source.path)
235 parent.path = posixpath.join(parent.path or '', source.path)
228 parent.path = posixpath.normpath(parent.path)
236 parent.path = posixpath.normpath(parent.path)
229 return str(parent)
237 return str(parent)
230 else: # recursion reached top repo
238 else: # recursion reached top repo
231 if util.safehasattr(repo, '_subtoppath'):
239 if util.safehasattr(repo, '_subtoppath'):
232 return repo._subtoppath
240 return repo._subtoppath
233 if push and repo.ui.config('paths', 'default-push'):
241 if push and repo.ui.config('paths', 'default-push'):
234 return repo.ui.config('paths', 'default-push')
242 return repo.ui.config('paths', 'default-push')
235 if repo.ui.config('paths', 'default'):
243 if repo.ui.config('paths', 'default'):
236 return repo.ui.config('paths', 'default')
244 return repo.ui.config('paths', 'default')
237 if abort:
245 if abort:
238 raise util.Abort(_("default path for subrepository %s not found") %
246 raise util.Abort(_("default path for subrepository %s not found") %
239 reporelpath(repo))
247 reporelpath(repo))
240
248
241 def itersubrepos(ctx1, ctx2):
249 def itersubrepos(ctx1, ctx2):
242 """find subrepos in ctx1 or ctx2"""
250 """find subrepos in ctx1 or ctx2"""
243 # Create a (subpath, ctx) mapping where we prefer subpaths from
251 # Create a (subpath, ctx) mapping where we prefer subpaths from
244 # ctx1. The subpaths from ctx2 are important when the .hgsub file
252 # ctx1. The subpaths from ctx2 are important when the .hgsub file
245 # has been modified (in ctx2) but not yet committed (in ctx1).
253 # has been modified (in ctx2) but not yet committed (in ctx1).
246 subpaths = dict.fromkeys(ctx2.substate, ctx2)
254 subpaths = dict.fromkeys(ctx2.substate, ctx2)
247 subpaths.update(dict.fromkeys(ctx1.substate, ctx1))
255 subpaths.update(dict.fromkeys(ctx1.substate, ctx1))
248 for subpath, ctx in sorted(subpaths.iteritems()):
256 for subpath, ctx in sorted(subpaths.iteritems()):
249 yield subpath, ctx.sub(subpath)
257 yield subpath, ctx.sub(subpath)
250
258
251 def subrepo(ctx, path):
259 def subrepo(ctx, path):
252 """return instance of the right subrepo class for subrepo in path"""
260 """return instance of the right subrepo class for subrepo in path"""
253 # subrepo inherently violates our import layering rules
261 # subrepo inherently violates our import layering rules
254 # because it wants to make repo objects from deep inside the stack
262 # because it wants to make repo objects from deep inside the stack
255 # so we manually delay the circular imports to not break
263 # so we manually delay the circular imports to not break
256 # scripts that don't use our demand-loading
264 # scripts that don't use our demand-loading
257 global hg
265 global hg
258 import hg as h
266 import hg as h
259 hg = h
267 hg = h
260
268
261 scmutil.pathauditor(ctx._repo.root)(path)
269 scmutil.pathauditor(ctx._repo.root)(path)
262 state = ctx.substate.get(path, nullstate)
270 state = ctx.substate.get(path, nullstate)
263 if state[2] not in types:
271 if state[2] not in types:
264 raise util.Abort(_('unknown subrepo type %s') % state[2])
272 raise util.Abort(_('unknown subrepo type %s') % state[2])
265 return types[state[2]](ctx, path, state[:2])
273 return types[state[2]](ctx, path, state[:2])
266
274
267 # subrepo classes need to implement the following abstract class:
275 # subrepo classes need to implement the following abstract class:
268
276
269 class abstractsubrepo(object):
277 class abstractsubrepo(object):
270
278
271 def dirty(self, ignoreupdate=False):
279 def dirty(self, ignoreupdate=False):
272 """returns true if the dirstate of the subrepo is dirty or does not
280 """returns true if the dirstate of the subrepo is dirty or does not
273 match current stored state. If ignoreupdate is true, only check
281 match current stored state. If ignoreupdate is true, only check
274 whether the subrepo has uncommitted changes in its dirstate.
282 whether the subrepo has uncommitted changes in its dirstate.
275 """
283 """
276 raise NotImplementedError
284 raise NotImplementedError
277
285
278 def basestate(self):
286 def basestate(self):
279 """current working directory base state, disregarding .hgsubstate
287 """current working directory base state, disregarding .hgsubstate
280 state and working directory modifications"""
288 state and working directory modifications"""
281 raise NotImplementedError
289 raise NotImplementedError
282
290
283 def checknested(self, path):
291 def checknested(self, path):
284 """check if path is a subrepository within this repository"""
292 """check if path is a subrepository within this repository"""
285 return False
293 return False
286
294
287 def commit(self, text, user, date):
295 def commit(self, text, user, date):
288 """commit the current changes to the subrepo with the given
296 """commit the current changes to the subrepo with the given
289 log message. Use given user and date if possible. Return the
297 log message. Use given user and date if possible. Return the
290 new state of the subrepo.
298 new state of the subrepo.
291 """
299 """
292 raise NotImplementedError
300 raise NotImplementedError
293
301
294 def remove(self):
302 def remove(self):
295 """remove the subrepo
303 """remove the subrepo
296
304
297 (should verify the dirstate is not dirty first)
305 (should verify the dirstate is not dirty first)
298 """
306 """
299 raise NotImplementedError
307 raise NotImplementedError
300
308
301 def get(self, state, overwrite=False):
309 def get(self, state, overwrite=False):
302 """run whatever commands are needed to put the subrepo into
310 """run whatever commands are needed to put the subrepo into
303 this state
311 this state
304 """
312 """
305 raise NotImplementedError
313 raise NotImplementedError
306
314
307 def merge(self, state):
315 def merge(self, state):
308 """merge currently-saved state with the new state."""
316 """merge currently-saved state with the new state."""
309 raise NotImplementedError
317 raise NotImplementedError
310
318
311 def push(self, opts):
319 def push(self, opts):
312 """perform whatever action is analogous to 'hg push'
320 """perform whatever action is analogous to 'hg push'
313
321
314 This may be a no-op on some systems.
322 This may be a no-op on some systems.
315 """
323 """
316 raise NotImplementedError
324 raise NotImplementedError
317
325
318 def add(self, ui, match, dryrun, listsubrepos, prefix, explicitonly):
326 def add(self, ui, match, dryrun, listsubrepos, prefix, explicitonly):
319 return []
327 return []
320
328
321 def status(self, rev2, **opts):
329 def status(self, rev2, **opts):
322 return [], [], [], [], [], [], []
330 return [], [], [], [], [], [], []
323
331
324 def diff(self, diffopts, node2, match, prefix, **opts):
332 def diff(self, diffopts, node2, match, prefix, **opts):
325 pass
333 pass
326
334
327 def outgoing(self, ui, dest, opts):
335 def outgoing(self, ui, dest, opts):
328 return 1
336 return 1
329
337
330 def incoming(self, ui, source, opts):
338 def incoming(self, ui, source, opts):
331 return 1
339 return 1
332
340
333 def files(self):
341 def files(self):
334 """return filename iterator"""
342 """return filename iterator"""
335 raise NotImplementedError
343 raise NotImplementedError
336
344
337 def filedata(self, name):
345 def filedata(self, name):
338 """return file data"""
346 """return file data"""
339 raise NotImplementedError
347 raise NotImplementedError
340
348
341 def fileflags(self, name):
349 def fileflags(self, name):
342 """return file flags"""
350 """return file flags"""
343 return ''
351 return ''
344
352
345 def archive(self, ui, archiver, prefix):
353 def archive(self, ui, archiver, prefix):
346 files = self.files()
354 files = self.files()
347 total = len(files)
355 total = len(files)
348 relpath = subrelpath(self)
356 relpath = subrelpath(self)
349 ui.progress(_('archiving (%s)') % relpath, 0,
357 ui.progress(_('archiving (%s)') % relpath, 0,
350 unit=_('files'), total=total)
358 unit=_('files'), total=total)
351 for i, name in enumerate(files):
359 for i, name in enumerate(files):
352 flags = self.fileflags(name)
360 flags = self.fileflags(name)
353 mode = 'x' in flags and 0755 or 0644
361 mode = 'x' in flags and 0755 or 0644
354 symlink = 'l' in flags
362 symlink = 'l' in flags
355 archiver.addfile(os.path.join(prefix, self._path, name),
363 archiver.addfile(os.path.join(prefix, self._path, name),
356 mode, symlink, self.filedata(name))
364 mode, symlink, self.filedata(name))
357 ui.progress(_('archiving (%s)') % relpath, i + 1,
365 ui.progress(_('archiving (%s)') % relpath, i + 1,
358 unit=_('files'), total=total)
366 unit=_('files'), total=total)
359 ui.progress(_('archiving (%s)') % relpath, None)
367 ui.progress(_('archiving (%s)') % relpath, None)
360
368
361 def walk(self, match):
369 def walk(self, match):
362 '''
370 '''
363 walk recursively through the directory tree, finding all files
371 walk recursively through the directory tree, finding all files
364 matched by the match function
372 matched by the match function
365 '''
373 '''
366 pass
374 pass
367
375
368 def forget(self, ui, match, prefix):
376 def forget(self, ui, match, prefix):
369 return ([], [])
377 return ([], [])
370
378
371 def revert(self, ui, substate, *pats, **opts):
379 def revert(self, ui, substate, *pats, **opts):
372 ui.warn('%s: reverting %s subrepos is unsupported\n' \
380 ui.warn('%s: reverting %s subrepos is unsupported\n' \
373 % (substate[0], substate[2]))
381 % (substate[0], substate[2]))
374 return []
382 return []
375
383
376 class hgsubrepo(abstractsubrepo):
384 class hgsubrepo(abstractsubrepo):
377 def __init__(self, ctx, path, state):
385 def __init__(self, ctx, path, state):
378 self._path = path
386 self._path = path
379 self._state = state
387 self._state = state
380 r = ctx._repo
388 r = ctx._repo
381 root = r.wjoin(path)
389 root = r.wjoin(path)
382 create = False
390 create = False
383 if not os.path.exists(os.path.join(root, '.hg')):
391 if not os.path.exists(os.path.join(root, '.hg')):
384 create = True
392 create = True
385 util.makedirs(root)
393 util.makedirs(root)
386 self._repo = hg.repository(r.ui, root, create=create)
394 self._repo = hg.repository(r.ui, root, create=create)
387 self._initrepo(r, state[0], create)
395 self._initrepo(r, state[0], create)
388
396
389 def _initrepo(self, parentrepo, source, create):
397 def _initrepo(self, parentrepo, source, create):
390 self._repo._subparent = parentrepo
398 self._repo._subparent = parentrepo
391 self._repo._subsource = source
399 self._repo._subsource = source
392
400
393 if create:
401 if create:
394 fp = self._repo.opener("hgrc", "w", text=True)
402 fp = self._repo.opener("hgrc", "w", text=True)
395 fp.write('[paths]\n')
403 fp.write('[paths]\n')
396
404
397 def addpathconfig(key, value):
405 def addpathconfig(key, value):
398 if value:
406 if value:
399 fp.write('%s = %s\n' % (key, value))
407 fp.write('%s = %s\n' % (key, value))
400 self._repo.ui.setconfig('paths', key, value)
408 self._repo.ui.setconfig('paths', key, value)
401
409
402 defpath = _abssource(self._repo, abort=False)
410 defpath = _abssource(self._repo, abort=False)
403 defpushpath = _abssource(self._repo, True, abort=False)
411 defpushpath = _abssource(self._repo, True, abort=False)
404 addpathconfig('default', defpath)
412 addpathconfig('default', defpath)
405 if defpath != defpushpath:
413 if defpath != defpushpath:
406 addpathconfig('default-push', defpushpath)
414 addpathconfig('default-push', defpushpath)
407 fp.close()
415 fp.close()
408
416
409 def add(self, ui, match, dryrun, listsubrepos, prefix, explicitonly):
417 def add(self, ui, match, dryrun, listsubrepos, prefix, explicitonly):
410 return cmdutil.add(ui, self._repo, match, dryrun, listsubrepos,
418 return cmdutil.add(ui, self._repo, match, dryrun, listsubrepos,
411 os.path.join(prefix, self._path), explicitonly)
419 os.path.join(prefix, self._path), explicitonly)
412
420
413 def status(self, rev2, **opts):
421 def status(self, rev2, **opts):
414 try:
422 try:
415 rev1 = self._state[1]
423 rev1 = self._state[1]
416 ctx1 = self._repo[rev1]
424 ctx1 = self._repo[rev1]
417 ctx2 = self._repo[rev2]
425 ctx2 = self._repo[rev2]
418 return self._repo.status(ctx1, ctx2, **opts)
426 return self._repo.status(ctx1, ctx2, **opts)
419 except error.RepoLookupError, inst:
427 except error.RepoLookupError, inst:
420 self._repo.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
428 self._repo.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
421 % (inst, subrelpath(self)))
429 % (inst, subrelpath(self)))
422 return [], [], [], [], [], [], []
430 return [], [], [], [], [], [], []
423
431
424 def diff(self, diffopts, node2, match, prefix, **opts):
432 def diff(self, diffopts, node2, match, prefix, **opts):
425 try:
433 try:
426 node1 = node.bin(self._state[1])
434 node1 = node.bin(self._state[1])
427 # We currently expect node2 to come from substate and be
435 # We currently expect node2 to come from substate and be
428 # in hex format
436 # in hex format
429 if node2 is not None:
437 if node2 is not None:
430 node2 = node.bin(node2)
438 node2 = node.bin(node2)
431 cmdutil.diffordiffstat(self._repo.ui, self._repo, diffopts,
439 cmdutil.diffordiffstat(self._repo.ui, self._repo, diffopts,
432 node1, node2, match,
440 node1, node2, match,
433 prefix=os.path.join(prefix, self._path),
441 prefix=os.path.join(prefix, self._path),
434 listsubrepos=True, **opts)
442 listsubrepos=True, **opts)
435 except error.RepoLookupError, inst:
443 except error.RepoLookupError, inst:
436 self._repo.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
444 self._repo.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
437 % (inst, subrelpath(self)))
445 % (inst, subrelpath(self)))
438
446
439 def archive(self, ui, archiver, prefix):
447 def archive(self, ui, archiver, prefix):
440 self._get(self._state + ('hg',))
448 self._get(self._state + ('hg',))
441 abstractsubrepo.archive(self, ui, archiver, prefix)
449 abstractsubrepo.archive(self, ui, archiver, prefix)
442
450
443 rev = self._state[1]
451 rev = self._state[1]
444 ctx = self._repo[rev]
452 ctx = self._repo[rev]
445 for subpath in ctx.substate:
453 for subpath in ctx.substate:
446 s = subrepo(ctx, subpath)
454 s = subrepo(ctx, subpath)
447 s.archive(ui, archiver, os.path.join(prefix, self._path))
455 s.archive(ui, archiver, os.path.join(prefix, self._path))
448
456
449 def dirty(self, ignoreupdate=False):
457 def dirty(self, ignoreupdate=False):
450 r = self._state[1]
458 r = self._state[1]
451 if r == '' and not ignoreupdate: # no state recorded
459 if r == '' and not ignoreupdate: # no state recorded
452 return True
460 return True
453 w = self._repo[None]
461 w = self._repo[None]
454 if r != w.p1().hex() and not ignoreupdate:
462 if r != w.p1().hex() and not ignoreupdate:
455 # different version checked out
463 # different version checked out
456 return True
464 return True
457 return w.dirty() # working directory changed
465 return w.dirty() # working directory changed
458
466
459 def basestate(self):
467 def basestate(self):
460 return self._repo['.'].hex()
468 return self._repo['.'].hex()
461
469
462 def checknested(self, path):
470 def checknested(self, path):
463 return self._repo._checknested(self._repo.wjoin(path))
471 return self._repo._checknested(self._repo.wjoin(path))
464
472
465 def commit(self, text, user, date):
473 def commit(self, text, user, date):
466 # don't bother committing in the subrepo if it's only been
474 # don't bother committing in the subrepo if it's only been
467 # updated
475 # updated
468 if not self.dirty(True):
476 if not self.dirty(True):
469 return self._repo['.'].hex()
477 return self._repo['.'].hex()
470 self._repo.ui.debug("committing subrepo %s\n" % subrelpath(self))
478 self._repo.ui.debug("committing subrepo %s\n" % subrelpath(self))
471 n = self._repo.commit(text, user, date)
479 n = self._repo.commit(text, user, date)
472 if not n:
480 if not n:
473 return self._repo['.'].hex() # different version checked out
481 return self._repo['.'].hex() # different version checked out
474 return node.hex(n)
482 return node.hex(n)
475
483
476 def remove(self):
484 def remove(self):
477 # we can't fully delete the repository as it may contain
485 # we can't fully delete the repository as it may contain
478 # local-only history
486 # local-only history
479 self._repo.ui.note(_('removing subrepo %s\n') % subrelpath(self))
487 self._repo.ui.note(_('removing subrepo %s\n') % subrelpath(self))
480 hg.clean(self._repo, node.nullid, False)
488 hg.clean(self._repo, node.nullid, False)
481
489
482 def _get(self, state):
490 def _get(self, state):
483 source, revision, kind = state
491 source, revision, kind = state
484 if revision not in self._repo:
492 if revision not in self._repo:
485 self._repo._subsource = source
493 self._repo._subsource = source
486 srcurl = _abssource(self._repo)
494 srcurl = _abssource(self._repo)
487 other = hg.peer(self._repo.ui, {}, srcurl)
495 other = hg.peer(self._repo.ui, {}, srcurl)
488 if len(self._repo) == 0:
496 if len(self._repo) == 0:
489 self._repo.ui.status(_('cloning subrepo %s from %s\n')
497 self._repo.ui.status(_('cloning subrepo %s from %s\n')
490 % (subrelpath(self), srcurl))
498 % (subrelpath(self), srcurl))
491 parentrepo = self._repo._subparent
499 parentrepo = self._repo._subparent
492 shutil.rmtree(self._repo.path)
500 shutil.rmtree(self._repo.path)
493 other, self._repo = hg.clone(self._repo._subparent.ui, {}, other,
501 other, self._repo = hg.clone(self._repo._subparent.ui, {}, other,
494 self._repo.root, update=False)
502 self._repo.root, update=False)
495 self._initrepo(parentrepo, source, create=True)
503 self._initrepo(parentrepo, source, create=True)
496 else:
504 else:
497 self._repo.ui.status(_('pulling subrepo %s from %s\n')
505 self._repo.ui.status(_('pulling subrepo %s from %s\n')
498 % (subrelpath(self), srcurl))
506 % (subrelpath(self), srcurl))
499 self._repo.pull(other)
507 self._repo.pull(other)
500 bookmarks.updatefromremote(self._repo.ui, self._repo, other,
508 bookmarks.updatefromremote(self._repo.ui, self._repo, other,
501 srcurl)
509 srcurl)
502
510
503 def get(self, state, overwrite=False):
511 def get(self, state, overwrite=False):
504 self._get(state)
512 self._get(state)
505 source, revision, kind = state
513 source, revision, kind = state
506 self._repo.ui.debug("getting subrepo %s\n" % self._path)
514 self._repo.ui.debug("getting subrepo %s\n" % self._path)
507 hg.clean(self._repo, revision, False)
515 hg.clean(self._repo, revision, False)
508
516
509 def merge(self, state):
517 def merge(self, state):
510 self._get(state)
518 self._get(state)
511 cur = self._repo['.']
519 cur = self._repo['.']
512 dst = self._repo[state[1]]
520 dst = self._repo[state[1]]
513 anc = dst.ancestor(cur)
521 anc = dst.ancestor(cur)
514
522
515 def mergefunc():
523 def mergefunc():
516 if anc == cur and dst.branch() == cur.branch():
524 if anc == cur and dst.branch() == cur.branch():
517 self._repo.ui.debug("updating subrepo %s\n" % subrelpath(self))
525 self._repo.ui.debug("updating subrepo %s\n" % subrelpath(self))
518 hg.update(self._repo, state[1])
526 hg.update(self._repo, state[1])
519 elif anc == dst:
527 elif anc == dst:
520 self._repo.ui.debug("skipping subrepo %s\n" % subrelpath(self))
528 self._repo.ui.debug("skipping subrepo %s\n" % subrelpath(self))
521 else:
529 else:
522 self._repo.ui.debug("merging subrepo %s\n" % subrelpath(self))
530 self._repo.ui.debug("merging subrepo %s\n" % subrelpath(self))
523 hg.merge(self._repo, state[1], remind=False)
531 hg.merge(self._repo, state[1], remind=False)
524
532
525 wctx = self._repo[None]
533 wctx = self._repo[None]
526 if self.dirty():
534 if self.dirty():
527 if anc != dst:
535 if anc != dst:
528 if _updateprompt(self._repo.ui, self, wctx.dirty(), cur, dst):
536 if _updateprompt(self._repo.ui, self, wctx.dirty(), cur, dst):
529 mergefunc()
537 mergefunc()
530 else:
538 else:
531 mergefunc()
539 mergefunc()
532 else:
540 else:
533 mergefunc()
541 mergefunc()
534
542
535 def push(self, opts):
543 def push(self, opts):
536 force = opts.get('force')
544 force = opts.get('force')
537 newbranch = opts.get('new_branch')
545 newbranch = opts.get('new_branch')
538 ssh = opts.get('ssh')
546 ssh = opts.get('ssh')
539
547
540 # push subrepos depth-first for coherent ordering
548 # push subrepos depth-first for coherent ordering
541 c = self._repo['']
549 c = self._repo['']
542 subs = c.substate # only repos that are committed
550 subs = c.substate # only repos that are committed
543 for s in sorted(subs):
551 for s in sorted(subs):
544 if c.sub(s).push(opts) == 0:
552 if c.sub(s).push(opts) == 0:
545 return False
553 return False
546
554
547 dsturl = _abssource(self._repo, True)
555 dsturl = _abssource(self._repo, True)
548 self._repo.ui.status(_('pushing subrepo %s to %s\n') %
556 self._repo.ui.status(_('pushing subrepo %s to %s\n') %
549 (subrelpath(self), dsturl))
557 (subrelpath(self), dsturl))
550 other = hg.peer(self._repo.ui, {'ssh': ssh}, dsturl)
558 other = hg.peer(self._repo.ui, {'ssh': ssh}, dsturl)
551 return self._repo.push(other, force, newbranch=newbranch)
559 return self._repo.push(other, force, newbranch=newbranch)
552
560
553 def outgoing(self, ui, dest, opts):
561 def outgoing(self, ui, dest, opts):
554 return hg.outgoing(ui, self._repo, _abssource(self._repo, True), opts)
562 return hg.outgoing(ui, self._repo, _abssource(self._repo, True), opts)
555
563
556 def incoming(self, ui, source, opts):
564 def incoming(self, ui, source, opts):
557 return hg.incoming(ui, self._repo, _abssource(self._repo, False), opts)
565 return hg.incoming(ui, self._repo, _abssource(self._repo, False), opts)
558
566
559 def files(self):
567 def files(self):
560 rev = self._state[1]
568 rev = self._state[1]
561 ctx = self._repo[rev]
569 ctx = self._repo[rev]
562 return ctx.manifest()
570 return ctx.manifest()
563
571
564 def filedata(self, name):
572 def filedata(self, name):
565 rev = self._state[1]
573 rev = self._state[1]
566 return self._repo[rev][name].data()
574 return self._repo[rev][name].data()
567
575
568 def fileflags(self, name):
576 def fileflags(self, name):
569 rev = self._state[1]
577 rev = self._state[1]
570 ctx = self._repo[rev]
578 ctx = self._repo[rev]
571 return ctx.flags(name)
579 return ctx.flags(name)
572
580
573 def walk(self, match):
581 def walk(self, match):
574 ctx = self._repo[None]
582 ctx = self._repo[None]
575 return ctx.walk(match)
583 return ctx.walk(match)
576
584
577 def forget(self, ui, match, prefix):
585 def forget(self, ui, match, prefix):
578 return cmdutil.forget(ui, self._repo, match,
586 return cmdutil.forget(ui, self._repo, match,
579 os.path.join(prefix, self._path), True)
587 os.path.join(prefix, self._path), True)
580
588
581 def revert(self, ui, substate, *pats, **opts):
589 def revert(self, ui, substate, *pats, **opts):
582 # reverting a subrepo is a 2 step process:
590 # reverting a subrepo is a 2 step process:
583 # 1. if the no_backup is not set, revert all modified
591 # 1. if the no_backup is not set, revert all modified
584 # files inside the subrepo
592 # files inside the subrepo
585 # 2. update the subrepo to the revision specified in
593 # 2. update the subrepo to the revision specified in
586 # the corresponding substate dictionary
594 # the corresponding substate dictionary
587 ui.status(_('reverting subrepo %s\n') % substate[0])
595 ui.status(_('reverting subrepo %s\n') % substate[0])
588 if not opts.get('no_backup'):
596 if not opts.get('no_backup'):
589 # Revert all files on the subrepo, creating backups
597 # Revert all files on the subrepo, creating backups
590 # Note that this will not recursively revert subrepos
598 # Note that this will not recursively revert subrepos
591 # We could do it if there was a set:subrepos() predicate
599 # We could do it if there was a set:subrepos() predicate
592 opts = opts.copy()
600 opts = opts.copy()
593 opts['date'] = None
601 opts['date'] = None
594 opts['rev'] = substate[1]
602 opts['rev'] = substate[1]
595
603
596 pats = []
604 pats = []
597 if not opts['all']:
605 if not opts['all']:
598 pats = ['set:modified()']
606 pats = ['set:modified()']
599 self.filerevert(ui, *pats, **opts)
607 self.filerevert(ui, *pats, **opts)
600
608
601 # Update the repo to the revision specified in the given substate
609 # Update the repo to the revision specified in the given substate
602 self.get(substate, overwrite=True)
610 self.get(substate, overwrite=True)
603
611
604 def filerevert(self, ui, *pats, **opts):
612 def filerevert(self, ui, *pats, **opts):
605 ctx = self._repo[opts['rev']]
613 ctx = self._repo[opts['rev']]
606 parents = self._repo.dirstate.parents()
614 parents = self._repo.dirstate.parents()
607 if opts['all']:
615 if opts['all']:
608 pats = ['set:modified()']
616 pats = ['set:modified()']
609 else:
617 else:
610 pats = []
618 pats = []
611 cmdutil.revert(ui, self._repo, ctx, parents, *pats, **opts)
619 cmdutil.revert(ui, self._repo, ctx, parents, *pats, **opts)
612
620
613 class svnsubrepo(abstractsubrepo):
621 class svnsubrepo(abstractsubrepo):
614 def __init__(self, ctx, path, state):
622 def __init__(self, ctx, path, state):
615 self._path = path
623 self._path = path
616 self._state = state
624 self._state = state
617 self._ctx = ctx
625 self._ctx = ctx
618 self._ui = ctx._repo.ui
626 self._ui = ctx._repo.ui
619 self._exe = util.findexe('svn')
627 self._exe = util.findexe('svn')
620 if not self._exe:
628 if not self._exe:
621 raise util.Abort(_("'svn' executable not found for subrepo '%s'")
629 raise util.Abort(_("'svn' executable not found for subrepo '%s'")
622 % self._path)
630 % self._path)
623
631
624 def _svncommand(self, commands, filename='', failok=False):
632 def _svncommand(self, commands, filename='', failok=False):
625 cmd = [self._exe]
633 cmd = [self._exe]
626 extrakw = {}
634 extrakw = {}
627 if not self._ui.interactive():
635 if not self._ui.interactive():
628 # Making stdin be a pipe should prevent svn from behaving
636 # Making stdin be a pipe should prevent svn from behaving
629 # interactively even if we can't pass --non-interactive.
637 # interactively even if we can't pass --non-interactive.
630 extrakw['stdin'] = subprocess.PIPE
638 extrakw['stdin'] = subprocess.PIPE
631 # Starting in svn 1.5 --non-interactive is a global flag
639 # Starting in svn 1.5 --non-interactive is a global flag
632 # instead of being per-command, but we need to support 1.4 so
640 # instead of being per-command, but we need to support 1.4 so
633 # we have to be intelligent about what commands take
641 # we have to be intelligent about what commands take
634 # --non-interactive.
642 # --non-interactive.
635 if commands[0] in ('update', 'checkout', 'commit'):
643 if commands[0] in ('update', 'checkout', 'commit'):
636 cmd.append('--non-interactive')
644 cmd.append('--non-interactive')
637 cmd.extend(commands)
645 cmd.extend(commands)
638 if filename is not None:
646 if filename is not None:
639 path = os.path.join(self._ctx._repo.origroot, self._path, filename)
647 path = os.path.join(self._ctx._repo.origroot, self._path, filename)
640 cmd.append(path)
648 cmd.append(path)
641 env = dict(os.environ)
649 env = dict(os.environ)
642 # Avoid localized output, preserve current locale for everything else.
650 # Avoid localized output, preserve current locale for everything else.
643 env['LC_MESSAGES'] = 'C'
651 env['LC_MESSAGES'] = 'C'
644 p = subprocess.Popen(cmd, bufsize=-1, close_fds=util.closefds,
652 p = subprocess.Popen(cmd, bufsize=-1, close_fds=util.closefds,
645 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
653 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
646 universal_newlines=True, env=env, **extrakw)
654 universal_newlines=True, env=env, **extrakw)
647 stdout, stderr = p.communicate()
655 stdout, stderr = p.communicate()
648 stderr = stderr.strip()
656 stderr = stderr.strip()
649 if not failok:
657 if not failok:
650 if p.returncode:
658 if p.returncode:
651 raise util.Abort(stderr or 'exited with code %d' % p.returncode)
659 raise util.Abort(stderr or 'exited with code %d' % p.returncode)
652 if stderr:
660 if stderr:
653 self._ui.warn(stderr + '\n')
661 self._ui.warn(stderr + '\n')
654 return stdout, stderr
662 return stdout, stderr
655
663
656 @propertycache
664 @propertycache
657 def _svnversion(self):
665 def _svnversion(self):
658 output, err = self._svncommand(['--version'], filename=None)
666 output, err = self._svncommand(['--version'], filename=None)
659 m = re.search(r'^svn,\s+version\s+(\d+)\.(\d+)', output)
667 m = re.search(r'^svn,\s+version\s+(\d+)\.(\d+)', output)
660 if not m:
668 if not m:
661 raise util.Abort(_('cannot retrieve svn tool version'))
669 raise util.Abort(_('cannot retrieve svn tool version'))
662 return (int(m.group(1)), int(m.group(2)))
670 return (int(m.group(1)), int(m.group(2)))
663
671
664 def _wcrevs(self):
672 def _wcrevs(self):
665 # Get the working directory revision as well as the last
673 # Get the working directory revision as well as the last
666 # commit revision so we can compare the subrepo state with
674 # commit revision so we can compare the subrepo state with
667 # both. We used to store the working directory one.
675 # both. We used to store the working directory one.
668 output, err = self._svncommand(['info', '--xml'])
676 output, err = self._svncommand(['info', '--xml'])
669 doc = xml.dom.minidom.parseString(output)
677 doc = xml.dom.minidom.parseString(output)
670 entries = doc.getElementsByTagName('entry')
678 entries = doc.getElementsByTagName('entry')
671 lastrev, rev = '0', '0'
679 lastrev, rev = '0', '0'
672 if entries:
680 if entries:
673 rev = str(entries[0].getAttribute('revision')) or '0'
681 rev = str(entries[0].getAttribute('revision')) or '0'
674 commits = entries[0].getElementsByTagName('commit')
682 commits = entries[0].getElementsByTagName('commit')
675 if commits:
683 if commits:
676 lastrev = str(commits[0].getAttribute('revision')) or '0'
684 lastrev = str(commits[0].getAttribute('revision')) or '0'
677 return (lastrev, rev)
685 return (lastrev, rev)
678
686
679 def _wcrev(self):
687 def _wcrev(self):
680 return self._wcrevs()[0]
688 return self._wcrevs()[0]
681
689
682 def _wcchanged(self):
690 def _wcchanged(self):
683 """Return (changes, extchanges, missing) where changes is True
691 """Return (changes, extchanges, missing) where changes is True
684 if the working directory was changed, extchanges is
692 if the working directory was changed, extchanges is
685 True if any of these changes concern an external entry and missing
693 True if any of these changes concern an external entry and missing
686 is True if any change is a missing entry.
694 is True if any change is a missing entry.
687 """
695 """
688 output, err = self._svncommand(['status', '--xml'])
696 output, err = self._svncommand(['status', '--xml'])
689 externals, changes, missing = [], [], []
697 externals, changes, missing = [], [], []
690 doc = xml.dom.minidom.parseString(output)
698 doc = xml.dom.minidom.parseString(output)
691 for e in doc.getElementsByTagName('entry'):
699 for e in doc.getElementsByTagName('entry'):
692 s = e.getElementsByTagName('wc-status')
700 s = e.getElementsByTagName('wc-status')
693 if not s:
701 if not s:
694 continue
702 continue
695 item = s[0].getAttribute('item')
703 item = s[0].getAttribute('item')
696 props = s[0].getAttribute('props')
704 props = s[0].getAttribute('props')
697 path = e.getAttribute('path')
705 path = e.getAttribute('path')
698 if item == 'external':
706 if item == 'external':
699 externals.append(path)
707 externals.append(path)
700 elif item == 'missing':
708 elif item == 'missing':
701 missing.append(path)
709 missing.append(path)
702 if (item not in ('', 'normal', 'unversioned', 'external')
710 if (item not in ('', 'normal', 'unversioned', 'external')
703 or props not in ('', 'none', 'normal')):
711 or props not in ('', 'none', 'normal')):
704 changes.append(path)
712 changes.append(path)
705 for path in changes:
713 for path in changes:
706 for ext in externals:
714 for ext in externals:
707 if path == ext or path.startswith(ext + os.sep):
715 if path == ext or path.startswith(ext + os.sep):
708 return True, True, bool(missing)
716 return True, True, bool(missing)
709 return bool(changes), False, bool(missing)
717 return bool(changes), False, bool(missing)
710
718
711 def dirty(self, ignoreupdate=False):
719 def dirty(self, ignoreupdate=False):
712 if not self._wcchanged()[0]:
720 if not self._wcchanged()[0]:
713 if self._state[1] in self._wcrevs() or ignoreupdate:
721 if self._state[1] in self._wcrevs() or ignoreupdate:
714 return False
722 return False
715 return True
723 return True
716
724
717 def basestate(self):
725 def basestate(self):
718 lastrev, rev = self._wcrevs()
726 lastrev, rev = self._wcrevs()
719 if lastrev != rev:
727 if lastrev != rev:
720 # Last committed rev is not the same than rev. We would
728 # Last committed rev is not the same than rev. We would
721 # like to take lastrev but we do not know if the subrepo
729 # like to take lastrev but we do not know if the subrepo
722 # URL exists at lastrev. Test it and fallback to rev it
730 # URL exists at lastrev. Test it and fallback to rev it
723 # is not there.
731 # is not there.
724 try:
732 try:
725 self._svncommand(['info', '%s@%s' % (self._state[0], lastrev)])
733 self._svncommand(['info', '%s@%s' % (self._state[0], lastrev)])
726 return lastrev
734 return lastrev
727 except error.Abort:
735 except error.Abort:
728 pass
736 pass
729 return rev
737 return rev
730
738
731 def commit(self, text, user, date):
739 def commit(self, text, user, date):
732 # user and date are out of our hands since svn is centralized
740 # user and date are out of our hands since svn is centralized
733 changed, extchanged, missing = self._wcchanged()
741 changed, extchanged, missing = self._wcchanged()
734 if not changed:
742 if not changed:
735 return self.basestate()
743 return self.basestate()
736 if extchanged:
744 if extchanged:
737 # Do not try to commit externals
745 # Do not try to commit externals
738 raise util.Abort(_('cannot commit svn externals'))
746 raise util.Abort(_('cannot commit svn externals'))
739 if missing:
747 if missing:
740 # svn can commit with missing entries but aborting like hg
748 # svn can commit with missing entries but aborting like hg
741 # seems a better approach.
749 # seems a better approach.
742 raise util.Abort(_('cannot commit missing svn entries'))
750 raise util.Abort(_('cannot commit missing svn entries'))
743 commitinfo, err = self._svncommand(['commit', '-m', text])
751 commitinfo, err = self._svncommand(['commit', '-m', text])
744 self._ui.status(commitinfo)
752 self._ui.status(commitinfo)
745 newrev = re.search('Committed revision ([0-9]+).', commitinfo)
753 newrev = re.search('Committed revision ([0-9]+).', commitinfo)
746 if not newrev:
754 if not newrev:
747 if not commitinfo.strip():
755 if not commitinfo.strip():
748 # Sometimes, our definition of "changed" differs from
756 # Sometimes, our definition of "changed" differs from
749 # svn one. For instance, svn ignores missing files
757 # svn one. For instance, svn ignores missing files
750 # when committing. If there are only missing files, no
758 # when committing. If there are only missing files, no
751 # commit is made, no output and no error code.
759 # commit is made, no output and no error code.
752 raise util.Abort(_('failed to commit svn changes'))
760 raise util.Abort(_('failed to commit svn changes'))
753 raise util.Abort(commitinfo.splitlines()[-1])
761 raise util.Abort(commitinfo.splitlines()[-1])
754 newrev = newrev.groups()[0]
762 newrev = newrev.groups()[0]
755 self._ui.status(self._svncommand(['update', '-r', newrev])[0])
763 self._ui.status(self._svncommand(['update', '-r', newrev])[0])
756 return newrev
764 return newrev
757
765
758 def remove(self):
766 def remove(self):
759 if self.dirty():
767 if self.dirty():
760 self._ui.warn(_('not removing repo %s because '
768 self._ui.warn(_('not removing repo %s because '
761 'it has changes.\n' % self._path))
769 'it has changes.\n' % self._path))
762 return
770 return
763 self._ui.note(_('removing subrepo %s\n') % self._path)
771 self._ui.note(_('removing subrepo %s\n') % self._path)
764
772
765 def onerror(function, path, excinfo):
773 def onerror(function, path, excinfo):
766 if function is not os.remove:
774 if function is not os.remove:
767 raise
775 raise
768 # read-only files cannot be unlinked under Windows
776 # read-only files cannot be unlinked under Windows
769 s = os.stat(path)
777 s = os.stat(path)
770 if (s.st_mode & stat.S_IWRITE) != 0:
778 if (s.st_mode & stat.S_IWRITE) != 0:
771 raise
779 raise
772 os.chmod(path, stat.S_IMODE(s.st_mode) | stat.S_IWRITE)
780 os.chmod(path, stat.S_IMODE(s.st_mode) | stat.S_IWRITE)
773 os.remove(path)
781 os.remove(path)
774
782
775 path = self._ctx._repo.wjoin(self._path)
783 path = self._ctx._repo.wjoin(self._path)
776 shutil.rmtree(path, onerror=onerror)
784 shutil.rmtree(path, onerror=onerror)
777 try:
785 try:
778 os.removedirs(os.path.dirname(path))
786 os.removedirs(os.path.dirname(path))
779 except OSError:
787 except OSError:
780 pass
788 pass
781
789
782 def get(self, state, overwrite=False):
790 def get(self, state, overwrite=False):
783 if overwrite:
791 if overwrite:
784 self._svncommand(['revert', '--recursive'])
792 self._svncommand(['revert', '--recursive'])
785 args = ['checkout']
793 args = ['checkout']
786 if self._svnversion >= (1, 5):
794 if self._svnversion >= (1, 5):
787 args.append('--force')
795 args.append('--force')
788 # The revision must be specified at the end of the URL to properly
796 # The revision must be specified at the end of the URL to properly
789 # update to a directory which has since been deleted and recreated.
797 # update to a directory which has since been deleted and recreated.
790 args.append('%s@%s' % (state[0], state[1]))
798 args.append('%s@%s' % (state[0], state[1]))
791 status, err = self._svncommand(args, failok=True)
799 status, err = self._svncommand(args, failok=True)
792 if not re.search('Checked out revision [0-9]+.', status):
800 if not re.search('Checked out revision [0-9]+.', status):
793 if ('is already a working copy for a different URL' in err
801 if ('is already a working copy for a different URL' in err
794 and (self._wcchanged()[:2] == (False, False))):
802 and (self._wcchanged()[:2] == (False, False))):
795 # obstructed but clean working copy, so just blow it away.
803 # obstructed but clean working copy, so just blow it away.
796 self.remove()
804 self.remove()
797 self.get(state, overwrite=False)
805 self.get(state, overwrite=False)
798 return
806 return
799 raise util.Abort((status or err).splitlines()[-1])
807 raise util.Abort((status or err).splitlines()[-1])
800 self._ui.status(status)
808 self._ui.status(status)
801
809
802 def merge(self, state):
810 def merge(self, state):
803 old = self._state[1]
811 old = self._state[1]
804 new = state[1]
812 new = state[1]
805 wcrev = self._wcrev()
813 wcrev = self._wcrev()
806 if new != wcrev:
814 if new != wcrev:
807 dirty = old == wcrev or self._wcchanged()[0]
815 dirty = old == wcrev or self._wcchanged()[0]
808 if _updateprompt(self._ui, self, dirty, wcrev, new):
816 if _updateprompt(self._ui, self, dirty, wcrev, new):
809 self.get(state, False)
817 self.get(state, False)
810
818
811 def push(self, opts):
819 def push(self, opts):
812 # push is a no-op for SVN
820 # push is a no-op for SVN
813 return True
821 return True
814
822
815 def files(self):
823 def files(self):
816 output = self._svncommand(['list', '--recursive', '--xml'])[0]
824 output = self._svncommand(['list', '--recursive', '--xml'])[0]
817 doc = xml.dom.minidom.parseString(output)
825 doc = xml.dom.minidom.parseString(output)
818 paths = []
826 paths = []
819 for e in doc.getElementsByTagName('entry'):
827 for e in doc.getElementsByTagName('entry'):
820 kind = str(e.getAttribute('kind'))
828 kind = str(e.getAttribute('kind'))
821 if kind != 'file':
829 if kind != 'file':
822 continue
830 continue
823 name = ''.join(c.data for c
831 name = ''.join(c.data for c
824 in e.getElementsByTagName('name')[0].childNodes
832 in e.getElementsByTagName('name')[0].childNodes
825 if c.nodeType == c.TEXT_NODE)
833 if c.nodeType == c.TEXT_NODE)
826 paths.append(name)
834 paths.append(name)
827 return paths
835 return paths
828
836
829 def filedata(self, name):
837 def filedata(self, name):
830 return self._svncommand(['cat'], name)[0]
838 return self._svncommand(['cat'], name)[0]
831
839
832
840
833 class gitsubrepo(abstractsubrepo):
841 class gitsubrepo(abstractsubrepo):
834 def __init__(self, ctx, path, state):
842 def __init__(self, ctx, path, state):
835 # TODO add git version check.
843 # TODO add git version check.
836 self._state = state
844 self._state = state
837 self._ctx = ctx
845 self._ctx = ctx
838 self._path = path
846 self._path = path
839 self._relpath = os.path.join(reporelpath(ctx._repo), path)
847 self._relpath = os.path.join(reporelpath(ctx._repo), path)
840 self._abspath = ctx._repo.wjoin(path)
848 self._abspath = ctx._repo.wjoin(path)
841 self._subparent = ctx._repo
849 self._subparent = ctx._repo
842 self._ui = ctx._repo.ui
850 self._ui = ctx._repo.ui
843
851
844 def _gitcommand(self, commands, env=None, stream=False):
852 def _gitcommand(self, commands, env=None, stream=False):
845 return self._gitdir(commands, env=env, stream=stream)[0]
853 return self._gitdir(commands, env=env, stream=stream)[0]
846
854
847 def _gitdir(self, commands, env=None, stream=False):
855 def _gitdir(self, commands, env=None, stream=False):
848 return self._gitnodir(commands, env=env, stream=stream,
856 return self._gitnodir(commands, env=env, stream=stream,
849 cwd=self._abspath)
857 cwd=self._abspath)
850
858
851 def _gitnodir(self, commands, env=None, stream=False, cwd=None):
859 def _gitnodir(self, commands, env=None, stream=False, cwd=None):
852 """Calls the git command
860 """Calls the git command
853
861
854 The methods tries to call the git command. versions previor to 1.6.0
862 The methods tries to call the git command. versions previor to 1.6.0
855 are not supported and very probably fail.
863 are not supported and very probably fail.
856 """
864 """
857 self._ui.debug('%s: git %s\n' % (self._relpath, ' '.join(commands)))
865 self._ui.debug('%s: git %s\n' % (self._relpath, ' '.join(commands)))
858 # unless ui.quiet is set, print git's stderr,
866 # unless ui.quiet is set, print git's stderr,
859 # which is mostly progress and useful info
867 # which is mostly progress and useful info
860 errpipe = None
868 errpipe = None
861 if self._ui.quiet:
869 if self._ui.quiet:
862 errpipe = open(os.devnull, 'w')
870 errpipe = open(os.devnull, 'w')
863 p = subprocess.Popen(['git'] + commands, bufsize=-1, cwd=cwd, env=env,
871 p = subprocess.Popen(['git'] + commands, bufsize=-1, cwd=cwd, env=env,
864 close_fds=util.closefds,
872 close_fds=util.closefds,
865 stdout=subprocess.PIPE, stderr=errpipe)
873 stdout=subprocess.PIPE, stderr=errpipe)
866 if stream:
874 if stream:
867 return p.stdout, None
875 return p.stdout, None
868
876
869 retdata = p.stdout.read().strip()
877 retdata = p.stdout.read().strip()
870 # wait for the child to exit to avoid race condition.
878 # wait for the child to exit to avoid race condition.
871 p.wait()
879 p.wait()
872
880
873 if p.returncode != 0 and p.returncode != 1:
881 if p.returncode != 0 and p.returncode != 1:
874 # there are certain error codes that are ok
882 # there are certain error codes that are ok
875 command = commands[0]
883 command = commands[0]
876 if command in ('cat-file', 'symbolic-ref'):
884 if command in ('cat-file', 'symbolic-ref'):
877 return retdata, p.returncode
885 return retdata, p.returncode
878 # for all others, abort
886 # for all others, abort
879 raise util.Abort('git %s error %d in %s' %
887 raise util.Abort('git %s error %d in %s' %
880 (command, p.returncode, self._relpath))
888 (command, p.returncode, self._relpath))
881
889
882 return retdata, p.returncode
890 return retdata, p.returncode
883
891
884 def _gitmissing(self):
892 def _gitmissing(self):
885 return not os.path.exists(os.path.join(self._abspath, '.git'))
893 return not os.path.exists(os.path.join(self._abspath, '.git'))
886
894
887 def _gitstate(self):
895 def _gitstate(self):
888 return self._gitcommand(['rev-parse', 'HEAD'])
896 return self._gitcommand(['rev-parse', 'HEAD'])
889
897
890 def _gitcurrentbranch(self):
898 def _gitcurrentbranch(self):
891 current, err = self._gitdir(['symbolic-ref', 'HEAD', '--quiet'])
899 current, err = self._gitdir(['symbolic-ref', 'HEAD', '--quiet'])
892 if err:
900 if err:
893 current = None
901 current = None
894 return current
902 return current
895
903
896 def _gitremote(self, remote):
904 def _gitremote(self, remote):
897 out = self._gitcommand(['remote', 'show', '-n', remote])
905 out = self._gitcommand(['remote', 'show', '-n', remote])
898 line = out.split('\n')[1]
906 line = out.split('\n')[1]
899 i = line.index('URL: ') + len('URL: ')
907 i = line.index('URL: ') + len('URL: ')
900 return line[i:]
908 return line[i:]
901
909
902 def _githavelocally(self, revision):
910 def _githavelocally(self, revision):
903 out, code = self._gitdir(['cat-file', '-e', revision])
911 out, code = self._gitdir(['cat-file', '-e', revision])
904 return code == 0
912 return code == 0
905
913
906 def _gitisancestor(self, r1, r2):
914 def _gitisancestor(self, r1, r2):
907 base = self._gitcommand(['merge-base', r1, r2])
915 base = self._gitcommand(['merge-base', r1, r2])
908 return base == r1
916 return base == r1
909
917
910 def _gitisbare(self):
918 def _gitisbare(self):
911 return self._gitcommand(['config', '--bool', 'core.bare']) == 'true'
919 return self._gitcommand(['config', '--bool', 'core.bare']) == 'true'
912
920
913 def _gitupdatestat(self):
921 def _gitupdatestat(self):
914 """This must be run before git diff-index.
922 """This must be run before git diff-index.
915 diff-index only looks at changes to file stat;
923 diff-index only looks at changes to file stat;
916 this command looks at file contents and updates the stat."""
924 this command looks at file contents and updates the stat."""
917 self._gitcommand(['update-index', '-q', '--refresh'])
925 self._gitcommand(['update-index', '-q', '--refresh'])
918
926
919 def _gitbranchmap(self):
927 def _gitbranchmap(self):
920 '''returns 2 things:
928 '''returns 2 things:
921 a map from git branch to revision
929 a map from git branch to revision
922 a map from revision to branches'''
930 a map from revision to branches'''
923 branch2rev = {}
931 branch2rev = {}
924 rev2branch = {}
932 rev2branch = {}
925
933
926 out = self._gitcommand(['for-each-ref', '--format',
934 out = self._gitcommand(['for-each-ref', '--format',
927 '%(objectname) %(refname)'])
935 '%(objectname) %(refname)'])
928 for line in out.split('\n'):
936 for line in out.split('\n'):
929 revision, ref = line.split(' ')
937 revision, ref = line.split(' ')
930 if (not ref.startswith('refs/heads/') and
938 if (not ref.startswith('refs/heads/') and
931 not ref.startswith('refs/remotes/')):
939 not ref.startswith('refs/remotes/')):
932 continue
940 continue
933 if ref.startswith('refs/remotes/') and ref.endswith('/HEAD'):
941 if ref.startswith('refs/remotes/') and ref.endswith('/HEAD'):
934 continue # ignore remote/HEAD redirects
942 continue # ignore remote/HEAD redirects
935 branch2rev[ref] = revision
943 branch2rev[ref] = revision
936 rev2branch.setdefault(revision, []).append(ref)
944 rev2branch.setdefault(revision, []).append(ref)
937 return branch2rev, rev2branch
945 return branch2rev, rev2branch
938
946
939 def _gittracking(self, branches):
947 def _gittracking(self, branches):
940 'return map of remote branch to local tracking branch'
948 'return map of remote branch to local tracking branch'
941 # assumes no more than one local tracking branch for each remote
949 # assumes no more than one local tracking branch for each remote
942 tracking = {}
950 tracking = {}
943 for b in branches:
951 for b in branches:
944 if b.startswith('refs/remotes/'):
952 if b.startswith('refs/remotes/'):
945 continue
953 continue
946 bname = b.split('/', 2)[2]
954 bname = b.split('/', 2)[2]
947 remote = self._gitcommand(['config', 'branch.%s.remote' % bname])
955 remote = self._gitcommand(['config', 'branch.%s.remote' % bname])
948 if remote:
956 if remote:
949 ref = self._gitcommand(['config', 'branch.%s.merge' % bname])
957 ref = self._gitcommand(['config', 'branch.%s.merge' % bname])
950 tracking['refs/remotes/%s/%s' %
958 tracking['refs/remotes/%s/%s' %
951 (remote, ref.split('/', 2)[2])] = b
959 (remote, ref.split('/', 2)[2])] = b
952 return tracking
960 return tracking
953
961
954 def _abssource(self, source):
962 def _abssource(self, source):
955 if '://' not in source:
963 if '://' not in source:
956 # recognize the scp syntax as an absolute source
964 # recognize the scp syntax as an absolute source
957 colon = source.find(':')
965 colon = source.find(':')
958 if colon != -1 and '/' not in source[:colon]:
966 if colon != -1 and '/' not in source[:colon]:
959 return source
967 return source
960 self._subsource = source
968 self._subsource = source
961 return _abssource(self)
969 return _abssource(self)
962
970
963 def _fetch(self, source, revision):
971 def _fetch(self, source, revision):
964 if self._gitmissing():
972 if self._gitmissing():
965 source = self._abssource(source)
973 source = self._abssource(source)
966 self._ui.status(_('cloning subrepo %s from %s\n') %
974 self._ui.status(_('cloning subrepo %s from %s\n') %
967 (self._relpath, source))
975 (self._relpath, source))
968 self._gitnodir(['clone', source, self._abspath])
976 self._gitnodir(['clone', source, self._abspath])
969 if self._githavelocally(revision):
977 if self._githavelocally(revision):
970 return
978 return
971 self._ui.status(_('pulling subrepo %s from %s\n') %
979 self._ui.status(_('pulling subrepo %s from %s\n') %
972 (self._relpath, self._gitremote('origin')))
980 (self._relpath, self._gitremote('origin')))
973 # try only origin: the originally cloned repo
981 # try only origin: the originally cloned repo
974 self._gitcommand(['fetch'])
982 self._gitcommand(['fetch'])
975 if not self._githavelocally(revision):
983 if not self._githavelocally(revision):
976 raise util.Abort(_("revision %s does not exist in subrepo %s\n") %
984 raise util.Abort(_("revision %s does not exist in subrepo %s\n") %
977 (revision, self._relpath))
985 (revision, self._relpath))
978
986
979 def dirty(self, ignoreupdate=False):
987 def dirty(self, ignoreupdate=False):
980 if self._gitmissing():
988 if self._gitmissing():
981 return self._state[1] != ''
989 return self._state[1] != ''
982 if self._gitisbare():
990 if self._gitisbare():
983 return True
991 return True
984 if not ignoreupdate and self._state[1] != self._gitstate():
992 if not ignoreupdate and self._state[1] != self._gitstate():
985 # different version checked out
993 # different version checked out
986 return True
994 return True
987 # check for staged changes or modified files; ignore untracked files
995 # check for staged changes or modified files; ignore untracked files
988 self._gitupdatestat()
996 self._gitupdatestat()
989 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
997 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
990 return code == 1
998 return code == 1
991
999
992 def basestate(self):
1000 def basestate(self):
993 return self._gitstate()
1001 return self._gitstate()
994
1002
995 def get(self, state, overwrite=False):
1003 def get(self, state, overwrite=False):
996 source, revision, kind = state
1004 source, revision, kind = state
997 if not revision:
1005 if not revision:
998 self.remove()
1006 self.remove()
999 return
1007 return
1000 self._fetch(source, revision)
1008 self._fetch(source, revision)
1001 # if the repo was set to be bare, unbare it
1009 # if the repo was set to be bare, unbare it
1002 if self._gitisbare():
1010 if self._gitisbare():
1003 self._gitcommand(['config', 'core.bare', 'false'])
1011 self._gitcommand(['config', 'core.bare', 'false'])
1004 if self._gitstate() == revision:
1012 if self._gitstate() == revision:
1005 self._gitcommand(['reset', '--hard', 'HEAD'])
1013 self._gitcommand(['reset', '--hard', 'HEAD'])
1006 return
1014 return
1007 elif self._gitstate() == revision:
1015 elif self._gitstate() == revision:
1008 if overwrite:
1016 if overwrite:
1009 # first reset the index to unmark new files for commit, because
1017 # first reset the index to unmark new files for commit, because
1010 # reset --hard will otherwise throw away files added for commit,
1018 # reset --hard will otherwise throw away files added for commit,
1011 # not just unmark them.
1019 # not just unmark them.
1012 self._gitcommand(['reset', 'HEAD'])
1020 self._gitcommand(['reset', 'HEAD'])
1013 self._gitcommand(['reset', '--hard', 'HEAD'])
1021 self._gitcommand(['reset', '--hard', 'HEAD'])
1014 return
1022 return
1015 branch2rev, rev2branch = self._gitbranchmap()
1023 branch2rev, rev2branch = self._gitbranchmap()
1016
1024
1017 def checkout(args):
1025 def checkout(args):
1018 cmd = ['checkout']
1026 cmd = ['checkout']
1019 if overwrite:
1027 if overwrite:
1020 # first reset the index to unmark new files for commit, because
1028 # first reset the index to unmark new files for commit, because
1021 # the -f option will otherwise throw away files added for
1029 # the -f option will otherwise throw away files added for
1022 # commit, not just unmark them.
1030 # commit, not just unmark them.
1023 self._gitcommand(['reset', 'HEAD'])
1031 self._gitcommand(['reset', 'HEAD'])
1024 cmd.append('-f')
1032 cmd.append('-f')
1025 self._gitcommand(cmd + args)
1033 self._gitcommand(cmd + args)
1026
1034
1027 def rawcheckout():
1035 def rawcheckout():
1028 # no branch to checkout, check it out with no branch
1036 # no branch to checkout, check it out with no branch
1029 self._ui.warn(_('checking out detached HEAD in subrepo %s\n') %
1037 self._ui.warn(_('checking out detached HEAD in subrepo %s\n') %
1030 self._relpath)
1038 self._relpath)
1031 self._ui.warn(_('check out a git branch if you intend '
1039 self._ui.warn(_('check out a git branch if you intend '
1032 'to make changes\n'))
1040 'to make changes\n'))
1033 checkout(['-q', revision])
1041 checkout(['-q', revision])
1034
1042
1035 if revision not in rev2branch:
1043 if revision not in rev2branch:
1036 rawcheckout()
1044 rawcheckout()
1037 return
1045 return
1038 branches = rev2branch[revision]
1046 branches = rev2branch[revision]
1039 firstlocalbranch = None
1047 firstlocalbranch = None
1040 for b in branches:
1048 for b in branches:
1041 if b == 'refs/heads/master':
1049 if b == 'refs/heads/master':
1042 # master trumps all other branches
1050 # master trumps all other branches
1043 checkout(['refs/heads/master'])
1051 checkout(['refs/heads/master'])
1044 return
1052 return
1045 if not firstlocalbranch and not b.startswith('refs/remotes/'):
1053 if not firstlocalbranch and not b.startswith('refs/remotes/'):
1046 firstlocalbranch = b
1054 firstlocalbranch = b
1047 if firstlocalbranch:
1055 if firstlocalbranch:
1048 checkout([firstlocalbranch])
1056 checkout([firstlocalbranch])
1049 return
1057 return
1050
1058
1051 tracking = self._gittracking(branch2rev.keys())
1059 tracking = self._gittracking(branch2rev.keys())
1052 # choose a remote branch already tracked if possible
1060 # choose a remote branch already tracked if possible
1053 remote = branches[0]
1061 remote = branches[0]
1054 if remote not in tracking:
1062 if remote not in tracking:
1055 for b in branches:
1063 for b in branches:
1056 if b in tracking:
1064 if b in tracking:
1057 remote = b
1065 remote = b
1058 break
1066 break
1059
1067
1060 if remote not in tracking:
1068 if remote not in tracking:
1061 # create a new local tracking branch
1069 # create a new local tracking branch
1062 local = remote.split('/', 2)[2]
1070 local = remote.split('/', 2)[2]
1063 checkout(['-b', local, remote])
1071 checkout(['-b', local, remote])
1064 elif self._gitisancestor(branch2rev[tracking[remote]], remote):
1072 elif self._gitisancestor(branch2rev[tracking[remote]], remote):
1065 # When updating to a tracked remote branch,
1073 # When updating to a tracked remote branch,
1066 # if the local tracking branch is downstream of it,
1074 # if the local tracking branch is downstream of it,
1067 # a normal `git pull` would have performed a "fast-forward merge"
1075 # a normal `git pull` would have performed a "fast-forward merge"
1068 # which is equivalent to updating the local branch to the remote.
1076 # which is equivalent to updating the local branch to the remote.
1069 # Since we are only looking at branching at update, we need to
1077 # Since we are only looking at branching at update, we need to
1070 # detect this situation and perform this action lazily.
1078 # detect this situation and perform this action lazily.
1071 if tracking[remote] != self._gitcurrentbranch():
1079 if tracking[remote] != self._gitcurrentbranch():
1072 checkout([tracking[remote]])
1080 checkout([tracking[remote]])
1073 self._gitcommand(['merge', '--ff', remote])
1081 self._gitcommand(['merge', '--ff', remote])
1074 else:
1082 else:
1075 # a real merge would be required, just checkout the revision
1083 # a real merge would be required, just checkout the revision
1076 rawcheckout()
1084 rawcheckout()
1077
1085
1078 def commit(self, text, user, date):
1086 def commit(self, text, user, date):
1079 if self._gitmissing():
1087 if self._gitmissing():
1080 raise util.Abort(_("subrepo %s is missing") % self._relpath)
1088 raise util.Abort(_("subrepo %s is missing") % self._relpath)
1081 cmd = ['commit', '-a', '-m', text]
1089 cmd = ['commit', '-a', '-m', text]
1082 env = os.environ.copy()
1090 env = os.environ.copy()
1083 if user:
1091 if user:
1084 cmd += ['--author', user]
1092 cmd += ['--author', user]
1085 if date:
1093 if date:
1086 # git's date parser silently ignores when seconds < 1e9
1094 # git's date parser silently ignores when seconds < 1e9
1087 # convert to ISO8601
1095 # convert to ISO8601
1088 env['GIT_AUTHOR_DATE'] = util.datestr(date,
1096 env['GIT_AUTHOR_DATE'] = util.datestr(date,
1089 '%Y-%m-%dT%H:%M:%S %1%2')
1097 '%Y-%m-%dT%H:%M:%S %1%2')
1090 self._gitcommand(cmd, env=env)
1098 self._gitcommand(cmd, env=env)
1091 # make sure commit works otherwise HEAD might not exist under certain
1099 # make sure commit works otherwise HEAD might not exist under certain
1092 # circumstances
1100 # circumstances
1093 return self._gitstate()
1101 return self._gitstate()
1094
1102
1095 def merge(self, state):
1103 def merge(self, state):
1096 source, revision, kind = state
1104 source, revision, kind = state
1097 self._fetch(source, revision)
1105 self._fetch(source, revision)
1098 base = self._gitcommand(['merge-base', revision, self._state[1]])
1106 base = self._gitcommand(['merge-base', revision, self._state[1]])
1099 self._gitupdatestat()
1107 self._gitupdatestat()
1100 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
1108 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
1101
1109
1102 def mergefunc():
1110 def mergefunc():
1103 if base == revision:
1111 if base == revision:
1104 self.get(state) # fast forward merge
1112 self.get(state) # fast forward merge
1105 elif base != self._state[1]:
1113 elif base != self._state[1]:
1106 self._gitcommand(['merge', '--no-commit', revision])
1114 self._gitcommand(['merge', '--no-commit', revision])
1107
1115
1108 if self.dirty():
1116 if self.dirty():
1109 if self._gitstate() != revision:
1117 if self._gitstate() != revision:
1110 dirty = self._gitstate() == self._state[1] or code != 0
1118 dirty = self._gitstate() == self._state[1] or code != 0
1111 if _updateprompt(self._ui, self, dirty,
1119 if _updateprompt(self._ui, self, dirty,
1112 self._state[1][:7], revision[:7]):
1120 self._state[1][:7], revision[:7]):
1113 mergefunc()
1121 mergefunc()
1114 else:
1122 else:
1115 mergefunc()
1123 mergefunc()
1116
1124
1117 def push(self, opts):
1125 def push(self, opts):
1118 force = opts.get('force')
1126 force = opts.get('force')
1119
1127
1120 if not self._state[1]:
1128 if not self._state[1]:
1121 return True
1129 return True
1122 if self._gitmissing():
1130 if self._gitmissing():
1123 raise util.Abort(_("subrepo %s is missing") % self._relpath)
1131 raise util.Abort(_("subrepo %s is missing") % self._relpath)
1124 # if a branch in origin contains the revision, nothing to do
1132 # if a branch in origin contains the revision, nothing to do
1125 branch2rev, rev2branch = self._gitbranchmap()
1133 branch2rev, rev2branch = self._gitbranchmap()
1126 if self._state[1] in rev2branch:
1134 if self._state[1] in rev2branch:
1127 for b in rev2branch[self._state[1]]:
1135 for b in rev2branch[self._state[1]]:
1128 if b.startswith('refs/remotes/origin/'):
1136 if b.startswith('refs/remotes/origin/'):
1129 return True
1137 return True
1130 for b, revision in branch2rev.iteritems():
1138 for b, revision in branch2rev.iteritems():
1131 if b.startswith('refs/remotes/origin/'):
1139 if b.startswith('refs/remotes/origin/'):
1132 if self._gitisancestor(self._state[1], revision):
1140 if self._gitisancestor(self._state[1], revision):
1133 return True
1141 return True
1134 # otherwise, try to push the currently checked out branch
1142 # otherwise, try to push the currently checked out branch
1135 cmd = ['push']
1143 cmd = ['push']
1136 if force:
1144 if force:
1137 cmd.append('--force')
1145 cmd.append('--force')
1138
1146
1139 current = self._gitcurrentbranch()
1147 current = self._gitcurrentbranch()
1140 if current:
1148 if current:
1141 # determine if the current branch is even useful
1149 # determine if the current branch is even useful
1142 if not self._gitisancestor(self._state[1], current):
1150 if not self._gitisancestor(self._state[1], current):
1143 self._ui.warn(_('unrelated git branch checked out '
1151 self._ui.warn(_('unrelated git branch checked out '
1144 'in subrepo %s\n') % self._relpath)
1152 'in subrepo %s\n') % self._relpath)
1145 return False
1153 return False
1146 self._ui.status(_('pushing branch %s of subrepo %s\n') %
1154 self._ui.status(_('pushing branch %s of subrepo %s\n') %
1147 (current.split('/', 2)[2], self._relpath))
1155 (current.split('/', 2)[2], self._relpath))
1148 self._gitcommand(cmd + ['origin', current])
1156 self._gitcommand(cmd + ['origin', current])
1149 return True
1157 return True
1150 else:
1158 else:
1151 self._ui.warn(_('no branch checked out in subrepo %s\n'
1159 self._ui.warn(_('no branch checked out in subrepo %s\n'
1152 'cannot push revision %s') %
1160 'cannot push revision %s') %
1153 (self._relpath, self._state[1]))
1161 (self._relpath, self._state[1]))
1154 return False
1162 return False
1155
1163
1156 def remove(self):
1164 def remove(self):
1157 if self._gitmissing():
1165 if self._gitmissing():
1158 return
1166 return
1159 if self.dirty():
1167 if self.dirty():
1160 self._ui.warn(_('not removing repo %s because '
1168 self._ui.warn(_('not removing repo %s because '
1161 'it has changes.\n') % self._relpath)
1169 'it has changes.\n') % self._relpath)
1162 return
1170 return
1163 # we can't fully delete the repository as it may contain
1171 # we can't fully delete the repository as it may contain
1164 # local-only history
1172 # local-only history
1165 self._ui.note(_('removing subrepo %s\n') % self._relpath)
1173 self._ui.note(_('removing subrepo %s\n') % self._relpath)
1166 self._gitcommand(['config', 'core.bare', 'true'])
1174 self._gitcommand(['config', 'core.bare', 'true'])
1167 for f in os.listdir(self._abspath):
1175 for f in os.listdir(self._abspath):
1168 if f == '.git':
1176 if f == '.git':
1169 continue
1177 continue
1170 path = os.path.join(self._abspath, f)
1178 path = os.path.join(self._abspath, f)
1171 if os.path.isdir(path) and not os.path.islink(path):
1179 if os.path.isdir(path) and not os.path.islink(path):
1172 shutil.rmtree(path)
1180 shutil.rmtree(path)
1173 else:
1181 else:
1174 os.remove(path)
1182 os.remove(path)
1175
1183
1176 def archive(self, ui, archiver, prefix):
1184 def archive(self, ui, archiver, prefix):
1177 source, revision = self._state
1185 source, revision = self._state
1178 if not revision:
1186 if not revision:
1179 return
1187 return
1180 self._fetch(source, revision)
1188 self._fetch(source, revision)
1181
1189
1182 # Parse git's native archive command.
1190 # Parse git's native archive command.
1183 # This should be much faster than manually traversing the trees
1191 # This should be much faster than manually traversing the trees
1184 # and objects with many subprocess calls.
1192 # and objects with many subprocess calls.
1185 tarstream = self._gitcommand(['archive', revision], stream=True)
1193 tarstream = self._gitcommand(['archive', revision], stream=True)
1186 tar = tarfile.open(fileobj=tarstream, mode='r|')
1194 tar = tarfile.open(fileobj=tarstream, mode='r|')
1187 relpath = subrelpath(self)
1195 relpath = subrelpath(self)
1188 ui.progress(_('archiving (%s)') % relpath, 0, unit=_('files'))
1196 ui.progress(_('archiving (%s)') % relpath, 0, unit=_('files'))
1189 for i, info in enumerate(tar):
1197 for i, info in enumerate(tar):
1190 if info.isdir():
1198 if info.isdir():
1191 continue
1199 continue
1192 if info.issym():
1200 if info.issym():
1193 data = info.linkname
1201 data = info.linkname
1194 else:
1202 else:
1195 data = tar.extractfile(info).read()
1203 data = tar.extractfile(info).read()
1196 archiver.addfile(os.path.join(prefix, self._path, info.name),
1204 archiver.addfile(os.path.join(prefix, self._path, info.name),
1197 info.mode, info.issym(), data)
1205 info.mode, info.issym(), data)
1198 ui.progress(_('archiving (%s)') % relpath, i + 1,
1206 ui.progress(_('archiving (%s)') % relpath, i + 1,
1199 unit=_('files'))
1207 unit=_('files'))
1200 ui.progress(_('archiving (%s)') % relpath, None)
1208 ui.progress(_('archiving (%s)') % relpath, None)
1201
1209
1202
1210
1203 def status(self, rev2, **opts):
1211 def status(self, rev2, **opts):
1204 rev1 = self._state[1]
1212 rev1 = self._state[1]
1205 if self._gitmissing() or not rev1:
1213 if self._gitmissing() or not rev1:
1206 # if the repo is missing, return no results
1214 # if the repo is missing, return no results
1207 return [], [], [], [], [], [], []
1215 return [], [], [], [], [], [], []
1208 modified, added, removed = [], [], []
1216 modified, added, removed = [], [], []
1209 self._gitupdatestat()
1217 self._gitupdatestat()
1210 if rev2:
1218 if rev2:
1211 command = ['diff-tree', rev1, rev2]
1219 command = ['diff-tree', rev1, rev2]
1212 else:
1220 else:
1213 command = ['diff-index', rev1]
1221 command = ['diff-index', rev1]
1214 out = self._gitcommand(command)
1222 out = self._gitcommand(command)
1215 for line in out.split('\n'):
1223 for line in out.split('\n'):
1216 tab = line.find('\t')
1224 tab = line.find('\t')
1217 if tab == -1:
1225 if tab == -1:
1218 continue
1226 continue
1219 status, f = line[tab - 1], line[tab + 1:]
1227 status, f = line[tab - 1], line[tab + 1:]
1220 if status == 'M':
1228 if status == 'M':
1221 modified.append(f)
1229 modified.append(f)
1222 elif status == 'A':
1230 elif status == 'A':
1223 added.append(f)
1231 added.append(f)
1224 elif status == 'D':
1232 elif status == 'D':
1225 removed.append(f)
1233 removed.append(f)
1226
1234
1227 deleted = unknown = ignored = clean = []
1235 deleted = unknown = ignored = clean = []
1228 return modified, added, removed, deleted, unknown, ignored, clean
1236 return modified, added, removed, deleted, unknown, ignored, clean
1229
1237
1230 types = {
1238 types = {
1231 'hg': hgsubrepo,
1239 'hg': hgsubrepo,
1232 'svn': svnsubrepo,
1240 'svn': svnsubrepo,
1233 'git': gitsubrepo,
1241 'git': gitsubrepo,
1234 }
1242 }
@@ -1,636 +1,633 b''
1 $ check_code="$TESTDIR"/../contrib/check-code.py
1 $ check_code="$TESTDIR"/../contrib/check-code.py
2 $ cd "$TESTDIR"/..
2 $ cd "$TESTDIR"/..
3 $ if hg identify -q > /dev/null; then :
3 $ if hg identify -q > /dev/null; then :
4 > else
4 > else
5 > echo "skipped: not a Mercurial working dir" >&2
5 > echo "skipped: not a Mercurial working dir" >&2
6 > exit 80
6 > exit 80
7 > fi
7 > fi
8 $ hg manifest | xargs "$check_code" || echo 'FAILURE IS NOT AN OPTION!!!'
8 $ hg manifest | xargs "$check_code" || echo 'FAILURE IS NOT AN OPTION!!!'
9
9
10 $ hg manifest | xargs "$check_code" --warnings --nolineno --per-file=0 || true
10 $ hg manifest | xargs "$check_code" --warnings --nolineno --per-file=0 || true
11 contrib/check-code.py:0:
11 contrib/check-code.py:0:
12 > # (r'^\s+[^_ \n][^_. \n]+_[^_\n]+\s*=', "don't use underbars in identifiers"),
12 > # (r'^\s+[^_ \n][^_. \n]+_[^_\n]+\s*=', "don't use underbars in identifiers"),
13 warning: line over 80 characters
13 warning: line over 80 characters
14 contrib/perf.py:0:
14 contrib/perf.py:0:
15 > except:
15 > except:
16 warning: naked except clause
16 warning: naked except clause
17 contrib/perf.py:0:
17 contrib/perf.py:0:
18 > #timer(lambda: sum(map(len, repo.dirstate.status(m, [], False, False, False))))
18 > #timer(lambda: sum(map(len, repo.dirstate.status(m, [], False, False, False))))
19 warning: line over 80 characters
19 warning: line over 80 characters
20 contrib/perf.py:0:
20 contrib/perf.py:0:
21 > except:
21 > except:
22 warning: naked except clause
22 warning: naked except clause
23 contrib/setup3k.py:0:
23 contrib/setup3k.py:0:
24 > except:
24 > except:
25 warning: naked except clause
25 warning: naked except clause
26 contrib/setup3k.py:0:
26 contrib/setup3k.py:0:
27 > except:
27 > except:
28 warning: naked except clause
28 warning: naked except clause
29 contrib/setup3k.py:0:
29 contrib/setup3k.py:0:
30 > except:
30 > except:
31 warning: naked except clause
31 warning: naked except clause
32 warning: naked except clause
32 warning: naked except clause
33 warning: naked except clause
33 warning: naked except clause
34 contrib/shrink-revlog.py:0:
34 contrib/shrink-revlog.py:0:
35 > except:
35 > except:
36 warning: naked except clause
36 warning: naked except clause
37 doc/gendoc.py:0:
37 doc/gendoc.py:0:
38 > "together with Mercurial. Help for other extensions is available "
38 > "together with Mercurial. Help for other extensions is available "
39 warning: line over 80 characters
39 warning: line over 80 characters
40 hgext/bugzilla.py:0:
40 hgext/bugzilla.py:0:
41 > raise util.Abort(_('cannot find bugzilla user id for %s or %s') %
41 > raise util.Abort(_('cannot find bugzilla user id for %s or %s') %
42 warning: line over 80 characters
42 warning: line over 80 characters
43 hgext/bugzilla.py:0:
43 hgext/bugzilla.py:0:
44 > bzdir = self.ui.config('bugzilla', 'bzdir', '/var/www/html/bugzilla')
44 > bzdir = self.ui.config('bugzilla', 'bzdir', '/var/www/html/bugzilla')
45 warning: line over 80 characters
45 warning: line over 80 characters
46 hgext/convert/__init__.py:0:
46 hgext/convert/__init__.py:0:
47 > ('', 'ancestors', '', _('show current changeset in ancestor branches')),
47 > ('', 'ancestors', '', _('show current changeset in ancestor branches')),
48 warning: line over 80 characters
48 warning: line over 80 characters
49 hgext/convert/bzr.py:0:
49 hgext/convert/bzr.py:0:
50 > except:
50 > except:
51 warning: naked except clause
51 warning: naked except clause
52 hgext/convert/common.py:0:
52 hgext/convert/common.py:0:
53 > except:
53 > except:
54 warning: naked except clause
54 warning: naked except clause
55 hgext/convert/common.py:0:
55 hgext/convert/common.py:0:
56 > except:
56 > except:
57 warning: naked except clause
57 warning: naked except clause
58 warning: naked except clause
58 warning: naked except clause
59 hgext/convert/convcmd.py:0:
59 hgext/convert/convcmd.py:0:
60 > except:
60 > except:
61 warning: naked except clause
61 warning: naked except clause
62 hgext/convert/cvs.py:0:
62 hgext/convert/cvs.py:0:
63 > # /1 :pserver:user@example.com:2401/cvsroot/foo Ah<Z
63 > # /1 :pserver:user@example.com:2401/cvsroot/foo Ah<Z
64 warning: line over 80 characters
64 warning: line over 80 characters
65 hgext/convert/cvsps.py:0:
65 hgext/convert/cvsps.py:0:
66 > assert len(branches) == 1, 'unknown branch: %s' % e.mergepoint
66 > assert len(branches) == 1, 'unknown branch: %s' % e.mergepoint
67 warning: line over 80 characters
67 warning: line over 80 characters
68 hgext/convert/cvsps.py:0:
68 hgext/convert/cvsps.py:0:
69 > ui.write('Ancestors: %s\n' % (','.join(r)))
69 > ui.write('Ancestors: %s\n' % (','.join(r)))
70 warning: unwrapped ui message
70 warning: unwrapped ui message
71 hgext/convert/cvsps.py:0:
71 hgext/convert/cvsps.py:0:
72 > ui.write('Parent: %d\n' % cs.parents[0].id)
72 > ui.write('Parent: %d\n' % cs.parents[0].id)
73 warning: unwrapped ui message
73 warning: unwrapped ui message
74 hgext/convert/cvsps.py:0:
74 hgext/convert/cvsps.py:0:
75 > ui.write('Parents: %s\n' %
75 > ui.write('Parents: %s\n' %
76 warning: unwrapped ui message
76 warning: unwrapped ui message
77 hgext/convert/cvsps.py:0:
77 hgext/convert/cvsps.py:0:
78 > except:
78 > except:
79 warning: naked except clause
79 warning: naked except clause
80 hgext/convert/cvsps.py:0:
80 hgext/convert/cvsps.py:0:
81 > ui.write('Branchpoints: %s \n' % ', '.join(branchpoints))
81 > ui.write('Branchpoints: %s \n' % ', '.join(branchpoints))
82 warning: unwrapped ui message
82 warning: unwrapped ui message
83 hgext/convert/cvsps.py:0:
83 hgext/convert/cvsps.py:0:
84 > ui.write('Author: %s\n' % cs.author)
84 > ui.write('Author: %s\n' % cs.author)
85 warning: unwrapped ui message
85 warning: unwrapped ui message
86 hgext/convert/cvsps.py:0:
86 hgext/convert/cvsps.py:0:
87 > ui.write('Branch: %s\n' % (cs.branch or 'HEAD'))
87 > ui.write('Branch: %s\n' % (cs.branch or 'HEAD'))
88 warning: unwrapped ui message
88 warning: unwrapped ui message
89 hgext/convert/cvsps.py:0:
89 hgext/convert/cvsps.py:0:
90 > ui.write('Date: %s\n' % util.datestr(cs.date,
90 > ui.write('Date: %s\n' % util.datestr(cs.date,
91 warning: unwrapped ui message
91 warning: unwrapped ui message
92 hgext/convert/cvsps.py:0:
92 hgext/convert/cvsps.py:0:
93 > ui.write('Log:\n')
93 > ui.write('Log:\n')
94 warning: unwrapped ui message
94 warning: unwrapped ui message
95 hgext/convert/cvsps.py:0:
95 hgext/convert/cvsps.py:0:
96 > ui.write('Members: \n')
96 > ui.write('Members: \n')
97 warning: unwrapped ui message
97 warning: unwrapped ui message
98 hgext/convert/cvsps.py:0:
98 hgext/convert/cvsps.py:0:
99 > ui.write('PatchSet %d \n' % cs.id)
99 > ui.write('PatchSet %d \n' % cs.id)
100 warning: unwrapped ui message
100 warning: unwrapped ui message
101 hgext/convert/cvsps.py:0:
101 hgext/convert/cvsps.py:0:
102 > ui.write('Tag%s: %s \n' % (['', 's'][len(cs.tags) > 1],
102 > ui.write('Tag%s: %s \n' % (['', 's'][len(cs.tags) > 1],
103 warning: unwrapped ui message
103 warning: unwrapped ui message
104 hgext/convert/git.py:0:
104 hgext/convert/git.py:0:
105 > except:
105 > except:
106 warning: naked except clause
106 warning: naked except clause
107 hgext/convert/git.py:0:
107 hgext/convert/git.py:0:
108 > fh = self.gitopen('git diff-tree --name-only --root -r %s "%s^%s" --'
108 > fh = self.gitopen('git diff-tree --name-only --root -r %s "%s^%s" --'
109 warning: line over 80 characters
109 warning: line over 80 characters
110 hgext/convert/hg.py:0:
110 hgext/convert/hg.py:0:
111 > # detect missing revlogs and abort on errors or populate self.ignored
111 > # detect missing revlogs and abort on errors or populate self.ignored
112 warning: line over 80 characters
112 warning: line over 80 characters
113 hgext/convert/hg.py:0:
113 hgext/convert/hg.py:0:
114 > except:
114 > except:
115 warning: naked except clause
115 warning: naked except clause
116 warning: naked except clause
116 warning: naked except clause
117 hgext/convert/hg.py:0:
117 hgext/convert/hg.py:0:
118 > except:
118 > except:
119 warning: naked except clause
119 warning: naked except clause
120 hgext/convert/monotone.py:0:
120 hgext/convert/monotone.py:0:
121 > except:
121 > except:
122 warning: naked except clause
122 warning: naked except clause
123 hgext/convert/monotone.py:0:
123 hgext/convert/monotone.py:0:
124 > except:
124 > except:
125 warning: naked except clause
125 warning: naked except clause
126 hgext/convert/subversion.py:0:
126 hgext/convert/subversion.py:0:
127 > raise util.Abort(_('svn: branch has no revision %s') % to_revnum)
127 > raise util.Abort(_('svn: branch has no revision %s') % to_revnum)
128 warning: line over 80 characters
128 warning: line over 80 characters
129 hgext/convert/subversion.py:0:
129 hgext/convert/subversion.py:0:
130 > except:
130 > except:
131 warning: naked except clause
131 warning: naked except clause
132 hgext/convert/subversion.py:0:
132 hgext/convert/subversion.py:0:
133 > args = [self.baseurl, relpaths, start, end, limit, discover_changed_paths,
133 > args = [self.baseurl, relpaths, start, end, limit, discover_changed_paths,
134 warning: line over 80 characters
134 warning: line over 80 characters
135 hgext/convert/subversion.py:0:
135 hgext/convert/subversion.py:0:
136 > self.trunkname = self.ui.config('convert', 'svn.trunk', 'trunk').strip('/')
136 > self.trunkname = self.ui.config('convert', 'svn.trunk', 'trunk').strip('/')
137 warning: line over 80 characters
137 warning: line over 80 characters
138 hgext/convert/subversion.py:0:
138 hgext/convert/subversion.py:0:
139 > except:
139 > except:
140 warning: naked except clause
140 warning: naked except clause
141 hgext/convert/subversion.py:0:
141 hgext/convert/subversion.py:0:
142 > def get_log_child(fp, url, paths, start, end, limit=0, discover_changed_paths=True,
142 > def get_log_child(fp, url, paths, start, end, limit=0, discover_changed_paths=True,
143 warning: line over 80 characters
143 warning: line over 80 characters
144 hgext/eol.py:0:
144 hgext/eol.py:0:
145 > if ui.configbool('eol', 'fix-trailing-newline', False) and s and s[-1] != '\n':
145 > if ui.configbool('eol', 'fix-trailing-newline', False) and s and s[-1] != '\n':
146 warning: line over 80 characters
146 warning: line over 80 characters
147 warning: line over 80 characters
147 warning: line over 80 characters
148 hgext/gpg.py:0:
148 hgext/gpg.py:0:
149 > except:
149 > except:
150 warning: naked except clause
150 warning: naked except clause
151 hgext/hgcia.py:0:
151 hgext/hgcia.py:0:
152 > except:
152 > except:
153 warning: naked except clause
153 warning: naked except clause
154 hgext/hgk.py:0:
154 hgext/hgk.py:0:
155 > ui.write("%s%s\n" % (prefix, description.replace('\n', nlprefix).strip()))
155 > ui.write("%s%s\n" % (prefix, description.replace('\n', nlprefix).strip()))
156 warning: line over 80 characters
156 warning: line over 80 characters
157 hgext/hgk.py:0:
157 hgext/hgk.py:0:
158 > ui.write("parent %s\n" % p)
158 > ui.write("parent %s\n" % p)
159 warning: unwrapped ui message
159 warning: unwrapped ui message
160 hgext/hgk.py:0:
160 hgext/hgk.py:0:
161 > ui.write('k=%s\nv=%s\n' % (name, value))
161 > ui.write('k=%s\nv=%s\n' % (name, value))
162 warning: unwrapped ui message
162 warning: unwrapped ui message
163 hgext/hgk.py:0:
163 hgext/hgk.py:0:
164 > ui.write("author %s %s %s\n" % (ctx.user(), int(date[0]), date[1]))
164 > ui.write("author %s %s %s\n" % (ctx.user(), int(date[0]), date[1]))
165 warning: unwrapped ui message
165 warning: unwrapped ui message
166 hgext/hgk.py:0:
166 hgext/hgk.py:0:
167 > ui.write("branch %s\n\n" % ctx.branch())
167 > ui.write("branch %s\n\n" % ctx.branch())
168 warning: unwrapped ui message
168 warning: unwrapped ui message
169 hgext/hgk.py:0:
169 hgext/hgk.py:0:
170 > ui.write("committer %s %s %s\n" % (committer, int(date[0]), date[1]))
170 > ui.write("committer %s %s %s\n" % (committer, int(date[0]), date[1]))
171 warning: unwrapped ui message
171 warning: unwrapped ui message
172 hgext/hgk.py:0:
172 hgext/hgk.py:0:
173 > ui.write("revision %d\n" % ctx.rev())
173 > ui.write("revision %d\n" % ctx.rev())
174 warning: unwrapped ui message
174 warning: unwrapped ui message
175 hgext/hgk.py:0:
175 hgext/hgk.py:0:
176 > ui.write("tree %s\n" % short(ctx.changeset()[0])) # use ctx.node() instead ??
176 > ui.write("tree %s\n" % short(ctx.changeset()[0])) # use ctx.node() instead ??
177 warning: line over 80 characters
177 warning: line over 80 characters
178 warning: unwrapped ui message
178 warning: unwrapped ui message
179 hgext/highlight/__init__.py:0:
179 hgext/highlight/__init__.py:0:
180 > extensions.wrapfunction(webcommands, '_filerevision', filerevision_highlight)
180 > extensions.wrapfunction(webcommands, '_filerevision', filerevision_highlight)
181 warning: line over 80 characters
181 warning: line over 80 characters
182 hgext/highlight/__init__.py:0:
182 hgext/highlight/__init__.py:0:
183 > return ['/* pygments_style = %s */\n\n' % pg_style, fmter.get_style_defs('')]
183 > return ['/* pygments_style = %s */\n\n' % pg_style, fmter.get_style_defs('')]
184 warning: line over 80 characters
184 warning: line over 80 characters
185 hgext/inotify/__init__.py:0:
185 hgext/inotify/__init__.py:0:
186 > if self._inotifyon and not ignored and not subrepos and not self._dirty:
186 > if self._inotifyon and not ignored and not subrepos and not self._dirty:
187 warning: line over 80 characters
187 warning: line over 80 characters
188 hgext/inotify/server.py:0:
188 hgext/inotify/server.py:0:
189 > except:
189 > except:
190 warning: naked except clause
190 warning: naked except clause
191 hgext/inotify/server.py:0:
191 hgext/inotify/server.py:0:
192 > except:
192 > except:
193 warning: naked except clause
193 warning: naked except clause
194 hgext/keyword.py:0:
194 hgext/keyword.py:0:
195 > ui.note("hg ci -m '%s'\n" % msg)
195 > ui.note("hg ci -m '%s'\n" % msg)
196 warning: unwrapped ui message
196 warning: unwrapped ui message
197 hgext/mq.py:0:
197 hgext/mq.py:0:
198 > raise util.Abort(_("cannot push --exact with applied patches"))
198 > raise util.Abort(_("cannot push --exact with applied patches"))
199 warning: line over 80 characters
199 warning: line over 80 characters
200 hgext/mq.py:0:
200 hgext/mq.py:0:
201 > raise util.Abort(_("cannot use --exact and --move together"))
201 > raise util.Abort(_("cannot use --exact and --move together"))
202 warning: line over 80 characters
202 warning: line over 80 characters
203 hgext/mq.py:0:
203 hgext/mq.py:0:
204 > self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
204 > self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
205 warning: line over 80 characters
205 warning: line over 80 characters
206 hgext/mq.py:0:
206 hgext/mq.py:0:
207 > except:
207 > except:
208 warning: naked except clause
208 warning: naked except clause
209 warning: naked except clause
209 warning: naked except clause
210 hgext/mq.py:0:
210 hgext/mq.py:0:
211 > except:
211 > except:
212 warning: naked except clause
212 warning: naked except clause
213 warning: naked except clause
213 warning: naked except clause
214 warning: naked except clause
214 warning: naked except clause
215 warning: naked except clause
215 warning: naked except clause
216 hgext/mq.py:0:
216 hgext/mq.py:0:
217 > raise util.Abort(_('cannot mix -l/--list with options or arguments'))
217 > raise util.Abort(_('cannot mix -l/--list with options or arguments'))
218 warning: line over 80 characters
218 warning: line over 80 characters
219 hgext/mq.py:0:
219 hgext/mq.py:0:
220 > raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
220 > raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
221 warning: line over 80 characters
221 warning: line over 80 characters
222 hgext/mq.py:0:
222 hgext/mq.py:0:
223 > ('', 'move', None, _('reorder patch series and apply only the patch'))],
223 > ('', 'move', None, _('reorder patch series and apply only the patch'))],
224 warning: line over 80 characters
224 warning: line over 80 characters
225 hgext/mq.py:0:
225 hgext/mq.py:0:
226 > ('U', 'noupdate', None, _('do not update the new working directories')),
226 > ('U', 'noupdate', None, _('do not update the new working directories')),
227 warning: line over 80 characters
227 warning: line over 80 characters
228 hgext/mq.py:0:
228 hgext/mq.py:0:
229 > ('e', 'exact', None, _('apply the target patch to its recorded parent')),
229 > ('e', 'exact', None, _('apply the target patch to its recorded parent')),
230 warning: line over 80 characters
230 warning: line over 80 characters
231 hgext/mq.py:0:
231 hgext/mq.py:0:
232 > except:
232 > except:
233 warning: naked except clause
233 warning: naked except clause
234 hgext/mq.py:0:
234 hgext/mq.py:0:
235 > ui.write("mq: %s\n" % ', '.join(m))
235 > ui.write("mq: %s\n" % ', '.join(m))
236 warning: unwrapped ui message
236 warning: unwrapped ui message
237 hgext/mq.py:0:
237 hgext/mq.py:0:
238 > repo.mq.qseries(repo, missing=opts.get('missing'), summary=opts.get('summary'))
238 > repo.mq.qseries(repo, missing=opts.get('missing'), summary=opts.get('summary'))
239 warning: line over 80 characters
239 warning: line over 80 characters
240 hgext/notify.py:0:
240 hgext/notify.py:0:
241 > ui.note(_('notify: suppressing notification for merge %d:%s\n') %
241 > ui.note(_('notify: suppressing notification for merge %d:%s\n') %
242 warning: line over 80 characters
242 warning: line over 80 characters
243 hgext/patchbomb.py:0:
243 hgext/patchbomb.py:0:
244 > binnode, seqno=idx, total=total)
244 > binnode, seqno=idx, total=total)
245 warning: line over 80 characters
245 warning: line over 80 characters
246 hgext/patchbomb.py:0:
246 hgext/patchbomb.py:0:
247 > except:
247 > except:
248 warning: naked except clause
248 warning: naked except clause
249 hgext/patchbomb.py:0:
249 hgext/patchbomb.py:0:
250 > ui.write('Subject: %s\n' % subj)
250 > ui.write('Subject: %s\n' % subj)
251 warning: unwrapped ui message
251 warning: unwrapped ui message
252 hgext/patchbomb.py:0:
252 hgext/patchbomb.py:0:
253 > p = mail.mimetextpatch('\n'.join(patchlines), 'x-patch', opts.get('test'))
253 > p = mail.mimetextpatch('\n'.join(patchlines), 'x-patch', opts.get('test'))
254 warning: line over 80 characters
254 warning: line over 80 characters
255 hgext/patchbomb.py:0:
255 hgext/patchbomb.py:0:
256 > ui.write('From: %s\n' % sender)
256 > ui.write('From: %s\n' % sender)
257 warning: unwrapped ui message
257 warning: unwrapped ui message
258 hgext/record.py:0:
258 hgext/record.py:0:
259 > ignoreblanklines=opts.get('ignore_blank_lines'))
259 > ignoreblanklines=opts.get('ignore_blank_lines'))
260 warning: line over 80 characters
260 warning: line over 80 characters
261 hgext/record.py:0:
261 hgext/record.py:0:
262 > ignorewsamount=opts.get('ignore_space_change'),
262 > ignorewsamount=opts.get('ignore_space_change'),
263 warning: line over 80 characters
263 warning: line over 80 characters
264 hgext/zeroconf/__init__.py:0:
264 hgext/zeroconf/__init__.py:0:
265 > publish(name, desc, path, util.getport(u.config("web", "port", 8000)))
265 > publish(name, desc, path, util.getport(u.config("web", "port", 8000)))
266 warning: line over 80 characters
266 warning: line over 80 characters
267 hgext/zeroconf/__init__.py:0:
267 hgext/zeroconf/__init__.py:0:
268 > except:
268 > except:
269 warning: naked except clause
269 warning: naked except clause
270 warning: naked except clause
270 warning: naked except clause
271 mercurial/bundlerepo.py:0:
271 mercurial/bundlerepo.py:0:
272 > is a bundlerepo for the obtained bundle when the original "other" is remote.
272 > is a bundlerepo for the obtained bundle when the original "other" is remote.
273 warning: line over 80 characters
273 warning: line over 80 characters
274 mercurial/bundlerepo.py:0:
274 mercurial/bundlerepo.py:0:
275 > "local" is a local repo from which to obtain the actual incoming changesets; it
275 > "local" is a local repo from which to obtain the actual incoming changesets; it
276 warning: line over 80 characters
276 warning: line over 80 characters
277 mercurial/bundlerepo.py:0:
277 mercurial/bundlerepo.py:0:
278 > tmp = discovery.findcommonincoming(repo, other, heads=onlyheads, force=force)
278 > tmp = discovery.findcommonincoming(repo, other, heads=onlyheads, force=force)
279 warning: line over 80 characters
279 warning: line over 80 characters
280 mercurial/commands.py:0:
280 mercurial/commands.py:0:
281 > " size " + basehdr + " link p1 p2 nodeid\n")
281 > " size " + basehdr + " link p1 p2 nodeid\n")
282 warning: line over 80 characters
282 warning: line over 80 characters
283 mercurial/commands.py:0:
283 mercurial/commands.py:0:
284 > raise util.Abort('cannot use localheads with old style discovery')
284 > raise util.Abort('cannot use localheads with old style discovery')
285 warning: line over 80 characters
285 warning: line over 80 characters
286 mercurial/commands.py:0:
286 mercurial/commands.py:0:
287 > ui.note('branch %s\n' % data)
287 > ui.note('branch %s\n' % data)
288 warning: unwrapped ui message
288 warning: unwrapped ui message
289 mercurial/commands.py:0:
289 mercurial/commands.py:0:
290 > ui.note('node %s\n' % str(data))
290 > ui.note('node %s\n' % str(data))
291 warning: unwrapped ui message
291 warning: unwrapped ui message
292 mercurial/commands.py:0:
292 mercurial/commands.py:0:
293 > ui.note('tag %s\n' % name)
293 > ui.note('tag %s\n' % name)
294 warning: unwrapped ui message
294 warning: unwrapped ui message
295 mercurial/commands.py:0:
295 mercurial/commands.py:0:
296 > ui.write("unpruned common: %s\n" % " ".join([short(n)
296 > ui.write("unpruned common: %s\n" % " ".join([short(n)
297 warning: unwrapped ui message
297 warning: unwrapped ui message
298 mercurial/commands.py:0:
298 mercurial/commands.py:0:
299 > yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
299 > yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
300 warning: line over 80 characters
300 warning: line over 80 characters
301 mercurial/commands.py:0:
301 mercurial/commands.py:0:
302 > yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
302 > yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
303 warning: line over 80 characters
303 warning: line over 80 characters
304 mercurial/commands.py:0:
304 mercurial/commands.py:0:
305 > except:
305 > except:
306 warning: naked except clause
306 warning: naked except clause
307 mercurial/commands.py:0:
307 mercurial/commands.py:0:
308 > ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to merge)\n"))
308 > ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to merge)\n"))
309 warning: line over 80 characters
309 warning: line over 80 characters
310 mercurial/commands.py:0:
310 mercurial/commands.py:0:
311 > ui.write("format: id, p1, p2, cset, delta base, len(delta)\n")
311 > ui.write("format: id, p1, p2, cset, delta base, len(delta)\n")
312 warning: unwrapped ui message
312 warning: unwrapped ui message
313 mercurial/commands.py:0:
313 mercurial/commands.py:0:
314 > ui.write("local is subset\n")
314 > ui.write("local is subset\n")
315 warning: unwrapped ui message
315 warning: unwrapped ui message
316 mercurial/commands.py:0:
316 mercurial/commands.py:0:
317 > ui.write("remote is subset\n")
317 > ui.write("remote is subset\n")
318 warning: unwrapped ui message
318 warning: unwrapped ui message
319 mercurial/commands.py:0:
319 mercurial/commands.py:0:
320 > ui.write(' other : ' + fmt2 % pcfmt(numoprev, numprev))
320 > ui.write(' other : ' + fmt2 % pcfmt(numoprev, numprev))
321 warning: line over 80 characters
321 warning: line over 80 characters
322 mercurial/commands.py:0:
322 mercurial/commands.py:0:
323 > ui.write(' where prev = p1 : ' + fmt2 % pcfmt(nump1prev, numprev))
323 > ui.write(' where prev = p1 : ' + fmt2 % pcfmt(nump1prev, numprev))
324 warning: line over 80 characters
324 warning: line over 80 characters
325 mercurial/commands.py:0:
325 mercurial/commands.py:0:
326 > ui.write(' where prev = p2 : ' + fmt2 % pcfmt(nump2prev, numprev))
326 > ui.write(' where prev = p2 : ' + fmt2 % pcfmt(nump2prev, numprev))
327 warning: line over 80 characters
327 warning: line over 80 characters
328 mercurial/commands.py:0:
328 mercurial/commands.py:0:
329 > ui.write('deltas against other : ' + fmt % pcfmt(numother, numdeltas))
329 > ui.write('deltas against other : ' + fmt % pcfmt(numother, numdeltas))
330 warning: line over 80 characters
330 warning: line over 80 characters
331 warning: unwrapped ui message
331 warning: unwrapped ui message
332 mercurial/commands.py:0:
332 mercurial/commands.py:0:
333 > ui.write('deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas))
333 > ui.write('deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas))
334 warning: unwrapped ui message
334 warning: unwrapped ui message
335 mercurial/commands.py:0:
335 mercurial/commands.py:0:
336 > ui.write('deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas))
336 > ui.write('deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas))
337 warning: unwrapped ui message
337 warning: unwrapped ui message
338 mercurial/commands.py:0:
338 mercurial/commands.py:0:
339 > except:
339 > except:
340 warning: naked except clause
340 warning: naked except clause
341 mercurial/commands.py:0:
341 mercurial/commands.py:0:
342 > revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
342 > revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
343 warning: line over 80 characters
343 warning: line over 80 characters
344 mercurial/commands.py:0:
344 mercurial/commands.py:0:
345 > ui.write("common heads: %s\n" % " ".join([short(n) for n in common]))
345 > ui.write("common heads: %s\n" % " ".join([short(n) for n in common]))
346 warning: unwrapped ui message
346 warning: unwrapped ui message
347 mercurial/commands.py:0:
347 mercurial/commands.py:0:
348 > ui.write("match: %s\n" % m(d[0]))
348 > ui.write("match: %s\n" % m(d[0]))
349 warning: unwrapped ui message
349 warning: unwrapped ui message
350 mercurial/commands.py:0:
350 mercurial/commands.py:0:
351 > ui.write('deltas against prev : ' + fmt % pcfmt(numprev, numdeltas))
351 > ui.write('deltas against prev : ' + fmt % pcfmt(numprev, numdeltas))
352 warning: unwrapped ui message
352 warning: unwrapped ui message
353 mercurial/commands.py:0:
353 mercurial/commands.py:0:
354 > ui.write('path %s\n' % k)
354 > ui.write('path %s\n' % k)
355 warning: unwrapped ui message
355 warning: unwrapped ui message
356 mercurial/commands.py:0:
356 mercurial/commands.py:0:
357 > ui.write('uncompressed data size (min/max/avg) : %d / %d / %d\n'
357 > ui.write('uncompressed data size (min/max/avg) : %d / %d / %d\n'
358 warning: unwrapped ui message
358 warning: unwrapped ui message
359 mercurial/commands.py:0:
359 mercurial/commands.py:0:
360 > Every ID must be a full-length hex node id string. Returns a list of 0s and 1s
360 > Every ID must be a full-length hex node id string. Returns a list of 0s and 1s
361 warning: line over 80 characters
361 warning: line over 80 characters
362 mercurial/commands.py:0:
362 mercurial/commands.py:0:
363 > remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl), opts.get('branch'))
363 > remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl), opts.get('branch'))
364 warning: line over 80 characters
364 warning: line over 80 characters
365 mercurial/commands.py:0:
365 mercurial/commands.py:0:
366 > ui.write("digraph G {\n")
366 > ui.write("digraph G {\n")
367 warning: unwrapped ui message
367 warning: unwrapped ui message
368 mercurial/commands.py:0:
368 mercurial/commands.py:0:
369 > ui.write("internal: %s %s\n" % d)
369 > ui.write("internal: %s %s\n" % d)
370 warning: unwrapped ui message
370 warning: unwrapped ui message
371 mercurial/commands.py:0:
371 mercurial/commands.py:0:
372 > ui.write("standard: %s\n" % util.datestr(d))
372 > ui.write("standard: %s\n" % util.datestr(d))
373 warning: unwrapped ui message
373 warning: unwrapped ui message
374 mercurial/commands.py:0:
374 mercurial/commands.py:0:
375 > ui.write('avg chain length : ' + fmt % avgchainlen)
375 > ui.write('avg chain length : ' + fmt % avgchainlen)
376 warning: unwrapped ui message
376 warning: unwrapped ui message
377 mercurial/commands.py:0:
377 mercurial/commands.py:0:
378 > ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
378 > ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
379 warning: unwrapped ui message
379 warning: unwrapped ui message
380 mercurial/commands.py:0:
380 mercurial/commands.py:0:
381 > ui.write('compression ratio : ' + fmt % compratio)
381 > ui.write('compression ratio : ' + fmt % compratio)
382 warning: unwrapped ui message
382 warning: unwrapped ui message
383 mercurial/commands.py:0:
383 mercurial/commands.py:0:
384 > ui.write('delta size (min/max/avg) : %d / %d / %d\n'
384 > ui.write('delta size (min/max/avg) : %d / %d / %d\n'
385 warning: unwrapped ui message
385 warning: unwrapped ui message
386 mercurial/commands.py:0:
386 mercurial/commands.py:0:
387 > ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
387 > ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
388 warning: unwrapped ui message
388 warning: unwrapped ui message
389 mercurial/commands.py:0:
389 mercurial/commands.py:0:
390 > ui.write('flags : %s\n' % ', '.join(flags))
390 > ui.write('flags : %s\n' % ', '.join(flags))
391 warning: unwrapped ui message
391 warning: unwrapped ui message
392 mercurial/commands.py:0:
392 mercurial/commands.py:0:
393 > ui.write('format : %d\n' % format)
393 > ui.write('format : %d\n' % format)
394 warning: unwrapped ui message
394 warning: unwrapped ui message
395 mercurial/commands.py:0:
395 mercurial/commands.py:0:
396 > ui.write('full revision size (min/max/avg) : %d / %d / %d\n'
396 > ui.write('full revision size (min/max/avg) : %d / %d / %d\n'
397 warning: unwrapped ui message
397 warning: unwrapped ui message
398 mercurial/commands.py:0:
398 mercurial/commands.py:0:
399 > ui.write('revision size : ' + fmt2 % totalsize)
399 > ui.write('revision size : ' + fmt2 % totalsize)
400 warning: unwrapped ui message
400 warning: unwrapped ui message
401 mercurial/commands.py:0:
401 mercurial/commands.py:0:
402 > ui.write('revisions : ' + fmt2 % numrevs)
402 > ui.write('revisions : ' + fmt2 % numrevs)
403 warning: unwrapped ui message
403 warning: unwrapped ui message
404 warning: unwrapped ui message
404 warning: unwrapped ui message
405 mercurial/commands.py:0:
405 mercurial/commands.py:0:
406 > ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
406 > ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
407 warning: unwrapped ui message
407 warning: unwrapped ui message
408 mercurial/commandserver.py:0:
408 mercurial/commandserver.py:0:
409 > # the ui here is really the repo ui so take its baseui so we don't end up
409 > # the ui here is really the repo ui so take its baseui so we don't end up
410 warning: line over 80 characters
410 warning: line over 80 characters
411 mercurial/context.py:0:
411 mercurial/context.py:0:
412 > return self._manifestdelta[path], self._manifestdelta.flags(path)
412 > return self._manifestdelta[path], self._manifestdelta.flags(path)
413 warning: line over 80 characters
413 warning: line over 80 characters
414 mercurial/dagparser.py:0:
414 mercurial/dagparser.py:0:
415 > raise util.Abort(_("invalid character in dag description: %s...") % s)
415 > raise util.Abort(_("invalid character in dag description: %s...") % s)
416 warning: line over 80 characters
416 warning: line over 80 characters
417 mercurial/dagparser.py:0:
417 mercurial/dagparser.py:0:
418 > >>> dagtext([('n', (0, [-1])), ('C', 'my command line'), ('n', (1, [0]))])
418 > >>> dagtext([('n', (0, [-1])), ('C', 'my command line'), ('n', (1, [0]))])
419 warning: line over 80 characters
419 warning: line over 80 characters
420 mercurial/dirstate.py:0:
420 mercurial/dirstate.py:0:
421 > if not st is None and not getkind(st.st_mode) in (regkind, lnkkind):
421 > if not st is None and not getkind(st.st_mode) in (regkind, lnkkind):
422 warning: line over 80 characters
422 warning: line over 80 characters
423 mercurial/discovery.py:0:
423 mercurial/discovery.py:0:
424 > If onlyheads is given, only nodes ancestral to nodes in onlyheads (inclusive)
424 > If onlyheads is given, only nodes ancestral to nodes in onlyheads (inclusive)
425 warning: line over 80 characters
425 warning: line over 80 characters
426 mercurial/discovery.py:0:
426 mercurial/discovery.py:0:
427 > def findcommonoutgoing(repo, other, onlyheads=None, force=False, commoninc=None):
427 > def findcommonoutgoing(repo, other, onlyheads=None, force=False, commoninc=None):
428 warning: line over 80 characters
428 warning: line over 80 characters
429 mercurial/dispatch.py:0:
429 mercurial/dispatch.py:0:
430 > " (.hg not found)") % os.getcwd())
430 > " (.hg not found)") % os.getcwd())
431 warning: line over 80 characters
431 warning: line over 80 characters
432 mercurial/dispatch.py:0:
432 mercurial/dispatch.py:0:
433 > except:
433 > except:
434 warning: naked except clause
434 warning: naked except clause
435 mercurial/dispatch.py:0:
435 mercurial/dispatch.py:0:
436 > return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d, [], {})
436 > return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d, [], {})
437 warning: line over 80 characters
437 warning: line over 80 characters
438 mercurial/dispatch.py:0:
438 mercurial/dispatch.py:0:
439 > def __init__(self, args, ui=None, repo=None, fin=None, fout=None, ferr=None):
439 > def __init__(self, args, ui=None, repo=None, fin=None, fout=None, ferr=None):
440 warning: line over 80 characters
440 warning: line over 80 characters
441 mercurial/dispatch.py:0:
441 mercurial/dispatch.py:0:
442 > except:
442 > except:
443 warning: naked except clause
443 warning: naked except clause
444 mercurial/hg.py:0:
444 mercurial/hg.py:0:
445 > except:
445 > except:
446 warning: naked except clause
446 warning: naked except clause
447 mercurial/hgweb/hgweb_mod.py:0:
447 mercurial/hgweb/hgweb_mod.py:0:
448 > self.maxshortchanges = int(self.config("web", "maxshortchanges", 60))
448 > self.maxshortchanges = int(self.config("web", "maxshortchanges", 60))
449 warning: line over 80 characters
449 warning: line over 80 characters
450 mercurial/keepalive.py:0:
450 mercurial/keepalive.py:0:
451 > except:
451 > except:
452 warning: naked except clause
452 warning: naked except clause
453 mercurial/keepalive.py:0:
453 mercurial/keepalive.py:0:
454 > except:
454 > except:
455 warning: naked except clause
455 warning: naked except clause
456 mercurial/localrepo.py:0:
456 mercurial/localrepo.py:0:
457 > # we return an integer indicating remote head count change
457 > # we return an integer indicating remote head count change
458 warning: line over 80 characters
458 warning: line over 80 characters
459 mercurial/localrepo.py:0:
459 mercurial/localrepo.py:0:
460 > raise util.Abort(_("empty or missing revlog for %s") % fname)
460 > raise util.Abort(_("empty or missing revlog for %s") % fname)
461 warning: line over 80 characters
461 warning: line over 80 characters
462 warning: line over 80 characters
462 warning: line over 80 characters
463 mercurial/localrepo.py:0:
463 mercurial/localrepo.py:0:
464 > if self._tagscache.tagtypes and name in self._tagscache.tagtypes:
464 > if self._tagscache.tagtypes and name in self._tagscache.tagtypes:
465 warning: line over 80 characters
465 warning: line over 80 characters
466 mercurial/localrepo.py:0:
466 mercurial/localrepo.py:0:
467 > self.hook("precommit", throw=True, parent1=hookp1, parent2=hookp2)
467 > self.hook("precommit", throw=True, parent1=hookp1, parent2=hookp2)
468 warning: line over 80 characters
468 warning: line over 80 characters
469 mercurial/localrepo.py:0:
469 mercurial/localrepo.py:0:
470 > # new requirements = old non-format requirements + new format-related
470 > # new requirements = old non-format requirements + new format-related
471 warning: line over 80 characters
471 warning: line over 80 characters
472 mercurial/localrepo.py:0:
472 mercurial/localrepo.py:0:
473 > except:
473 > except:
474 warning: naked except clause
474 warning: naked except clause
475 mercurial/localrepo.py:0:
475 mercurial/localrepo.py:0:
476 > """return status of files between two nodes or node and working directory
476 > """return status of files between two nodes or node and working directory
477 warning: line over 80 characters
477 warning: line over 80 characters
478 mercurial/localrepo.py:0:
478 mercurial/localrepo.py:0:
479 > '''Returns a tagscache object that contains various tags related caches.'''
479 > '''Returns a tagscache object that contains various tags related caches.'''
480 warning: line over 80 characters
480 warning: line over 80 characters
481 mercurial/manifest.py:0:
481 mercurial/manifest.py:0:
482 > return "".join(struct.pack(">lll", start, end, len(content)) + content
482 > return "".join(struct.pack(">lll", start, end, len(content)) + content
483 warning: line over 80 characters
483 warning: line over 80 characters
484 mercurial/merge.py:0:
484 mercurial/merge.py:0:
485 > subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx), overwrite)
485 > subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx), overwrite)
486 warning: line over 80 characters
486 warning: line over 80 characters
487 mercurial/patch.py:0:
487 mercurial/patch.py:0:
488 > modified, added, removed, copy, getfilectx, opts, losedata, prefix)
488 > modified, added, removed, copy, getfilectx, opts, losedata, prefix)
489 warning: line over 80 characters
489 warning: line over 80 characters
490 mercurial/patch.py:0:
490 mercurial/patch.py:0:
491 > diffhelpers.addlines(lr, self.hunk, self.lena, self.lenb, self.a, self.b)
491 > diffhelpers.addlines(lr, self.hunk, self.lena, self.lenb, self.a, self.b)
492 warning: line over 80 characters
492 warning: line over 80 characters
493 mercurial/patch.py:0:
493 mercurial/patch.py:0:
494 > output.append(_(' %d files changed, %d insertions(+), %d deletions(-)\n')
494 > output.append(_(' %d files changed, %d insertions(+), %d deletions(-)\n')
495 warning: line over 80 characters
495 warning: line over 80 characters
496 mercurial/patch.py:0:
496 mercurial/patch.py:0:
497 > except:
497 > except:
498 warning: naked except clause
498 warning: naked except clause
499 mercurial/pure/base85.py:0:
500 > raise OverflowError('Base85 overflow in hunk starting at byte %d' % i)
501 warning: line over 80 characters
502 mercurial/pure/mpatch.py:0:
499 mercurial/pure/mpatch.py:0:
503 > frags.extend(reversed(new)) # what was left at the end
500 > frags.extend(reversed(new)) # what was left at the end
504 warning: line over 80 characters
501 warning: line over 80 characters
505 mercurial/repair.py:0:
502 mercurial/repair.py:0:
506 > except:
503 > except:
507 warning: naked except clause
504 warning: naked except clause
508 mercurial/repair.py:0:
505 mercurial/repair.py:0:
509 > except:
506 > except:
510 warning: naked except clause
507 warning: naked except clause
511 mercurial/revset.py:0:
508 mercurial/revset.py:0:
512 > elif c.isalnum() or c in '._' or ord(c) > 127: # gather up a symbol/keyword
509 > elif c.isalnum() or c in '._' or ord(c) > 127: # gather up a symbol/keyword
513 warning: line over 80 characters
510 warning: line over 80 characters
514 mercurial/revset.py:0:
511 mercurial/revset.py:0:
515 > Changesets that are the Nth ancestor (first parents only) of a changeset in set.
512 > Changesets that are the Nth ancestor (first parents only) of a changeset in set.
516 warning: line over 80 characters
513 warning: line over 80 characters
517 mercurial/scmutil.py:0:
514 mercurial/scmutil.py:0:
518 > raise util.Abort(_("path '%s' is inside nested repo %r") %
515 > raise util.Abort(_("path '%s' is inside nested repo %r") %
519 warning: line over 80 characters
516 warning: line over 80 characters
520 mercurial/scmutil.py:0:
517 mercurial/scmutil.py:0:
521 > "requires features '%s' (upgrade Mercurial)") % "', '".join(missings))
518 > "requires features '%s' (upgrade Mercurial)") % "', '".join(missings))
522 warning: line over 80 characters
519 warning: line over 80 characters
523 mercurial/scmutil.py:0:
520 mercurial/scmutil.py:0:
524 > elif repo.dirstate[abs] != 'r' and (not good or not os.path.lexists(target)
521 > elif repo.dirstate[abs] != 'r' and (not good or not os.path.lexists(target)
525 warning: line over 80 characters
522 warning: line over 80 characters
526 mercurial/setdiscovery.py:0:
523 mercurial/setdiscovery.py:0:
527 > # treat remote heads (and maybe own heads) as a first implicit sample response
524 > # treat remote heads (and maybe own heads) as a first implicit sample response
528 warning: line over 80 characters
525 warning: line over 80 characters
529 mercurial/setdiscovery.py:0:
526 mercurial/setdiscovery.py:0:
530 > undecided = dag.nodeset() # own nodes where I don't know if remote knows them
527 > undecided = dag.nodeset() # own nodes where I don't know if remote knows them
531 warning: line over 80 characters
528 warning: line over 80 characters
532 mercurial/similar.py:0:
529 mercurial/similar.py:0:
533 > repo.ui.progress(_('searching for similar files'), i, total=len(removed))
530 > repo.ui.progress(_('searching for similar files'), i, total=len(removed))
534 warning: line over 80 characters
531 warning: line over 80 characters
535 mercurial/simplemerge.py:0:
532 mercurial/simplemerge.py:0:
536 > for zmatch, zend, amatch, aend, bmatch, bend in self.find_sync_regions():
533 > for zmatch, zend, amatch, aend, bmatch, bend in self.find_sync_regions():
537 warning: line over 80 characters
534 warning: line over 80 characters
538 mercurial/sshrepo.py:0:
535 mercurial/sshrepo.py:0:
539 > self._abort(error.RepoError(_("no suitable response from remote hg")))
536 > self._abort(error.RepoError(_("no suitable response from remote hg")))
540 warning: line over 80 characters
537 warning: line over 80 characters
541 mercurial/sshrepo.py:0:
538 mercurial/sshrepo.py:0:
542 > except:
539 > except:
543 warning: naked except clause
540 warning: naked except clause
544 mercurial/subrepo.py:0:
541 mercurial/subrepo.py:0:
545 > other, self._repo = hg.clone(self._repo._subparent.ui, {}, other,
542 > other, self._repo = hg.clone(self._repo._subparent.ui, {}, other,
546 warning: line over 80 characters
543 warning: line over 80 characters
547 mercurial/subrepo.py:0:
544 mercurial/subrepo.py:0:
548 > msg = (_(' subrepository sources for %s differ (in checked out version)\n'
545 > msg = (_(' subrepository sources for %s differ (in checked out version)\n'
549 warning: line over 80 characters
546 warning: line over 80 characters
550 mercurial/transaction.py:0:
547 mercurial/transaction.py:0:
551 > except:
548 > except:
552 warning: naked except clause
549 warning: naked except clause
553 mercurial/ui.py:0:
550 mercurial/ui.py:0:
554 > traceback.print_exception(exc[0], exc[1], exc[2], file=self.ferr)
551 > traceback.print_exception(exc[0], exc[1], exc[2], file=self.ferr)
555 warning: line over 80 characters
552 warning: line over 80 characters
556 mercurial/url.py:0:
553 mercurial/url.py:0:
557 > conn = httpsconnection(host, port, keyfile, certfile, *args, **kwargs)
554 > conn = httpsconnection(host, port, keyfile, certfile, *args, **kwargs)
558 warning: line over 80 characters
555 warning: line over 80 characters
559 mercurial/util.py:0:
556 mercurial/util.py:0:
560 > except:
557 > except:
561 warning: naked except clause
558 warning: naked except clause
562 mercurial/util.py:0:
559 mercurial/util.py:0:
563 > except:
560 > except:
564 warning: naked except clause
561 warning: naked except clause
565 mercurial/verify.py:0:
562 mercurial/verify.py:0:
566 > except:
563 > except:
567 warning: naked except clause
564 warning: naked except clause
568 mercurial/verify.py:0:
565 mercurial/verify.py:0:
569 > except:
566 > except:
570 warning: naked except clause
567 warning: naked except clause
571 mercurial/wireproto.py:0:
568 mercurial/wireproto.py:0:
572 > # Assuming the future to be filled with the result from the batched request
569 > # Assuming the future to be filled with the result from the batched request
573 warning: line over 80 characters
570 warning: line over 80 characters
574 mercurial/wireproto.py:0:
571 mercurial/wireproto.py:0:
575 > '''remote must support _submitbatch(encbatch) and _submitone(op, encargs)'''
572 > '''remote must support _submitbatch(encbatch) and _submitone(op, encargs)'''
576 warning: line over 80 characters
573 warning: line over 80 characters
577 mercurial/wireproto.py:0:
574 mercurial/wireproto.py:0:
578 > All methods invoked on instances of this class are simply queued and return a
575 > All methods invoked on instances of this class are simply queued and return a
579 warning: line over 80 characters
576 warning: line over 80 characters
580 mercurial/wireproto.py:0:
577 mercurial/wireproto.py:0:
581 > The decorator returns a function which wraps this coroutine as a plain method,
578 > The decorator returns a function which wraps this coroutine as a plain method,
582 warning: line over 80 characters
579 warning: line over 80 characters
583 setup.py:0:
580 setup.py:0:
584 > raise SystemExit("Python headers are required to build Mercurial")
581 > raise SystemExit("Python headers are required to build Mercurial")
585 warning: line over 80 characters
582 warning: line over 80 characters
586 setup.py:0:
583 setup.py:0:
587 > except:
584 > except:
588 warning: naked except clause
585 warning: naked except clause
589 setup.py:0:
586 setup.py:0:
590 > # build_py), it will not find osutil & friends, thinking that those modules are
587 > # build_py), it will not find osutil & friends, thinking that those modules are
591 warning: line over 80 characters
588 warning: line over 80 characters
592 setup.py:0:
589 setup.py:0:
593 > except:
590 > except:
594 warning: naked except clause
591 warning: naked except clause
595 warning: naked except clause
592 warning: naked except clause
596 setup.py:0:
593 setup.py:0:
597 > isironpython = platform.python_implementation().lower().find("ironpython") != -1
594 > isironpython = platform.python_implementation().lower().find("ironpython") != -1
598 warning: line over 80 characters
595 warning: line over 80 characters
599 setup.py:0:
596 setup.py:0:
600 > except:
597 > except:
601 warning: naked except clause
598 warning: naked except clause
602 warning: naked except clause
599 warning: naked except clause
603 warning: naked except clause
600 warning: naked except clause
604 tests/autodiff.py:0:
601 tests/autodiff.py:0:
605 > ui.write('data lost for: %s\n' % fn)
602 > ui.write('data lost for: %s\n' % fn)
606 warning: unwrapped ui message
603 warning: unwrapped ui message
607 tests/run-tests.py:0:
604 tests/run-tests.py:0:
608 > except:
605 > except:
609 warning: naked except clause
606 warning: naked except clause
610 tests/test-commandserver.py:0:
607 tests/test-commandserver.py:0:
611 > 'hooks.pre-identify=python:test-commandserver.hook', 'id'],
608 > 'hooks.pre-identify=python:test-commandserver.hook', 'id'],
612 warning: line over 80 characters
609 warning: line over 80 characters
613 tests/test-commandserver.py:0:
610 tests/test-commandserver.py:0:
614 > # the cached repo local hgrc contains ui.foo=bar, so showconfig should show it
611 > # the cached repo local hgrc contains ui.foo=bar, so showconfig should show it
615 warning: line over 80 characters
612 warning: line over 80 characters
616 tests/test-commandserver.py:0:
613 tests/test-commandserver.py:0:
617 > print '%c, %r' % (ch, re.sub('encoding: [a-zA-Z0-9-]+', 'encoding: ***', data))
614 > print '%c, %r' % (ch, re.sub('encoding: [a-zA-Z0-9-]+', 'encoding: ***', data))
618 warning: line over 80 characters
615 warning: line over 80 characters
619 tests/test-filecache.py:0:
616 tests/test-filecache.py:0:
620 > except:
617 > except:
621 warning: naked except clause
618 warning: naked except clause
622 tests/test-filecache.py:0:
619 tests/test-filecache.py:0:
623 > if subprocess.call(['python', '%s/hghave' % os.environ['TESTDIR'], 'cacheable']):
620 > if subprocess.call(['python', '%s/hghave' % os.environ['TESTDIR'], 'cacheable']):
624 warning: line over 80 characters
621 warning: line over 80 characters
625 tests/test-ui-color.py:0:
622 tests/test-ui-color.py:0:
626 > testui.warn('warning\n')
623 > testui.warn('warning\n')
627 warning: unwrapped ui message
624 warning: unwrapped ui message
628 tests/test-ui-color.py:0:
625 tests/test-ui-color.py:0:
629 > testui.write('buffered\n')
626 > testui.write('buffered\n')
630 warning: unwrapped ui message
627 warning: unwrapped ui message
631 tests/test-walkrepo.py:0:
628 tests/test-walkrepo.py:0:
632 > print "Found %d repositories when I should have found 2" % (len(reposet),)
629 > print "Found %d repositories when I should have found 2" % (len(reposet),)
633 warning: line over 80 characters
630 warning: line over 80 characters
634 tests/test-walkrepo.py:0:
631 tests/test-walkrepo.py:0:
635 > print "Found %d repositories when I should have found 3" % (len(reposet),)
632 > print "Found %d repositories when I should have found 3" % (len(reposet),)
636 warning: line over 80 characters
633 warning: line over 80 characters
@@ -1,1147 +1,1172 b''
1 $ "$TESTDIR/hghave" symlink unix-permissions serve || exit 80
1 $ "$TESTDIR/hghave" symlink unix-permissions serve || exit 80
2 $ USERCACHE=`pwd`/cache; export USERCACHE
2 $ USERCACHE=`pwd`/cache; export USERCACHE
3 $ mkdir -p ${USERCACHE}
3 $ mkdir -p ${USERCACHE}
4 $ cat >> $HGRCPATH <<EOF
4 $ cat >> $HGRCPATH <<EOF
5 > [extensions]
5 > [extensions]
6 > largefiles=
6 > largefiles=
7 > purge=
7 > purge=
8 > rebase=
8 > rebase=
9 > transplant=
9 > transplant=
10 > [phases]
10 > [phases]
11 > publish=False
11 > publish=False
12 > [largefiles]
12 > [largefiles]
13 > minsize=2
13 > minsize=2
14 > patterns=glob:**.dat
14 > patterns=glob:**.dat
15 > usercache=${USERCACHE}
15 > usercache=${USERCACHE}
16 > [hooks]
16 > [hooks]
17 > precommit=echo "Invoking status precommit hook"; hg status
17 > precommit=echo "Invoking status precommit hook"; hg status
18 > EOF
18 > EOF
19
19
20 Create the repo with a couple of revisions of both large and normal
20 Create the repo with a couple of revisions of both large and normal
21 files, testing that status correctly shows largefiles and that summary output
21 files, testing that status correctly shows largefiles and that summary output
22 is correct.
22 is correct.
23
23
24 $ hg init a
24 $ hg init a
25 $ cd a
25 $ cd a
26 $ mkdir sub
26 $ mkdir sub
27 $ echo normal1 > normal1
27 $ echo normal1 > normal1
28 $ echo normal2 > sub/normal2
28 $ echo normal2 > sub/normal2
29 $ echo large1 > large1
29 $ echo large1 > large1
30 $ echo large2 > sub/large2
30 $ echo large2 > sub/large2
31 $ hg add normal1 sub/normal2
31 $ hg add normal1 sub/normal2
32 $ hg add --large large1 sub/large2
32 $ hg add --large large1 sub/large2
33 $ hg commit -m "add files"
33 $ hg commit -m "add files"
34 Invoking status precommit hook
34 Invoking status precommit hook
35 A large1
35 A large1
36 A normal1
36 A normal1
37 A sub/large2
37 A sub/large2
38 A sub/normal2
38 A sub/normal2
39 $ echo normal11 > normal1
39 $ echo normal11 > normal1
40 $ echo normal22 > sub/normal2
40 $ echo normal22 > sub/normal2
41 $ echo large11 > large1
41 $ echo large11 > large1
42 $ echo large22 > sub/large2
42 $ echo large22 > sub/large2
43 $ hg commit -m "edit files"
43 $ hg commit -m "edit files"
44 Invoking status precommit hook
44 Invoking status precommit hook
45 M large1
45 M large1
46 M normal1
46 M normal1
47 M sub/large2
47 M sub/large2
48 M sub/normal2
48 M sub/normal2
49 $ hg sum --large
49 $ hg sum --large
50 parent: 1:ce8896473775 tip
50 parent: 1:ce8896473775 tip
51 edit files
51 edit files
52 branch: default
52 branch: default
53 commit: (clean)
53 commit: (clean)
54 update: (current)
54 update: (current)
55 largefiles: No remote repo
55 largefiles: No remote repo
56
56
57 Commit preserved largefile contents.
57 Commit preserved largefile contents.
58
58
59 $ cat normal1
59 $ cat normal1
60 normal11
60 normal11
61 $ cat large1
61 $ cat large1
62 large11
62 large11
63 $ cat sub/normal2
63 $ cat sub/normal2
64 normal22
64 normal22
65 $ cat sub/large2
65 $ cat sub/large2
66 large22
66 large22
67
67
68 Test status, subdir and unknown files
68 Test status, subdir and unknown files
69
69
70 $ echo unknown > sub/unknown
70 $ echo unknown > sub/unknown
71 $ hg st --all
71 $ hg st --all
72 ? sub/unknown
72 ? sub/unknown
73 C large1
73 C large1
74 C normal1
74 C normal1
75 C sub/large2
75 C sub/large2
76 C sub/normal2
76 C sub/normal2
77 $ hg st --all sub
77 $ hg st --all sub
78 ? sub/unknown
78 ? sub/unknown
79 C sub/large2
79 C sub/large2
80 C sub/normal2
80 C sub/normal2
81 $ rm sub/unknown
81 $ rm sub/unknown
82
82
83 Remove both largefiles and normal files.
83 Remove both largefiles and normal files.
84
84
85 $ hg remove normal1 large1
85 $ hg remove normal1 large1
86 $ hg status large1
86 $ hg status large1
87 R large1
87 R large1
88 $ hg commit -m "remove files"
88 $ hg commit -m "remove files"
89 Invoking status precommit hook
89 Invoking status precommit hook
90 R large1
90 R large1
91 R normal1
91 R normal1
92 $ ls
92 $ ls
93 sub
93 sub
94 $ echo "testlargefile" > large1-test
94 $ echo "testlargefile" > large1-test
95 $ hg add --large large1-test
95 $ hg add --large large1-test
96 $ hg st
96 $ hg st
97 A large1-test
97 A large1-test
98 $ hg rm large1-test
98 $ hg rm large1-test
99 not removing large1-test: file has been marked for add (use forget to undo)
99 not removing large1-test: file has been marked for add (use forget to undo)
100 $ hg st
100 $ hg st
101 A large1-test
101 A large1-test
102 $ hg forget large1-test
102 $ hg forget large1-test
103 $ hg st
103 $ hg st
104 ? large1-test
104 ? large1-test
105 $ rm large1-test
105 $ rm large1-test
106
106
107 Copy both largefiles and normal files (testing that status output is correct).
107 Copy both largefiles and normal files (testing that status output is correct).
108
108
109 $ hg cp sub/normal2 normal1
109 $ hg cp sub/normal2 normal1
110 $ hg cp sub/large2 large1
110 $ hg cp sub/large2 large1
111 $ hg commit -m "copy files"
111 $ hg commit -m "copy files"
112 Invoking status precommit hook
112 Invoking status precommit hook
113 A large1
113 A large1
114 A normal1
114 A normal1
115 $ cat normal1
115 $ cat normal1
116 normal22
116 normal22
117 $ cat large1
117 $ cat large1
118 large22
118 large22
119
119
120 Test moving largefiles and verify that normal files are also unaffected.
120 Test moving largefiles and verify that normal files are also unaffected.
121
121
122 $ hg mv normal1 normal3
122 $ hg mv normal1 normal3
123 $ hg mv large1 large3
123 $ hg mv large1 large3
124 $ hg mv sub/normal2 sub/normal4
124 $ hg mv sub/normal2 sub/normal4
125 $ hg mv sub/large2 sub/large4
125 $ hg mv sub/large2 sub/large4
126 $ hg commit -m "move files"
126 $ hg commit -m "move files"
127 Invoking status precommit hook
127 Invoking status precommit hook
128 A large3
128 A large3
129 A normal3
129 A normal3
130 A sub/large4
130 A sub/large4
131 A sub/normal4
131 A sub/normal4
132 R large1
132 R large1
133 R normal1
133 R normal1
134 R sub/large2
134 R sub/large2
135 R sub/normal2
135 R sub/normal2
136 $ cat normal3
136 $ cat normal3
137 normal22
137 normal22
138 $ cat large3
138 $ cat large3
139 large22
139 large22
140 $ cat sub/normal4
140 $ cat sub/normal4
141 normal22
141 normal22
142 $ cat sub/large4
142 $ cat sub/large4
143 large22
143 large22
144
144
145 Test display of largefiles in hgweb
145 Test display of largefiles in hgweb
146
146
147 $ hg serve -d -p $HGPORT --pid-file ../hg.pid
147 $ hg serve -d -p $HGPORT --pid-file ../hg.pid
148 $ cat ../hg.pid >> $DAEMON_PIDS
148 $ cat ../hg.pid >> $DAEMON_PIDS
149 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/file/tip/?style=raw'
149 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/file/tip/?style=raw'
150 200 Script output follows
150 200 Script output follows
151
151
152
152
153 drwxr-xr-x sub
153 drwxr-xr-x sub
154 -rw-r--r-- 41 large3
154 -rw-r--r-- 41 large3
155 -rw-r--r-- 9 normal3
155 -rw-r--r-- 9 normal3
156
156
157
157
158 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/file/tip/sub/?style=raw'
158 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/file/tip/sub/?style=raw'
159 200 Script output follows
159 200 Script output follows
160
160
161
161
162 -rw-r--r-- 41 large4
162 -rw-r--r-- 41 large4
163 -rw-r--r-- 9 normal4
163 -rw-r--r-- 9 normal4
164
164
165
165
166 $ "$TESTDIR/killdaemons.py"
166 $ "$TESTDIR/killdaemons.py"
167
167
168 Test archiving the various revisions. These hit corner cases known with
168 Test archiving the various revisions. These hit corner cases known with
169 archiving.
169 archiving.
170
170
171 $ hg archive -r 0 ../archive0
171 $ hg archive -r 0 ../archive0
172 $ hg archive -r 1 ../archive1
172 $ hg archive -r 1 ../archive1
173 $ hg archive -r 2 ../archive2
173 $ hg archive -r 2 ../archive2
174 $ hg archive -r 3 ../archive3
174 $ hg archive -r 3 ../archive3
175 $ hg archive -r 4 ../archive4
175 $ hg archive -r 4 ../archive4
176 $ cd ../archive0
176 $ cd ../archive0
177 $ cat normal1
177 $ cat normal1
178 normal1
178 normal1
179 $ cat large1
179 $ cat large1
180 large1
180 large1
181 $ cat sub/normal2
181 $ cat sub/normal2
182 normal2
182 normal2
183 $ cat sub/large2
183 $ cat sub/large2
184 large2
184 large2
185 $ cd ../archive1
185 $ cd ../archive1
186 $ cat normal1
186 $ cat normal1
187 normal11
187 normal11
188 $ cat large1
188 $ cat large1
189 large11
189 large11
190 $ cat sub/normal2
190 $ cat sub/normal2
191 normal22
191 normal22
192 $ cat sub/large2
192 $ cat sub/large2
193 large22
193 large22
194 $ cd ../archive2
194 $ cd ../archive2
195 $ ls
195 $ ls
196 sub
196 sub
197 $ cat sub/normal2
197 $ cat sub/normal2
198 normal22
198 normal22
199 $ cat sub/large2
199 $ cat sub/large2
200 large22
200 large22
201 $ cd ../archive3
201 $ cd ../archive3
202 $ cat normal1
202 $ cat normal1
203 normal22
203 normal22
204 $ cat large1
204 $ cat large1
205 large22
205 large22
206 $ cat sub/normal2
206 $ cat sub/normal2
207 normal22
207 normal22
208 $ cat sub/large2
208 $ cat sub/large2
209 large22
209 large22
210 $ cd ../archive4
210 $ cd ../archive4
211 $ cat normal3
211 $ cat normal3
212 normal22
212 normal22
213 $ cat large3
213 $ cat large3
214 large22
214 large22
215 $ cat sub/normal4
215 $ cat sub/normal4
216 normal22
216 normal22
217 $ cat sub/large4
217 $ cat sub/large4
218 large22
218 large22
219
219
220 Commit corner case: specify files to commit.
220 Commit corner case: specify files to commit.
221
221
222 $ cd ../a
222 $ cd ../a
223 $ echo normal3 > normal3
223 $ echo normal3 > normal3
224 $ echo large3 > large3
224 $ echo large3 > large3
225 $ echo normal4 > sub/normal4
225 $ echo normal4 > sub/normal4
226 $ echo large4 > sub/large4
226 $ echo large4 > sub/large4
227 $ hg commit normal3 large3 sub/normal4 sub/large4 -m "edit files again"
227 $ hg commit normal3 large3 sub/normal4 sub/large4 -m "edit files again"
228 Invoking status precommit hook
228 Invoking status precommit hook
229 M large3
229 M large3
230 M normal3
230 M normal3
231 M sub/large4
231 M sub/large4
232 M sub/normal4
232 M sub/normal4
233 $ cat normal3
233 $ cat normal3
234 normal3
234 normal3
235 $ cat large3
235 $ cat large3
236 large3
236 large3
237 $ cat sub/normal4
237 $ cat sub/normal4
238 normal4
238 normal4
239 $ cat sub/large4
239 $ cat sub/large4
240 large4
240 large4
241
241
242 One more commit corner case: commit from a subdirectory.
242 One more commit corner case: commit from a subdirectory.
243
243
244 $ cd ../a
244 $ cd ../a
245 $ echo normal33 > normal3
245 $ echo normal33 > normal3
246 $ echo large33 > large3
246 $ echo large33 > large3
247 $ echo normal44 > sub/normal4
247 $ echo normal44 > sub/normal4
248 $ echo large44 > sub/large4
248 $ echo large44 > sub/large4
249 $ cd sub
249 $ cd sub
250 $ hg commit -m "edit files yet again"
250 $ hg commit -m "edit files yet again"
251 Invoking status precommit hook
251 Invoking status precommit hook
252 M large3
252 M large3
253 M normal3
253 M normal3
254 M sub/large4
254 M sub/large4
255 M sub/normal4
255 M sub/normal4
256 $ cat ../normal3
256 $ cat ../normal3
257 normal33
257 normal33
258 $ cat ../large3
258 $ cat ../large3
259 large33
259 large33
260 $ cat normal4
260 $ cat normal4
261 normal44
261 normal44
262 $ cat large4
262 $ cat large4
263 large44
263 large44
264
264
265 Committing standins is not allowed.
265 Committing standins is not allowed.
266
266
267 $ cd ..
267 $ cd ..
268 $ echo large3 > large3
268 $ echo large3 > large3
269 $ hg commit .hglf/large3 -m "try to commit standin"
269 $ hg commit .hglf/large3 -m "try to commit standin"
270 abort: file ".hglf/large3" is a largefile standin
270 abort: file ".hglf/large3" is a largefile standin
271 (commit the largefile itself instead)
271 (commit the largefile itself instead)
272 [255]
272 [255]
273
273
274 Corner cases for adding largefiles.
274 Corner cases for adding largefiles.
275
275
276 $ echo large5 > large5
276 $ echo large5 > large5
277 $ hg add --large large5
277 $ hg add --large large5
278 $ hg add --large large5
278 $ hg add --large large5
279 large5 already a largefile
279 large5 already a largefile
280 $ mkdir sub2
280 $ mkdir sub2
281 $ echo large6 > sub2/large6
281 $ echo large6 > sub2/large6
282 $ echo large7 > sub2/large7
282 $ echo large7 > sub2/large7
283 $ hg add --large sub2
283 $ hg add --large sub2
284 adding sub2/large6 as a largefile (glob)
284 adding sub2/large6 as a largefile (glob)
285 adding sub2/large7 as a largefile (glob)
285 adding sub2/large7 as a largefile (glob)
286 $ hg st
286 $ hg st
287 M large3
287 M large3
288 A large5
288 A large5
289 A sub2/large6
289 A sub2/large6
290 A sub2/large7
290 A sub2/large7
291
291
292 Test "hg status" with combination of 'file pattern' and 'directory
292 Test "hg status" with combination of 'file pattern' and 'directory
293 pattern' for largefiles:
293 pattern' for largefiles:
294
294
295 $ hg status sub2/large6 sub2
295 $ hg status sub2/large6 sub2
296 A sub2/large6
296 A sub2/large6
297 A sub2/large7
297 A sub2/large7
298
298
299 Config settings (pattern **.dat, minsize 2 MB) are respected.
299 Config settings (pattern **.dat, minsize 2 MB) are respected.
300
300
301 $ echo testdata > test.dat
301 $ echo testdata > test.dat
302 $ dd bs=1k count=2k if=/dev/zero of=reallylarge > /dev/null 2> /dev/null
302 $ dd bs=1k count=2k if=/dev/zero of=reallylarge > /dev/null 2> /dev/null
303 $ hg add
303 $ hg add
304 adding reallylarge as a largefile
304 adding reallylarge as a largefile
305 adding test.dat as a largefile
305 adding test.dat as a largefile
306
306
307 Test that minsize and --lfsize handle float values;
307 Test that minsize and --lfsize handle float values;
308 also tests that --lfsize overrides largefiles.minsize.
308 also tests that --lfsize overrides largefiles.minsize.
309 (0.250 MB = 256 kB = 262144 B)
309 (0.250 MB = 256 kB = 262144 B)
310
310
311 $ dd if=/dev/zero of=ratherlarge bs=1024 count=256 > /dev/null 2> /dev/null
311 $ dd if=/dev/zero of=ratherlarge bs=1024 count=256 > /dev/null 2> /dev/null
312 $ dd if=/dev/zero of=medium bs=1024 count=128 > /dev/null 2> /dev/null
312 $ dd if=/dev/zero of=medium bs=1024 count=128 > /dev/null 2> /dev/null
313 $ hg --config largefiles.minsize=.25 add
313 $ hg --config largefiles.minsize=.25 add
314 adding ratherlarge as a largefile
314 adding ratherlarge as a largefile
315 adding medium
315 adding medium
316 $ hg forget medium
316 $ hg forget medium
317 $ hg --config largefiles.minsize=.25 add --lfsize=.125
317 $ hg --config largefiles.minsize=.25 add --lfsize=.125
318 adding medium as a largefile
318 adding medium as a largefile
319 $ dd if=/dev/zero of=notlarge bs=1024 count=127 > /dev/null 2> /dev/null
319 $ dd if=/dev/zero of=notlarge bs=1024 count=127 > /dev/null 2> /dev/null
320 $ hg --config largefiles.minsize=.25 add --lfsize=.125
320 $ hg --config largefiles.minsize=.25 add --lfsize=.125
321 adding notlarge
321 adding notlarge
322 $ hg forget notlarge
322 $ hg forget notlarge
323
323
324 Test forget on largefiles.
324 Test forget on largefiles.
325
325
326 $ hg forget large3 large5 test.dat reallylarge ratherlarge medium
326 $ hg forget large3 large5 test.dat reallylarge ratherlarge medium
327 $ hg commit -m "add/edit more largefiles"
327 $ hg commit -m "add/edit more largefiles"
328 Invoking status precommit hook
328 Invoking status precommit hook
329 A sub2/large6
329 A sub2/large6
330 A sub2/large7
330 A sub2/large7
331 R large3
331 R large3
332 ? large5
332 ? large5
333 ? medium
333 ? medium
334 ? notlarge
334 ? notlarge
335 ? ratherlarge
335 ? ratherlarge
336 ? reallylarge
336 ? reallylarge
337 ? test.dat
337 ? test.dat
338 $ hg st
338 $ hg st
339 ? large3
339 ? large3
340 ? large5
340 ? large5
341 ? medium
341 ? medium
342 ? notlarge
342 ? notlarge
343 ? ratherlarge
343 ? ratherlarge
344 ? reallylarge
344 ? reallylarge
345 ? test.dat
345 ? test.dat
346
346
347 Purge with largefiles: verify that largefiles are still in the working
347 Purge with largefiles: verify that largefiles are still in the working
348 dir after a purge.
348 dir after a purge.
349
349
350 $ hg purge --all
350 $ hg purge --all
351 $ cat sub/large4
351 $ cat sub/large4
352 large44
352 large44
353 $ cat sub2/large6
353 $ cat sub2/large6
354 large6
354 large6
355 $ cat sub2/large7
355 $ cat sub2/large7
356 large7
356 large7
357
357
358 Test addremove: verify that files that should be added as largfiles are added as
358 Test addremove: verify that files that should be added as largfiles are added as
359 such and that already-existing largfiles are not added as normal files by
359 such and that already-existing largfiles are not added as normal files by
360 accident.
360 accident.
361
361
362 $ rm normal3
362 $ rm normal3
363 $ rm sub/large4
363 $ rm sub/large4
364 $ echo "testing addremove with patterns" > testaddremove.dat
364 $ echo "testing addremove with patterns" > testaddremove.dat
365 $ echo "normaladdremove" > normaladdremove
365 $ echo "normaladdremove" > normaladdremove
366 $ hg addremove
366 $ hg addremove
367 removing sub/large4
367 removing sub/large4
368 adding testaddremove.dat as a largefile
368 adding testaddremove.dat as a largefile
369 removing normal3
369 removing normal3
370 adding normaladdremove
370 adding normaladdremove
371
371
372 Clone a largefiles repo.
372 Clone a largefiles repo.
373
373
374 $ hg clone . ../b
374 $ hg clone . ../b
375 updating to branch default
375 updating to branch default
376 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
376 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
377 getting changed largefiles
377 getting changed largefiles
378 3 largefiles updated, 0 removed
378 3 largefiles updated, 0 removed
379 $ cd ../b
379 $ cd ../b
380 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
380 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
381 7:daea875e9014 add/edit more largefiles
381 7:daea875e9014 add/edit more largefiles
382 6:4355d653f84f edit files yet again
382 6:4355d653f84f edit files yet again
383 5:9d5af5072dbd edit files again
383 5:9d5af5072dbd edit files again
384 4:74c02385b94c move files
384 4:74c02385b94c move files
385 3:9e8fbc4bce62 copy files
385 3:9e8fbc4bce62 copy files
386 2:51a0ae4d5864 remove files
386 2:51a0ae4d5864 remove files
387 1:ce8896473775 edit files
387 1:ce8896473775 edit files
388 0:30d30fe6a5be add files
388 0:30d30fe6a5be add files
389 $ cat normal3
389 $ cat normal3
390 normal33
390 normal33
391 $ cat sub/normal4
391 $ cat sub/normal4
392 normal44
392 normal44
393 $ cat sub/large4
393 $ cat sub/large4
394 large44
394 large44
395 $ cat sub2/large6
395 $ cat sub2/large6
396 large6
396 large6
397 $ cat sub2/large7
397 $ cat sub2/large7
398 large7
398 large7
399 $ cd ..
399 $ cd ..
400 $ hg clone a -r 3 c
400 $ hg clone a -r 3 c
401 adding changesets
401 adding changesets
402 adding manifests
402 adding manifests
403 adding file changes
403 adding file changes
404 added 4 changesets with 10 changes to 4 files
404 added 4 changesets with 10 changes to 4 files
405 updating to branch default
405 updating to branch default
406 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
406 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
407 getting changed largefiles
407 getting changed largefiles
408 2 largefiles updated, 0 removed
408 2 largefiles updated, 0 removed
409 $ cd c
409 $ cd c
410 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
410 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
411 3:9e8fbc4bce62 copy files
411 3:9e8fbc4bce62 copy files
412 2:51a0ae4d5864 remove files
412 2:51a0ae4d5864 remove files
413 1:ce8896473775 edit files
413 1:ce8896473775 edit files
414 0:30d30fe6a5be add files
414 0:30d30fe6a5be add files
415 $ cat normal1
415 $ cat normal1
416 normal22
416 normal22
417 $ cat large1
417 $ cat large1
418 large22
418 large22
419 $ cat sub/normal2
419 $ cat sub/normal2
420 normal22
420 normal22
421 $ cat sub/large2
421 $ cat sub/large2
422 large22
422 large22
423
423
424 Old revisions of a clone have correct largefiles content (this also
424 Old revisions of a clone have correct largefiles content (this also
425 tests update).
425 tests update).
426
426
427 $ hg update -r 1
427 $ hg update -r 1
428 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
428 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
429 getting changed largefiles
429 getting changed largefiles
430 1 largefiles updated, 0 removed
430 1 largefiles updated, 0 removed
431 $ cat large1
431 $ cat large1
432 large11
432 large11
433 $ cat sub/large2
433 $ cat sub/large2
434 large22
434 large22
435
435
436 Rebasing between two repositories does not revert largefiles to old
436 Rebasing between two repositories does not revert largefiles to old
437 revisions (this was a very bad bug that took a lot of work to fix).
437 revisions (this was a very bad bug that took a lot of work to fix).
438
438
439 $ cd ..
439 $ cd ..
440 $ hg clone a d
440 $ hg clone a d
441 updating to branch default
441 updating to branch default
442 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
442 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
443 getting changed largefiles
443 getting changed largefiles
444 3 largefiles updated, 0 removed
444 3 largefiles updated, 0 removed
445 $ cd b
445 $ cd b
446 $ echo large4-modified > sub/large4
446 $ echo large4-modified > sub/large4
447 $ echo normal3-modified > normal3
447 $ echo normal3-modified > normal3
448 $ hg commit -m "modify normal file and largefile in repo b"
448 $ hg commit -m "modify normal file and largefile in repo b"
449 Invoking status precommit hook
449 Invoking status precommit hook
450 M normal3
450 M normal3
451 M sub/large4
451 M sub/large4
452 $ cd ../d
452 $ cd ../d
453 $ echo large6-modified > sub2/large6
453 $ echo large6-modified > sub2/large6
454 $ echo normal4-modified > sub/normal4
454 $ echo normal4-modified > sub/normal4
455 $ hg commit -m "modify normal file largefile in repo d"
455 $ hg commit -m "modify normal file largefile in repo d"
456 Invoking status precommit hook
456 Invoking status precommit hook
457 M sub/normal4
457 M sub/normal4
458 M sub2/large6
458 M sub2/large6
459 $ cd ..
459 $ cd ..
460 $ hg clone d e
460 $ hg clone d e
461 updating to branch default
461 updating to branch default
462 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
462 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
463 getting changed largefiles
463 getting changed largefiles
464 3 largefiles updated, 0 removed
464 3 largefiles updated, 0 removed
465 $ cd d
465 $ cd d
466 $ hg pull --rebase ../b
466 $ hg pull --rebase ../b
467 pulling from ../b
467 pulling from ../b
468 searching for changes
468 searching for changes
469 adding changesets
469 adding changesets
470 adding manifests
470 adding manifests
471 adding file changes
471 adding file changes
472 added 1 changesets with 2 changes to 2 files (+1 heads)
472 added 1 changesets with 2 changes to 2 files (+1 heads)
473 Invoking status precommit hook
473 Invoking status precommit hook
474 M sub/normal4
474 M sub/normal4
475 M sub2/large6
475 M sub2/large6
476 saved backup bundle to $TESTTMP/d/.hg/strip-backup/f574fb32bb45-backup.hg
476 saved backup bundle to $TESTTMP/d/.hg/strip-backup/f574fb32bb45-backup.hg
477 nothing to rebase
477 nothing to rebase
478 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
478 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
479 9:598410d3eb9a modify normal file largefile in repo d
479 9:598410d3eb9a modify normal file largefile in repo d
480 8:a381d2c8c80e modify normal file and largefile in repo b
480 8:a381d2c8c80e modify normal file and largefile in repo b
481 7:daea875e9014 add/edit more largefiles
481 7:daea875e9014 add/edit more largefiles
482 6:4355d653f84f edit files yet again
482 6:4355d653f84f edit files yet again
483 5:9d5af5072dbd edit files again
483 5:9d5af5072dbd edit files again
484 4:74c02385b94c move files
484 4:74c02385b94c move files
485 3:9e8fbc4bce62 copy files
485 3:9e8fbc4bce62 copy files
486 2:51a0ae4d5864 remove files
486 2:51a0ae4d5864 remove files
487 1:ce8896473775 edit files
487 1:ce8896473775 edit files
488 0:30d30fe6a5be add files
488 0:30d30fe6a5be add files
489 $ cat normal3
489 $ cat normal3
490 normal3-modified
490 normal3-modified
491 $ cat sub/normal4
491 $ cat sub/normal4
492 normal4-modified
492 normal4-modified
493 $ cat sub/large4
493 $ cat sub/large4
494 large4-modified
494 large4-modified
495 $ cat sub2/large6
495 $ cat sub2/large6
496 large6-modified
496 large6-modified
497 $ cat sub2/large7
497 $ cat sub2/large7
498 large7
498 large7
499 $ cd ../e
499 $ cd ../e
500 $ hg pull ../b
500 $ hg pull ../b
501 pulling from ../b
501 pulling from ../b
502 searching for changes
502 searching for changes
503 adding changesets
503 adding changesets
504 adding manifests
504 adding manifests
505 adding file changes
505 adding file changes
506 added 1 changesets with 2 changes to 2 files (+1 heads)
506 added 1 changesets with 2 changes to 2 files (+1 heads)
507 (run 'hg heads' to see heads, 'hg merge' to merge)
507 (run 'hg heads' to see heads, 'hg merge' to merge)
508 caching new largefiles
508 caching new largefiles
509 0 largefiles cached
509 0 largefiles cached
510 $ hg rebase
510 $ hg rebase
511 Invoking status precommit hook
511 Invoking status precommit hook
512 M sub/normal4
512 M sub/normal4
513 M sub2/large6
513 M sub2/large6
514 saved backup bundle to $TESTTMP/e/.hg/strip-backup/f574fb32bb45-backup.hg
514 saved backup bundle to $TESTTMP/e/.hg/strip-backup/f574fb32bb45-backup.hg
515 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
515 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
516 9:598410d3eb9a modify normal file largefile in repo d
516 9:598410d3eb9a modify normal file largefile in repo d
517 8:a381d2c8c80e modify normal file and largefile in repo b
517 8:a381d2c8c80e modify normal file and largefile in repo b
518 7:daea875e9014 add/edit more largefiles
518 7:daea875e9014 add/edit more largefiles
519 6:4355d653f84f edit files yet again
519 6:4355d653f84f edit files yet again
520 5:9d5af5072dbd edit files again
520 5:9d5af5072dbd edit files again
521 4:74c02385b94c move files
521 4:74c02385b94c move files
522 3:9e8fbc4bce62 copy files
522 3:9e8fbc4bce62 copy files
523 2:51a0ae4d5864 remove files
523 2:51a0ae4d5864 remove files
524 1:ce8896473775 edit files
524 1:ce8896473775 edit files
525 0:30d30fe6a5be add files
525 0:30d30fe6a5be add files
526 $ cat normal3
526 $ cat normal3
527 normal3-modified
527 normal3-modified
528 $ cat sub/normal4
528 $ cat sub/normal4
529 normal4-modified
529 normal4-modified
530 $ cat sub/large4
530 $ cat sub/large4
531 large4-modified
531 large4-modified
532 $ cat sub2/large6
532 $ cat sub2/large6
533 large6-modified
533 large6-modified
534 $ cat sub2/large7
534 $ cat sub2/large7
535 large7
535 large7
536
536
537 Rollback on largefiles.
537 Rollback on largefiles.
538
538
539 $ echo large4-modified-again > sub/large4
539 $ echo large4-modified-again > sub/large4
540 $ hg commit -m "Modify large4 again"
540 $ hg commit -m "Modify large4 again"
541 Invoking status precommit hook
541 Invoking status precommit hook
542 M sub/large4
542 M sub/large4
543 $ hg rollback
543 $ hg rollback
544 repository tip rolled back to revision 9 (undo commit)
544 repository tip rolled back to revision 9 (undo commit)
545 working directory now based on revision 9
545 working directory now based on revision 9
546 $ hg st
546 $ hg st
547 M sub/large4
547 M sub/large4
548 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
548 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
549 9:598410d3eb9a modify normal file largefile in repo d
549 9:598410d3eb9a modify normal file largefile in repo d
550 8:a381d2c8c80e modify normal file and largefile in repo b
550 8:a381d2c8c80e modify normal file and largefile in repo b
551 7:daea875e9014 add/edit more largefiles
551 7:daea875e9014 add/edit more largefiles
552 6:4355d653f84f edit files yet again
552 6:4355d653f84f edit files yet again
553 5:9d5af5072dbd edit files again
553 5:9d5af5072dbd edit files again
554 4:74c02385b94c move files
554 4:74c02385b94c move files
555 3:9e8fbc4bce62 copy files
555 3:9e8fbc4bce62 copy files
556 2:51a0ae4d5864 remove files
556 2:51a0ae4d5864 remove files
557 1:ce8896473775 edit files
557 1:ce8896473775 edit files
558 0:30d30fe6a5be add files
558 0:30d30fe6a5be add files
559 $ cat sub/large4
559 $ cat sub/large4
560 large4-modified-again
560 large4-modified-again
561
561
562 "update --check" refuses to update with uncommitted changes.
562 "update --check" refuses to update with uncommitted changes.
563 $ hg update --check 8
563 $ hg update --check 8
564 abort: uncommitted local changes
564 abort: uncommitted local changes
565 [255]
565 [255]
566
566
567 "update --clean" leaves correct largefiles in working copy.
567 "update --clean" leaves correct largefiles in working copy.
568
568
569 $ hg update --clean
569 $ hg update --clean
570 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
570 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
571 getting changed largefiles
571 getting changed largefiles
572 1 largefiles updated, 0 removed
572 1 largefiles updated, 0 removed
573 $ cat normal3
573 $ cat normal3
574 normal3-modified
574 normal3-modified
575 $ cat sub/normal4
575 $ cat sub/normal4
576 normal4-modified
576 normal4-modified
577 $ cat sub/large4
577 $ cat sub/large4
578 large4-modified
578 large4-modified
579 $ cat sub2/large6
579 $ cat sub2/large6
580 large6-modified
580 large6-modified
581 $ cat sub2/large7
581 $ cat sub2/large7
582 large7
582 large7
583
583
584 Now "update check" is happy.
584 Now "update check" is happy.
585 $ hg update --check 8
585 $ hg update --check 8
586 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
586 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
587 getting changed largefiles
587 getting changed largefiles
588 1 largefiles updated, 0 removed
588 1 largefiles updated, 0 removed
589 $ hg update --check
589 $ hg update --check
590 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
590 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
591 getting changed largefiles
591 getting changed largefiles
592 1 largefiles updated, 0 removed
592 1 largefiles updated, 0 removed
593
593
594 Test removing empty largefiles directories on update
594 Test removing empty largefiles directories on update
595 $ test -d sub2 && echo "sub2 exists"
595 $ test -d sub2 && echo "sub2 exists"
596 sub2 exists
596 sub2 exists
597 $ hg update -q null
597 $ hg update -q null
598 $ test -d sub2 && echo "error: sub2 should not exist anymore"
598 $ test -d sub2 && echo "error: sub2 should not exist anymore"
599 [1]
599 [1]
600 $ hg update -q
600 $ hg update -q
601
601
602 Test hg remove removes empty largefiles directories
602 Test hg remove removes empty largefiles directories
603 $ test -d sub2 && echo "sub2 exists"
603 $ test -d sub2 && echo "sub2 exists"
604 sub2 exists
604 sub2 exists
605 $ hg remove sub2/*
605 $ hg remove sub2/*
606 $ test -d sub2 && echo "error: sub2 should not exist anymore"
606 $ test -d sub2 && echo "error: sub2 should not exist anymore"
607 [1]
607 [1]
608 $ hg revert sub2/large6 sub2/large7
608 $ hg revert sub2/large6 sub2/large7
609
609
610 "revert" works on largefiles (and normal files too).
610 "revert" works on largefiles (and normal files too).
611 $ echo hack3 >> normal3
611 $ echo hack3 >> normal3
612 $ echo hack4 >> sub/normal4
612 $ echo hack4 >> sub/normal4
613 $ echo hack4 >> sub/large4
613 $ echo hack4 >> sub/large4
614 $ rm sub2/large6
614 $ rm sub2/large6
615 $ hg revert sub2/large6
615 $ hg revert sub2/large6
616 $ hg rm sub2/large6
616 $ hg rm sub2/large6
617 $ echo new >> sub2/large8
617 $ echo new >> sub2/large8
618 $ hg add --large sub2/large8
618 $ hg add --large sub2/large8
619 # XXX we don't really want to report that we're reverting the standin;
619 # XXX we don't really want to report that we're reverting the standin;
620 # that's just an implementation detail. But I don't see an obvious fix. ;-(
620 # that's just an implementation detail. But I don't see an obvious fix. ;-(
621 $ hg revert sub
621 $ hg revert sub
622 reverting .hglf/sub/large4 (glob)
622 reverting .hglf/sub/large4 (glob)
623 reverting sub/normal4 (glob)
623 reverting sub/normal4 (glob)
624 $ hg status
624 $ hg status
625 M normal3
625 M normal3
626 A sub2/large8
626 A sub2/large8
627 R sub2/large6
627 R sub2/large6
628 ? sub/large4.orig
628 ? sub/large4.orig
629 ? sub/normal4.orig
629 ? sub/normal4.orig
630 $ cat sub/normal4
630 $ cat sub/normal4
631 normal4-modified
631 normal4-modified
632 $ cat sub/large4
632 $ cat sub/large4
633 large4-modified
633 large4-modified
634 $ hg revert -a --no-backup
634 $ hg revert -a --no-backup
635 undeleting .hglf/sub2/large6 (glob)
635 undeleting .hglf/sub2/large6 (glob)
636 forgetting .hglf/sub2/large8 (glob)
636 forgetting .hglf/sub2/large8 (glob)
637 reverting normal3
637 reverting normal3
638 $ hg status
638 $ hg status
639 ? sub/large4.orig
639 ? sub/large4.orig
640 ? sub/normal4.orig
640 ? sub/normal4.orig
641 ? sub2/large8
641 ? sub2/large8
642 $ cat normal3
642 $ cat normal3
643 normal3-modified
643 normal3-modified
644 $ cat sub2/large6
644 $ cat sub2/large6
645 large6-modified
645 large6-modified
646 $ rm sub/*.orig sub2/large8
646 $ rm sub/*.orig sub2/large8
647
647
648 revert some files to an older revision
648 revert some files to an older revision
649 $ hg revert --no-backup -r 8 sub2
649 $ hg revert --no-backup -r 8 sub2
650 reverting .hglf/sub2/large6 (glob)
650 reverting .hglf/sub2/large6 (glob)
651 $ cat sub2/large6
651 $ cat sub2/large6
652 large6
652 large6
653 $ hg revert --no-backup sub2
653 $ hg revert --no-backup sub2
654 reverting .hglf/sub2/large6 (glob)
654 reverting .hglf/sub2/large6 (glob)
655 $ hg status
655 $ hg status
656
656
657 "verify --large" actually verifies largefiles
657 "verify --large" actually verifies largefiles
658
658
659 $ hg verify --large
659 $ hg verify --large
660 checking changesets
660 checking changesets
661 checking manifests
661 checking manifests
662 crosschecking files in changesets and manifests
662 crosschecking files in changesets and manifests
663 checking files
663 checking files
664 10 files, 10 changesets, 28 total revisions
664 10 files, 10 changesets, 28 total revisions
665 searching 1 changesets for largefiles
665 searching 1 changesets for largefiles
666 verified existence of 3 revisions of 3 largefiles
666 verified existence of 3 revisions of 3 largefiles
667
667
668 Merging does not revert to old versions of largefiles and also check
668 Merging does not revert to old versions of largefiles and also check
669 that merging after having pulled from a non-default remote works
669 that merging after having pulled from a non-default remote works
670 correctly.
670 correctly.
671
671
672 $ cd ..
672 $ cd ..
673 $ hg clone -r 7 e temp
673 $ hg clone -r 7 e temp
674 adding changesets
674 adding changesets
675 adding manifests
675 adding manifests
676 adding file changes
676 adding file changes
677 added 8 changesets with 24 changes to 10 files
677 added 8 changesets with 24 changes to 10 files
678 updating to branch default
678 updating to branch default
679 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
679 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
680 getting changed largefiles
680 getting changed largefiles
681 3 largefiles updated, 0 removed
681 3 largefiles updated, 0 removed
682 $ hg clone temp f
682 $ hg clone temp f
683 updating to branch default
683 updating to branch default
684 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
684 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
685 getting changed largefiles
685 getting changed largefiles
686 3 largefiles updated, 0 removed
686 3 largefiles updated, 0 removed
687 # Delete the largefiles in the largefiles system cache so that we have an
687 # Delete the largefiles in the largefiles system cache so that we have an
688 # opportunity to test that caching after a pull works.
688 # opportunity to test that caching after a pull works.
689 $ rm ${USERCACHE}/*
689 $ rm ${USERCACHE}/*
690 $ cd f
690 $ cd f
691 $ echo "large4-merge-test" > sub/large4
691 $ echo "large4-merge-test" > sub/large4
692 $ hg commit -m "Modify large4 to test merge"
692 $ hg commit -m "Modify large4 to test merge"
693 Invoking status precommit hook
693 Invoking status precommit hook
694 M sub/large4
694 M sub/large4
695 $ hg pull ../e
695 $ hg pull ../e
696 pulling from ../e
696 pulling from ../e
697 searching for changes
697 searching for changes
698 adding changesets
698 adding changesets
699 adding manifests
699 adding manifests
700 adding file changes
700 adding file changes
701 added 2 changesets with 4 changes to 4 files (+1 heads)
701 added 2 changesets with 4 changes to 4 files (+1 heads)
702 (run 'hg heads' to see heads, 'hg merge' to merge)
702 (run 'hg heads' to see heads, 'hg merge' to merge)
703 caching new largefiles
703 caching new largefiles
704 2 largefiles cached
704 2 largefiles cached
705 $ hg merge
705 $ hg merge
706 merging sub/large4
706 merging sub/large4
707 largefile sub/large4 has a merge conflict
707 largefile sub/large4 has a merge conflict
708 keep (l)ocal or take (o)ther? l
708 keep (l)ocal or take (o)ther? l
709 3 files updated, 1 files merged, 0 files removed, 0 files unresolved
709 3 files updated, 1 files merged, 0 files removed, 0 files unresolved
710 (branch merge, don't forget to commit)
710 (branch merge, don't forget to commit)
711 getting changed largefiles
711 getting changed largefiles
712 1 largefiles updated, 0 removed
712 1 largefiles updated, 0 removed
713 $ hg commit -m "Merge repos e and f"
713 $ hg commit -m "Merge repos e and f"
714 Invoking status precommit hook
714 Invoking status precommit hook
715 M normal3
715 M normal3
716 M sub/normal4
716 M sub/normal4
717 M sub2/large6
717 M sub2/large6
718 $ cat normal3
718 $ cat normal3
719 normal3-modified
719 normal3-modified
720 $ cat sub/normal4
720 $ cat sub/normal4
721 normal4-modified
721 normal4-modified
722 $ cat sub/large4
722 $ cat sub/large4
723 large4-merge-test
723 large4-merge-test
724 $ cat sub2/large6
724 $ cat sub2/large6
725 large6-modified
725 large6-modified
726 $ cat sub2/large7
726 $ cat sub2/large7
727 large7
727 large7
728
728
729 Test status after merging with a branch that introduces a new largefile:
729 Test status after merging with a branch that introduces a new largefile:
730
730
731 $ echo large > large
731 $ echo large > large
732 $ hg add --large large
732 $ hg add --large large
733 $ hg commit -m 'add largefile'
733 $ hg commit -m 'add largefile'
734 Invoking status precommit hook
734 Invoking status precommit hook
735 A large
735 A large
736 $ hg update -q ".^"
736 $ hg update -q ".^"
737 $ echo change >> normal3
737 $ echo change >> normal3
738 $ hg commit -m 'some change'
738 $ hg commit -m 'some change'
739 Invoking status precommit hook
739 Invoking status precommit hook
740 M normal3
740 M normal3
741 created new head
741 created new head
742 $ hg merge
742 $ hg merge
743 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
743 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
744 (branch merge, don't forget to commit)
744 (branch merge, don't forget to commit)
745 getting changed largefiles
745 getting changed largefiles
746 1 largefiles updated, 0 removed
746 1 largefiles updated, 0 removed
747 $ hg status
747 $ hg status
748 M large
748 M large
749
749
750 Test that a normal file and a largefile with the same name and path cannot
750 Test that a normal file and a largefile with the same name and path cannot
751 coexist.
751 coexist.
752
752
753 $ rm sub2/large7
753 $ rm sub2/large7
754 $ echo "largeasnormal" > sub2/large7
754 $ echo "largeasnormal" > sub2/large7
755 $ hg add sub2/large7
755 $ hg add sub2/large7
756 sub2/large7 already a largefile
756 sub2/large7 already a largefile
757
757
758 Test that transplanting a largefile change works correctly.
758 Test that transplanting a largefile change works correctly.
759
759
760 $ cd ..
760 $ cd ..
761 $ hg clone -r 8 d g
761 $ hg clone -r 8 d g
762 adding changesets
762 adding changesets
763 adding manifests
763 adding manifests
764 adding file changes
764 adding file changes
765 added 9 changesets with 26 changes to 10 files
765 added 9 changesets with 26 changes to 10 files
766 updating to branch default
766 updating to branch default
767 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
767 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
768 getting changed largefiles
768 getting changed largefiles
769 3 largefiles updated, 0 removed
769 3 largefiles updated, 0 removed
770 $ cd g
770 $ cd g
771 $ hg transplant -s ../d 598410d3eb9a
771 $ hg transplant -s ../d 598410d3eb9a
772 searching for changes
772 searching for changes
773 searching for changes
773 searching for changes
774 adding changesets
774 adding changesets
775 adding manifests
775 adding manifests
776 adding file changes
776 adding file changes
777 added 1 changesets with 2 changes to 2 files
777 added 1 changesets with 2 changes to 2 files
778 getting changed largefiles
778 getting changed largefiles
779 1 largefiles updated, 0 removed
779 1 largefiles updated, 0 removed
780 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
780 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
781 9:598410d3eb9a modify normal file largefile in repo d
781 9:598410d3eb9a modify normal file largefile in repo d
782 8:a381d2c8c80e modify normal file and largefile in repo b
782 8:a381d2c8c80e modify normal file and largefile in repo b
783 7:daea875e9014 add/edit more largefiles
783 7:daea875e9014 add/edit more largefiles
784 6:4355d653f84f edit files yet again
784 6:4355d653f84f edit files yet again
785 5:9d5af5072dbd edit files again
785 5:9d5af5072dbd edit files again
786 4:74c02385b94c move files
786 4:74c02385b94c move files
787 3:9e8fbc4bce62 copy files
787 3:9e8fbc4bce62 copy files
788 2:51a0ae4d5864 remove files
788 2:51a0ae4d5864 remove files
789 1:ce8896473775 edit files
789 1:ce8896473775 edit files
790 0:30d30fe6a5be add files
790 0:30d30fe6a5be add files
791 $ cat normal3
791 $ cat normal3
792 normal3-modified
792 normal3-modified
793 $ cat sub/normal4
793 $ cat sub/normal4
794 normal4-modified
794 normal4-modified
795 $ cat sub/large4
795 $ cat sub/large4
796 large4-modified
796 large4-modified
797 $ cat sub2/large6
797 $ cat sub2/large6
798 large6-modified
798 large6-modified
799 $ cat sub2/large7
799 $ cat sub2/large7
800 large7
800 large7
801
801
802 Cat a largefile
802 Cat a largefile
803 $ hg cat normal3
803 $ hg cat normal3
804 normal3-modified
804 normal3-modified
805 $ hg cat sub/large4
805 $ hg cat sub/large4
806 large4-modified
806 large4-modified
807 $ rm ${USERCACHE}/*
807 $ rm ${USERCACHE}/*
808 $ hg cat -r a381d2c8c80e -o cat.out sub/large4
808 $ hg cat -r a381d2c8c80e -o cat.out sub/large4
809 $ cat cat.out
809 $ cat cat.out
810 large4-modified
810 large4-modified
811 $ rm cat.out
811 $ rm cat.out
812 $ hg cat -r a381d2c8c80e normal3
812 $ hg cat -r a381d2c8c80e normal3
813 normal3-modified
813 normal3-modified
814
814
815 Test that renaming a largefile results in correct output for status
815 Test that renaming a largefile results in correct output for status
816
816
817 $ hg rename sub/large4 large4-renamed
817 $ hg rename sub/large4 large4-renamed
818 $ hg commit -m "test rename output"
818 $ hg commit -m "test rename output"
819 Invoking status precommit hook
819 Invoking status precommit hook
820 A large4-renamed
820 A large4-renamed
821 R sub/large4
821 R sub/large4
822 $ cat large4-renamed
822 $ cat large4-renamed
823 large4-modified
823 large4-modified
824 $ cd sub2
824 $ cd sub2
825 $ hg rename large6 large6-renamed
825 $ hg rename large6 large6-renamed
826 $ hg st
826 $ hg st
827 A sub2/large6-renamed
827 A sub2/large6-renamed
828 R sub2/large6
828 R sub2/large6
829 $ cd ..
829 $ cd ..
830
830
831 Test --normal flag
831 Test --normal flag
832
832
833 $ dd if=/dev/zero bs=2k count=11k > new-largefile 2> /dev/null
833 $ dd if=/dev/zero bs=2k count=11k > new-largefile 2> /dev/null
834 $ hg add --normal --large new-largefile
834 $ hg add --normal --large new-largefile
835 abort: --normal cannot be used with --large
835 abort: --normal cannot be used with --large
836 [255]
836 [255]
837 $ hg add --normal new-largefile
837 $ hg add --normal new-largefile
838 new-largefile: up to 69 MB of RAM may be required to manage this file
838 new-largefile: up to 69 MB of RAM may be required to manage this file
839 (use 'hg revert new-largefile' to cancel the pending addition)
839 (use 'hg revert new-largefile' to cancel the pending addition)
840 $ cd ..
840 $ cd ..
841
841
842 vanilla clients not locked out from largefiles servers on vanilla repos
842 vanilla clients not locked out from largefiles servers on vanilla repos
843 $ mkdir r1
843 $ mkdir r1
844 $ cd r1
844 $ cd r1
845 $ hg init
845 $ hg init
846 $ echo c1 > f1
846 $ echo c1 > f1
847 $ hg add f1
847 $ hg add f1
848 $ hg commit -m "m1"
848 $ hg commit -m "m1"
849 Invoking status precommit hook
849 Invoking status precommit hook
850 A f1
850 A f1
851 $ cd ..
851 $ cd ..
852 $ hg serve -R r1 -d -p $HGPORT --pid-file hg.pid
852 $ hg serve -R r1 -d -p $HGPORT --pid-file hg.pid
853 $ cat hg.pid >> $DAEMON_PIDS
853 $ cat hg.pid >> $DAEMON_PIDS
854 $ hg --config extensions.largefiles=! clone http://localhost:$HGPORT r2
854 $ hg --config extensions.largefiles=! clone http://localhost:$HGPORT r2
855 requesting all changes
855 requesting all changes
856 adding changesets
856 adding changesets
857 adding manifests
857 adding manifests
858 adding file changes
858 adding file changes
859 added 1 changesets with 1 changes to 1 files
859 added 1 changesets with 1 changes to 1 files
860 updating to branch default
860 updating to branch default
861 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
861 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
862
862
863 largefiles clients still work with vanilla servers
863 largefiles clients still work with vanilla servers
864 $ hg --config extensions.largefiles=! serve -R r1 -d -p $HGPORT1 --pid-file hg.pid
864 $ hg --config extensions.largefiles=! serve -R r1 -d -p $HGPORT1 --pid-file hg.pid
865 $ cat hg.pid >> $DAEMON_PIDS
865 $ cat hg.pid >> $DAEMON_PIDS
866 $ hg clone http://localhost:$HGPORT1 r3
866 $ hg clone http://localhost:$HGPORT1 r3
867 requesting all changes
867 requesting all changes
868 adding changesets
868 adding changesets
869 adding manifests
869 adding manifests
870 adding file changes
870 adding file changes
871 added 1 changesets with 1 changes to 1 files
871 added 1 changesets with 1 changes to 1 files
872 updating to branch default
872 updating to branch default
873 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
873 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
874
874
875 vanilla clients locked out from largefiles http repos
875 vanilla clients locked out from largefiles http repos
876 $ mkdir r4
876 $ mkdir r4
877 $ cd r4
877 $ cd r4
878 $ hg init
878 $ hg init
879 $ echo c1 > f1
879 $ echo c1 > f1
880 $ hg add --large f1
880 $ hg add --large f1
881 $ hg commit -m "m1"
881 $ hg commit -m "m1"
882 Invoking status precommit hook
882 Invoking status precommit hook
883 A f1
883 A f1
884 $ cd ..
884 $ cd ..
885 $ hg serve -R r4 -d -p $HGPORT2 --pid-file hg.pid
885 $ hg serve -R r4 -d -p $HGPORT2 --pid-file hg.pid
886 $ cat hg.pid >> $DAEMON_PIDS
886 $ cat hg.pid >> $DAEMON_PIDS
887 $ hg --config extensions.largefiles=! clone http://localhost:$HGPORT2 r5
887 $ hg --config extensions.largefiles=! clone http://localhost:$HGPORT2 r5
888 abort: remote error:
888 abort: remote error:
889
889
890 This repository uses the largefiles extension.
890 This repository uses the largefiles extension.
891
891
892 Please enable it in your Mercurial config file.
892 Please enable it in your Mercurial config file.
893 [255]
893 [255]
894
894
895 used all HGPORTs, kill all daemons
895 used all HGPORTs, kill all daemons
896 $ "$TESTDIR/killdaemons.py"
896 $ "$TESTDIR/killdaemons.py"
897
897
898 vanilla clients locked out from largefiles ssh repos
898 vanilla clients locked out from largefiles ssh repos
899 $ hg --config extensions.largefiles=! clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/r4 r5
899 $ hg --config extensions.largefiles=! clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/r4 r5
900 abort: remote error:
900 abort: remote error:
901
901
902 This repository uses the largefiles extension.
902 This repository uses the largefiles extension.
903
903
904 Please enable it in your Mercurial config file.
904 Please enable it in your Mercurial config file.
905 [255]
905 [255]
906
906
907 largefiles clients refuse to push largefiles repos to vanilla servers
907 largefiles clients refuse to push largefiles repos to vanilla servers
908 $ mkdir r6
908 $ mkdir r6
909 $ cd r6
909 $ cd r6
910 $ hg init
910 $ hg init
911 $ echo c1 > f1
911 $ echo c1 > f1
912 $ hg add f1
912 $ hg add f1
913 $ hg commit -m "m1"
913 $ hg commit -m "m1"
914 Invoking status precommit hook
914 Invoking status precommit hook
915 A f1
915 A f1
916 $ cat >> .hg/hgrc <<!
916 $ cat >> .hg/hgrc <<!
917 > [web]
917 > [web]
918 > push_ssl = false
918 > push_ssl = false
919 > allow_push = *
919 > allow_push = *
920 > !
920 > !
921 $ cd ..
921 $ cd ..
922 $ hg clone r6 r7
922 $ hg clone r6 r7
923 updating to branch default
923 updating to branch default
924 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
924 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
925 $ cd r7
925 $ cd r7
926 $ echo c2 > f2
926 $ echo c2 > f2
927 $ hg add --large f2
927 $ hg add --large f2
928 $ hg commit -m "m2"
928 $ hg commit -m "m2"
929 Invoking status precommit hook
929 Invoking status precommit hook
930 A f2
930 A f2
931 $ hg --config extensions.largefiles=! -R ../r6 serve -d -p $HGPORT --pid-file ../hg.pid
931 $ hg --config extensions.largefiles=! -R ../r6 serve -d -p $HGPORT --pid-file ../hg.pid
932 $ cat ../hg.pid >> $DAEMON_PIDS
932 $ cat ../hg.pid >> $DAEMON_PIDS
933 $ hg push http://localhost:$HGPORT
933 $ hg push http://localhost:$HGPORT
934 pushing to http://localhost:$HGPORT/
934 pushing to http://localhost:$HGPORT/
935 searching for changes
935 searching for changes
936 abort: http://localhost:$HGPORT/ does not appear to be a largefile store
936 abort: http://localhost:$HGPORT/ does not appear to be a largefile store
937 [255]
937 [255]
938 $ cd ..
938 $ cd ..
939
939
940 putlfile errors are shown (issue3123)
940 putlfile errors are shown (issue3123)
941 Corrupt the cached largefile in r7
941 Corrupt the cached largefile in r7
942 $ echo corruption > $USERCACHE/4cdac4d8b084d0b599525cf732437fb337d422a8
942 $ echo corruption > $USERCACHE/4cdac4d8b084d0b599525cf732437fb337d422a8
943 $ hg init empty
943 $ hg init empty
944 $ hg serve -R empty -d -p $HGPORT1 --pid-file hg.pid \
944 $ hg serve -R empty -d -p $HGPORT1 --pid-file hg.pid \
945 > --config 'web.allow_push=*' --config web.push_ssl=False
945 > --config 'web.allow_push=*' --config web.push_ssl=False
946 $ cat hg.pid >> $DAEMON_PIDS
946 $ cat hg.pid >> $DAEMON_PIDS
947 $ hg push -R r7 http://localhost:$HGPORT1
947 $ hg push -R r7 http://localhost:$HGPORT1
948 pushing to http://localhost:$HGPORT1/
948 pushing to http://localhost:$HGPORT1/
949 searching for changes
949 searching for changes
950 remote: largefiles: failed to put 4cdac4d8b084d0b599525cf732437fb337d422a8 into store: largefile contents do not match hash
950 remote: largefiles: failed to put 4cdac4d8b084d0b599525cf732437fb337d422a8 into store: largefile contents do not match hash
951 abort: remotestore: could not put $TESTTMP/r7/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8 to remote store http://localhost:$HGPORT1/
951 abort: remotestore: could not put $TESTTMP/r7/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8 to remote store http://localhost:$HGPORT1/
952 [255]
952 [255]
953 $ rm -rf empty
953 $ rm -rf empty
954
954
955 Push a largefiles repository to a served empty repository
956 $ hg init r8
957 $ echo c3 > r8/f1
958 $ hg add --large r8/f1 -R r8
959 $ hg commit -m "m1" -R r8
960 Invoking status precommit hook
961 A f1
962 $ hg init empty
963 $ hg serve -R empty -d -p $HGPORT2 --pid-file hg.pid \
964 > --config 'web.allow_push=*' --config web.push_ssl=False
965 $ cat hg.pid >> $DAEMON_PIDS
966 $ rm ${USERCACHE}/*
967 $ hg push -R r8 http://localhost:$HGPORT2
968 pushing to http://localhost:$HGPORT2/
969 searching for changes
970 searching for changes
971 remote: adding changesets
972 remote: adding manifests
973 remote: adding file changes
974 remote: added 1 changesets with 1 changes to 1 files
975 $ rm -rf empty
976
977 used all HGPORTs, kill all daemons
978 $ "$TESTDIR/killdaemons.py"
979
955 Clone a local repository owned by another user
980 Clone a local repository owned by another user
956 We have to simulate that here by setting $HOME and removing write permissions
981 We have to simulate that here by setting $HOME and removing write permissions
957 $ ORIGHOME="$HOME"
982 $ ORIGHOME="$HOME"
958 $ mkdir alice
983 $ mkdir alice
959 $ HOME="`pwd`/alice"
984 $ HOME="`pwd`/alice"
960 $ cd alice
985 $ cd alice
961 $ hg init pubrepo
986 $ hg init pubrepo
962 $ cd pubrepo
987 $ cd pubrepo
963 $ dd if=/dev/zero bs=1k count=11k > a-large-file 2> /dev/null
988 $ dd if=/dev/zero bs=1k count=11k > a-large-file 2> /dev/null
964 $ hg add --large a-large-file
989 $ hg add --large a-large-file
965 $ hg commit -m "Add a large file"
990 $ hg commit -m "Add a large file"
966 Invoking status precommit hook
991 Invoking status precommit hook
967 A a-large-file
992 A a-large-file
968 $ cd ..
993 $ cd ..
969 $ chmod -R a-w pubrepo
994 $ chmod -R a-w pubrepo
970 $ cd ..
995 $ cd ..
971 $ mkdir bob
996 $ mkdir bob
972 $ HOME="`pwd`/bob"
997 $ HOME="`pwd`/bob"
973 $ cd bob
998 $ cd bob
974 $ hg clone --pull ../alice/pubrepo pubrepo
999 $ hg clone --pull ../alice/pubrepo pubrepo
975 requesting all changes
1000 requesting all changes
976 adding changesets
1001 adding changesets
977 adding manifests
1002 adding manifests
978 adding file changes
1003 adding file changes
979 added 1 changesets with 1 changes to 1 files
1004 added 1 changesets with 1 changes to 1 files
980 updating to branch default
1005 updating to branch default
981 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1006 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
982 getting changed largefiles
1007 getting changed largefiles
983 1 largefiles updated, 0 removed
1008 1 largefiles updated, 0 removed
984 $ cd ..
1009 $ cd ..
985 $ chmod -R u+w alice/pubrepo
1010 $ chmod -R u+w alice/pubrepo
986 $ HOME="$ORIGHOME"
1011 $ HOME="$ORIGHOME"
987
1012
988 Symlink to a large largefile should behave the same as a symlink to a normal file
1013 Symlink to a large largefile should behave the same as a symlink to a normal file
989 $ hg init largesymlink
1014 $ hg init largesymlink
990 $ cd largesymlink
1015 $ cd largesymlink
991 $ dd if=/dev/zero bs=1k count=10k of=largefile 2>/dev/null
1016 $ dd if=/dev/zero bs=1k count=10k of=largefile 2>/dev/null
992 $ hg add --large largefile
1017 $ hg add --large largefile
993 $ hg commit -m "commit a large file"
1018 $ hg commit -m "commit a large file"
994 Invoking status precommit hook
1019 Invoking status precommit hook
995 A largefile
1020 A largefile
996 $ ln -s largefile largelink
1021 $ ln -s largefile largelink
997 $ hg add largelink
1022 $ hg add largelink
998 $ hg commit -m "commit a large symlink"
1023 $ hg commit -m "commit a large symlink"
999 Invoking status precommit hook
1024 Invoking status precommit hook
1000 A largelink
1025 A largelink
1001 $ rm -f largelink
1026 $ rm -f largelink
1002 $ hg up >/dev/null
1027 $ hg up >/dev/null
1003 $ test -f largelink
1028 $ test -f largelink
1004 [1]
1029 [1]
1005 $ test -L largelink
1030 $ test -L largelink
1006 [1]
1031 [1]
1007 $ rm -f largelink # make next part of the test independent of the previous
1032 $ rm -f largelink # make next part of the test independent of the previous
1008 $ hg up -C >/dev/null
1033 $ hg up -C >/dev/null
1009 $ test -f largelink
1034 $ test -f largelink
1010 $ test -L largelink
1035 $ test -L largelink
1011 $ cd ..
1036 $ cd ..
1012
1037
1013 test for pattern matching on 'hg status':
1038 test for pattern matching on 'hg status':
1014 to boost performance, largefiles checks whether specified patterns are
1039 to boost performance, largefiles checks whether specified patterns are
1015 related to largefiles in working directory (NOT to STANDIN) or not.
1040 related to largefiles in working directory (NOT to STANDIN) or not.
1016
1041
1017 $ hg init statusmatch
1042 $ hg init statusmatch
1018 $ cd statusmatch
1043 $ cd statusmatch
1019
1044
1020 $ mkdir -p a/b/c/d
1045 $ mkdir -p a/b/c/d
1021 $ echo normal > a/b/c/d/e.normal.txt
1046 $ echo normal > a/b/c/d/e.normal.txt
1022 $ hg add a/b/c/d/e.normal.txt
1047 $ hg add a/b/c/d/e.normal.txt
1023 $ echo large > a/b/c/d/e.large.txt
1048 $ echo large > a/b/c/d/e.large.txt
1024 $ hg add --large a/b/c/d/e.large.txt
1049 $ hg add --large a/b/c/d/e.large.txt
1025 $ mkdir -p a/b/c/x
1050 $ mkdir -p a/b/c/x
1026 $ echo normal > a/b/c/x/y.normal.txt
1051 $ echo normal > a/b/c/x/y.normal.txt
1027 $ hg add a/b/c/x/y.normal.txt
1052 $ hg add a/b/c/x/y.normal.txt
1028 $ hg commit -m 'add files'
1053 $ hg commit -m 'add files'
1029 Invoking status precommit hook
1054 Invoking status precommit hook
1030 A a/b/c/d/e.large.txt
1055 A a/b/c/d/e.large.txt
1031 A a/b/c/d/e.normal.txt
1056 A a/b/c/d/e.normal.txt
1032 A a/b/c/x/y.normal.txt
1057 A a/b/c/x/y.normal.txt
1033
1058
1034 (1) no pattern: no performance boost
1059 (1) no pattern: no performance boost
1035 $ hg status -A
1060 $ hg status -A
1036 C a/b/c/d/e.large.txt
1061 C a/b/c/d/e.large.txt
1037 C a/b/c/d/e.normal.txt
1062 C a/b/c/d/e.normal.txt
1038 C a/b/c/x/y.normal.txt
1063 C a/b/c/x/y.normal.txt
1039
1064
1040 (2) pattern not related to largefiles: performance boost
1065 (2) pattern not related to largefiles: performance boost
1041 $ hg status -A a/b/c/x
1066 $ hg status -A a/b/c/x
1042 C a/b/c/x/y.normal.txt
1067 C a/b/c/x/y.normal.txt
1043
1068
1044 (3) pattern related to largefiles: no performance boost
1069 (3) pattern related to largefiles: no performance boost
1045 $ hg status -A a/b/c/d
1070 $ hg status -A a/b/c/d
1046 C a/b/c/d/e.large.txt
1071 C a/b/c/d/e.large.txt
1047 C a/b/c/d/e.normal.txt
1072 C a/b/c/d/e.normal.txt
1048
1073
1049 (4) pattern related to STANDIN (not to largefiles): performance boost
1074 (4) pattern related to STANDIN (not to largefiles): performance boost
1050 $ hg status -A .hglf/a
1075 $ hg status -A .hglf/a
1051 C .hglf/a/b/c/d/e.large.txt
1076 C .hglf/a/b/c/d/e.large.txt
1052
1077
1053 (5) mixed case: no performance boost
1078 (5) mixed case: no performance boost
1054 $ hg status -A a/b/c/x a/b/c/d
1079 $ hg status -A a/b/c/x a/b/c/d
1055 C a/b/c/d/e.large.txt
1080 C a/b/c/d/e.large.txt
1056 C a/b/c/d/e.normal.txt
1081 C a/b/c/d/e.normal.txt
1057 C a/b/c/x/y.normal.txt
1082 C a/b/c/x/y.normal.txt
1058
1083
1059 verify that largefiles doesn't break filesets
1084 verify that largefiles doesn't break filesets
1060
1085
1061 $ hg log --rev . --exclude "set:binary()"
1086 $ hg log --rev . --exclude "set:binary()"
1062 changeset: 0:41bd42f10efa
1087 changeset: 0:41bd42f10efa
1063 tag: tip
1088 tag: tip
1064 user: test
1089 user: test
1065 date: Thu Jan 01 00:00:00 1970 +0000
1090 date: Thu Jan 01 00:00:00 1970 +0000
1066 summary: add files
1091 summary: add files
1067
1092
1068 verify that large files in subrepos handled properly
1093 verify that large files in subrepos handled properly
1069 $ hg init subrepo
1094 $ hg init subrepo
1070 $ echo "subrepo = subrepo" > .hgsub
1095 $ echo "subrepo = subrepo" > .hgsub
1071 $ hg add .hgsub
1096 $ hg add .hgsub
1072 $ hg ci -m "add subrepo"
1097 $ hg ci -m "add subrepo"
1073 Invoking status precommit hook
1098 Invoking status precommit hook
1074 A .hgsub
1099 A .hgsub
1075 ? .hgsubstate
1100 ? .hgsubstate
1076 $ echo "rev 1" > subrepo/large.txt
1101 $ echo "rev 1" > subrepo/large.txt
1077 $ hg -R subrepo add --large subrepo/large.txt
1102 $ hg -R subrepo add --large subrepo/large.txt
1078 $ hg sum
1103 $ hg sum
1079 parent: 1:8ee150ea2e9c tip
1104 parent: 1:8ee150ea2e9c tip
1080 add subrepo
1105 add subrepo
1081 branch: default
1106 branch: default
1082 commit: 1 subrepos
1107 commit: 1 subrepos
1083 update: (current)
1108 update: (current)
1084 $ hg st
1109 $ hg st
1085 $ hg st -S
1110 $ hg st -S
1086 A subrepo/large.txt
1111 A subrepo/large.txt
1087 $ hg ci -S -m "commit top repo"
1112 $ hg ci -S -m "commit top repo"
1088 committing subrepository subrepo
1113 committing subrepository subrepo
1089 Invoking status precommit hook
1114 Invoking status precommit hook
1090 A large.txt
1115 A large.txt
1091 Invoking status precommit hook
1116 Invoking status precommit hook
1092 M .hgsubstate
1117 M .hgsubstate
1093 # No differences
1118 # No differences
1094 $ hg st -S
1119 $ hg st -S
1095 $ hg sum
1120 $ hg sum
1096 parent: 2:ce4cd0c527a6 tip
1121 parent: 2:ce4cd0c527a6 tip
1097 commit top repo
1122 commit top repo
1098 branch: default
1123 branch: default
1099 commit: (clean)
1124 commit: (clean)
1100 update: (current)
1125 update: (current)
1101 $ echo "rev 2" > subrepo/large.txt
1126 $ echo "rev 2" > subrepo/large.txt
1102 $ hg st -S
1127 $ hg st -S
1103 M subrepo/large.txt
1128 M subrepo/large.txt
1104 $ hg sum
1129 $ hg sum
1105 parent: 2:ce4cd0c527a6 tip
1130 parent: 2:ce4cd0c527a6 tip
1106 commit top repo
1131 commit top repo
1107 branch: default
1132 branch: default
1108 commit: 1 subrepos
1133 commit: 1 subrepos
1109 update: (current)
1134 update: (current)
1110 $ hg ci -m "this commit should fail without -S"
1135 $ hg ci -m "this commit should fail without -S"
1111 abort: uncommitted changes in subrepo subrepo
1136 abort: uncommitted changes in subrepo subrepo
1112 (use --subrepos for recursive commit)
1137 (use --subrepos for recursive commit)
1113 [255]
1138 [255]
1114
1139
1115 Add a normal file to the subrepo, then test archiving
1140 Add a normal file to the subrepo, then test archiving
1116
1141
1117 $ echo 'normal file' > subrepo/normal.txt
1142 $ echo 'normal file' > subrepo/normal.txt
1118 $ hg -R subrepo add subrepo/normal.txt
1143 $ hg -R subrepo add subrepo/normal.txt
1119
1144
1120 Lock in subrepo, otherwise the change isn't archived
1145 Lock in subrepo, otherwise the change isn't archived
1121
1146
1122 $ hg ci -S -m "add normal file to top level"
1147 $ hg ci -S -m "add normal file to top level"
1123 committing subrepository subrepo
1148 committing subrepository subrepo
1124 Invoking status precommit hook
1149 Invoking status precommit hook
1125 M large.txt
1150 M large.txt
1126 A normal.txt
1151 A normal.txt
1127 Invoking status precommit hook
1152 Invoking status precommit hook
1128 M .hgsubstate
1153 M .hgsubstate
1129 $ hg archive -S lf_subrepo_archive
1154 $ hg archive -S lf_subrepo_archive
1130 $ find lf_subrepo_archive | sort
1155 $ find lf_subrepo_archive | sort
1131 lf_subrepo_archive
1156 lf_subrepo_archive
1132 lf_subrepo_archive/.hg_archival.txt
1157 lf_subrepo_archive/.hg_archival.txt
1133 lf_subrepo_archive/.hgsub
1158 lf_subrepo_archive/.hgsub
1134 lf_subrepo_archive/.hgsubstate
1159 lf_subrepo_archive/.hgsubstate
1135 lf_subrepo_archive/a
1160 lf_subrepo_archive/a
1136 lf_subrepo_archive/a/b
1161 lf_subrepo_archive/a/b
1137 lf_subrepo_archive/a/b/c
1162 lf_subrepo_archive/a/b/c
1138 lf_subrepo_archive/a/b/c/d
1163 lf_subrepo_archive/a/b/c/d
1139 lf_subrepo_archive/a/b/c/d/e.large.txt
1164 lf_subrepo_archive/a/b/c/d/e.large.txt
1140 lf_subrepo_archive/a/b/c/d/e.normal.txt
1165 lf_subrepo_archive/a/b/c/d/e.normal.txt
1141 lf_subrepo_archive/a/b/c/x
1166 lf_subrepo_archive/a/b/c/x
1142 lf_subrepo_archive/a/b/c/x/y.normal.txt
1167 lf_subrepo_archive/a/b/c/x/y.normal.txt
1143 lf_subrepo_archive/subrepo
1168 lf_subrepo_archive/subrepo
1144 lf_subrepo_archive/subrepo/large.txt
1169 lf_subrepo_archive/subrepo/large.txt
1145 lf_subrepo_archive/subrepo/normal.txt
1170 lf_subrepo_archive/subrepo/normal.txt
1146
1171
1147 $ cd ..
1172 $ cd ..
@@ -1,53 +1,69 b''
1 $ hg init repo
1 $ hg init repo
2 $ cd repo
2 $ cd repo
3 $ hg init subrepo
3 $ hg init subrepo
4 $ echo a > subrepo/a
4 $ echo a > subrepo/a
5 $ hg -R subrepo ci -Am adda
5 $ hg -R subrepo ci -Am adda
6 adding a
6 adding a
7 $ echo 'subrepo = subrepo' > .hgsub
7 $ echo 'subrepo = subrepo' > .hgsub
8 $ hg ci -Am addsubrepo
8 $ hg ci -Am addsubrepo
9 adding .hgsub
9 adding .hgsub
10 $ echo b > subrepo/b
10 $ echo b > subrepo/b
11 $ hg -R subrepo ci -Am addb
11 $ hg -R subrepo ci -Am addb
12 adding b
12 adding b
13 $ hg ci -m updatedsub
13 $ hg ci -m updatedsub
14
14
15 ignore blanklines in .hgsubstate
16
17 >>> file('.hgsubstate', 'wb').write('\n\n \t \n \n')
18 $ hg st --subrepos
19 M .hgsubstate
20 $ hg revert -qC .hgsubstate
21
22 abort more gracefully on .hgsubstate parsing error
23
24 $ cp .hgsubstate .hgsubstate.old
25 >>> file('.hgsubstate', 'wb').write('\ninvalid')
26 $ hg st --subrepos
27 abort: invalid subrepository revision specifier in .hgsubstate line 2
28 [255]
29 $ mv .hgsubstate.old .hgsubstate
30
15 delete .hgsub and revert it
31 delete .hgsub and revert it
16
32
17 $ rm .hgsub
33 $ rm .hgsub
18 $ hg revert .hgsub
34 $ hg revert .hgsub
19 warning: subrepo spec file .hgsub not found
35 warning: subrepo spec file .hgsub not found
20 warning: subrepo spec file .hgsub not found
36 warning: subrepo spec file .hgsub not found
21
37
22 delete .hgsubstate and revert it
38 delete .hgsubstate and revert it
23
39
24 $ rm .hgsubstate
40 $ rm .hgsubstate
25 $ hg revert .hgsubstate
41 $ hg revert .hgsubstate
26
42
27 delete .hgsub and update
43 delete .hgsub and update
28
44
29 $ rm .hgsub
45 $ rm .hgsub
30 $ hg up 0
46 $ hg up 0
31 warning: subrepo spec file .hgsub not found
47 warning: subrepo spec file .hgsub not found
32 warning: subrepo spec file .hgsub not found
48 warning: subrepo spec file .hgsub not found
33 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
49 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
34 $ hg st
50 $ hg st
35 warning: subrepo spec file .hgsub not found
51 warning: subrepo spec file .hgsub not found
36 ! .hgsub
52 ! .hgsub
37 $ ls subrepo
53 $ ls subrepo
38 a
54 a
39
55
40 delete .hgsubstate and update
56 delete .hgsubstate and update
41
57
42 $ hg up -C
58 $ hg up -C
43 warning: subrepo spec file .hgsub not found
59 warning: subrepo spec file .hgsub not found
44 warning: subrepo spec file .hgsub not found
60 warning: subrepo spec file .hgsub not found
45 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
61 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
46 $ rm .hgsubstate
62 $ rm .hgsubstate
47 $ hg up 0
63 $ hg up 0
48 remote changed .hgsubstate which local deleted
64 remote changed .hgsubstate which local deleted
49 use (c)hanged version or leave (d)eleted? c
65 use (c)hanged version or leave (d)eleted? c
50 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
66 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
51 $ hg st
67 $ hg st
52 $ ls subrepo
68 $ ls subrepo
53 a
69 a
General Comments 0
You need to be logged in to leave comments. Login now