##// END OF EJS Templates
bookmarks: mark divergent bookmarks with book@pathalias when source in [paths]
Matt Mackall -
r15614:260a6449 default
parent child Browse files
Show More
@@ -1,221 +1,228
1 # Mercurial bookmark support code
1 # Mercurial bookmark support code
2 #
2 #
3 # Copyright 2008 David Soria Parra <dsp@php.net>
3 # Copyright 2008 David Soria Parra <dsp@php.net>
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 mercurial.i18n import _
8 from mercurial.i18n import _
9 from mercurial.node import hex
9 from mercurial.node import hex
10 from mercurial import encoding, error, util
10 from mercurial import encoding, error, util
11 import errno, os
11 import errno, os
12
12
13 def valid(mark):
13 def valid(mark):
14 for c in (':', '\0', '\n', '\r'):
14 for c in (':', '\0', '\n', '\r'):
15 if c in mark:
15 if c in mark:
16 return False
16 return False
17 return True
17 return True
18
18
19 def read(repo):
19 def read(repo):
20 '''Parse .hg/bookmarks file and return a dictionary
20 '''Parse .hg/bookmarks file and return a dictionary
21
21
22 Bookmarks are stored as {HASH}\\s{NAME}\\n (localtags format) values
22 Bookmarks are stored as {HASH}\\s{NAME}\\n (localtags format) values
23 in the .hg/bookmarks file.
23 in the .hg/bookmarks file.
24 Read the file and return a (name=>nodeid) dictionary
24 Read the file and return a (name=>nodeid) dictionary
25 '''
25 '''
26 bookmarks = {}
26 bookmarks = {}
27 try:
27 try:
28 for line in repo.opener('bookmarks'):
28 for line in repo.opener('bookmarks'):
29 line = line.strip()
29 line = line.strip()
30 if not line:
30 if not line:
31 continue
31 continue
32 if ' ' not in line:
32 if ' ' not in line:
33 repo.ui.warn(_('malformed line in .hg/bookmarks: %r\n') % line)
33 repo.ui.warn(_('malformed line in .hg/bookmarks: %r\n') % line)
34 continue
34 continue
35 sha, refspec = line.split(' ', 1)
35 sha, refspec = line.split(' ', 1)
36 refspec = encoding.tolocal(refspec)
36 refspec = encoding.tolocal(refspec)
37 try:
37 try:
38 bookmarks[refspec] = repo.changelog.lookup(sha)
38 bookmarks[refspec] = repo.changelog.lookup(sha)
39 except error.RepoLookupError:
39 except error.RepoLookupError:
40 pass
40 pass
41 except IOError, inst:
41 except IOError, inst:
42 if inst.errno != errno.ENOENT:
42 if inst.errno != errno.ENOENT:
43 raise
43 raise
44 return bookmarks
44 return bookmarks
45
45
46 def readcurrent(repo):
46 def readcurrent(repo):
47 '''Get the current bookmark
47 '''Get the current bookmark
48
48
49 If we use gittishsh branches we have a current bookmark that
49 If we use gittishsh branches we have a current bookmark that
50 we are on. This function returns the name of the bookmark. It
50 we are on. This function returns the name of the bookmark. It
51 is stored in .hg/bookmarks.current
51 is stored in .hg/bookmarks.current
52 '''
52 '''
53 mark = None
53 mark = None
54 try:
54 try:
55 file = repo.opener('bookmarks.current')
55 file = repo.opener('bookmarks.current')
56 except IOError, inst:
56 except IOError, inst:
57 if inst.errno != errno.ENOENT:
57 if inst.errno != errno.ENOENT:
58 raise
58 raise
59 return None
59 return None
60 try:
60 try:
61 # No readline() in posixfile_nt, reading everything is cheap
61 # No readline() in posixfile_nt, reading everything is cheap
62 mark = encoding.tolocal((file.readlines() or [''])[0])
62 mark = encoding.tolocal((file.readlines() or [''])[0])
63 if mark == '' or mark not in repo._bookmarks:
63 if mark == '' or mark not in repo._bookmarks:
64 mark = None
64 mark = None
65 finally:
65 finally:
66 file.close()
66 file.close()
67 return mark
67 return mark
68
68
69 def write(repo):
69 def write(repo):
70 '''Write bookmarks
70 '''Write bookmarks
71
71
72 Write the given bookmark => hash dictionary to the .hg/bookmarks file
72 Write the given bookmark => hash dictionary to the .hg/bookmarks file
73 in a format equal to those of localtags.
73 in a format equal to those of localtags.
74
74
75 We also store a backup of the previous state in undo.bookmarks that
75 We also store a backup of the previous state in undo.bookmarks that
76 can be copied back on rollback.
76 can be copied back on rollback.
77 '''
77 '''
78 refs = repo._bookmarks
78 refs = repo._bookmarks
79
79
80 if repo._bookmarkcurrent not in refs:
80 if repo._bookmarkcurrent not in refs:
81 setcurrent(repo, None)
81 setcurrent(repo, None)
82 for mark in refs.keys():
82 for mark in refs.keys():
83 if not valid(mark):
83 if not valid(mark):
84 raise util.Abort(_("bookmark '%s' contains illegal "
84 raise util.Abort(_("bookmark '%s' contains illegal "
85 "character" % mark))
85 "character" % mark))
86
86
87 wlock = repo.wlock()
87 wlock = repo.wlock()
88 try:
88 try:
89
89
90 file = repo.opener('bookmarks', 'w', atomictemp=True)
90 file = repo.opener('bookmarks', 'w', atomictemp=True)
91 for refspec, node in refs.iteritems():
91 for refspec, node in refs.iteritems():
92 file.write("%s %s\n" % (hex(node), encoding.fromlocal(refspec)))
92 file.write("%s %s\n" % (hex(node), encoding.fromlocal(refspec)))
93 file.close()
93 file.close()
94
94
95 # touch 00changelog.i so hgweb reloads bookmarks (no lock needed)
95 # touch 00changelog.i so hgweb reloads bookmarks (no lock needed)
96 try:
96 try:
97 os.utime(repo.sjoin('00changelog.i'), None)
97 os.utime(repo.sjoin('00changelog.i'), None)
98 except OSError:
98 except OSError:
99 pass
99 pass
100
100
101 finally:
101 finally:
102 wlock.release()
102 wlock.release()
103
103
104 def setcurrent(repo, mark):
104 def setcurrent(repo, mark):
105 '''Set the name of the bookmark that we are currently on
105 '''Set the name of the bookmark that we are currently on
106
106
107 Set the name of the bookmark that we are on (hg update <bookmark>).
107 Set the name of the bookmark that we are on (hg update <bookmark>).
108 The name is recorded in .hg/bookmarks.current
108 The name is recorded in .hg/bookmarks.current
109 '''
109 '''
110 current = repo._bookmarkcurrent
110 current = repo._bookmarkcurrent
111 if current == mark:
111 if current == mark:
112 return
112 return
113
113
114 if mark not in repo._bookmarks:
114 if mark not in repo._bookmarks:
115 mark = ''
115 mark = ''
116 if not valid(mark):
116 if not valid(mark):
117 raise util.Abort(_("bookmark '%s' contains illegal "
117 raise util.Abort(_("bookmark '%s' contains illegal "
118 "character" % mark))
118 "character" % mark))
119
119
120 wlock = repo.wlock()
120 wlock = repo.wlock()
121 try:
121 try:
122 file = repo.opener('bookmarks.current', 'w', atomictemp=True)
122 file = repo.opener('bookmarks.current', 'w', atomictemp=True)
123 file.write(encoding.fromlocal(mark))
123 file.write(encoding.fromlocal(mark))
124 file.close()
124 file.close()
125 finally:
125 finally:
126 wlock.release()
126 wlock.release()
127 repo._bookmarkcurrent = mark
127 repo._bookmarkcurrent = mark
128
128
129 def updatecurrentbookmark(repo, oldnode, curbranch):
129 def updatecurrentbookmark(repo, oldnode, curbranch):
130 try:
130 try:
131 update(repo, oldnode, repo.branchtags()[curbranch])
131 update(repo, oldnode, repo.branchtags()[curbranch])
132 except KeyError:
132 except KeyError:
133 if curbranch == "default": # no default branch!
133 if curbranch == "default": # no default branch!
134 update(repo, oldnode, repo.lookup("tip"))
134 update(repo, oldnode, repo.lookup("tip"))
135 else:
135 else:
136 raise util.Abort(_("branch %s not found") % curbranch)
136 raise util.Abort(_("branch %s not found") % curbranch)
137
137
138 def update(repo, parents, node):
138 def update(repo, parents, node):
139 marks = repo._bookmarks
139 marks = repo._bookmarks
140 update = False
140 update = False
141 mark = repo._bookmarkcurrent
141 mark = repo._bookmarkcurrent
142 if mark and marks[mark] in parents:
142 if mark and marks[mark] in parents:
143 old = repo[marks[mark]]
143 old = repo[marks[mark]]
144 new = repo[node]
144 new = repo[node]
145 if new in old.descendants():
145 if new in old.descendants():
146 marks[mark] = new.node()
146 marks[mark] = new.node()
147 update = True
147 update = True
148 if update:
148 if update:
149 repo._writebookmarks(marks)
149 repo._writebookmarks(marks)
150
150
151 def listbookmarks(repo):
151 def listbookmarks(repo):
152 # We may try to list bookmarks on a repo type that does not
152 # We may try to list bookmarks on a repo type that does not
153 # support it (e.g., statichttprepository).
153 # support it (e.g., statichttprepository).
154 marks = getattr(repo, '_bookmarks', {})
154 marks = getattr(repo, '_bookmarks', {})
155
155
156 d = {}
156 d = {}
157 for k, v in marks.iteritems():
157 for k, v in marks.iteritems():
158 # don't expose local divergent bookmarks
158 # don't expose local divergent bookmarks
159 if '@' not in k and not k.endswith('@'):
159 if '@' not in k and not k.endswith('@'):
160 d[k] = hex(v)
160 d[k] = hex(v)
161 return d
161 return d
162
162
163 def pushbookmark(repo, key, old, new):
163 def pushbookmark(repo, key, old, new):
164 w = repo.wlock()
164 w = repo.wlock()
165 try:
165 try:
166 marks = repo._bookmarks
166 marks = repo._bookmarks
167 if hex(marks.get(key, '')) != old:
167 if hex(marks.get(key, '')) != old:
168 return False
168 return False
169 if new == '':
169 if new == '':
170 del marks[key]
170 del marks[key]
171 else:
171 else:
172 if new not in repo:
172 if new not in repo:
173 return False
173 return False
174 marks[key] = repo[new].node()
174 marks[key] = repo[new].node()
175 write(repo)
175 write(repo)
176 return True
176 return True
177 finally:
177 finally:
178 w.release()
178 w.release()
179
179
180 def updatefromremote(ui, repo, remote):
180 def updatefromremote(ui, repo, remote, path):
181 ui.debug("checking for updated bookmarks\n")
181 ui.debug("checking for updated bookmarks\n")
182 rb = remote.listkeys('bookmarks')
182 rb = remote.listkeys('bookmarks')
183 changed = False
183 changed = False
184 for k in rb.keys():
184 for k in rb.keys():
185 if k in repo._bookmarks:
185 if k in repo._bookmarks:
186 nr, nl = rb[k], repo._bookmarks[k]
186 nr, nl = rb[k], repo._bookmarks[k]
187 if nr in repo:
187 if nr in repo:
188 cr = repo[nr]
188 cr = repo[nr]
189 cl = repo[nl]
189 cl = repo[nl]
190 if cl.rev() >= cr.rev():
190 if cl.rev() >= cr.rev():
191 continue
191 continue
192 if cr in cl.descendants():
192 if cr in cl.descendants():
193 repo._bookmarks[k] = cr.node()
193 repo._bookmarks[k] = cr.node()
194 changed = True
194 changed = True
195 ui.status(_("updating bookmark %s\n") % k)
195 ui.status(_("updating bookmark %s\n") % k)
196 else:
196 else:
197 # find a unique @ suffix
197 for x in range(1, 100):
198 for x in range(1, 100):
198 n = '%s@%d' % (k, x)
199 n = '%s@%d' % (k, x)
199 if n not in repo._bookmarks:
200 if n not in repo._bookmarks:
200 break
201 break
202 # try to use an @pathalias suffix
203 # if an @pathalias already exists, we overwrite (update) it
204 for p, u in ui.configitems("paths"):
205 if path == u:
206 n = '%s@%s' % (k, p)
207
201 repo._bookmarks[n] = cr.node()
208 repo._bookmarks[n] = cr.node()
202 changed = True
209 changed = True
203 ui.warn(_("divergent bookmark %s stored as %s\n") % (k, n))
210 ui.warn(_("divergent bookmark %s stored as %s\n") % (k, n))
204
211
205 if changed:
212 if changed:
206 write(repo)
213 write(repo)
207
214
208 def diff(ui, repo, remote):
215 def diff(ui, repo, remote):
209 ui.status(_("searching for changed bookmarks\n"))
216 ui.status(_("searching for changed bookmarks\n"))
210
217
211 lmarks = repo.listkeys('bookmarks')
218 lmarks = repo.listkeys('bookmarks')
212 rmarks = remote.listkeys('bookmarks')
219 rmarks = remote.listkeys('bookmarks')
213
220
214 diff = sorted(set(rmarks) - set(lmarks))
221 diff = sorted(set(rmarks) - set(lmarks))
215 for k in diff:
222 for k in diff:
216 ui.write(" %-25s %s\n" % (k, rmarks[k][:12]))
223 ui.write(" %-25s %s\n" % (k, rmarks[k][:12]))
217
224
218 if len(diff) <= 0:
225 if len(diff) <= 0:
219 ui.status(_("no changed bookmarks found\n"))
226 ui.status(_("no changed bookmarks found\n"))
220 return 1
227 return 1
221 return 0
228 return 0
@@ -1,5701 +1,5701
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 match as matchmod
16 import match as matchmod
17 import merge as mergemod
17 import merge as mergemod
18 import minirst, revset, fileset
18 import minirst, revset, fileset
19 import dagparser, context, simplemerge
19 import dagparser, context, simplemerge
20 import random, setdiscovery, treediscovery, dagutil
20 import random, setdiscovery, treediscovery, dagutil
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="")
176 opts.get('subrepos'), prefix="")
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 opmap = [('user', ' ', lambda x: ui.shortuser(x[0].user())),
251 opmap = [('user', ' ', lambda x: ui.shortuser(x[0].user())),
252 ('number', ' ', lambda x: str(x[0].rev())),
252 ('number', ' ', lambda x: str(x[0].rev())),
253 ('changeset', ' ', lambda x: short(x[0].node())),
253 ('changeset', ' ', lambda x: short(x[0].node())),
254 ('date', ' ', getdate),
254 ('date', ' ', getdate),
255 ('file', ' ', lambda x: x[0].path()),
255 ('file', ' ', lambda x: x[0].path()),
256 ('line_number', ':', lambda x: str(x[1])),
256 ('line_number', ':', lambda x: str(x[1])),
257 ]
257 ]
258
258
259 if (not opts.get('user') and not opts.get('changeset')
259 if (not opts.get('user') and not opts.get('changeset')
260 and not opts.get('date') and not opts.get('file')):
260 and not opts.get('date') and not opts.get('file')):
261 opts['number'] = True
261 opts['number'] = True
262
262
263 linenumber = opts.get('line_number') is not None
263 linenumber = opts.get('line_number') is not None
264 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
264 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
265 raise util.Abort(_('at least one of -n/-c is required for -l'))
265 raise util.Abort(_('at least one of -n/-c is required for -l'))
266
266
267 funcmap = [(func, sep) for op, sep, func in opmap if opts.get(op)]
267 funcmap = [(func, sep) for op, sep, func in opmap if opts.get(op)]
268 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
268 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
269
269
270 def bad(x, y):
270 def bad(x, y):
271 raise util.Abort("%s: %s" % (x, y))
271 raise util.Abort("%s: %s" % (x, y))
272
272
273 ctx = scmutil.revsingle(repo, opts.get('rev'))
273 ctx = scmutil.revsingle(repo, opts.get('rev'))
274 m = scmutil.match(ctx, pats, opts)
274 m = scmutil.match(ctx, pats, opts)
275 m.bad = bad
275 m.bad = bad
276 follow = not opts.get('no_follow')
276 follow = not opts.get('no_follow')
277 diffopts = patch.diffopts(ui, opts, section='annotate')
277 diffopts = patch.diffopts(ui, opts, section='annotate')
278 for abs in ctx.walk(m):
278 for abs in ctx.walk(m):
279 fctx = ctx[abs]
279 fctx = ctx[abs]
280 if not opts.get('text') and util.binary(fctx.data()):
280 if not opts.get('text') and util.binary(fctx.data()):
281 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
281 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
282 continue
282 continue
283
283
284 lines = fctx.annotate(follow=follow, linenumber=linenumber,
284 lines = fctx.annotate(follow=follow, linenumber=linenumber,
285 diffopts=diffopts)
285 diffopts=diffopts)
286 pieces = []
286 pieces = []
287
287
288 for f, sep in funcmap:
288 for f, sep in funcmap:
289 l = [f(n) for n, dummy in lines]
289 l = [f(n) for n, dummy in lines]
290 if l:
290 if l:
291 sized = [(x, encoding.colwidth(x)) for x in l]
291 sized = [(x, encoding.colwidth(x)) for x in l]
292 ml = max([w for x, w in sized])
292 ml = max([w for x, w in sized])
293 pieces.append(["%s%s%s" % (sep, ' ' * (ml - w), x)
293 pieces.append(["%s%s%s" % (sep, ' ' * (ml - w), x)
294 for x, w in sized])
294 for x, w in sized])
295
295
296 if pieces:
296 if pieces:
297 for p, l in zip(zip(*pieces), lines):
297 for p, l in zip(zip(*pieces), lines):
298 ui.write("%s: %s" % ("".join(p), l[1]))
298 ui.write("%s: %s" % ("".join(p), l[1]))
299
299
300 @command('archive',
300 @command('archive',
301 [('', 'no-decode', None, _('do not pass files through decoders')),
301 [('', 'no-decode', None, _('do not pass files through decoders')),
302 ('p', 'prefix', '', _('directory prefix for files in archive'),
302 ('p', 'prefix', '', _('directory prefix for files in archive'),
303 _('PREFIX')),
303 _('PREFIX')),
304 ('r', 'rev', '', _('revision to distribute'), _('REV')),
304 ('r', 'rev', '', _('revision to distribute'), _('REV')),
305 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
305 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
306 ] + subrepoopts + walkopts,
306 ] + subrepoopts + walkopts,
307 _('[OPTION]... DEST'))
307 _('[OPTION]... DEST'))
308 def archive(ui, repo, dest, **opts):
308 def archive(ui, repo, dest, **opts):
309 '''create an unversioned archive of a repository revision
309 '''create an unversioned archive of a repository revision
310
310
311 By default, the revision used is the parent of the working
311 By default, the revision used is the parent of the working
312 directory; use -r/--rev to specify a different revision.
312 directory; use -r/--rev to specify a different revision.
313
313
314 The archive type is automatically detected based on file
314 The archive type is automatically detected based on file
315 extension (or override using -t/--type).
315 extension (or override using -t/--type).
316
316
317 .. container:: verbose
317 .. container:: verbose
318
318
319 Examples:
319 Examples:
320
320
321 - create a zip file containing the 1.0 release::
321 - create a zip file containing the 1.0 release::
322
322
323 hg archive -r 1.0 project-1.0.zip
323 hg archive -r 1.0 project-1.0.zip
324
324
325 - create a tarball excluding .hg files::
325 - create a tarball excluding .hg files::
326
326
327 hg archive project.tar.gz -X ".hg*"
327 hg archive project.tar.gz -X ".hg*"
328
328
329 Valid types are:
329 Valid types are:
330
330
331 :``files``: a directory full of files (default)
331 :``files``: a directory full of files (default)
332 :``tar``: tar archive, uncompressed
332 :``tar``: tar archive, uncompressed
333 :``tbz2``: tar archive, compressed using bzip2
333 :``tbz2``: tar archive, compressed using bzip2
334 :``tgz``: tar archive, compressed using gzip
334 :``tgz``: tar archive, compressed using gzip
335 :``uzip``: zip archive, uncompressed
335 :``uzip``: zip archive, uncompressed
336 :``zip``: zip archive, compressed using deflate
336 :``zip``: zip archive, compressed using deflate
337
337
338 The exact name of the destination archive or directory is given
338 The exact name of the destination archive or directory is given
339 using a format string; see :hg:`help export` for details.
339 using a format string; see :hg:`help export` for details.
340
340
341 Each member added to an archive file has a directory prefix
341 Each member added to an archive file has a directory prefix
342 prepended. Use -p/--prefix to specify a format string for the
342 prepended. Use -p/--prefix to specify a format string for the
343 prefix. The default is the basename of the archive, with suffixes
343 prefix. The default is the basename of the archive, with suffixes
344 removed.
344 removed.
345
345
346 Returns 0 on success.
346 Returns 0 on success.
347 '''
347 '''
348
348
349 ctx = scmutil.revsingle(repo, opts.get('rev'))
349 ctx = scmutil.revsingle(repo, opts.get('rev'))
350 if not ctx:
350 if not ctx:
351 raise util.Abort(_('no working directory: please specify a revision'))
351 raise util.Abort(_('no working directory: please specify a revision'))
352 node = ctx.node()
352 node = ctx.node()
353 dest = cmdutil.makefilename(repo, dest, node)
353 dest = cmdutil.makefilename(repo, dest, node)
354 if os.path.realpath(dest) == repo.root:
354 if os.path.realpath(dest) == repo.root:
355 raise util.Abort(_('repository root cannot be destination'))
355 raise util.Abort(_('repository root cannot be destination'))
356
356
357 kind = opts.get('type') or archival.guesskind(dest) or 'files'
357 kind = opts.get('type') or archival.guesskind(dest) or 'files'
358 prefix = opts.get('prefix')
358 prefix = opts.get('prefix')
359
359
360 if dest == '-':
360 if dest == '-':
361 if kind == 'files':
361 if kind == 'files':
362 raise util.Abort(_('cannot archive plain files to stdout'))
362 raise util.Abort(_('cannot archive plain files to stdout'))
363 dest = cmdutil.makefileobj(repo, dest)
363 dest = cmdutil.makefileobj(repo, dest)
364 if not prefix:
364 if not prefix:
365 prefix = os.path.basename(repo.root) + '-%h'
365 prefix = os.path.basename(repo.root) + '-%h'
366
366
367 prefix = cmdutil.makefilename(repo, prefix, node)
367 prefix = cmdutil.makefilename(repo, prefix, node)
368 matchfn = scmutil.match(ctx, [], opts)
368 matchfn = scmutil.match(ctx, [], opts)
369 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
369 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
370 matchfn, prefix, subrepos=opts.get('subrepos'))
370 matchfn, prefix, subrepos=opts.get('subrepos'))
371
371
372 @command('backout',
372 @command('backout',
373 [('', 'merge', None, _('merge with old dirstate parent after backout')),
373 [('', 'merge', None, _('merge with old dirstate parent after backout')),
374 ('', 'parent', '',
374 ('', 'parent', '',
375 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
375 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
376 ('r', 'rev', '', _('revision to backout'), _('REV')),
376 ('r', 'rev', '', _('revision to backout'), _('REV')),
377 ] + mergetoolopts + walkopts + commitopts + commitopts2,
377 ] + mergetoolopts + walkopts + commitopts + commitopts2,
378 _('[OPTION]... [-r] REV'))
378 _('[OPTION]... [-r] REV'))
379 def backout(ui, repo, node=None, rev=None, **opts):
379 def backout(ui, repo, node=None, rev=None, **opts):
380 '''reverse effect of earlier changeset
380 '''reverse effect of earlier changeset
381
381
382 Prepare a new changeset with the effect of REV undone in the
382 Prepare a new changeset with the effect of REV undone in the
383 current working directory.
383 current working directory.
384
384
385 If REV is the parent of the working directory, then this new changeset
385 If REV is the parent of the working directory, then this new changeset
386 is committed automatically. Otherwise, hg needs to merge the
386 is committed automatically. Otherwise, hg needs to merge the
387 changes and the merged result is left uncommitted.
387 changes and the merged result is left uncommitted.
388
388
389 .. note::
389 .. note::
390 backout cannot be used to fix either an unwanted or
390 backout cannot be used to fix either an unwanted or
391 incorrect merge.
391 incorrect merge.
392
392
393 .. container:: verbose
393 .. container:: verbose
394
394
395 By default, the pending changeset will have one parent,
395 By default, the pending changeset will have one parent,
396 maintaining a linear history. With --merge, the pending
396 maintaining a linear history. With --merge, the pending
397 changeset will instead have two parents: the old parent of the
397 changeset will instead have two parents: the old parent of the
398 working directory and a new child of REV that simply undoes REV.
398 working directory and a new child of REV that simply undoes REV.
399
399
400 Before version 1.7, the behavior without --merge was equivalent
400 Before version 1.7, the behavior without --merge was equivalent
401 to specifying --merge followed by :hg:`update --clean .` to
401 to specifying --merge followed by :hg:`update --clean .` to
402 cancel the merge and leave the child of REV as a head to be
402 cancel the merge and leave the child of REV as a head to be
403 merged separately.
403 merged separately.
404
404
405 See :hg:`help dates` for a list of formats valid for -d/--date.
405 See :hg:`help dates` for a list of formats valid for -d/--date.
406
406
407 Returns 0 on success.
407 Returns 0 on success.
408 '''
408 '''
409 if rev and node:
409 if rev and node:
410 raise util.Abort(_("please specify just one revision"))
410 raise util.Abort(_("please specify just one revision"))
411
411
412 if not rev:
412 if not rev:
413 rev = node
413 rev = node
414
414
415 if not rev:
415 if not rev:
416 raise util.Abort(_("please specify a revision to backout"))
416 raise util.Abort(_("please specify a revision to backout"))
417
417
418 date = opts.get('date')
418 date = opts.get('date')
419 if date:
419 if date:
420 opts['date'] = util.parsedate(date)
420 opts['date'] = util.parsedate(date)
421
421
422 cmdutil.bailifchanged(repo)
422 cmdutil.bailifchanged(repo)
423 node = scmutil.revsingle(repo, rev).node()
423 node = scmutil.revsingle(repo, rev).node()
424
424
425 op1, op2 = repo.dirstate.parents()
425 op1, op2 = repo.dirstate.parents()
426 a = repo.changelog.ancestor(op1, node)
426 a = repo.changelog.ancestor(op1, node)
427 if a != node:
427 if a != node:
428 raise util.Abort(_('cannot backout change on a different branch'))
428 raise util.Abort(_('cannot backout change on a different branch'))
429
429
430 p1, p2 = repo.changelog.parents(node)
430 p1, p2 = repo.changelog.parents(node)
431 if p1 == nullid:
431 if p1 == nullid:
432 raise util.Abort(_('cannot backout a change with no parents'))
432 raise util.Abort(_('cannot backout a change with no parents'))
433 if p2 != nullid:
433 if p2 != nullid:
434 if not opts.get('parent'):
434 if not opts.get('parent'):
435 raise util.Abort(_('cannot backout a merge changeset'))
435 raise util.Abort(_('cannot backout a merge changeset'))
436 p = repo.lookup(opts['parent'])
436 p = repo.lookup(opts['parent'])
437 if p not in (p1, p2):
437 if p not in (p1, p2):
438 raise util.Abort(_('%s is not a parent of %s') %
438 raise util.Abort(_('%s is not a parent of %s') %
439 (short(p), short(node)))
439 (short(p), short(node)))
440 parent = p
440 parent = p
441 else:
441 else:
442 if opts.get('parent'):
442 if opts.get('parent'):
443 raise util.Abort(_('cannot use --parent on non-merge changeset'))
443 raise util.Abort(_('cannot use --parent on non-merge changeset'))
444 parent = p1
444 parent = p1
445
445
446 # the backout should appear on the same branch
446 # the backout should appear on the same branch
447 branch = repo.dirstate.branch()
447 branch = repo.dirstate.branch()
448 hg.clean(repo, node, show_stats=False)
448 hg.clean(repo, node, show_stats=False)
449 repo.dirstate.setbranch(branch)
449 repo.dirstate.setbranch(branch)
450 revert_opts = opts.copy()
450 revert_opts = opts.copy()
451 revert_opts['date'] = None
451 revert_opts['date'] = None
452 revert_opts['all'] = True
452 revert_opts['all'] = True
453 revert_opts['rev'] = hex(parent)
453 revert_opts['rev'] = hex(parent)
454 revert_opts['no_backup'] = None
454 revert_opts['no_backup'] = None
455 revert(ui, repo, **revert_opts)
455 revert(ui, repo, **revert_opts)
456 if not opts.get('merge') and op1 != node:
456 if not opts.get('merge') and op1 != node:
457 try:
457 try:
458 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
458 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
459 return hg.update(repo, op1)
459 return hg.update(repo, op1)
460 finally:
460 finally:
461 ui.setconfig('ui', 'forcemerge', '')
461 ui.setconfig('ui', 'forcemerge', '')
462
462
463 commit_opts = opts.copy()
463 commit_opts = opts.copy()
464 commit_opts['addremove'] = False
464 commit_opts['addremove'] = False
465 if not commit_opts['message'] and not commit_opts['logfile']:
465 if not commit_opts['message'] and not commit_opts['logfile']:
466 # we don't translate commit messages
466 # we don't translate commit messages
467 commit_opts['message'] = "Backed out changeset %s" % short(node)
467 commit_opts['message'] = "Backed out changeset %s" % short(node)
468 commit_opts['force_editor'] = True
468 commit_opts['force_editor'] = True
469 commit(ui, repo, **commit_opts)
469 commit(ui, repo, **commit_opts)
470 def nice(node):
470 def nice(node):
471 return '%d:%s' % (repo.changelog.rev(node), short(node))
471 return '%d:%s' % (repo.changelog.rev(node), short(node))
472 ui.status(_('changeset %s backs out changeset %s\n') %
472 ui.status(_('changeset %s backs out changeset %s\n') %
473 (nice(repo.changelog.tip()), nice(node)))
473 (nice(repo.changelog.tip()), nice(node)))
474 if opts.get('merge') and op1 != node:
474 if opts.get('merge') and op1 != node:
475 hg.clean(repo, op1, show_stats=False)
475 hg.clean(repo, op1, show_stats=False)
476 ui.status(_('merging with changeset %s\n')
476 ui.status(_('merging with changeset %s\n')
477 % nice(repo.changelog.tip()))
477 % nice(repo.changelog.tip()))
478 try:
478 try:
479 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
479 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
480 return hg.merge(repo, hex(repo.changelog.tip()))
480 return hg.merge(repo, hex(repo.changelog.tip()))
481 finally:
481 finally:
482 ui.setconfig('ui', 'forcemerge', '')
482 ui.setconfig('ui', 'forcemerge', '')
483 return 0
483 return 0
484
484
485 @command('bisect',
485 @command('bisect',
486 [('r', 'reset', False, _('reset bisect state')),
486 [('r', 'reset', False, _('reset bisect state')),
487 ('g', 'good', False, _('mark changeset good')),
487 ('g', 'good', False, _('mark changeset good')),
488 ('b', 'bad', False, _('mark changeset bad')),
488 ('b', 'bad', False, _('mark changeset bad')),
489 ('s', 'skip', False, _('skip testing changeset')),
489 ('s', 'skip', False, _('skip testing changeset')),
490 ('e', 'extend', False, _('extend the bisect range')),
490 ('e', 'extend', False, _('extend the bisect range')),
491 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
491 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
492 ('U', 'noupdate', False, _('do not update to target'))],
492 ('U', 'noupdate', False, _('do not update to target'))],
493 _("[-gbsr] [-U] [-c CMD] [REV]"))
493 _("[-gbsr] [-U] [-c CMD] [REV]"))
494 def bisect(ui, repo, rev=None, extra=None, command=None,
494 def bisect(ui, repo, rev=None, extra=None, command=None,
495 reset=None, good=None, bad=None, skip=None, extend=None,
495 reset=None, good=None, bad=None, skip=None, extend=None,
496 noupdate=None):
496 noupdate=None):
497 """subdivision search of changesets
497 """subdivision search of changesets
498
498
499 This command helps to find changesets which introduce problems. To
499 This command helps to find changesets which introduce problems. To
500 use, mark the earliest changeset you know exhibits the problem as
500 use, mark the earliest changeset you know exhibits the problem as
501 bad, then mark the latest changeset which is free from the problem
501 bad, then mark the latest changeset which is free from the problem
502 as good. Bisect will update your working directory to a revision
502 as good. Bisect will update your working directory to a revision
503 for testing (unless the -U/--noupdate option is specified). Once
503 for testing (unless the -U/--noupdate option is specified). Once
504 you have performed tests, mark the working directory as good or
504 you have performed tests, mark the working directory as good or
505 bad, and bisect will either update to another candidate changeset
505 bad, and bisect will either update to another candidate changeset
506 or announce that it has found the bad revision.
506 or announce that it has found the bad revision.
507
507
508 As a shortcut, you can also use the revision argument to mark a
508 As a shortcut, you can also use the revision argument to mark a
509 revision as good or bad without checking it out first.
509 revision as good or bad without checking it out first.
510
510
511 If you supply a command, it will be used for automatic bisection.
511 If you supply a command, it will be used for automatic bisection.
512 Its exit status will be used to mark revisions as good or bad:
512 Its exit status will be used to mark revisions as good or bad:
513 status 0 means good, 125 means to skip the revision, 127
513 status 0 means good, 125 means to skip the revision, 127
514 (command not found) will abort the bisection, and any other
514 (command not found) will abort the bisection, and any other
515 non-zero exit status means the revision is bad.
515 non-zero exit status means the revision is bad.
516
516
517 .. container:: verbose
517 .. container:: verbose
518
518
519 Some examples:
519 Some examples:
520
520
521 - start a bisection with known bad revision 12, and good revision 34::
521 - start a bisection with known bad revision 12, and good revision 34::
522
522
523 hg bisect --bad 34
523 hg bisect --bad 34
524 hg bisect --good 12
524 hg bisect --good 12
525
525
526 - advance the current bisection by marking current revision as good or
526 - advance the current bisection by marking current revision as good or
527 bad::
527 bad::
528
528
529 hg bisect --good
529 hg bisect --good
530 hg bisect --bad
530 hg bisect --bad
531
531
532 - mark the current revision, or a known revision, to be skipped (eg. if
532 - mark the current revision, or a known revision, to be skipped (eg. if
533 that revision is not usable because of another issue)::
533 that revision is not usable because of another issue)::
534
534
535 hg bisect --skip
535 hg bisect --skip
536 hg bisect --skip 23
536 hg bisect --skip 23
537
537
538 - forget the current bisection::
538 - forget the current bisection::
539
539
540 hg bisect --reset
540 hg bisect --reset
541
541
542 - use 'make && make tests' to automatically find the first broken
542 - use 'make && make tests' to automatically find the first broken
543 revision::
543 revision::
544
544
545 hg bisect --reset
545 hg bisect --reset
546 hg bisect --bad 34
546 hg bisect --bad 34
547 hg bisect --good 12
547 hg bisect --good 12
548 hg bisect --command 'make && make tests'
548 hg bisect --command 'make && make tests'
549
549
550 - see all changesets whose states are already known in the current
550 - see all changesets whose states are already known in the current
551 bisection::
551 bisection::
552
552
553 hg log -r "bisect(pruned)"
553 hg log -r "bisect(pruned)"
554
554
555 - see all changesets that took part in the current bisection::
555 - see all changesets that took part in the current bisection::
556
556
557 hg log -r "bisect(range)"
557 hg log -r "bisect(range)"
558
558
559 - with the graphlog extension, you can even get a nice graph::
559 - with the graphlog extension, you can even get a nice graph::
560
560
561 hg log --graph -r "bisect(range)"
561 hg log --graph -r "bisect(range)"
562
562
563 See :hg:`help revsets` for more about the `bisect()` keyword.
563 See :hg:`help revsets` for more about the `bisect()` keyword.
564
564
565 Returns 0 on success.
565 Returns 0 on success.
566 """
566 """
567 def extendbisectrange(nodes, good):
567 def extendbisectrange(nodes, good):
568 # bisect is incomplete when it ends on a merge node and
568 # bisect is incomplete when it ends on a merge node and
569 # one of the parent was not checked.
569 # one of the parent was not checked.
570 parents = repo[nodes[0]].parents()
570 parents = repo[nodes[0]].parents()
571 if len(parents) > 1:
571 if len(parents) > 1:
572 side = good and state['bad'] or state['good']
572 side = good and state['bad'] or state['good']
573 num = len(set(i.node() for i in parents) & set(side))
573 num = len(set(i.node() for i in parents) & set(side))
574 if num == 1:
574 if num == 1:
575 return parents[0].ancestor(parents[1])
575 return parents[0].ancestor(parents[1])
576 return None
576 return None
577
577
578 def print_result(nodes, good):
578 def print_result(nodes, good):
579 displayer = cmdutil.show_changeset(ui, repo, {})
579 displayer = cmdutil.show_changeset(ui, repo, {})
580 if len(nodes) == 1:
580 if len(nodes) == 1:
581 # narrowed it down to a single revision
581 # narrowed it down to a single revision
582 if good:
582 if good:
583 ui.write(_("The first good revision is:\n"))
583 ui.write(_("The first good revision is:\n"))
584 else:
584 else:
585 ui.write(_("The first bad revision is:\n"))
585 ui.write(_("The first bad revision is:\n"))
586 displayer.show(repo[nodes[0]])
586 displayer.show(repo[nodes[0]])
587 extendnode = extendbisectrange(nodes, good)
587 extendnode = extendbisectrange(nodes, good)
588 if extendnode is not None:
588 if extendnode is not None:
589 ui.write(_('Not all ancestors of this changeset have been'
589 ui.write(_('Not all ancestors of this changeset have been'
590 ' checked.\nUse bisect --extend to continue the '
590 ' checked.\nUse bisect --extend to continue the '
591 'bisection from\nthe common ancestor, %s.\n')
591 'bisection from\nthe common ancestor, %s.\n')
592 % extendnode)
592 % extendnode)
593 else:
593 else:
594 # multiple possible revisions
594 # multiple possible revisions
595 if good:
595 if good:
596 ui.write(_("Due to skipped revisions, the first "
596 ui.write(_("Due to skipped revisions, the first "
597 "good revision could be any of:\n"))
597 "good revision could be any of:\n"))
598 else:
598 else:
599 ui.write(_("Due to skipped revisions, the first "
599 ui.write(_("Due to skipped revisions, the first "
600 "bad revision could be any of:\n"))
600 "bad revision could be any of:\n"))
601 for n in nodes:
601 for n in nodes:
602 displayer.show(repo[n])
602 displayer.show(repo[n])
603 displayer.close()
603 displayer.close()
604
604
605 def check_state(state, interactive=True):
605 def check_state(state, interactive=True):
606 if not state['good'] or not state['bad']:
606 if not state['good'] or not state['bad']:
607 if (good or bad or skip or reset) and interactive:
607 if (good or bad or skip or reset) and interactive:
608 return
608 return
609 if not state['good']:
609 if not state['good']:
610 raise util.Abort(_('cannot bisect (no known good revisions)'))
610 raise util.Abort(_('cannot bisect (no known good revisions)'))
611 else:
611 else:
612 raise util.Abort(_('cannot bisect (no known bad revisions)'))
612 raise util.Abort(_('cannot bisect (no known bad revisions)'))
613 return True
613 return True
614
614
615 # backward compatibility
615 # backward compatibility
616 if rev in "good bad reset init".split():
616 if rev in "good bad reset init".split():
617 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
617 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
618 cmd, rev, extra = rev, extra, None
618 cmd, rev, extra = rev, extra, None
619 if cmd == "good":
619 if cmd == "good":
620 good = True
620 good = True
621 elif cmd == "bad":
621 elif cmd == "bad":
622 bad = True
622 bad = True
623 else:
623 else:
624 reset = True
624 reset = True
625 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
625 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
626 raise util.Abort(_('incompatible arguments'))
626 raise util.Abort(_('incompatible arguments'))
627
627
628 if reset:
628 if reset:
629 p = repo.join("bisect.state")
629 p = repo.join("bisect.state")
630 if os.path.exists(p):
630 if os.path.exists(p):
631 os.unlink(p)
631 os.unlink(p)
632 return
632 return
633
633
634 state = hbisect.load_state(repo)
634 state = hbisect.load_state(repo)
635
635
636 if command:
636 if command:
637 changesets = 1
637 changesets = 1
638 try:
638 try:
639 while changesets:
639 while changesets:
640 # update state
640 # update state
641 status = util.system(command, out=ui.fout)
641 status = util.system(command, out=ui.fout)
642 if status == 125:
642 if status == 125:
643 transition = "skip"
643 transition = "skip"
644 elif status == 0:
644 elif status == 0:
645 transition = "good"
645 transition = "good"
646 # status < 0 means process was killed
646 # status < 0 means process was killed
647 elif status == 127:
647 elif status == 127:
648 raise util.Abort(_("failed to execute %s") % command)
648 raise util.Abort(_("failed to execute %s") % command)
649 elif status < 0:
649 elif status < 0:
650 raise util.Abort(_("%s killed") % command)
650 raise util.Abort(_("%s killed") % command)
651 else:
651 else:
652 transition = "bad"
652 transition = "bad"
653 ctx = scmutil.revsingle(repo, rev)
653 ctx = scmutil.revsingle(repo, rev)
654 rev = None # clear for future iterations
654 rev = None # clear for future iterations
655 state[transition].append(ctx.node())
655 state[transition].append(ctx.node())
656 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
656 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
657 check_state(state, interactive=False)
657 check_state(state, interactive=False)
658 # bisect
658 # bisect
659 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
659 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
660 # update to next check
660 # update to next check
661 cmdutil.bailifchanged(repo)
661 cmdutil.bailifchanged(repo)
662 hg.clean(repo, nodes[0], show_stats=False)
662 hg.clean(repo, nodes[0], show_stats=False)
663 finally:
663 finally:
664 hbisect.save_state(repo, state)
664 hbisect.save_state(repo, state)
665 print_result(nodes, good)
665 print_result(nodes, good)
666 return
666 return
667
667
668 # update state
668 # update state
669
669
670 if rev:
670 if rev:
671 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
671 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
672 else:
672 else:
673 nodes = [repo.lookup('.')]
673 nodes = [repo.lookup('.')]
674
674
675 if good or bad or skip:
675 if good or bad or skip:
676 if good:
676 if good:
677 state['good'] += nodes
677 state['good'] += nodes
678 elif bad:
678 elif bad:
679 state['bad'] += nodes
679 state['bad'] += nodes
680 elif skip:
680 elif skip:
681 state['skip'] += nodes
681 state['skip'] += nodes
682 hbisect.save_state(repo, state)
682 hbisect.save_state(repo, state)
683
683
684 if not check_state(state):
684 if not check_state(state):
685 return
685 return
686
686
687 # actually bisect
687 # actually bisect
688 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
688 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
689 if extend:
689 if extend:
690 if not changesets:
690 if not changesets:
691 extendnode = extendbisectrange(nodes, good)
691 extendnode = extendbisectrange(nodes, good)
692 if extendnode is not None:
692 if extendnode is not None:
693 ui.write(_("Extending search to changeset %d:%s\n"
693 ui.write(_("Extending search to changeset %d:%s\n"
694 % (extendnode.rev(), extendnode)))
694 % (extendnode.rev(), extendnode)))
695 if noupdate:
695 if noupdate:
696 return
696 return
697 cmdutil.bailifchanged(repo)
697 cmdutil.bailifchanged(repo)
698 return hg.clean(repo, extendnode.node())
698 return hg.clean(repo, extendnode.node())
699 raise util.Abort(_("nothing to extend"))
699 raise util.Abort(_("nothing to extend"))
700
700
701 if changesets == 0:
701 if changesets == 0:
702 print_result(nodes, good)
702 print_result(nodes, good)
703 else:
703 else:
704 assert len(nodes) == 1 # only a single node can be tested next
704 assert len(nodes) == 1 # only a single node can be tested next
705 node = nodes[0]
705 node = nodes[0]
706 # compute the approximate number of remaining tests
706 # compute the approximate number of remaining tests
707 tests, size = 0, 2
707 tests, size = 0, 2
708 while size <= changesets:
708 while size <= changesets:
709 tests, size = tests + 1, size * 2
709 tests, size = tests + 1, size * 2
710 rev = repo.changelog.rev(node)
710 rev = repo.changelog.rev(node)
711 ui.write(_("Testing changeset %d:%s "
711 ui.write(_("Testing changeset %d:%s "
712 "(%d changesets remaining, ~%d tests)\n")
712 "(%d changesets remaining, ~%d tests)\n")
713 % (rev, short(node), changesets, tests))
713 % (rev, short(node), changesets, tests))
714 if not noupdate:
714 if not noupdate:
715 cmdutil.bailifchanged(repo)
715 cmdutil.bailifchanged(repo)
716 return hg.clean(repo, node)
716 return hg.clean(repo, node)
717
717
718 @command('bookmarks',
718 @command('bookmarks',
719 [('f', 'force', False, _('force')),
719 [('f', 'force', False, _('force')),
720 ('r', 'rev', '', _('revision'), _('REV')),
720 ('r', 'rev', '', _('revision'), _('REV')),
721 ('d', 'delete', False, _('delete a given bookmark')),
721 ('d', 'delete', False, _('delete a given bookmark')),
722 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
722 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
723 ('i', 'inactive', False, _('do not mark a new bookmark active'))],
723 ('i', 'inactive', False, _('do not mark a new bookmark active'))],
724 _('hg bookmarks [-f] [-d] [-i] [-m NAME] [-r REV] [NAME]'))
724 _('hg bookmarks [-f] [-d] [-i] [-m NAME] [-r REV] [NAME]'))
725 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False,
725 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False,
726 rename=None, inactive=False):
726 rename=None, inactive=False):
727 '''track a line of development with movable markers
727 '''track a line of development with movable markers
728
728
729 Bookmarks are pointers to certain commits that move when
729 Bookmarks are pointers to certain commits that move when
730 committing. Bookmarks are local. They can be renamed, copied and
730 committing. Bookmarks are local. They can be renamed, copied and
731 deleted. It is possible to use bookmark names in :hg:`merge` and
731 deleted. It is possible to use bookmark names in :hg:`merge` and
732 :hg:`update` to merge and update respectively to a given bookmark.
732 :hg:`update` to merge and update respectively to a given bookmark.
733
733
734 You can use :hg:`bookmark NAME` to set a bookmark on the working
734 You can use :hg:`bookmark NAME` to set a bookmark on the working
735 directory's parent revision with the given name. If you specify
735 directory's parent revision with the given name. If you specify
736 a revision using -r REV (where REV may be an existing bookmark),
736 a revision using -r REV (where REV may be an existing bookmark),
737 the bookmark is assigned to that revision.
737 the bookmark is assigned to that revision.
738
738
739 Bookmarks can be pushed and pulled between repositories (see :hg:`help
739 Bookmarks can be pushed and pulled between repositories (see :hg:`help
740 push` and :hg:`help pull`). This requires both the local and remote
740 push` and :hg:`help pull`). This requires both the local and remote
741 repositories to support bookmarks. For versions prior to 1.8, this means
741 repositories to support bookmarks. For versions prior to 1.8, this means
742 the bookmarks extension must be enabled.
742 the bookmarks extension must be enabled.
743 '''
743 '''
744 hexfn = ui.debugflag and hex or short
744 hexfn = ui.debugflag and hex or short
745 marks = repo._bookmarks
745 marks = repo._bookmarks
746 cur = repo.changectx('.').node()
746 cur = repo.changectx('.').node()
747
747
748 if delete:
748 if delete:
749 if mark is None:
749 if mark is None:
750 raise util.Abort(_("bookmark name required"))
750 raise util.Abort(_("bookmark name required"))
751 if mark not in marks:
751 if mark not in marks:
752 raise util.Abort(_("bookmark '%s' does not exist") % mark)
752 raise util.Abort(_("bookmark '%s' does not exist") % mark)
753 if mark == repo._bookmarkcurrent:
753 if mark == repo._bookmarkcurrent:
754 bookmarks.setcurrent(repo, None)
754 bookmarks.setcurrent(repo, None)
755 del marks[mark]
755 del marks[mark]
756 bookmarks.write(repo)
756 bookmarks.write(repo)
757 return
757 return
758
758
759 if rename:
759 if rename:
760 if rename not in marks:
760 if rename not in marks:
761 raise util.Abort(_("bookmark '%s' does not exist") % rename)
761 raise util.Abort(_("bookmark '%s' does not exist") % rename)
762 if mark in marks and not force:
762 if mark in marks and not force:
763 raise util.Abort(_("bookmark '%s' already exists "
763 raise util.Abort(_("bookmark '%s' already exists "
764 "(use -f to force)") % mark)
764 "(use -f to force)") % mark)
765 if mark is None:
765 if mark is None:
766 raise util.Abort(_("new bookmark name required"))
766 raise util.Abort(_("new bookmark name required"))
767 marks[mark] = marks[rename]
767 marks[mark] = marks[rename]
768 if repo._bookmarkcurrent == rename and not inactive:
768 if repo._bookmarkcurrent == rename and not inactive:
769 bookmarks.setcurrent(repo, mark)
769 bookmarks.setcurrent(repo, mark)
770 del marks[rename]
770 del marks[rename]
771 bookmarks.write(repo)
771 bookmarks.write(repo)
772 return
772 return
773
773
774 if mark is not None:
774 if mark is not None:
775 if "\n" in mark:
775 if "\n" in mark:
776 raise util.Abort(_("bookmark name cannot contain newlines"))
776 raise util.Abort(_("bookmark name cannot contain newlines"))
777 mark = mark.strip()
777 mark = mark.strip()
778 if not mark:
778 if not mark:
779 raise util.Abort(_("bookmark names cannot consist entirely of "
779 raise util.Abort(_("bookmark names cannot consist entirely of "
780 "whitespace"))
780 "whitespace"))
781 if inactive and mark == repo._bookmarkcurrent:
781 if inactive and mark == repo._bookmarkcurrent:
782 bookmarks.setcurrent(repo, None)
782 bookmarks.setcurrent(repo, None)
783 return
783 return
784 if mark in marks and not force:
784 if mark in marks and not force:
785 raise util.Abort(_("bookmark '%s' already exists "
785 raise util.Abort(_("bookmark '%s' already exists "
786 "(use -f to force)") % mark)
786 "(use -f to force)") % mark)
787 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
787 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
788 and not force):
788 and not force):
789 raise util.Abort(
789 raise util.Abort(
790 _("a bookmark cannot have the name of an existing branch"))
790 _("a bookmark cannot have the name of an existing branch"))
791 if rev:
791 if rev:
792 marks[mark] = repo.lookup(rev)
792 marks[mark] = repo.lookup(rev)
793 else:
793 else:
794 marks[mark] = cur
794 marks[mark] = cur
795 if not inactive and cur == marks[mark]:
795 if not inactive and cur == marks[mark]:
796 bookmarks.setcurrent(repo, mark)
796 bookmarks.setcurrent(repo, mark)
797 bookmarks.write(repo)
797 bookmarks.write(repo)
798 return
798 return
799
799
800 if mark is None:
800 if mark is None:
801 if rev:
801 if rev:
802 raise util.Abort(_("bookmark name required"))
802 raise util.Abort(_("bookmark name required"))
803 if len(marks) == 0:
803 if len(marks) == 0:
804 ui.status(_("no bookmarks set\n"))
804 ui.status(_("no bookmarks set\n"))
805 else:
805 else:
806 for bmark, n in sorted(marks.iteritems()):
806 for bmark, n in sorted(marks.iteritems()):
807 current = repo._bookmarkcurrent
807 current = repo._bookmarkcurrent
808 if bmark == current and n == cur:
808 if bmark == current and n == cur:
809 prefix, label = '*', 'bookmarks.current'
809 prefix, label = '*', 'bookmarks.current'
810 else:
810 else:
811 prefix, label = ' ', ''
811 prefix, label = ' ', ''
812
812
813 if ui.quiet:
813 if ui.quiet:
814 ui.write("%s\n" % bmark, label=label)
814 ui.write("%s\n" % bmark, label=label)
815 else:
815 else:
816 ui.write(" %s %-25s %d:%s\n" % (
816 ui.write(" %s %-25s %d:%s\n" % (
817 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
817 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
818 label=label)
818 label=label)
819 return
819 return
820
820
821 @command('branch',
821 @command('branch',
822 [('f', 'force', None,
822 [('f', 'force', None,
823 _('set branch name even if it shadows an existing branch')),
823 _('set branch name even if it shadows an existing branch')),
824 ('C', 'clean', None, _('reset branch name to parent branch name'))],
824 ('C', 'clean', None, _('reset branch name to parent branch name'))],
825 _('[-fC] [NAME]'))
825 _('[-fC] [NAME]'))
826 def branch(ui, repo, label=None, **opts):
826 def branch(ui, repo, label=None, **opts):
827 """set or show the current branch name
827 """set or show the current branch name
828
828
829 .. note::
829 .. note::
830 Branch names are permanent and global. Use :hg:`bookmark` to create a
830 Branch names are permanent and global. Use :hg:`bookmark` to create a
831 light-weight bookmark instead. See :hg:`help glossary` for more
831 light-weight bookmark instead. See :hg:`help glossary` for more
832 information about named branches and bookmarks.
832 information about named branches and bookmarks.
833
833
834 With no argument, show the current branch name. With one argument,
834 With no argument, show the current branch name. With one argument,
835 set the working directory branch name (the branch will not exist
835 set the working directory branch name (the branch will not exist
836 in the repository until the next commit). Standard practice
836 in the repository until the next commit). Standard practice
837 recommends that primary development take place on the 'default'
837 recommends that primary development take place on the 'default'
838 branch.
838 branch.
839
839
840 Unless -f/--force is specified, branch will not let you set a
840 Unless -f/--force is specified, branch will not let you set a
841 branch name that already exists, even if it's inactive.
841 branch name that already exists, even if it's inactive.
842
842
843 Use -C/--clean to reset the working directory branch to that of
843 Use -C/--clean to reset the working directory branch to that of
844 the parent of the working directory, negating a previous branch
844 the parent of the working directory, negating a previous branch
845 change.
845 change.
846
846
847 Use the command :hg:`update` to switch to an existing branch. Use
847 Use the command :hg:`update` to switch to an existing branch. Use
848 :hg:`commit --close-branch` to mark this branch as closed.
848 :hg:`commit --close-branch` to mark this branch as closed.
849
849
850 Returns 0 on success.
850 Returns 0 on success.
851 """
851 """
852
852
853 if opts.get('clean'):
853 if opts.get('clean'):
854 label = repo[None].p1().branch()
854 label = repo[None].p1().branch()
855 repo.dirstate.setbranch(label)
855 repo.dirstate.setbranch(label)
856 ui.status(_('reset working directory to branch %s\n') % label)
856 ui.status(_('reset working directory to branch %s\n') % label)
857 elif label:
857 elif label:
858 if not opts.get('force') and label in repo.branchtags():
858 if not opts.get('force') and label in repo.branchtags():
859 if label not in [p.branch() for p in repo.parents()]:
859 if label not in [p.branch() for p in repo.parents()]:
860 raise util.Abort(_('a branch of the same name already exists'),
860 raise util.Abort(_('a branch of the same name already exists'),
861 # i18n: "it" refers to an existing branch
861 # i18n: "it" refers to an existing branch
862 hint=_("use 'hg update' to switch to it"))
862 hint=_("use 'hg update' to switch to it"))
863 repo.dirstate.setbranch(label)
863 repo.dirstate.setbranch(label)
864 ui.status(_('marked working directory as branch %s\n') % label)
864 ui.status(_('marked working directory as branch %s\n') % label)
865 else:
865 else:
866 ui.write("%s\n" % repo.dirstate.branch())
866 ui.write("%s\n" % repo.dirstate.branch())
867
867
868 @command('branches',
868 @command('branches',
869 [('a', 'active', False, _('show only branches that have unmerged heads')),
869 [('a', 'active', False, _('show only branches that have unmerged heads')),
870 ('c', 'closed', False, _('show normal and closed branches'))],
870 ('c', 'closed', False, _('show normal and closed branches'))],
871 _('[-ac]'))
871 _('[-ac]'))
872 def branches(ui, repo, active=False, closed=False):
872 def branches(ui, repo, active=False, closed=False):
873 """list repository named branches
873 """list repository named branches
874
874
875 List the repository's named branches, indicating which ones are
875 List the repository's named branches, indicating which ones are
876 inactive. If -c/--closed is specified, also list branches which have
876 inactive. If -c/--closed is specified, also list branches which have
877 been marked closed (see :hg:`commit --close-branch`).
877 been marked closed (see :hg:`commit --close-branch`).
878
878
879 If -a/--active is specified, only show active branches. A branch
879 If -a/--active is specified, only show active branches. A branch
880 is considered active if it contains repository heads.
880 is considered active if it contains repository heads.
881
881
882 Use the command :hg:`update` to switch to an existing branch.
882 Use the command :hg:`update` to switch to an existing branch.
883
883
884 Returns 0.
884 Returns 0.
885 """
885 """
886
886
887 hexfunc = ui.debugflag and hex or short
887 hexfunc = ui.debugflag and hex or short
888 activebranches = [repo[n].branch() for n in repo.heads()]
888 activebranches = [repo[n].branch() for n in repo.heads()]
889 def testactive(tag, node):
889 def testactive(tag, node):
890 realhead = tag in activebranches
890 realhead = tag in activebranches
891 open = node in repo.branchheads(tag, closed=False)
891 open = node in repo.branchheads(tag, closed=False)
892 return realhead and open
892 return realhead and open
893 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
893 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
894 for tag, node in repo.branchtags().items()],
894 for tag, node in repo.branchtags().items()],
895 reverse=True)
895 reverse=True)
896
896
897 for isactive, node, tag in branches:
897 for isactive, node, tag in branches:
898 if (not active) or isactive:
898 if (not active) or isactive:
899 if ui.quiet:
899 if ui.quiet:
900 ui.write("%s\n" % tag)
900 ui.write("%s\n" % tag)
901 else:
901 else:
902 hn = repo.lookup(node)
902 hn = repo.lookup(node)
903 if isactive:
903 if isactive:
904 label = 'branches.active'
904 label = 'branches.active'
905 notice = ''
905 notice = ''
906 elif hn not in repo.branchheads(tag, closed=False):
906 elif hn not in repo.branchheads(tag, closed=False):
907 if not closed:
907 if not closed:
908 continue
908 continue
909 label = 'branches.closed'
909 label = 'branches.closed'
910 notice = _(' (closed)')
910 notice = _(' (closed)')
911 else:
911 else:
912 label = 'branches.inactive'
912 label = 'branches.inactive'
913 notice = _(' (inactive)')
913 notice = _(' (inactive)')
914 if tag == repo.dirstate.branch():
914 if tag == repo.dirstate.branch():
915 label = 'branches.current'
915 label = 'branches.current'
916 rev = str(node).rjust(31 - encoding.colwidth(tag))
916 rev = str(node).rjust(31 - encoding.colwidth(tag))
917 rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
917 rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
918 tag = ui.label(tag, label)
918 tag = ui.label(tag, label)
919 ui.write("%s %s%s\n" % (tag, rev, notice))
919 ui.write("%s %s%s\n" % (tag, rev, notice))
920
920
921 @command('bundle',
921 @command('bundle',
922 [('f', 'force', None, _('run even when the destination is unrelated')),
922 [('f', 'force', None, _('run even when the destination is unrelated')),
923 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
923 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
924 _('REV')),
924 _('REV')),
925 ('b', 'branch', [], _('a specific branch you would like to bundle'),
925 ('b', 'branch', [], _('a specific branch you would like to bundle'),
926 _('BRANCH')),
926 _('BRANCH')),
927 ('', 'base', [],
927 ('', 'base', [],
928 _('a base changeset assumed to be available at the destination'),
928 _('a base changeset assumed to be available at the destination'),
929 _('REV')),
929 _('REV')),
930 ('a', 'all', None, _('bundle all changesets in the repository')),
930 ('a', 'all', None, _('bundle all changesets in the repository')),
931 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
931 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
932 ] + remoteopts,
932 ] + remoteopts,
933 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
933 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
934 def bundle(ui, repo, fname, dest=None, **opts):
934 def bundle(ui, repo, fname, dest=None, **opts):
935 """create a changegroup file
935 """create a changegroup file
936
936
937 Generate a compressed changegroup file collecting changesets not
937 Generate a compressed changegroup file collecting changesets not
938 known to be in another repository.
938 known to be in another repository.
939
939
940 If you omit the destination repository, then hg assumes the
940 If you omit the destination repository, then hg assumes the
941 destination will have all the nodes you specify with --base
941 destination will have all the nodes you specify with --base
942 parameters. To create a bundle containing all changesets, use
942 parameters. To create a bundle containing all changesets, use
943 -a/--all (or --base null).
943 -a/--all (or --base null).
944
944
945 You can change compression method with the -t/--type option.
945 You can change compression method with the -t/--type option.
946 The available compression methods are: none, bzip2, and
946 The available compression methods are: none, bzip2, and
947 gzip (by default, bundles are compressed using bzip2).
947 gzip (by default, bundles are compressed using bzip2).
948
948
949 The bundle file can then be transferred using conventional means
949 The bundle file can then be transferred using conventional means
950 and applied to another repository with the unbundle or pull
950 and applied to another repository with the unbundle or pull
951 command. This is useful when direct push and pull are not
951 command. This is useful when direct push and pull are not
952 available or when exporting an entire repository is undesirable.
952 available or when exporting an entire repository is undesirable.
953
953
954 Applying bundles preserves all changeset contents including
954 Applying bundles preserves all changeset contents including
955 permissions, copy/rename information, and revision history.
955 permissions, copy/rename information, and revision history.
956
956
957 Returns 0 on success, 1 if no changes found.
957 Returns 0 on success, 1 if no changes found.
958 """
958 """
959 revs = None
959 revs = None
960 if 'rev' in opts:
960 if 'rev' in opts:
961 revs = scmutil.revrange(repo, opts['rev'])
961 revs = scmutil.revrange(repo, opts['rev'])
962
962
963 if opts.get('all'):
963 if opts.get('all'):
964 base = ['null']
964 base = ['null']
965 else:
965 else:
966 base = scmutil.revrange(repo, opts.get('base'))
966 base = scmutil.revrange(repo, opts.get('base'))
967 if base:
967 if base:
968 if dest:
968 if dest:
969 raise util.Abort(_("--base is incompatible with specifying "
969 raise util.Abort(_("--base is incompatible with specifying "
970 "a destination"))
970 "a destination"))
971 common = [repo.lookup(rev) for rev in base]
971 common = [repo.lookup(rev) for rev in base]
972 heads = revs and map(repo.lookup, revs) or revs
972 heads = revs and map(repo.lookup, revs) or revs
973 else:
973 else:
974 dest = ui.expandpath(dest or 'default-push', dest or 'default')
974 dest = ui.expandpath(dest or 'default-push', dest or 'default')
975 dest, branches = hg.parseurl(dest, opts.get('branch'))
975 dest, branches = hg.parseurl(dest, opts.get('branch'))
976 other = hg.peer(repo, opts, dest)
976 other = hg.peer(repo, opts, dest)
977 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
977 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
978 heads = revs and map(repo.lookup, revs) or revs
978 heads = revs and map(repo.lookup, revs) or revs
979 common, outheads = discovery.findcommonoutgoing(repo, other,
979 common, outheads = discovery.findcommonoutgoing(repo, other,
980 onlyheads=heads,
980 onlyheads=heads,
981 force=opts.get('force'))
981 force=opts.get('force'))
982
982
983 cg = repo.getbundle('bundle', common=common, heads=heads)
983 cg = repo.getbundle('bundle', common=common, heads=heads)
984 if not cg:
984 if not cg:
985 ui.status(_("no changes found\n"))
985 ui.status(_("no changes found\n"))
986 return 1
986 return 1
987
987
988 bundletype = opts.get('type', 'bzip2').lower()
988 bundletype = opts.get('type', 'bzip2').lower()
989 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
989 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
990 bundletype = btypes.get(bundletype)
990 bundletype = btypes.get(bundletype)
991 if bundletype not in changegroup.bundletypes:
991 if bundletype not in changegroup.bundletypes:
992 raise util.Abort(_('unknown bundle type specified with --type'))
992 raise util.Abort(_('unknown bundle type specified with --type'))
993
993
994 changegroup.writebundle(cg, fname, bundletype)
994 changegroup.writebundle(cg, fname, bundletype)
995
995
996 @command('cat',
996 @command('cat',
997 [('o', 'output', '',
997 [('o', 'output', '',
998 _('print output to file with formatted name'), _('FORMAT')),
998 _('print output to file with formatted name'), _('FORMAT')),
999 ('r', 'rev', '', _('print the given revision'), _('REV')),
999 ('r', 'rev', '', _('print the given revision'), _('REV')),
1000 ('', 'decode', None, _('apply any matching decode filter')),
1000 ('', 'decode', None, _('apply any matching decode filter')),
1001 ] + walkopts,
1001 ] + walkopts,
1002 _('[OPTION]... FILE...'))
1002 _('[OPTION]... FILE...'))
1003 def cat(ui, repo, file1, *pats, **opts):
1003 def cat(ui, repo, file1, *pats, **opts):
1004 """output the current or given revision of files
1004 """output the current or given revision of files
1005
1005
1006 Print the specified files as they were at the given revision. If
1006 Print the specified files as they were at the given revision. If
1007 no revision is given, the parent of the working directory is used,
1007 no revision is given, the parent of the working directory is used,
1008 or tip if no revision is checked out.
1008 or tip if no revision is checked out.
1009
1009
1010 Output may be to a file, in which case the name of the file is
1010 Output may be to a file, in which case the name of the file is
1011 given using a format string. The formatting rules are the same as
1011 given using a format string. The formatting rules are the same as
1012 for the export command, with the following additions:
1012 for the export command, with the following additions:
1013
1013
1014 :``%s``: basename of file being printed
1014 :``%s``: basename of file being printed
1015 :``%d``: dirname of file being printed, or '.' if in repository root
1015 :``%d``: dirname of file being printed, or '.' if in repository root
1016 :``%p``: root-relative path name of file being printed
1016 :``%p``: root-relative path name of file being printed
1017
1017
1018 Returns 0 on success.
1018 Returns 0 on success.
1019 """
1019 """
1020 ctx = scmutil.revsingle(repo, opts.get('rev'))
1020 ctx = scmutil.revsingle(repo, opts.get('rev'))
1021 err = 1
1021 err = 1
1022 m = scmutil.match(ctx, (file1,) + pats, opts)
1022 m = scmutil.match(ctx, (file1,) + pats, opts)
1023 for abs in ctx.walk(m):
1023 for abs in ctx.walk(m):
1024 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1024 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1025 pathname=abs)
1025 pathname=abs)
1026 data = ctx[abs].data()
1026 data = ctx[abs].data()
1027 if opts.get('decode'):
1027 if opts.get('decode'):
1028 data = repo.wwritedata(abs, data)
1028 data = repo.wwritedata(abs, data)
1029 fp.write(data)
1029 fp.write(data)
1030 fp.close()
1030 fp.close()
1031 err = 0
1031 err = 0
1032 return err
1032 return err
1033
1033
1034 @command('^clone',
1034 @command('^clone',
1035 [('U', 'noupdate', None,
1035 [('U', 'noupdate', None,
1036 _('the clone will include an empty working copy (only a repository)')),
1036 _('the clone will include an empty working copy (only a repository)')),
1037 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
1037 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
1038 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1038 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1039 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1039 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1040 ('', 'pull', None, _('use pull protocol to copy metadata')),
1040 ('', 'pull', None, _('use pull protocol to copy metadata')),
1041 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1041 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1042 ] + remoteopts,
1042 ] + remoteopts,
1043 _('[OPTION]... SOURCE [DEST]'))
1043 _('[OPTION]... SOURCE [DEST]'))
1044 def clone(ui, source, dest=None, **opts):
1044 def clone(ui, source, dest=None, **opts):
1045 """make a copy of an existing repository
1045 """make a copy of an existing repository
1046
1046
1047 Create a copy of an existing repository in a new directory.
1047 Create a copy of an existing repository in a new directory.
1048
1048
1049 If no destination directory name is specified, it defaults to the
1049 If no destination directory name is specified, it defaults to the
1050 basename of the source.
1050 basename of the source.
1051
1051
1052 The location of the source is added to the new repository's
1052 The location of the source is added to the new repository's
1053 ``.hg/hgrc`` file, as the default to be used for future pulls.
1053 ``.hg/hgrc`` file, as the default to be used for future pulls.
1054
1054
1055 Only local paths and ``ssh://`` URLs are supported as
1055 Only local paths and ``ssh://`` URLs are supported as
1056 destinations. For ``ssh://`` destinations, no working directory or
1056 destinations. For ``ssh://`` destinations, no working directory or
1057 ``.hg/hgrc`` will be created on the remote side.
1057 ``.hg/hgrc`` will be created on the remote side.
1058
1058
1059 To pull only a subset of changesets, specify one or more revisions
1059 To pull only a subset of changesets, specify one or more revisions
1060 identifiers with -r/--rev or branches with -b/--branch. The
1060 identifiers with -r/--rev or branches with -b/--branch. The
1061 resulting clone will contain only the specified changesets and
1061 resulting clone will contain only the specified changesets and
1062 their ancestors. These options (or 'clone src#rev dest') imply
1062 their ancestors. These options (or 'clone src#rev dest') imply
1063 --pull, even for local source repositories. Note that specifying a
1063 --pull, even for local source repositories. Note that specifying a
1064 tag will include the tagged changeset but not the changeset
1064 tag will include the tagged changeset but not the changeset
1065 containing the tag.
1065 containing the tag.
1066
1066
1067 To check out a particular version, use -u/--update, or
1067 To check out a particular version, use -u/--update, or
1068 -U/--noupdate to create a clone with no working directory.
1068 -U/--noupdate to create a clone with no working directory.
1069
1069
1070 .. container:: verbose
1070 .. container:: verbose
1071
1071
1072 For efficiency, hardlinks are used for cloning whenever the
1072 For efficiency, hardlinks are used for cloning whenever the
1073 source and destination are on the same filesystem (note this
1073 source and destination are on the same filesystem (note this
1074 applies only to the repository data, not to the working
1074 applies only to the repository data, not to the working
1075 directory). Some filesystems, such as AFS, implement hardlinking
1075 directory). Some filesystems, such as AFS, implement hardlinking
1076 incorrectly, but do not report errors. In these cases, use the
1076 incorrectly, but do not report errors. In these cases, use the
1077 --pull option to avoid hardlinking.
1077 --pull option to avoid hardlinking.
1078
1078
1079 In some cases, you can clone repositories and the working
1079 In some cases, you can clone repositories and the working
1080 directory using full hardlinks with ::
1080 directory using full hardlinks with ::
1081
1081
1082 $ cp -al REPO REPOCLONE
1082 $ cp -al REPO REPOCLONE
1083
1083
1084 This is the fastest way to clone, but it is not always safe. The
1084 This is the fastest way to clone, but it is not always safe. The
1085 operation is not atomic (making sure REPO is not modified during
1085 operation is not atomic (making sure REPO is not modified during
1086 the operation is up to you) and you have to make sure your
1086 the operation is up to you) and you have to make sure your
1087 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1087 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1088 so). Also, this is not compatible with certain extensions that
1088 so). Also, this is not compatible with certain extensions that
1089 place their metadata under the .hg directory, such as mq.
1089 place their metadata under the .hg directory, such as mq.
1090
1090
1091 Mercurial will update the working directory to the first applicable
1091 Mercurial will update the working directory to the first applicable
1092 revision from this list:
1092 revision from this list:
1093
1093
1094 a) null if -U or the source repository has no changesets
1094 a) null if -U or the source repository has no changesets
1095 b) if -u . and the source repository is local, the first parent of
1095 b) if -u . and the source repository is local, the first parent of
1096 the source repository's working directory
1096 the source repository's working directory
1097 c) the changeset specified with -u (if a branch name, this means the
1097 c) the changeset specified with -u (if a branch name, this means the
1098 latest head of that branch)
1098 latest head of that branch)
1099 d) the changeset specified with -r
1099 d) the changeset specified with -r
1100 e) the tipmost head specified with -b
1100 e) the tipmost head specified with -b
1101 f) the tipmost head specified with the url#branch source syntax
1101 f) the tipmost head specified with the url#branch source syntax
1102 g) the tipmost head of the default branch
1102 g) the tipmost head of the default branch
1103 h) tip
1103 h) tip
1104
1104
1105 Examples:
1105 Examples:
1106
1106
1107 - clone a remote repository to a new directory named hg/::
1107 - clone a remote repository to a new directory named hg/::
1108
1108
1109 hg clone http://selenic.com/hg
1109 hg clone http://selenic.com/hg
1110
1110
1111 - create a lightweight local clone::
1111 - create a lightweight local clone::
1112
1112
1113 hg clone project/ project-feature/
1113 hg clone project/ project-feature/
1114
1114
1115 - clone from an absolute path on an ssh server (note double-slash)::
1115 - clone from an absolute path on an ssh server (note double-slash)::
1116
1116
1117 hg clone ssh://user@server//home/projects/alpha/
1117 hg clone ssh://user@server//home/projects/alpha/
1118
1118
1119 - do a high-speed clone over a LAN while checking out a
1119 - do a high-speed clone over a LAN while checking out a
1120 specified version::
1120 specified version::
1121
1121
1122 hg clone --uncompressed http://server/repo -u 1.5
1122 hg clone --uncompressed http://server/repo -u 1.5
1123
1123
1124 - create a repository without changesets after a particular revision::
1124 - create a repository without changesets after a particular revision::
1125
1125
1126 hg clone -r 04e544 experimental/ good/
1126 hg clone -r 04e544 experimental/ good/
1127
1127
1128 - clone (and track) a particular named branch::
1128 - clone (and track) a particular named branch::
1129
1129
1130 hg clone http://selenic.com/hg#stable
1130 hg clone http://selenic.com/hg#stable
1131
1131
1132 See :hg:`help urls` for details on specifying URLs.
1132 See :hg:`help urls` for details on specifying URLs.
1133
1133
1134 Returns 0 on success.
1134 Returns 0 on success.
1135 """
1135 """
1136 if opts.get('noupdate') and opts.get('updaterev'):
1136 if opts.get('noupdate') and opts.get('updaterev'):
1137 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1137 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1138
1138
1139 r = hg.clone(ui, opts, source, dest,
1139 r = hg.clone(ui, opts, source, dest,
1140 pull=opts.get('pull'),
1140 pull=opts.get('pull'),
1141 stream=opts.get('uncompressed'),
1141 stream=opts.get('uncompressed'),
1142 rev=opts.get('rev'),
1142 rev=opts.get('rev'),
1143 update=opts.get('updaterev') or not opts.get('noupdate'),
1143 update=opts.get('updaterev') or not opts.get('noupdate'),
1144 branch=opts.get('branch'))
1144 branch=opts.get('branch'))
1145
1145
1146 return r is None
1146 return r is None
1147
1147
1148 @command('^commit|ci',
1148 @command('^commit|ci',
1149 [('A', 'addremove', None,
1149 [('A', 'addremove', None,
1150 _('mark new/missing files as added/removed before committing')),
1150 _('mark new/missing files as added/removed before committing')),
1151 ('', 'close-branch', None,
1151 ('', 'close-branch', None,
1152 _('mark a branch as closed, hiding it from the branch list')),
1152 _('mark a branch as closed, hiding it from the branch list')),
1153 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1153 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1154 _('[OPTION]... [FILE]...'))
1154 _('[OPTION]... [FILE]...'))
1155 def commit(ui, repo, *pats, **opts):
1155 def commit(ui, repo, *pats, **opts):
1156 """commit the specified files or all outstanding changes
1156 """commit the specified files or all outstanding changes
1157
1157
1158 Commit changes to the given files into the repository. Unlike a
1158 Commit changes to the given files into the repository. Unlike a
1159 centralized SCM, this operation is a local operation. See
1159 centralized SCM, this operation is a local operation. See
1160 :hg:`push` for a way to actively distribute your changes.
1160 :hg:`push` for a way to actively distribute your changes.
1161
1161
1162 If a list of files is omitted, all changes reported by :hg:`status`
1162 If a list of files is omitted, all changes reported by :hg:`status`
1163 will be committed.
1163 will be committed.
1164
1164
1165 If you are committing the result of a merge, do not provide any
1165 If you are committing the result of a merge, do not provide any
1166 filenames or -I/-X filters.
1166 filenames or -I/-X filters.
1167
1167
1168 If no commit message is specified, Mercurial starts your
1168 If no commit message is specified, Mercurial starts your
1169 configured editor where you can enter a message. In case your
1169 configured editor where you can enter a message. In case your
1170 commit fails, you will find a backup of your message in
1170 commit fails, you will find a backup of your message in
1171 ``.hg/last-message.txt``.
1171 ``.hg/last-message.txt``.
1172
1172
1173 See :hg:`help dates` for a list of formats valid for -d/--date.
1173 See :hg:`help dates` for a list of formats valid for -d/--date.
1174
1174
1175 Returns 0 on success, 1 if nothing changed.
1175 Returns 0 on success, 1 if nothing changed.
1176 """
1176 """
1177 if opts.get('subrepos'):
1177 if opts.get('subrepos'):
1178 # Let --subrepos on the command line overide config setting.
1178 # Let --subrepos on the command line overide config setting.
1179 ui.setconfig('ui', 'commitsubrepos', True)
1179 ui.setconfig('ui', 'commitsubrepos', True)
1180
1180
1181 extra = {}
1181 extra = {}
1182 if opts.get('close_branch'):
1182 if opts.get('close_branch'):
1183 if repo['.'].node() not in repo.branchheads():
1183 if repo['.'].node() not in repo.branchheads():
1184 # The topo heads set is included in the branch heads set of the
1184 # The topo heads set is included in the branch heads set of the
1185 # current branch, so it's sufficient to test branchheads
1185 # current branch, so it's sufficient to test branchheads
1186 raise util.Abort(_('can only close branch heads'))
1186 raise util.Abort(_('can only close branch heads'))
1187 extra['close'] = 1
1187 extra['close'] = 1
1188 e = cmdutil.commiteditor
1188 e = cmdutil.commiteditor
1189 if opts.get('force_editor'):
1189 if opts.get('force_editor'):
1190 e = cmdutil.commitforceeditor
1190 e = cmdutil.commitforceeditor
1191
1191
1192 def commitfunc(ui, repo, message, match, opts):
1192 def commitfunc(ui, repo, message, match, opts):
1193 return repo.commit(message, opts.get('user'), opts.get('date'), match,
1193 return repo.commit(message, opts.get('user'), opts.get('date'), match,
1194 editor=e, extra=extra)
1194 editor=e, extra=extra)
1195
1195
1196 branch = repo[None].branch()
1196 branch = repo[None].branch()
1197 bheads = repo.branchheads(branch)
1197 bheads = repo.branchheads(branch)
1198
1198
1199 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1199 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1200 if not node:
1200 if not node:
1201 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1201 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1202 if stat[3]:
1202 if stat[3]:
1203 ui.status(_("nothing changed (%d missing files, see 'hg status')\n")
1203 ui.status(_("nothing changed (%d missing files, see 'hg status')\n")
1204 % len(stat[3]))
1204 % len(stat[3]))
1205 else:
1205 else:
1206 ui.status(_("nothing changed\n"))
1206 ui.status(_("nothing changed\n"))
1207 return 1
1207 return 1
1208
1208
1209 ctx = repo[node]
1209 ctx = repo[node]
1210 parents = ctx.parents()
1210 parents = ctx.parents()
1211
1211
1212 if (bheads and node not in bheads and not
1212 if (bheads and node not in bheads and not
1213 [x for x in parents if x.node() in bheads and x.branch() == branch]):
1213 [x for x in parents if x.node() in bheads and x.branch() == branch]):
1214 ui.status(_('created new head\n'))
1214 ui.status(_('created new head\n'))
1215 # The message is not printed for initial roots. For the other
1215 # The message is not printed for initial roots. For the other
1216 # changesets, it is printed in the following situations:
1216 # changesets, it is printed in the following situations:
1217 #
1217 #
1218 # Par column: for the 2 parents with ...
1218 # Par column: for the 2 parents with ...
1219 # N: null or no parent
1219 # N: null or no parent
1220 # B: parent is on another named branch
1220 # B: parent is on another named branch
1221 # C: parent is a regular non head changeset
1221 # C: parent is a regular non head changeset
1222 # H: parent was a branch head of the current branch
1222 # H: parent was a branch head of the current branch
1223 # Msg column: whether we print "created new head" message
1223 # Msg column: whether we print "created new head" message
1224 # In the following, it is assumed that there already exists some
1224 # In the following, it is assumed that there already exists some
1225 # initial branch heads of the current branch, otherwise nothing is
1225 # initial branch heads of the current branch, otherwise nothing is
1226 # printed anyway.
1226 # printed anyway.
1227 #
1227 #
1228 # Par Msg Comment
1228 # Par Msg Comment
1229 # NN y additional topo root
1229 # NN y additional topo root
1230 #
1230 #
1231 # BN y additional branch root
1231 # BN y additional branch root
1232 # CN y additional topo head
1232 # CN y additional topo head
1233 # HN n usual case
1233 # HN n usual case
1234 #
1234 #
1235 # BB y weird additional branch root
1235 # BB y weird additional branch root
1236 # CB y branch merge
1236 # CB y branch merge
1237 # HB n merge with named branch
1237 # HB n merge with named branch
1238 #
1238 #
1239 # CC y additional head from merge
1239 # CC y additional head from merge
1240 # CH n merge with a head
1240 # CH n merge with a head
1241 #
1241 #
1242 # HH n head merge: head count decreases
1242 # HH n head merge: head count decreases
1243
1243
1244 if not opts.get('close_branch'):
1244 if not opts.get('close_branch'):
1245 for r in parents:
1245 for r in parents:
1246 if r.extra().get('close') and r.branch() == branch:
1246 if r.extra().get('close') and r.branch() == branch:
1247 ui.status(_('reopening closed branch head %d\n') % r)
1247 ui.status(_('reopening closed branch head %d\n') % r)
1248
1248
1249 if ui.debugflag:
1249 if ui.debugflag:
1250 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
1250 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
1251 elif ui.verbose:
1251 elif ui.verbose:
1252 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
1252 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
1253
1253
1254 @command('copy|cp',
1254 @command('copy|cp',
1255 [('A', 'after', None, _('record a copy that has already occurred')),
1255 [('A', 'after', None, _('record a copy that has already occurred')),
1256 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1256 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1257 ] + walkopts + dryrunopts,
1257 ] + walkopts + dryrunopts,
1258 _('[OPTION]... [SOURCE]... DEST'))
1258 _('[OPTION]... [SOURCE]... DEST'))
1259 def copy(ui, repo, *pats, **opts):
1259 def copy(ui, repo, *pats, **opts):
1260 """mark files as copied for the next commit
1260 """mark files as copied for the next commit
1261
1261
1262 Mark dest as having copies of source files. If dest is a
1262 Mark dest as having copies of source files. If dest is a
1263 directory, copies are put in that directory. If dest is a file,
1263 directory, copies are put in that directory. If dest is a file,
1264 the source must be a single file.
1264 the source must be a single file.
1265
1265
1266 By default, this command copies the contents of files as they
1266 By default, this command copies the contents of files as they
1267 exist in the working directory. If invoked with -A/--after, the
1267 exist in the working directory. If invoked with -A/--after, the
1268 operation is recorded, but no copying is performed.
1268 operation is recorded, but no copying is performed.
1269
1269
1270 This command takes effect with the next commit. To undo a copy
1270 This command takes effect with the next commit. To undo a copy
1271 before that, see :hg:`revert`.
1271 before that, see :hg:`revert`.
1272
1272
1273 Returns 0 on success, 1 if errors are encountered.
1273 Returns 0 on success, 1 if errors are encountered.
1274 """
1274 """
1275 wlock = repo.wlock(False)
1275 wlock = repo.wlock(False)
1276 try:
1276 try:
1277 return cmdutil.copy(ui, repo, pats, opts)
1277 return cmdutil.copy(ui, repo, pats, opts)
1278 finally:
1278 finally:
1279 wlock.release()
1279 wlock.release()
1280
1280
1281 @command('debugancestor', [], _('[INDEX] REV1 REV2'))
1281 @command('debugancestor', [], _('[INDEX] REV1 REV2'))
1282 def debugancestor(ui, repo, *args):
1282 def debugancestor(ui, repo, *args):
1283 """find the ancestor revision of two revisions in a given index"""
1283 """find the ancestor revision of two revisions in a given index"""
1284 if len(args) == 3:
1284 if len(args) == 3:
1285 index, rev1, rev2 = args
1285 index, rev1, rev2 = args
1286 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1286 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1287 lookup = r.lookup
1287 lookup = r.lookup
1288 elif len(args) == 2:
1288 elif len(args) == 2:
1289 if not repo:
1289 if not repo:
1290 raise util.Abort(_("there is no Mercurial repository here "
1290 raise util.Abort(_("there is no Mercurial repository here "
1291 "(.hg not found)"))
1291 "(.hg not found)"))
1292 rev1, rev2 = args
1292 rev1, rev2 = args
1293 r = repo.changelog
1293 r = repo.changelog
1294 lookup = repo.lookup
1294 lookup = repo.lookup
1295 else:
1295 else:
1296 raise util.Abort(_('either two or three arguments required'))
1296 raise util.Abort(_('either two or three arguments required'))
1297 a = r.ancestor(lookup(rev1), lookup(rev2))
1297 a = r.ancestor(lookup(rev1), lookup(rev2))
1298 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1298 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1299
1299
1300 @command('debugbuilddag',
1300 @command('debugbuilddag',
1301 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1301 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1302 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1302 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1303 ('n', 'new-file', None, _('add new file at each rev'))],
1303 ('n', 'new-file', None, _('add new file at each rev'))],
1304 _('[OPTION]... [TEXT]'))
1304 _('[OPTION]... [TEXT]'))
1305 def debugbuilddag(ui, repo, text=None,
1305 def debugbuilddag(ui, repo, text=None,
1306 mergeable_file=False,
1306 mergeable_file=False,
1307 overwritten_file=False,
1307 overwritten_file=False,
1308 new_file=False):
1308 new_file=False):
1309 """builds a repo with a given DAG from scratch in the current empty repo
1309 """builds a repo with a given DAG from scratch in the current empty repo
1310
1310
1311 The description of the DAG is read from stdin if not given on the
1311 The description of the DAG is read from stdin if not given on the
1312 command line.
1312 command line.
1313
1313
1314 Elements:
1314 Elements:
1315
1315
1316 - "+n" is a linear run of n nodes based on the current default parent
1316 - "+n" is a linear run of n nodes based on the current default parent
1317 - "." is a single node based on the current default parent
1317 - "." is a single node based on the current default parent
1318 - "$" resets the default parent to null (implied at the start);
1318 - "$" resets the default parent to null (implied at the start);
1319 otherwise the default parent is always the last node created
1319 otherwise the default parent is always the last node created
1320 - "<p" sets the default parent to the backref p
1320 - "<p" sets the default parent to the backref p
1321 - "*p" is a fork at parent p, which is a backref
1321 - "*p" is a fork at parent p, which is a backref
1322 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1322 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1323 - "/p2" is a merge of the preceding node and p2
1323 - "/p2" is a merge of the preceding node and p2
1324 - ":tag" defines a local tag for the preceding node
1324 - ":tag" defines a local tag for the preceding node
1325 - "@branch" sets the named branch for subsequent nodes
1325 - "@branch" sets the named branch for subsequent nodes
1326 - "#...\\n" is a comment up to the end of the line
1326 - "#...\\n" is a comment up to the end of the line
1327
1327
1328 Whitespace between the above elements is ignored.
1328 Whitespace between the above elements is ignored.
1329
1329
1330 A backref is either
1330 A backref is either
1331
1331
1332 - a number n, which references the node curr-n, where curr is the current
1332 - a number n, which references the node curr-n, where curr is the current
1333 node, or
1333 node, or
1334 - the name of a local tag you placed earlier using ":tag", or
1334 - the name of a local tag you placed earlier using ":tag", or
1335 - empty to denote the default parent.
1335 - empty to denote the default parent.
1336
1336
1337 All string valued-elements are either strictly alphanumeric, or must
1337 All string valued-elements are either strictly alphanumeric, or must
1338 be enclosed in double quotes ("..."), with "\\" as escape character.
1338 be enclosed in double quotes ("..."), with "\\" as escape character.
1339 """
1339 """
1340
1340
1341 if text is None:
1341 if text is None:
1342 ui.status(_("reading DAG from stdin\n"))
1342 ui.status(_("reading DAG from stdin\n"))
1343 text = ui.fin.read()
1343 text = ui.fin.read()
1344
1344
1345 cl = repo.changelog
1345 cl = repo.changelog
1346 if len(cl) > 0:
1346 if len(cl) > 0:
1347 raise util.Abort(_('repository is not empty'))
1347 raise util.Abort(_('repository is not empty'))
1348
1348
1349 # determine number of revs in DAG
1349 # determine number of revs in DAG
1350 total = 0
1350 total = 0
1351 for type, data in dagparser.parsedag(text):
1351 for type, data in dagparser.parsedag(text):
1352 if type == 'n':
1352 if type == 'n':
1353 total += 1
1353 total += 1
1354
1354
1355 if mergeable_file:
1355 if mergeable_file:
1356 linesperrev = 2
1356 linesperrev = 2
1357 # make a file with k lines per rev
1357 # make a file with k lines per rev
1358 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1358 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1359 initialmergedlines.append("")
1359 initialmergedlines.append("")
1360
1360
1361 tags = []
1361 tags = []
1362
1362
1363 tr = repo.transaction("builddag")
1363 tr = repo.transaction("builddag")
1364 try:
1364 try:
1365
1365
1366 at = -1
1366 at = -1
1367 atbranch = 'default'
1367 atbranch = 'default'
1368 nodeids = []
1368 nodeids = []
1369 ui.progress(_('building'), 0, unit=_('revisions'), total=total)
1369 ui.progress(_('building'), 0, unit=_('revisions'), total=total)
1370 for type, data in dagparser.parsedag(text):
1370 for type, data in dagparser.parsedag(text):
1371 if type == 'n':
1371 if type == 'n':
1372 ui.note('node %s\n' % str(data))
1372 ui.note('node %s\n' % str(data))
1373 id, ps = data
1373 id, ps = data
1374
1374
1375 files = []
1375 files = []
1376 fctxs = {}
1376 fctxs = {}
1377
1377
1378 p2 = None
1378 p2 = None
1379 if mergeable_file:
1379 if mergeable_file:
1380 fn = "mf"
1380 fn = "mf"
1381 p1 = repo[ps[0]]
1381 p1 = repo[ps[0]]
1382 if len(ps) > 1:
1382 if len(ps) > 1:
1383 p2 = repo[ps[1]]
1383 p2 = repo[ps[1]]
1384 pa = p1.ancestor(p2)
1384 pa = p1.ancestor(p2)
1385 base, local, other = [x[fn].data() for x in pa, p1, p2]
1385 base, local, other = [x[fn].data() for x in pa, p1, p2]
1386 m3 = simplemerge.Merge3Text(base, local, other)
1386 m3 = simplemerge.Merge3Text(base, local, other)
1387 ml = [l.strip() for l in m3.merge_lines()]
1387 ml = [l.strip() for l in m3.merge_lines()]
1388 ml.append("")
1388 ml.append("")
1389 elif at > 0:
1389 elif at > 0:
1390 ml = p1[fn].data().split("\n")
1390 ml = p1[fn].data().split("\n")
1391 else:
1391 else:
1392 ml = initialmergedlines
1392 ml = initialmergedlines
1393 ml[id * linesperrev] += " r%i" % id
1393 ml[id * linesperrev] += " r%i" % id
1394 mergedtext = "\n".join(ml)
1394 mergedtext = "\n".join(ml)
1395 files.append(fn)
1395 files.append(fn)
1396 fctxs[fn] = context.memfilectx(fn, mergedtext)
1396 fctxs[fn] = context.memfilectx(fn, mergedtext)
1397
1397
1398 if overwritten_file:
1398 if overwritten_file:
1399 fn = "of"
1399 fn = "of"
1400 files.append(fn)
1400 files.append(fn)
1401 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1401 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1402
1402
1403 if new_file:
1403 if new_file:
1404 fn = "nf%i" % id
1404 fn = "nf%i" % id
1405 files.append(fn)
1405 files.append(fn)
1406 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1406 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1407 if len(ps) > 1:
1407 if len(ps) > 1:
1408 if not p2:
1408 if not p2:
1409 p2 = repo[ps[1]]
1409 p2 = repo[ps[1]]
1410 for fn in p2:
1410 for fn in p2:
1411 if fn.startswith("nf"):
1411 if fn.startswith("nf"):
1412 files.append(fn)
1412 files.append(fn)
1413 fctxs[fn] = p2[fn]
1413 fctxs[fn] = p2[fn]
1414
1414
1415 def fctxfn(repo, cx, path):
1415 def fctxfn(repo, cx, path):
1416 return fctxs.get(path)
1416 return fctxs.get(path)
1417
1417
1418 if len(ps) == 0 or ps[0] < 0:
1418 if len(ps) == 0 or ps[0] < 0:
1419 pars = [None, None]
1419 pars = [None, None]
1420 elif len(ps) == 1:
1420 elif len(ps) == 1:
1421 pars = [nodeids[ps[0]], None]
1421 pars = [nodeids[ps[0]], None]
1422 else:
1422 else:
1423 pars = [nodeids[p] for p in ps]
1423 pars = [nodeids[p] for p in ps]
1424 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1424 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1425 date=(id, 0),
1425 date=(id, 0),
1426 user="debugbuilddag",
1426 user="debugbuilddag",
1427 extra={'branch': atbranch})
1427 extra={'branch': atbranch})
1428 nodeid = repo.commitctx(cx)
1428 nodeid = repo.commitctx(cx)
1429 nodeids.append(nodeid)
1429 nodeids.append(nodeid)
1430 at = id
1430 at = id
1431 elif type == 'l':
1431 elif type == 'l':
1432 id, name = data
1432 id, name = data
1433 ui.note('tag %s\n' % name)
1433 ui.note('tag %s\n' % name)
1434 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1434 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1435 elif type == 'a':
1435 elif type == 'a':
1436 ui.note('branch %s\n' % data)
1436 ui.note('branch %s\n' % data)
1437 atbranch = data
1437 atbranch = data
1438 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1438 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1439 tr.close()
1439 tr.close()
1440 finally:
1440 finally:
1441 ui.progress(_('building'), None)
1441 ui.progress(_('building'), None)
1442 tr.release()
1442 tr.release()
1443
1443
1444 if tags:
1444 if tags:
1445 repo.opener.write("localtags", "".join(tags))
1445 repo.opener.write("localtags", "".join(tags))
1446
1446
1447 @command('debugbundle', [('a', 'all', None, _('show all details'))], _('FILE'))
1447 @command('debugbundle', [('a', 'all', None, _('show all details'))], _('FILE'))
1448 def debugbundle(ui, bundlepath, all=None, **opts):
1448 def debugbundle(ui, bundlepath, all=None, **opts):
1449 """lists the contents of a bundle"""
1449 """lists the contents of a bundle"""
1450 f = url.open(ui, bundlepath)
1450 f = url.open(ui, bundlepath)
1451 try:
1451 try:
1452 gen = changegroup.readbundle(f, bundlepath)
1452 gen = changegroup.readbundle(f, bundlepath)
1453 if all:
1453 if all:
1454 ui.write("format: id, p1, p2, cset, delta base, len(delta)\n")
1454 ui.write("format: id, p1, p2, cset, delta base, len(delta)\n")
1455
1455
1456 def showchunks(named):
1456 def showchunks(named):
1457 ui.write("\n%s\n" % named)
1457 ui.write("\n%s\n" % named)
1458 chain = None
1458 chain = None
1459 while True:
1459 while True:
1460 chunkdata = gen.deltachunk(chain)
1460 chunkdata = gen.deltachunk(chain)
1461 if not chunkdata:
1461 if not chunkdata:
1462 break
1462 break
1463 node = chunkdata['node']
1463 node = chunkdata['node']
1464 p1 = chunkdata['p1']
1464 p1 = chunkdata['p1']
1465 p2 = chunkdata['p2']
1465 p2 = chunkdata['p2']
1466 cs = chunkdata['cs']
1466 cs = chunkdata['cs']
1467 deltabase = chunkdata['deltabase']
1467 deltabase = chunkdata['deltabase']
1468 delta = chunkdata['delta']
1468 delta = chunkdata['delta']
1469 ui.write("%s %s %s %s %s %s\n" %
1469 ui.write("%s %s %s %s %s %s\n" %
1470 (hex(node), hex(p1), hex(p2),
1470 (hex(node), hex(p1), hex(p2),
1471 hex(cs), hex(deltabase), len(delta)))
1471 hex(cs), hex(deltabase), len(delta)))
1472 chain = node
1472 chain = node
1473
1473
1474 chunkdata = gen.changelogheader()
1474 chunkdata = gen.changelogheader()
1475 showchunks("changelog")
1475 showchunks("changelog")
1476 chunkdata = gen.manifestheader()
1476 chunkdata = gen.manifestheader()
1477 showchunks("manifest")
1477 showchunks("manifest")
1478 while True:
1478 while True:
1479 chunkdata = gen.filelogheader()
1479 chunkdata = gen.filelogheader()
1480 if not chunkdata:
1480 if not chunkdata:
1481 break
1481 break
1482 fname = chunkdata['filename']
1482 fname = chunkdata['filename']
1483 showchunks(fname)
1483 showchunks(fname)
1484 else:
1484 else:
1485 chunkdata = gen.changelogheader()
1485 chunkdata = gen.changelogheader()
1486 chain = None
1486 chain = None
1487 while True:
1487 while True:
1488 chunkdata = gen.deltachunk(chain)
1488 chunkdata = gen.deltachunk(chain)
1489 if not chunkdata:
1489 if not chunkdata:
1490 break
1490 break
1491 node = chunkdata['node']
1491 node = chunkdata['node']
1492 ui.write("%s\n" % hex(node))
1492 ui.write("%s\n" % hex(node))
1493 chain = node
1493 chain = node
1494 finally:
1494 finally:
1495 f.close()
1495 f.close()
1496
1496
1497 @command('debugcheckstate', [], '')
1497 @command('debugcheckstate', [], '')
1498 def debugcheckstate(ui, repo):
1498 def debugcheckstate(ui, repo):
1499 """validate the correctness of the current dirstate"""
1499 """validate the correctness of the current dirstate"""
1500 parent1, parent2 = repo.dirstate.parents()
1500 parent1, parent2 = repo.dirstate.parents()
1501 m1 = repo[parent1].manifest()
1501 m1 = repo[parent1].manifest()
1502 m2 = repo[parent2].manifest()
1502 m2 = repo[parent2].manifest()
1503 errors = 0
1503 errors = 0
1504 for f in repo.dirstate:
1504 for f in repo.dirstate:
1505 state = repo.dirstate[f]
1505 state = repo.dirstate[f]
1506 if state in "nr" and f not in m1:
1506 if state in "nr" and f not in m1:
1507 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1507 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1508 errors += 1
1508 errors += 1
1509 if state in "a" and f in m1:
1509 if state in "a" and f in m1:
1510 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1510 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1511 errors += 1
1511 errors += 1
1512 if state in "m" and f not in m1 and f not in m2:
1512 if state in "m" and f not in m1 and f not in m2:
1513 ui.warn(_("%s in state %s, but not in either manifest\n") %
1513 ui.warn(_("%s in state %s, but not in either manifest\n") %
1514 (f, state))
1514 (f, state))
1515 errors += 1
1515 errors += 1
1516 for f in m1:
1516 for f in m1:
1517 state = repo.dirstate[f]
1517 state = repo.dirstate[f]
1518 if state not in "nrm":
1518 if state not in "nrm":
1519 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1519 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1520 errors += 1
1520 errors += 1
1521 if errors:
1521 if errors:
1522 error = _(".hg/dirstate inconsistent with current parent's manifest")
1522 error = _(".hg/dirstate inconsistent with current parent's manifest")
1523 raise util.Abort(error)
1523 raise util.Abort(error)
1524
1524
1525 @command('debugcommands', [], _('[COMMAND]'))
1525 @command('debugcommands', [], _('[COMMAND]'))
1526 def debugcommands(ui, cmd='', *args):
1526 def debugcommands(ui, cmd='', *args):
1527 """list all available commands and options"""
1527 """list all available commands and options"""
1528 for cmd, vals in sorted(table.iteritems()):
1528 for cmd, vals in sorted(table.iteritems()):
1529 cmd = cmd.split('|')[0].strip('^')
1529 cmd = cmd.split('|')[0].strip('^')
1530 opts = ', '.join([i[1] for i in vals[1]])
1530 opts = ', '.join([i[1] for i in vals[1]])
1531 ui.write('%s: %s\n' % (cmd, opts))
1531 ui.write('%s: %s\n' % (cmd, opts))
1532
1532
1533 @command('debugcomplete',
1533 @command('debugcomplete',
1534 [('o', 'options', None, _('show the command options'))],
1534 [('o', 'options', None, _('show the command options'))],
1535 _('[-o] CMD'))
1535 _('[-o] CMD'))
1536 def debugcomplete(ui, cmd='', **opts):
1536 def debugcomplete(ui, cmd='', **opts):
1537 """returns the completion list associated with the given command"""
1537 """returns the completion list associated with the given command"""
1538
1538
1539 if opts.get('options'):
1539 if opts.get('options'):
1540 options = []
1540 options = []
1541 otables = [globalopts]
1541 otables = [globalopts]
1542 if cmd:
1542 if cmd:
1543 aliases, entry = cmdutil.findcmd(cmd, table, False)
1543 aliases, entry = cmdutil.findcmd(cmd, table, False)
1544 otables.append(entry[1])
1544 otables.append(entry[1])
1545 for t in otables:
1545 for t in otables:
1546 for o in t:
1546 for o in t:
1547 if "(DEPRECATED)" in o[3]:
1547 if "(DEPRECATED)" in o[3]:
1548 continue
1548 continue
1549 if o[0]:
1549 if o[0]:
1550 options.append('-%s' % o[0])
1550 options.append('-%s' % o[0])
1551 options.append('--%s' % o[1])
1551 options.append('--%s' % o[1])
1552 ui.write("%s\n" % "\n".join(options))
1552 ui.write("%s\n" % "\n".join(options))
1553 return
1553 return
1554
1554
1555 cmdlist = cmdutil.findpossible(cmd, table)
1555 cmdlist = cmdutil.findpossible(cmd, table)
1556 if ui.verbose:
1556 if ui.verbose:
1557 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1557 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1558 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1558 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1559
1559
1560 @command('debugdag',
1560 @command('debugdag',
1561 [('t', 'tags', None, _('use tags as labels')),
1561 [('t', 'tags', None, _('use tags as labels')),
1562 ('b', 'branches', None, _('annotate with branch names')),
1562 ('b', 'branches', None, _('annotate with branch names')),
1563 ('', 'dots', None, _('use dots for runs')),
1563 ('', 'dots', None, _('use dots for runs')),
1564 ('s', 'spaces', None, _('separate elements by spaces'))],
1564 ('s', 'spaces', None, _('separate elements by spaces'))],
1565 _('[OPTION]... [FILE [REV]...]'))
1565 _('[OPTION]... [FILE [REV]...]'))
1566 def debugdag(ui, repo, file_=None, *revs, **opts):
1566 def debugdag(ui, repo, file_=None, *revs, **opts):
1567 """format the changelog or an index DAG as a concise textual description
1567 """format the changelog or an index DAG as a concise textual description
1568
1568
1569 If you pass a revlog index, the revlog's DAG is emitted. If you list
1569 If you pass a revlog index, the revlog's DAG is emitted. If you list
1570 revision numbers, they get labelled in the output as rN.
1570 revision numbers, they get labelled in the output as rN.
1571
1571
1572 Otherwise, the changelog DAG of the current repo is emitted.
1572 Otherwise, the changelog DAG of the current repo is emitted.
1573 """
1573 """
1574 spaces = opts.get('spaces')
1574 spaces = opts.get('spaces')
1575 dots = opts.get('dots')
1575 dots = opts.get('dots')
1576 if file_:
1576 if file_:
1577 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1577 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1578 revs = set((int(r) for r in revs))
1578 revs = set((int(r) for r in revs))
1579 def events():
1579 def events():
1580 for r in rlog:
1580 for r in rlog:
1581 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1581 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1582 if r in revs:
1582 if r in revs:
1583 yield 'l', (r, "r%i" % r)
1583 yield 'l', (r, "r%i" % r)
1584 elif repo:
1584 elif repo:
1585 cl = repo.changelog
1585 cl = repo.changelog
1586 tags = opts.get('tags')
1586 tags = opts.get('tags')
1587 branches = opts.get('branches')
1587 branches = opts.get('branches')
1588 if tags:
1588 if tags:
1589 labels = {}
1589 labels = {}
1590 for l, n in repo.tags().items():
1590 for l, n in repo.tags().items():
1591 labels.setdefault(cl.rev(n), []).append(l)
1591 labels.setdefault(cl.rev(n), []).append(l)
1592 def events():
1592 def events():
1593 b = "default"
1593 b = "default"
1594 for r in cl:
1594 for r in cl:
1595 if branches:
1595 if branches:
1596 newb = cl.read(cl.node(r))[5]['branch']
1596 newb = cl.read(cl.node(r))[5]['branch']
1597 if newb != b:
1597 if newb != b:
1598 yield 'a', newb
1598 yield 'a', newb
1599 b = newb
1599 b = newb
1600 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1600 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1601 if tags:
1601 if tags:
1602 ls = labels.get(r)
1602 ls = labels.get(r)
1603 if ls:
1603 if ls:
1604 for l in ls:
1604 for l in ls:
1605 yield 'l', (r, l)
1605 yield 'l', (r, l)
1606 else:
1606 else:
1607 raise util.Abort(_('need repo for changelog dag'))
1607 raise util.Abort(_('need repo for changelog dag'))
1608
1608
1609 for line in dagparser.dagtextlines(events(),
1609 for line in dagparser.dagtextlines(events(),
1610 addspaces=spaces,
1610 addspaces=spaces,
1611 wraplabels=True,
1611 wraplabels=True,
1612 wrapannotations=True,
1612 wrapannotations=True,
1613 wrapnonlinear=dots,
1613 wrapnonlinear=dots,
1614 usedots=dots,
1614 usedots=dots,
1615 maxlinewidth=70):
1615 maxlinewidth=70):
1616 ui.write(line)
1616 ui.write(line)
1617 ui.write("\n")
1617 ui.write("\n")
1618
1618
1619 @command('debugdata',
1619 @command('debugdata',
1620 [('c', 'changelog', False, _('open changelog')),
1620 [('c', 'changelog', False, _('open changelog')),
1621 ('m', 'manifest', False, _('open manifest'))],
1621 ('m', 'manifest', False, _('open manifest'))],
1622 _('-c|-m|FILE REV'))
1622 _('-c|-m|FILE REV'))
1623 def debugdata(ui, repo, file_, rev = None, **opts):
1623 def debugdata(ui, repo, file_, rev = None, **opts):
1624 """dump the contents of a data file revision"""
1624 """dump the contents of a data file revision"""
1625 if opts.get('changelog') or opts.get('manifest'):
1625 if opts.get('changelog') or opts.get('manifest'):
1626 file_, rev = None, file_
1626 file_, rev = None, file_
1627 elif rev is None:
1627 elif rev is None:
1628 raise error.CommandError('debugdata', _('invalid arguments'))
1628 raise error.CommandError('debugdata', _('invalid arguments'))
1629 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
1629 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
1630 try:
1630 try:
1631 ui.write(r.revision(r.lookup(rev)))
1631 ui.write(r.revision(r.lookup(rev)))
1632 except KeyError:
1632 except KeyError:
1633 raise util.Abort(_('invalid revision identifier %s') % rev)
1633 raise util.Abort(_('invalid revision identifier %s') % rev)
1634
1634
1635 @command('debugdate',
1635 @command('debugdate',
1636 [('e', 'extended', None, _('try extended date formats'))],
1636 [('e', 'extended', None, _('try extended date formats'))],
1637 _('[-e] DATE [RANGE]'))
1637 _('[-e] DATE [RANGE]'))
1638 def debugdate(ui, date, range=None, **opts):
1638 def debugdate(ui, date, range=None, **opts):
1639 """parse and display a date"""
1639 """parse and display a date"""
1640 if opts["extended"]:
1640 if opts["extended"]:
1641 d = util.parsedate(date, util.extendeddateformats)
1641 d = util.parsedate(date, util.extendeddateformats)
1642 else:
1642 else:
1643 d = util.parsedate(date)
1643 d = util.parsedate(date)
1644 ui.write("internal: %s %s\n" % d)
1644 ui.write("internal: %s %s\n" % d)
1645 ui.write("standard: %s\n" % util.datestr(d))
1645 ui.write("standard: %s\n" % util.datestr(d))
1646 if range:
1646 if range:
1647 m = util.matchdate(range)
1647 m = util.matchdate(range)
1648 ui.write("match: %s\n" % m(d[0]))
1648 ui.write("match: %s\n" % m(d[0]))
1649
1649
1650 @command('debugdiscovery',
1650 @command('debugdiscovery',
1651 [('', 'old', None, _('use old-style discovery')),
1651 [('', 'old', None, _('use old-style discovery')),
1652 ('', 'nonheads', None,
1652 ('', 'nonheads', None,
1653 _('use old-style discovery with non-heads included')),
1653 _('use old-style discovery with non-heads included')),
1654 ] + remoteopts,
1654 ] + remoteopts,
1655 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
1655 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
1656 def debugdiscovery(ui, repo, remoteurl="default", **opts):
1656 def debugdiscovery(ui, repo, remoteurl="default", **opts):
1657 """runs the changeset discovery protocol in isolation"""
1657 """runs the changeset discovery protocol in isolation"""
1658 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl), opts.get('branch'))
1658 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl), opts.get('branch'))
1659 remote = hg.peer(repo, opts, remoteurl)
1659 remote = hg.peer(repo, opts, remoteurl)
1660 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
1660 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
1661
1661
1662 # make sure tests are repeatable
1662 # make sure tests are repeatable
1663 random.seed(12323)
1663 random.seed(12323)
1664
1664
1665 def doit(localheads, remoteheads):
1665 def doit(localheads, remoteheads):
1666 if opts.get('old'):
1666 if opts.get('old'):
1667 if localheads:
1667 if localheads:
1668 raise util.Abort('cannot use localheads with old style discovery')
1668 raise util.Abort('cannot use localheads with old style discovery')
1669 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
1669 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
1670 force=True)
1670 force=True)
1671 common = set(common)
1671 common = set(common)
1672 if not opts.get('nonheads'):
1672 if not opts.get('nonheads'):
1673 ui.write("unpruned common: %s\n" % " ".join([short(n)
1673 ui.write("unpruned common: %s\n" % " ".join([short(n)
1674 for n in common]))
1674 for n in common]))
1675 dag = dagutil.revlogdag(repo.changelog)
1675 dag = dagutil.revlogdag(repo.changelog)
1676 all = dag.ancestorset(dag.internalizeall(common))
1676 all = dag.ancestorset(dag.internalizeall(common))
1677 common = dag.externalizeall(dag.headsetofconnecteds(all))
1677 common = dag.externalizeall(dag.headsetofconnecteds(all))
1678 else:
1678 else:
1679 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
1679 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
1680 common = set(common)
1680 common = set(common)
1681 rheads = set(hds)
1681 rheads = set(hds)
1682 lheads = set(repo.heads())
1682 lheads = set(repo.heads())
1683 ui.write("common heads: %s\n" % " ".join([short(n) for n in common]))
1683 ui.write("common heads: %s\n" % " ".join([short(n) for n in common]))
1684 if lheads <= common:
1684 if lheads <= common:
1685 ui.write("local is subset\n")
1685 ui.write("local is subset\n")
1686 elif rheads <= common:
1686 elif rheads <= common:
1687 ui.write("remote is subset\n")
1687 ui.write("remote is subset\n")
1688
1688
1689 serverlogs = opts.get('serverlog')
1689 serverlogs = opts.get('serverlog')
1690 if serverlogs:
1690 if serverlogs:
1691 for filename in serverlogs:
1691 for filename in serverlogs:
1692 logfile = open(filename, 'r')
1692 logfile = open(filename, 'r')
1693 try:
1693 try:
1694 line = logfile.readline()
1694 line = logfile.readline()
1695 while line:
1695 while line:
1696 parts = line.strip().split(';')
1696 parts = line.strip().split(';')
1697 op = parts[1]
1697 op = parts[1]
1698 if op == 'cg':
1698 if op == 'cg':
1699 pass
1699 pass
1700 elif op == 'cgss':
1700 elif op == 'cgss':
1701 doit(parts[2].split(' '), parts[3].split(' '))
1701 doit(parts[2].split(' '), parts[3].split(' '))
1702 elif op == 'unb':
1702 elif op == 'unb':
1703 doit(parts[3].split(' '), parts[2].split(' '))
1703 doit(parts[3].split(' '), parts[2].split(' '))
1704 line = logfile.readline()
1704 line = logfile.readline()
1705 finally:
1705 finally:
1706 logfile.close()
1706 logfile.close()
1707
1707
1708 else:
1708 else:
1709 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
1709 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
1710 opts.get('remote_head'))
1710 opts.get('remote_head'))
1711 localrevs = opts.get('local_head')
1711 localrevs = opts.get('local_head')
1712 doit(localrevs, remoterevs)
1712 doit(localrevs, remoterevs)
1713
1713
1714 @command('debugfileset', [], ('REVSPEC'))
1714 @command('debugfileset', [], ('REVSPEC'))
1715 def debugfileset(ui, repo, expr):
1715 def debugfileset(ui, repo, expr):
1716 '''parse and apply a fileset specification'''
1716 '''parse and apply a fileset specification'''
1717 if ui.verbose:
1717 if ui.verbose:
1718 tree = fileset.parse(expr)[0]
1718 tree = fileset.parse(expr)[0]
1719 ui.note(tree, "\n")
1719 ui.note(tree, "\n")
1720
1720
1721 for f in fileset.getfileset(repo[None], expr):
1721 for f in fileset.getfileset(repo[None], expr):
1722 ui.write("%s\n" % f)
1722 ui.write("%s\n" % f)
1723
1723
1724 @command('debugfsinfo', [], _('[PATH]'))
1724 @command('debugfsinfo', [], _('[PATH]'))
1725 def debugfsinfo(ui, path = "."):
1725 def debugfsinfo(ui, path = "."):
1726 """show information detected about current filesystem"""
1726 """show information detected about current filesystem"""
1727 util.writefile('.debugfsinfo', '')
1727 util.writefile('.debugfsinfo', '')
1728 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1728 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1729 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1729 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1730 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1730 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1731 and 'yes' or 'no'))
1731 and 'yes' or 'no'))
1732 os.unlink('.debugfsinfo')
1732 os.unlink('.debugfsinfo')
1733
1733
1734 @command('debuggetbundle',
1734 @command('debuggetbundle',
1735 [('H', 'head', [], _('id of head node'), _('ID')),
1735 [('H', 'head', [], _('id of head node'), _('ID')),
1736 ('C', 'common', [], _('id of common node'), _('ID')),
1736 ('C', 'common', [], _('id of common node'), _('ID')),
1737 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
1737 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
1738 _('REPO FILE [-H|-C ID]...'))
1738 _('REPO FILE [-H|-C ID]...'))
1739 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
1739 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
1740 """retrieves a bundle from a repo
1740 """retrieves a bundle from a repo
1741
1741
1742 Every ID must be a full-length hex node id string. Saves the bundle to the
1742 Every ID must be a full-length hex node id string. Saves the bundle to the
1743 given file.
1743 given file.
1744 """
1744 """
1745 repo = hg.peer(ui, opts, repopath)
1745 repo = hg.peer(ui, opts, repopath)
1746 if not repo.capable('getbundle'):
1746 if not repo.capable('getbundle'):
1747 raise util.Abort("getbundle() not supported by target repository")
1747 raise util.Abort("getbundle() not supported by target repository")
1748 args = {}
1748 args = {}
1749 if common:
1749 if common:
1750 args['common'] = [bin(s) for s in common]
1750 args['common'] = [bin(s) for s in common]
1751 if head:
1751 if head:
1752 args['heads'] = [bin(s) for s in head]
1752 args['heads'] = [bin(s) for s in head]
1753 bundle = repo.getbundle('debug', **args)
1753 bundle = repo.getbundle('debug', **args)
1754
1754
1755 bundletype = opts.get('type', 'bzip2').lower()
1755 bundletype = opts.get('type', 'bzip2').lower()
1756 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1756 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1757 bundletype = btypes.get(bundletype)
1757 bundletype = btypes.get(bundletype)
1758 if bundletype not in changegroup.bundletypes:
1758 if bundletype not in changegroup.bundletypes:
1759 raise util.Abort(_('unknown bundle type specified with --type'))
1759 raise util.Abort(_('unknown bundle type specified with --type'))
1760 changegroup.writebundle(bundle, bundlepath, bundletype)
1760 changegroup.writebundle(bundle, bundlepath, bundletype)
1761
1761
1762 @command('debugignore', [], '')
1762 @command('debugignore', [], '')
1763 def debugignore(ui, repo, *values, **opts):
1763 def debugignore(ui, repo, *values, **opts):
1764 """display the combined ignore pattern"""
1764 """display the combined ignore pattern"""
1765 ignore = repo.dirstate._ignore
1765 ignore = repo.dirstate._ignore
1766 includepat = getattr(ignore, 'includepat', None)
1766 includepat = getattr(ignore, 'includepat', None)
1767 if includepat is not None:
1767 if includepat is not None:
1768 ui.write("%s\n" % includepat)
1768 ui.write("%s\n" % includepat)
1769 else:
1769 else:
1770 raise util.Abort(_("no ignore patterns found"))
1770 raise util.Abort(_("no ignore patterns found"))
1771
1771
1772 @command('debugindex',
1772 @command('debugindex',
1773 [('c', 'changelog', False, _('open changelog')),
1773 [('c', 'changelog', False, _('open changelog')),
1774 ('m', 'manifest', False, _('open manifest')),
1774 ('m', 'manifest', False, _('open manifest')),
1775 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
1775 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
1776 _('[-f FORMAT] -c|-m|FILE'))
1776 _('[-f FORMAT] -c|-m|FILE'))
1777 def debugindex(ui, repo, file_ = None, **opts):
1777 def debugindex(ui, repo, file_ = None, **opts):
1778 """dump the contents of an index file"""
1778 """dump the contents of an index file"""
1779 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
1779 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
1780 format = opts.get('format', 0)
1780 format = opts.get('format', 0)
1781 if format not in (0, 1):
1781 if format not in (0, 1):
1782 raise util.Abort(_("unknown format %d") % format)
1782 raise util.Abort(_("unknown format %d") % format)
1783
1783
1784 generaldelta = r.version & revlog.REVLOGGENERALDELTA
1784 generaldelta = r.version & revlog.REVLOGGENERALDELTA
1785 if generaldelta:
1785 if generaldelta:
1786 basehdr = ' delta'
1786 basehdr = ' delta'
1787 else:
1787 else:
1788 basehdr = ' base'
1788 basehdr = ' base'
1789
1789
1790 if format == 0:
1790 if format == 0:
1791 ui.write(" rev offset length " + basehdr + " linkrev"
1791 ui.write(" rev offset length " + basehdr + " linkrev"
1792 " nodeid p1 p2\n")
1792 " nodeid p1 p2\n")
1793 elif format == 1:
1793 elif format == 1:
1794 ui.write(" rev flag offset length"
1794 ui.write(" rev flag offset length"
1795 " size " + basehdr + " link p1 p2 nodeid\n")
1795 " size " + basehdr + " link p1 p2 nodeid\n")
1796
1796
1797 for i in r:
1797 for i in r:
1798 node = r.node(i)
1798 node = r.node(i)
1799 if generaldelta:
1799 if generaldelta:
1800 base = r.deltaparent(i)
1800 base = r.deltaparent(i)
1801 else:
1801 else:
1802 base = r.chainbase(i)
1802 base = r.chainbase(i)
1803 if format == 0:
1803 if format == 0:
1804 try:
1804 try:
1805 pp = r.parents(node)
1805 pp = r.parents(node)
1806 except:
1806 except:
1807 pp = [nullid, nullid]
1807 pp = [nullid, nullid]
1808 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1808 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1809 i, r.start(i), r.length(i), base, r.linkrev(i),
1809 i, r.start(i), r.length(i), base, r.linkrev(i),
1810 short(node), short(pp[0]), short(pp[1])))
1810 short(node), short(pp[0]), short(pp[1])))
1811 elif format == 1:
1811 elif format == 1:
1812 pr = r.parentrevs(i)
1812 pr = r.parentrevs(i)
1813 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1813 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1814 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1814 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1815 base, r.linkrev(i), pr[0], pr[1], short(node)))
1815 base, r.linkrev(i), pr[0], pr[1], short(node)))
1816
1816
1817 @command('debugindexdot', [], _('FILE'))
1817 @command('debugindexdot', [], _('FILE'))
1818 def debugindexdot(ui, repo, file_):
1818 def debugindexdot(ui, repo, file_):
1819 """dump an index DAG as a graphviz dot file"""
1819 """dump an index DAG as a graphviz dot file"""
1820 r = None
1820 r = None
1821 if repo:
1821 if repo:
1822 filelog = repo.file(file_)
1822 filelog = repo.file(file_)
1823 if len(filelog):
1823 if len(filelog):
1824 r = filelog
1824 r = filelog
1825 if not r:
1825 if not r:
1826 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1826 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1827 ui.write("digraph G {\n")
1827 ui.write("digraph G {\n")
1828 for i in r:
1828 for i in r:
1829 node = r.node(i)
1829 node = r.node(i)
1830 pp = r.parents(node)
1830 pp = r.parents(node)
1831 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1831 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1832 if pp[1] != nullid:
1832 if pp[1] != nullid:
1833 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1833 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1834 ui.write("}\n")
1834 ui.write("}\n")
1835
1835
1836 @command('debuginstall', [], '')
1836 @command('debuginstall', [], '')
1837 def debuginstall(ui):
1837 def debuginstall(ui):
1838 '''test Mercurial installation
1838 '''test Mercurial installation
1839
1839
1840 Returns 0 on success.
1840 Returns 0 on success.
1841 '''
1841 '''
1842
1842
1843 def writetemp(contents):
1843 def writetemp(contents):
1844 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1844 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1845 f = os.fdopen(fd, "wb")
1845 f = os.fdopen(fd, "wb")
1846 f.write(contents)
1846 f.write(contents)
1847 f.close()
1847 f.close()
1848 return name
1848 return name
1849
1849
1850 problems = 0
1850 problems = 0
1851
1851
1852 # encoding
1852 # encoding
1853 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1853 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1854 try:
1854 try:
1855 encoding.fromlocal("test")
1855 encoding.fromlocal("test")
1856 except util.Abort, inst:
1856 except util.Abort, inst:
1857 ui.write(" %s\n" % inst)
1857 ui.write(" %s\n" % inst)
1858 ui.write(_(" (check that your locale is properly set)\n"))
1858 ui.write(_(" (check that your locale is properly set)\n"))
1859 problems += 1
1859 problems += 1
1860
1860
1861 # compiled modules
1861 # compiled modules
1862 ui.status(_("Checking installed modules (%s)...\n")
1862 ui.status(_("Checking installed modules (%s)...\n")
1863 % os.path.dirname(__file__))
1863 % os.path.dirname(__file__))
1864 try:
1864 try:
1865 import bdiff, mpatch, base85, osutil
1865 import bdiff, mpatch, base85, osutil
1866 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
1866 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
1867 except Exception, inst:
1867 except Exception, inst:
1868 ui.write(" %s\n" % inst)
1868 ui.write(" %s\n" % inst)
1869 ui.write(_(" One or more extensions could not be found"))
1869 ui.write(_(" One or more extensions could not be found"))
1870 ui.write(_(" (check that you compiled the extensions)\n"))
1870 ui.write(_(" (check that you compiled the extensions)\n"))
1871 problems += 1
1871 problems += 1
1872
1872
1873 # templates
1873 # templates
1874 import templater
1874 import templater
1875 p = templater.templatepath()
1875 p = templater.templatepath()
1876 ui.status(_("Checking templates (%s)...\n") % ' '.join(p))
1876 ui.status(_("Checking templates (%s)...\n") % ' '.join(p))
1877 try:
1877 try:
1878 templater.templater(templater.templatepath("map-cmdline.default"))
1878 templater.templater(templater.templatepath("map-cmdline.default"))
1879 except Exception, inst:
1879 except Exception, inst:
1880 ui.write(" %s\n" % inst)
1880 ui.write(" %s\n" % inst)
1881 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1881 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1882 problems += 1
1882 problems += 1
1883
1883
1884 # editor
1884 # editor
1885 ui.status(_("Checking commit editor...\n"))
1885 ui.status(_("Checking commit editor...\n"))
1886 editor = ui.geteditor()
1886 editor = ui.geteditor()
1887 cmdpath = util.findexe(editor) or util.findexe(editor.split()[0])
1887 cmdpath = util.findexe(editor) or util.findexe(editor.split()[0])
1888 if not cmdpath:
1888 if not cmdpath:
1889 if editor == 'vi':
1889 if editor == 'vi':
1890 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1890 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1891 ui.write(_(" (specify a commit editor in your configuration"
1891 ui.write(_(" (specify a commit editor in your configuration"
1892 " file)\n"))
1892 " file)\n"))
1893 else:
1893 else:
1894 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1894 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1895 ui.write(_(" (specify a commit editor in your configuration"
1895 ui.write(_(" (specify a commit editor in your configuration"
1896 " file)\n"))
1896 " file)\n"))
1897 problems += 1
1897 problems += 1
1898
1898
1899 # check username
1899 # check username
1900 ui.status(_("Checking username...\n"))
1900 ui.status(_("Checking username...\n"))
1901 try:
1901 try:
1902 ui.username()
1902 ui.username()
1903 except util.Abort, e:
1903 except util.Abort, e:
1904 ui.write(" %s\n" % e)
1904 ui.write(" %s\n" % e)
1905 ui.write(_(" (specify a username in your configuration file)\n"))
1905 ui.write(_(" (specify a username in your configuration file)\n"))
1906 problems += 1
1906 problems += 1
1907
1907
1908 if not problems:
1908 if not problems:
1909 ui.status(_("No problems detected\n"))
1909 ui.status(_("No problems detected\n"))
1910 else:
1910 else:
1911 ui.write(_("%s problems detected,"
1911 ui.write(_("%s problems detected,"
1912 " please check your install!\n") % problems)
1912 " please check your install!\n") % problems)
1913
1913
1914 return problems
1914 return problems
1915
1915
1916 @command('debugknown', [], _('REPO ID...'))
1916 @command('debugknown', [], _('REPO ID...'))
1917 def debugknown(ui, repopath, *ids, **opts):
1917 def debugknown(ui, repopath, *ids, **opts):
1918 """test whether node ids are known to a repo
1918 """test whether node ids are known to a repo
1919
1919
1920 Every ID must be a full-length hex node id string. Returns a list of 0s and 1s
1920 Every ID must be a full-length hex node id string. Returns a list of 0s and 1s
1921 indicating unknown/known.
1921 indicating unknown/known.
1922 """
1922 """
1923 repo = hg.peer(ui, opts, repopath)
1923 repo = hg.peer(ui, opts, repopath)
1924 if not repo.capable('known'):
1924 if not repo.capable('known'):
1925 raise util.Abort("known() not supported by target repository")
1925 raise util.Abort("known() not supported by target repository")
1926 flags = repo.known([bin(s) for s in ids])
1926 flags = repo.known([bin(s) for s in ids])
1927 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
1927 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
1928
1928
1929 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'))
1929 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'))
1930 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
1930 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
1931 '''access the pushkey key/value protocol
1931 '''access the pushkey key/value protocol
1932
1932
1933 With two args, list the keys in the given namespace.
1933 With two args, list the keys in the given namespace.
1934
1934
1935 With five args, set a key to new if it currently is set to old.
1935 With five args, set a key to new if it currently is set to old.
1936 Reports success or failure.
1936 Reports success or failure.
1937 '''
1937 '''
1938
1938
1939 target = hg.peer(ui, {}, repopath)
1939 target = hg.peer(ui, {}, repopath)
1940 if keyinfo:
1940 if keyinfo:
1941 key, old, new = keyinfo
1941 key, old, new = keyinfo
1942 r = target.pushkey(namespace, key, old, new)
1942 r = target.pushkey(namespace, key, old, new)
1943 ui.status(str(r) + '\n')
1943 ui.status(str(r) + '\n')
1944 return not r
1944 return not r
1945 else:
1945 else:
1946 for k, v in target.listkeys(namespace).iteritems():
1946 for k, v in target.listkeys(namespace).iteritems():
1947 ui.write("%s\t%s\n" % (k.encode('string-escape'),
1947 ui.write("%s\t%s\n" % (k.encode('string-escape'),
1948 v.encode('string-escape')))
1948 v.encode('string-escape')))
1949
1949
1950 @command('debugrebuildstate',
1950 @command('debugrebuildstate',
1951 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
1951 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
1952 _('[-r REV] [REV]'))
1952 _('[-r REV] [REV]'))
1953 def debugrebuildstate(ui, repo, rev="tip"):
1953 def debugrebuildstate(ui, repo, rev="tip"):
1954 """rebuild the dirstate as it would look like for the given revision"""
1954 """rebuild the dirstate as it would look like for the given revision"""
1955 ctx = scmutil.revsingle(repo, rev)
1955 ctx = scmutil.revsingle(repo, rev)
1956 wlock = repo.wlock()
1956 wlock = repo.wlock()
1957 try:
1957 try:
1958 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1958 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1959 finally:
1959 finally:
1960 wlock.release()
1960 wlock.release()
1961
1961
1962 @command('debugrename',
1962 @command('debugrename',
1963 [('r', 'rev', '', _('revision to debug'), _('REV'))],
1963 [('r', 'rev', '', _('revision to debug'), _('REV'))],
1964 _('[-r REV] FILE'))
1964 _('[-r REV] FILE'))
1965 def debugrename(ui, repo, file1, *pats, **opts):
1965 def debugrename(ui, repo, file1, *pats, **opts):
1966 """dump rename information"""
1966 """dump rename information"""
1967
1967
1968 ctx = scmutil.revsingle(repo, opts.get('rev'))
1968 ctx = scmutil.revsingle(repo, opts.get('rev'))
1969 m = scmutil.match(ctx, (file1,) + pats, opts)
1969 m = scmutil.match(ctx, (file1,) + pats, opts)
1970 for abs in ctx.walk(m):
1970 for abs in ctx.walk(m):
1971 fctx = ctx[abs]
1971 fctx = ctx[abs]
1972 o = fctx.filelog().renamed(fctx.filenode())
1972 o = fctx.filelog().renamed(fctx.filenode())
1973 rel = m.rel(abs)
1973 rel = m.rel(abs)
1974 if o:
1974 if o:
1975 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1975 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1976 else:
1976 else:
1977 ui.write(_("%s not renamed\n") % rel)
1977 ui.write(_("%s not renamed\n") % rel)
1978
1978
1979 @command('debugrevlog',
1979 @command('debugrevlog',
1980 [('c', 'changelog', False, _('open changelog')),
1980 [('c', 'changelog', False, _('open changelog')),
1981 ('m', 'manifest', False, _('open manifest')),
1981 ('m', 'manifest', False, _('open manifest')),
1982 ('d', 'dump', False, _('dump index data'))],
1982 ('d', 'dump', False, _('dump index data'))],
1983 _('-c|-m|FILE'))
1983 _('-c|-m|FILE'))
1984 def debugrevlog(ui, repo, file_ = None, **opts):
1984 def debugrevlog(ui, repo, file_ = None, **opts):
1985 """show data and statistics about a revlog"""
1985 """show data and statistics about a revlog"""
1986 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
1986 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
1987
1987
1988 if opts.get("dump"):
1988 if opts.get("dump"):
1989 numrevs = len(r)
1989 numrevs = len(r)
1990 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
1990 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
1991 " rawsize totalsize compression heads\n")
1991 " rawsize totalsize compression heads\n")
1992 ts = 0
1992 ts = 0
1993 heads = set()
1993 heads = set()
1994 for rev in xrange(numrevs):
1994 for rev in xrange(numrevs):
1995 dbase = r.deltaparent(rev)
1995 dbase = r.deltaparent(rev)
1996 if dbase == -1:
1996 if dbase == -1:
1997 dbase = rev
1997 dbase = rev
1998 cbase = r.chainbase(rev)
1998 cbase = r.chainbase(rev)
1999 p1, p2 = r.parentrevs(rev)
1999 p1, p2 = r.parentrevs(rev)
2000 rs = r.rawsize(rev)
2000 rs = r.rawsize(rev)
2001 ts = ts + rs
2001 ts = ts + rs
2002 heads -= set(r.parentrevs(rev))
2002 heads -= set(r.parentrevs(rev))
2003 heads.add(rev)
2003 heads.add(rev)
2004 ui.write("%d %d %d %d %d %d %d %d %d %d %d %d %d\n" %
2004 ui.write("%d %d %d %d %d %d %d %d %d %d %d %d %d\n" %
2005 (rev, p1, p2, r.start(rev), r.end(rev),
2005 (rev, p1, p2, r.start(rev), r.end(rev),
2006 r.start(dbase), r.start(cbase),
2006 r.start(dbase), r.start(cbase),
2007 r.start(p1), r.start(p2),
2007 r.start(p1), r.start(p2),
2008 rs, ts, ts / r.end(rev), len(heads)))
2008 rs, ts, ts / r.end(rev), len(heads)))
2009 return 0
2009 return 0
2010
2010
2011 v = r.version
2011 v = r.version
2012 format = v & 0xFFFF
2012 format = v & 0xFFFF
2013 flags = []
2013 flags = []
2014 gdelta = False
2014 gdelta = False
2015 if v & revlog.REVLOGNGINLINEDATA:
2015 if v & revlog.REVLOGNGINLINEDATA:
2016 flags.append('inline')
2016 flags.append('inline')
2017 if v & revlog.REVLOGGENERALDELTA:
2017 if v & revlog.REVLOGGENERALDELTA:
2018 gdelta = True
2018 gdelta = True
2019 flags.append('generaldelta')
2019 flags.append('generaldelta')
2020 if not flags:
2020 if not flags:
2021 flags = ['(none)']
2021 flags = ['(none)']
2022
2022
2023 nummerges = 0
2023 nummerges = 0
2024 numfull = 0
2024 numfull = 0
2025 numprev = 0
2025 numprev = 0
2026 nump1 = 0
2026 nump1 = 0
2027 nump2 = 0
2027 nump2 = 0
2028 numother = 0
2028 numother = 0
2029 nump1prev = 0
2029 nump1prev = 0
2030 nump2prev = 0
2030 nump2prev = 0
2031 chainlengths = []
2031 chainlengths = []
2032
2032
2033 datasize = [None, 0, 0L]
2033 datasize = [None, 0, 0L]
2034 fullsize = [None, 0, 0L]
2034 fullsize = [None, 0, 0L]
2035 deltasize = [None, 0, 0L]
2035 deltasize = [None, 0, 0L]
2036
2036
2037 def addsize(size, l):
2037 def addsize(size, l):
2038 if l[0] is None or size < l[0]:
2038 if l[0] is None or size < l[0]:
2039 l[0] = size
2039 l[0] = size
2040 if size > l[1]:
2040 if size > l[1]:
2041 l[1] = size
2041 l[1] = size
2042 l[2] += size
2042 l[2] += size
2043
2043
2044 numrevs = len(r)
2044 numrevs = len(r)
2045 for rev in xrange(numrevs):
2045 for rev in xrange(numrevs):
2046 p1, p2 = r.parentrevs(rev)
2046 p1, p2 = r.parentrevs(rev)
2047 delta = r.deltaparent(rev)
2047 delta = r.deltaparent(rev)
2048 if format > 0:
2048 if format > 0:
2049 addsize(r.rawsize(rev), datasize)
2049 addsize(r.rawsize(rev), datasize)
2050 if p2 != nullrev:
2050 if p2 != nullrev:
2051 nummerges += 1
2051 nummerges += 1
2052 size = r.length(rev)
2052 size = r.length(rev)
2053 if delta == nullrev:
2053 if delta == nullrev:
2054 chainlengths.append(0)
2054 chainlengths.append(0)
2055 numfull += 1
2055 numfull += 1
2056 addsize(size, fullsize)
2056 addsize(size, fullsize)
2057 else:
2057 else:
2058 chainlengths.append(chainlengths[delta] + 1)
2058 chainlengths.append(chainlengths[delta] + 1)
2059 addsize(size, deltasize)
2059 addsize(size, deltasize)
2060 if delta == rev - 1:
2060 if delta == rev - 1:
2061 numprev += 1
2061 numprev += 1
2062 if delta == p1:
2062 if delta == p1:
2063 nump1prev += 1
2063 nump1prev += 1
2064 elif delta == p2:
2064 elif delta == p2:
2065 nump2prev += 1
2065 nump2prev += 1
2066 elif delta == p1:
2066 elif delta == p1:
2067 nump1 += 1
2067 nump1 += 1
2068 elif delta == p2:
2068 elif delta == p2:
2069 nump2 += 1
2069 nump2 += 1
2070 elif delta != nullrev:
2070 elif delta != nullrev:
2071 numother += 1
2071 numother += 1
2072
2072
2073 numdeltas = numrevs - numfull
2073 numdeltas = numrevs - numfull
2074 numoprev = numprev - nump1prev - nump2prev
2074 numoprev = numprev - nump1prev - nump2prev
2075 totalrawsize = datasize[2]
2075 totalrawsize = datasize[2]
2076 datasize[2] /= numrevs
2076 datasize[2] /= numrevs
2077 fulltotal = fullsize[2]
2077 fulltotal = fullsize[2]
2078 fullsize[2] /= numfull
2078 fullsize[2] /= numfull
2079 deltatotal = deltasize[2]
2079 deltatotal = deltasize[2]
2080 deltasize[2] /= numrevs - numfull
2080 deltasize[2] /= numrevs - numfull
2081 totalsize = fulltotal + deltatotal
2081 totalsize = fulltotal + deltatotal
2082 avgchainlen = sum(chainlengths) / numrevs
2082 avgchainlen = sum(chainlengths) / numrevs
2083 compratio = totalrawsize / totalsize
2083 compratio = totalrawsize / totalsize
2084
2084
2085 basedfmtstr = '%%%dd\n'
2085 basedfmtstr = '%%%dd\n'
2086 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2086 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2087
2087
2088 def dfmtstr(max):
2088 def dfmtstr(max):
2089 return basedfmtstr % len(str(max))
2089 return basedfmtstr % len(str(max))
2090 def pcfmtstr(max, padding=0):
2090 def pcfmtstr(max, padding=0):
2091 return basepcfmtstr % (len(str(max)), ' ' * padding)
2091 return basepcfmtstr % (len(str(max)), ' ' * padding)
2092
2092
2093 def pcfmt(value, total):
2093 def pcfmt(value, total):
2094 return (value, 100 * float(value) / total)
2094 return (value, 100 * float(value) / total)
2095
2095
2096 ui.write('format : %d\n' % format)
2096 ui.write('format : %d\n' % format)
2097 ui.write('flags : %s\n' % ', '.join(flags))
2097 ui.write('flags : %s\n' % ', '.join(flags))
2098
2098
2099 ui.write('\n')
2099 ui.write('\n')
2100 fmt = pcfmtstr(totalsize)
2100 fmt = pcfmtstr(totalsize)
2101 fmt2 = dfmtstr(totalsize)
2101 fmt2 = dfmtstr(totalsize)
2102 ui.write('revisions : ' + fmt2 % numrevs)
2102 ui.write('revisions : ' + fmt2 % numrevs)
2103 ui.write(' merges : ' + fmt % pcfmt(nummerges, numrevs))
2103 ui.write(' merges : ' + fmt % pcfmt(nummerges, numrevs))
2104 ui.write(' normal : ' + fmt % pcfmt(numrevs - nummerges, numrevs))
2104 ui.write(' normal : ' + fmt % pcfmt(numrevs - nummerges, numrevs))
2105 ui.write('revisions : ' + fmt2 % numrevs)
2105 ui.write('revisions : ' + fmt2 % numrevs)
2106 ui.write(' full : ' + fmt % pcfmt(numfull, numrevs))
2106 ui.write(' full : ' + fmt % pcfmt(numfull, numrevs))
2107 ui.write(' deltas : ' + fmt % pcfmt(numdeltas, numrevs))
2107 ui.write(' deltas : ' + fmt % pcfmt(numdeltas, numrevs))
2108 ui.write('revision size : ' + fmt2 % totalsize)
2108 ui.write('revision size : ' + fmt2 % totalsize)
2109 ui.write(' full : ' + fmt % pcfmt(fulltotal, totalsize))
2109 ui.write(' full : ' + fmt % pcfmt(fulltotal, totalsize))
2110 ui.write(' deltas : ' + fmt % pcfmt(deltatotal, totalsize))
2110 ui.write(' deltas : ' + fmt % pcfmt(deltatotal, totalsize))
2111
2111
2112 ui.write('\n')
2112 ui.write('\n')
2113 fmt = dfmtstr(max(avgchainlen, compratio))
2113 fmt = dfmtstr(max(avgchainlen, compratio))
2114 ui.write('avg chain length : ' + fmt % avgchainlen)
2114 ui.write('avg chain length : ' + fmt % avgchainlen)
2115 ui.write('compression ratio : ' + fmt % compratio)
2115 ui.write('compression ratio : ' + fmt % compratio)
2116
2116
2117 if format > 0:
2117 if format > 0:
2118 ui.write('\n')
2118 ui.write('\n')
2119 ui.write('uncompressed data size (min/max/avg) : %d / %d / %d\n'
2119 ui.write('uncompressed data size (min/max/avg) : %d / %d / %d\n'
2120 % tuple(datasize))
2120 % tuple(datasize))
2121 ui.write('full revision size (min/max/avg) : %d / %d / %d\n'
2121 ui.write('full revision size (min/max/avg) : %d / %d / %d\n'
2122 % tuple(fullsize))
2122 % tuple(fullsize))
2123 ui.write('delta size (min/max/avg) : %d / %d / %d\n'
2123 ui.write('delta size (min/max/avg) : %d / %d / %d\n'
2124 % tuple(deltasize))
2124 % tuple(deltasize))
2125
2125
2126 if numdeltas > 0:
2126 if numdeltas > 0:
2127 ui.write('\n')
2127 ui.write('\n')
2128 fmt = pcfmtstr(numdeltas)
2128 fmt = pcfmtstr(numdeltas)
2129 fmt2 = pcfmtstr(numdeltas, 4)
2129 fmt2 = pcfmtstr(numdeltas, 4)
2130 ui.write('deltas against prev : ' + fmt % pcfmt(numprev, numdeltas))
2130 ui.write('deltas against prev : ' + fmt % pcfmt(numprev, numdeltas))
2131 if numprev > 0:
2131 if numprev > 0:
2132 ui.write(' where prev = p1 : ' + fmt2 % pcfmt(nump1prev, numprev))
2132 ui.write(' where prev = p1 : ' + fmt2 % pcfmt(nump1prev, numprev))
2133 ui.write(' where prev = p2 : ' + fmt2 % pcfmt(nump2prev, numprev))
2133 ui.write(' where prev = p2 : ' + fmt2 % pcfmt(nump2prev, numprev))
2134 ui.write(' other : ' + fmt2 % pcfmt(numoprev, numprev))
2134 ui.write(' other : ' + fmt2 % pcfmt(numoprev, numprev))
2135 if gdelta:
2135 if gdelta:
2136 ui.write('deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas))
2136 ui.write('deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas))
2137 ui.write('deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas))
2137 ui.write('deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas))
2138 ui.write('deltas against other : ' + fmt % pcfmt(numother, numdeltas))
2138 ui.write('deltas against other : ' + fmt % pcfmt(numother, numdeltas))
2139
2139
2140 @command('debugrevspec', [], ('REVSPEC'))
2140 @command('debugrevspec', [], ('REVSPEC'))
2141 def debugrevspec(ui, repo, expr):
2141 def debugrevspec(ui, repo, expr):
2142 '''parse and apply a revision specification'''
2142 '''parse and apply a revision specification'''
2143 if ui.verbose:
2143 if ui.verbose:
2144 tree = revset.parse(expr)[0]
2144 tree = revset.parse(expr)[0]
2145 ui.note(tree, "\n")
2145 ui.note(tree, "\n")
2146 newtree = revset.findaliases(ui, tree)
2146 newtree = revset.findaliases(ui, tree)
2147 if newtree != tree:
2147 if newtree != tree:
2148 ui.note(newtree, "\n")
2148 ui.note(newtree, "\n")
2149 func = revset.match(ui, expr)
2149 func = revset.match(ui, expr)
2150 for c in func(repo, range(len(repo))):
2150 for c in func(repo, range(len(repo))):
2151 ui.write("%s\n" % c)
2151 ui.write("%s\n" % c)
2152
2152
2153 @command('debugsetparents', [], _('REV1 [REV2]'))
2153 @command('debugsetparents', [], _('REV1 [REV2]'))
2154 def debugsetparents(ui, repo, rev1, rev2=None):
2154 def debugsetparents(ui, repo, rev1, rev2=None):
2155 """manually set the parents of the current working directory
2155 """manually set the parents of the current working directory
2156
2156
2157 This is useful for writing repository conversion tools, but should
2157 This is useful for writing repository conversion tools, but should
2158 be used with care.
2158 be used with care.
2159
2159
2160 Returns 0 on success.
2160 Returns 0 on success.
2161 """
2161 """
2162
2162
2163 r1 = scmutil.revsingle(repo, rev1).node()
2163 r1 = scmutil.revsingle(repo, rev1).node()
2164 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2164 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2165
2165
2166 wlock = repo.wlock()
2166 wlock = repo.wlock()
2167 try:
2167 try:
2168 repo.dirstate.setparents(r1, r2)
2168 repo.dirstate.setparents(r1, r2)
2169 finally:
2169 finally:
2170 wlock.release()
2170 wlock.release()
2171
2171
2172 @command('debugstate',
2172 @command('debugstate',
2173 [('', 'nodates', None, _('do not display the saved mtime')),
2173 [('', 'nodates', None, _('do not display the saved mtime')),
2174 ('', 'datesort', None, _('sort by saved mtime'))],
2174 ('', 'datesort', None, _('sort by saved mtime'))],
2175 _('[OPTION]...'))
2175 _('[OPTION]...'))
2176 def debugstate(ui, repo, nodates=None, datesort=None):
2176 def debugstate(ui, repo, nodates=None, datesort=None):
2177 """show the contents of the current dirstate"""
2177 """show the contents of the current dirstate"""
2178 timestr = ""
2178 timestr = ""
2179 showdate = not nodates
2179 showdate = not nodates
2180 if datesort:
2180 if datesort:
2181 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2181 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2182 else:
2182 else:
2183 keyfunc = None # sort by filename
2183 keyfunc = None # sort by filename
2184 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2184 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2185 if showdate:
2185 if showdate:
2186 if ent[3] == -1:
2186 if ent[3] == -1:
2187 # Pad or slice to locale representation
2187 # Pad or slice to locale representation
2188 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
2188 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
2189 time.localtime(0)))
2189 time.localtime(0)))
2190 timestr = 'unset'
2190 timestr = 'unset'
2191 timestr = (timestr[:locale_len] +
2191 timestr = (timestr[:locale_len] +
2192 ' ' * (locale_len - len(timestr)))
2192 ' ' * (locale_len - len(timestr)))
2193 else:
2193 else:
2194 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2194 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2195 time.localtime(ent[3]))
2195 time.localtime(ent[3]))
2196 if ent[1] & 020000:
2196 if ent[1] & 020000:
2197 mode = 'lnk'
2197 mode = 'lnk'
2198 else:
2198 else:
2199 mode = '%3o' % (ent[1] & 0777 & ~util.umask)
2199 mode = '%3o' % (ent[1] & 0777 & ~util.umask)
2200 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2200 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2201 for f in repo.dirstate.copies():
2201 for f in repo.dirstate.copies():
2202 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2202 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2203
2203
2204 @command('debugsub',
2204 @command('debugsub',
2205 [('r', 'rev', '',
2205 [('r', 'rev', '',
2206 _('revision to check'), _('REV'))],
2206 _('revision to check'), _('REV'))],
2207 _('[-r REV] [REV]'))
2207 _('[-r REV] [REV]'))
2208 def debugsub(ui, repo, rev=None):
2208 def debugsub(ui, repo, rev=None):
2209 ctx = scmutil.revsingle(repo, rev, None)
2209 ctx = scmutil.revsingle(repo, rev, None)
2210 for k, v in sorted(ctx.substate.items()):
2210 for k, v in sorted(ctx.substate.items()):
2211 ui.write('path %s\n' % k)
2211 ui.write('path %s\n' % k)
2212 ui.write(' source %s\n' % v[0])
2212 ui.write(' source %s\n' % v[0])
2213 ui.write(' revision %s\n' % v[1])
2213 ui.write(' revision %s\n' % v[1])
2214
2214
2215 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'))
2215 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'))
2216 def debugwalk(ui, repo, *pats, **opts):
2216 def debugwalk(ui, repo, *pats, **opts):
2217 """show how files match on given patterns"""
2217 """show how files match on given patterns"""
2218 m = scmutil.match(repo[None], pats, opts)
2218 m = scmutil.match(repo[None], pats, opts)
2219 items = list(repo.walk(m))
2219 items = list(repo.walk(m))
2220 if not items:
2220 if not items:
2221 return
2221 return
2222 fmt = 'f %%-%ds %%-%ds %%s' % (
2222 fmt = 'f %%-%ds %%-%ds %%s' % (
2223 max([len(abs) for abs in items]),
2223 max([len(abs) for abs in items]),
2224 max([len(m.rel(abs)) for abs in items]))
2224 max([len(m.rel(abs)) for abs in items]))
2225 for abs in items:
2225 for abs in items:
2226 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
2226 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
2227 ui.write("%s\n" % line.rstrip())
2227 ui.write("%s\n" % line.rstrip())
2228
2228
2229 @command('debugwireargs',
2229 @command('debugwireargs',
2230 [('', 'three', '', 'three'),
2230 [('', 'three', '', 'three'),
2231 ('', 'four', '', 'four'),
2231 ('', 'four', '', 'four'),
2232 ('', 'five', '', 'five'),
2232 ('', 'five', '', 'five'),
2233 ] + remoteopts,
2233 ] + remoteopts,
2234 _('REPO [OPTIONS]... [ONE [TWO]]'))
2234 _('REPO [OPTIONS]... [ONE [TWO]]'))
2235 def debugwireargs(ui, repopath, *vals, **opts):
2235 def debugwireargs(ui, repopath, *vals, **opts):
2236 repo = hg.peer(ui, opts, repopath)
2236 repo = hg.peer(ui, opts, repopath)
2237 for opt in remoteopts:
2237 for opt in remoteopts:
2238 del opts[opt[1]]
2238 del opts[opt[1]]
2239 args = {}
2239 args = {}
2240 for k, v in opts.iteritems():
2240 for k, v in opts.iteritems():
2241 if v:
2241 if v:
2242 args[k] = v
2242 args[k] = v
2243 # run twice to check that we don't mess up the stream for the next command
2243 # run twice to check that we don't mess up the stream for the next command
2244 res1 = repo.debugwireargs(*vals, **args)
2244 res1 = repo.debugwireargs(*vals, **args)
2245 res2 = repo.debugwireargs(*vals, **args)
2245 res2 = repo.debugwireargs(*vals, **args)
2246 ui.write("%s\n" % res1)
2246 ui.write("%s\n" % res1)
2247 if res1 != res2:
2247 if res1 != res2:
2248 ui.warn("%s\n" % res2)
2248 ui.warn("%s\n" % res2)
2249
2249
2250 @command('^diff',
2250 @command('^diff',
2251 [('r', 'rev', [], _('revision'), _('REV')),
2251 [('r', 'rev', [], _('revision'), _('REV')),
2252 ('c', 'change', '', _('change made by revision'), _('REV'))
2252 ('c', 'change', '', _('change made by revision'), _('REV'))
2253 ] + diffopts + diffopts2 + walkopts + subrepoopts,
2253 ] + diffopts + diffopts2 + walkopts + subrepoopts,
2254 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'))
2254 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'))
2255 def diff(ui, repo, *pats, **opts):
2255 def diff(ui, repo, *pats, **opts):
2256 """diff repository (or selected files)
2256 """diff repository (or selected files)
2257
2257
2258 Show differences between revisions for the specified files.
2258 Show differences between revisions for the specified files.
2259
2259
2260 Differences between files are shown using the unified diff format.
2260 Differences between files are shown using the unified diff format.
2261
2261
2262 .. note::
2262 .. note::
2263 diff may generate unexpected results for merges, as it will
2263 diff may generate unexpected results for merges, as it will
2264 default to comparing against the working directory's first
2264 default to comparing against the working directory's first
2265 parent changeset if no revisions are specified.
2265 parent changeset if no revisions are specified.
2266
2266
2267 When two revision arguments are given, then changes are shown
2267 When two revision arguments are given, then changes are shown
2268 between those revisions. If only one revision is specified then
2268 between those revisions. If only one revision is specified then
2269 that revision is compared to the working directory, and, when no
2269 that revision is compared to the working directory, and, when no
2270 revisions are specified, the working directory files are compared
2270 revisions are specified, the working directory files are compared
2271 to its parent.
2271 to its parent.
2272
2272
2273 Alternatively you can specify -c/--change with a revision to see
2273 Alternatively you can specify -c/--change with a revision to see
2274 the changes in that changeset relative to its first parent.
2274 the changes in that changeset relative to its first parent.
2275
2275
2276 Without the -a/--text option, diff will avoid generating diffs of
2276 Without the -a/--text option, diff will avoid generating diffs of
2277 files it detects as binary. With -a, diff will generate a diff
2277 files it detects as binary. With -a, diff will generate a diff
2278 anyway, probably with undesirable results.
2278 anyway, probably with undesirable results.
2279
2279
2280 Use the -g/--git option to generate diffs in the git extended diff
2280 Use the -g/--git option to generate diffs in the git extended diff
2281 format. For more information, read :hg:`help diffs`.
2281 format. For more information, read :hg:`help diffs`.
2282
2282
2283 .. container:: verbose
2283 .. container:: verbose
2284
2284
2285 Examples:
2285 Examples:
2286
2286
2287 - compare a file in the current working directory to its parent::
2287 - compare a file in the current working directory to its parent::
2288
2288
2289 hg diff foo.c
2289 hg diff foo.c
2290
2290
2291 - compare two historical versions of a directory, with rename info::
2291 - compare two historical versions of a directory, with rename info::
2292
2292
2293 hg diff --git -r 1.0:1.2 lib/
2293 hg diff --git -r 1.0:1.2 lib/
2294
2294
2295 - get change stats relative to the last change on some date::
2295 - get change stats relative to the last change on some date::
2296
2296
2297 hg diff --stat -r "date('may 2')"
2297 hg diff --stat -r "date('may 2')"
2298
2298
2299 - diff all newly-added files that contain a keyword::
2299 - diff all newly-added files that contain a keyword::
2300
2300
2301 hg diff "set:added() and grep(GNU)"
2301 hg diff "set:added() and grep(GNU)"
2302
2302
2303 - compare a revision and its parents::
2303 - compare a revision and its parents::
2304
2304
2305 hg diff -c 9353 # compare against first parent
2305 hg diff -c 9353 # compare against first parent
2306 hg diff -r 9353^:9353 # same using revset syntax
2306 hg diff -r 9353^:9353 # same using revset syntax
2307 hg diff -r 9353^2:9353 # compare against the second parent
2307 hg diff -r 9353^2:9353 # compare against the second parent
2308
2308
2309 Returns 0 on success.
2309 Returns 0 on success.
2310 """
2310 """
2311
2311
2312 revs = opts.get('rev')
2312 revs = opts.get('rev')
2313 change = opts.get('change')
2313 change = opts.get('change')
2314 stat = opts.get('stat')
2314 stat = opts.get('stat')
2315 reverse = opts.get('reverse')
2315 reverse = opts.get('reverse')
2316
2316
2317 if revs and change:
2317 if revs and change:
2318 msg = _('cannot specify --rev and --change at the same time')
2318 msg = _('cannot specify --rev and --change at the same time')
2319 raise util.Abort(msg)
2319 raise util.Abort(msg)
2320 elif change:
2320 elif change:
2321 node2 = scmutil.revsingle(repo, change, None).node()
2321 node2 = scmutil.revsingle(repo, change, None).node()
2322 node1 = repo[node2].p1().node()
2322 node1 = repo[node2].p1().node()
2323 else:
2323 else:
2324 node1, node2 = scmutil.revpair(repo, revs)
2324 node1, node2 = scmutil.revpair(repo, revs)
2325
2325
2326 if reverse:
2326 if reverse:
2327 node1, node2 = node2, node1
2327 node1, node2 = node2, node1
2328
2328
2329 diffopts = patch.diffopts(ui, opts)
2329 diffopts = patch.diffopts(ui, opts)
2330 m = scmutil.match(repo[node2], pats, opts)
2330 m = scmutil.match(repo[node2], pats, opts)
2331 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2331 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2332 listsubrepos=opts.get('subrepos'))
2332 listsubrepos=opts.get('subrepos'))
2333
2333
2334 @command('^export',
2334 @command('^export',
2335 [('o', 'output', '',
2335 [('o', 'output', '',
2336 _('print output to file with formatted name'), _('FORMAT')),
2336 _('print output to file with formatted name'), _('FORMAT')),
2337 ('', 'switch-parent', None, _('diff against the second parent')),
2337 ('', 'switch-parent', None, _('diff against the second parent')),
2338 ('r', 'rev', [], _('revisions to export'), _('REV')),
2338 ('r', 'rev', [], _('revisions to export'), _('REV')),
2339 ] + diffopts,
2339 ] + diffopts,
2340 _('[OPTION]... [-o OUTFILESPEC] REV...'))
2340 _('[OPTION]... [-o OUTFILESPEC] REV...'))
2341 def export(ui, repo, *changesets, **opts):
2341 def export(ui, repo, *changesets, **opts):
2342 """dump the header and diffs for one or more changesets
2342 """dump the header and diffs for one or more changesets
2343
2343
2344 Print the changeset header and diffs for one or more revisions.
2344 Print the changeset header and diffs for one or more revisions.
2345
2345
2346 The information shown in the changeset header is: author, date,
2346 The information shown in the changeset header is: author, date,
2347 branch name (if non-default), changeset hash, parent(s) and commit
2347 branch name (if non-default), changeset hash, parent(s) and commit
2348 comment.
2348 comment.
2349
2349
2350 .. note::
2350 .. note::
2351 export may generate unexpected diff output for merge
2351 export may generate unexpected diff output for merge
2352 changesets, as it will compare the merge changeset against its
2352 changesets, as it will compare the merge changeset against its
2353 first parent only.
2353 first parent only.
2354
2354
2355 Output may be to a file, in which case the name of the file is
2355 Output may be to a file, in which case the name of the file is
2356 given using a format string. The formatting rules are as follows:
2356 given using a format string. The formatting rules are as follows:
2357
2357
2358 :``%%``: literal "%" character
2358 :``%%``: literal "%" character
2359 :``%H``: changeset hash (40 hexadecimal digits)
2359 :``%H``: changeset hash (40 hexadecimal digits)
2360 :``%N``: number of patches being generated
2360 :``%N``: number of patches being generated
2361 :``%R``: changeset revision number
2361 :``%R``: changeset revision number
2362 :``%b``: basename of the exporting repository
2362 :``%b``: basename of the exporting repository
2363 :``%h``: short-form changeset hash (12 hexadecimal digits)
2363 :``%h``: short-form changeset hash (12 hexadecimal digits)
2364 :``%m``: first line of the commit message (only alphanumeric characters)
2364 :``%m``: first line of the commit message (only alphanumeric characters)
2365 :``%n``: zero-padded sequence number, starting at 1
2365 :``%n``: zero-padded sequence number, starting at 1
2366 :``%r``: zero-padded changeset revision number
2366 :``%r``: zero-padded changeset revision number
2367
2367
2368 Without the -a/--text option, export will avoid generating diffs
2368 Without the -a/--text option, export will avoid generating diffs
2369 of files it detects as binary. With -a, export will generate a
2369 of files it detects as binary. With -a, export will generate a
2370 diff anyway, probably with undesirable results.
2370 diff anyway, probably with undesirable results.
2371
2371
2372 Use the -g/--git option to generate diffs in the git extended diff
2372 Use the -g/--git option to generate diffs in the git extended diff
2373 format. See :hg:`help diffs` for more information.
2373 format. See :hg:`help diffs` for more information.
2374
2374
2375 With the --switch-parent option, the diff will be against the
2375 With the --switch-parent option, the diff will be against the
2376 second parent. It can be useful to review a merge.
2376 second parent. It can be useful to review a merge.
2377
2377
2378 .. container:: verbose
2378 .. container:: verbose
2379
2379
2380 Examples:
2380 Examples:
2381
2381
2382 - use export and import to transplant a bugfix to the current
2382 - use export and import to transplant a bugfix to the current
2383 branch::
2383 branch::
2384
2384
2385 hg export -r 9353 | hg import -
2385 hg export -r 9353 | hg import -
2386
2386
2387 - export all the changesets between two revisions to a file with
2387 - export all the changesets between two revisions to a file with
2388 rename information::
2388 rename information::
2389
2389
2390 hg export --git -r 123:150 > changes.txt
2390 hg export --git -r 123:150 > changes.txt
2391
2391
2392 - split outgoing changes into a series of patches with
2392 - split outgoing changes into a series of patches with
2393 descriptive names::
2393 descriptive names::
2394
2394
2395 hg export -r "outgoing()" -o "%n-%m.patch"
2395 hg export -r "outgoing()" -o "%n-%m.patch"
2396
2396
2397 Returns 0 on success.
2397 Returns 0 on success.
2398 """
2398 """
2399 changesets += tuple(opts.get('rev', []))
2399 changesets += tuple(opts.get('rev', []))
2400 if not changesets:
2400 if not changesets:
2401 raise util.Abort(_("export requires at least one changeset"))
2401 raise util.Abort(_("export requires at least one changeset"))
2402 revs = scmutil.revrange(repo, changesets)
2402 revs = scmutil.revrange(repo, changesets)
2403 if len(revs) > 1:
2403 if len(revs) > 1:
2404 ui.note(_('exporting patches:\n'))
2404 ui.note(_('exporting patches:\n'))
2405 else:
2405 else:
2406 ui.note(_('exporting patch:\n'))
2406 ui.note(_('exporting patch:\n'))
2407 cmdutil.export(repo, revs, template=opts.get('output'),
2407 cmdutil.export(repo, revs, template=opts.get('output'),
2408 switch_parent=opts.get('switch_parent'),
2408 switch_parent=opts.get('switch_parent'),
2409 opts=patch.diffopts(ui, opts))
2409 opts=patch.diffopts(ui, opts))
2410
2410
2411 @command('^forget', walkopts, _('[OPTION]... FILE...'))
2411 @command('^forget', walkopts, _('[OPTION]... FILE...'))
2412 def forget(ui, repo, *pats, **opts):
2412 def forget(ui, repo, *pats, **opts):
2413 """forget the specified files on the next commit
2413 """forget the specified files on the next commit
2414
2414
2415 Mark the specified files so they will no longer be tracked
2415 Mark the specified files so they will no longer be tracked
2416 after the next commit.
2416 after the next commit.
2417
2417
2418 This only removes files from the current branch, not from the
2418 This only removes files from the current branch, not from the
2419 entire project history, and it does not delete them from the
2419 entire project history, and it does not delete them from the
2420 working directory.
2420 working directory.
2421
2421
2422 To undo a forget before the next commit, see :hg:`add`.
2422 To undo a forget before the next commit, see :hg:`add`.
2423
2423
2424 .. container:: verbose
2424 .. container:: verbose
2425
2425
2426 Examples:
2426 Examples:
2427
2427
2428 - forget newly-added binary files::
2428 - forget newly-added binary files::
2429
2429
2430 hg forget "set:added() and binary()"
2430 hg forget "set:added() and binary()"
2431
2431
2432 - forget files that would be excluded by .hgignore::
2432 - forget files that would be excluded by .hgignore::
2433
2433
2434 hg forget "set:hgignore()"
2434 hg forget "set:hgignore()"
2435
2435
2436 Returns 0 on success.
2436 Returns 0 on success.
2437 """
2437 """
2438
2438
2439 if not pats:
2439 if not pats:
2440 raise util.Abort(_('no files specified'))
2440 raise util.Abort(_('no files specified'))
2441
2441
2442 wctx = repo[None]
2442 wctx = repo[None]
2443 m = scmutil.match(wctx, pats, opts)
2443 m = scmutil.match(wctx, pats, opts)
2444 s = repo.status(match=m, clean=True)
2444 s = repo.status(match=m, clean=True)
2445 forget = sorted(s[0] + s[1] + s[3] + s[6])
2445 forget = sorted(s[0] + s[1] + s[3] + s[6])
2446 subforget = {}
2446 subforget = {}
2447 errs = 0
2447 errs = 0
2448
2448
2449 for subpath in wctx.substate:
2449 for subpath in wctx.substate:
2450 sub = wctx.sub(subpath)
2450 sub = wctx.sub(subpath)
2451 try:
2451 try:
2452 submatch = matchmod.narrowmatcher(subpath, m)
2452 submatch = matchmod.narrowmatcher(subpath, m)
2453 for fsub in sub.walk(submatch):
2453 for fsub in sub.walk(submatch):
2454 if submatch.exact(fsub):
2454 if submatch.exact(fsub):
2455 subforget[subpath + '/' + fsub] = (fsub, sub)
2455 subforget[subpath + '/' + fsub] = (fsub, sub)
2456 except error.LookupError:
2456 except error.LookupError:
2457 ui.status(_("skipping missing subrepository: %s\n") % subpath)
2457 ui.status(_("skipping missing subrepository: %s\n") % subpath)
2458
2458
2459 for f in m.files():
2459 for f in m.files():
2460 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2460 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2461 if f not in subforget:
2461 if f not in subforget:
2462 if os.path.exists(m.rel(f)):
2462 if os.path.exists(m.rel(f)):
2463 ui.warn(_('not removing %s: file is already untracked\n')
2463 ui.warn(_('not removing %s: file is already untracked\n')
2464 % m.rel(f))
2464 % m.rel(f))
2465 errs = 1
2465 errs = 1
2466
2466
2467 for f in forget:
2467 for f in forget:
2468 if ui.verbose or not m.exact(f):
2468 if ui.verbose or not m.exact(f):
2469 ui.status(_('removing %s\n') % m.rel(f))
2469 ui.status(_('removing %s\n') % m.rel(f))
2470
2470
2471 if ui.verbose:
2471 if ui.verbose:
2472 for f in sorted(subforget.keys()):
2472 for f in sorted(subforget.keys()):
2473 ui.status(_('removing %s\n') % m.rel(f))
2473 ui.status(_('removing %s\n') % m.rel(f))
2474
2474
2475 wctx.forget(forget)
2475 wctx.forget(forget)
2476
2476
2477 for f in sorted(subforget.keys()):
2477 for f in sorted(subforget.keys()):
2478 fsub, sub = subforget[f]
2478 fsub, sub = subforget[f]
2479 sub.forget([fsub])
2479 sub.forget([fsub])
2480
2480
2481 return errs
2481 return errs
2482
2482
2483 @command(
2483 @command(
2484 'graft',
2484 'graft',
2485 [('c', 'continue', False, _('resume interrupted graft')),
2485 [('c', 'continue', False, _('resume interrupted graft')),
2486 ('e', 'edit', False, _('invoke editor on commit messages')),
2486 ('e', 'edit', False, _('invoke editor on commit messages')),
2487 ('D', 'currentdate', False,
2487 ('D', 'currentdate', False,
2488 _('record the current date as commit date')),
2488 _('record the current date as commit date')),
2489 ('U', 'currentuser', False,
2489 ('U', 'currentuser', False,
2490 _('record the current user as committer'), _('DATE'))]
2490 _('record the current user as committer'), _('DATE'))]
2491 + commitopts2 + mergetoolopts,
2491 + commitopts2 + mergetoolopts,
2492 _('[OPTION]... REVISION...'))
2492 _('[OPTION]... REVISION...'))
2493 def graft(ui, repo, *revs, **opts):
2493 def graft(ui, repo, *revs, **opts):
2494 '''copy changes from other branches onto the current branch
2494 '''copy changes from other branches onto the current branch
2495
2495
2496 This command uses Mercurial's merge logic to copy individual
2496 This command uses Mercurial's merge logic to copy individual
2497 changes from other branches without merging branches in the
2497 changes from other branches without merging branches in the
2498 history graph. This is sometimes known as 'backporting' or
2498 history graph. This is sometimes known as 'backporting' or
2499 'cherry-picking'. By default, graft will copy user, date, and
2499 'cherry-picking'. By default, graft will copy user, date, and
2500 description from the source changesets.
2500 description from the source changesets.
2501
2501
2502 Changesets that are ancestors of the current revision, that have
2502 Changesets that are ancestors of the current revision, that have
2503 already been grafted, or that are merges will be skipped.
2503 already been grafted, or that are merges will be skipped.
2504
2504
2505 If a graft merge results in conflicts, the graft process is
2505 If a graft merge results in conflicts, the graft process is
2506 aborted so that the current merge can be manually resolved. Once
2506 aborted so that the current merge can be manually resolved. Once
2507 all conflicts are addressed, the graft process can be continued
2507 all conflicts are addressed, the graft process can be continued
2508 with the -c/--continue option.
2508 with the -c/--continue option.
2509
2509
2510 .. note::
2510 .. note::
2511 The -c/--continue option does not reapply earlier options.
2511 The -c/--continue option does not reapply earlier options.
2512
2512
2513 .. container:: verbose
2513 .. container:: verbose
2514
2514
2515 Examples:
2515 Examples:
2516
2516
2517 - copy a single change to the stable branch and edit its description::
2517 - copy a single change to the stable branch and edit its description::
2518
2518
2519 hg update stable
2519 hg update stable
2520 hg graft --edit 9393
2520 hg graft --edit 9393
2521
2521
2522 - graft a range of changesets with one exception, updating dates::
2522 - graft a range of changesets with one exception, updating dates::
2523
2523
2524 hg graft -D "2085::2093 and not 2091"
2524 hg graft -D "2085::2093 and not 2091"
2525
2525
2526 - continue a graft after resolving conflicts::
2526 - continue a graft after resolving conflicts::
2527
2527
2528 hg graft -c
2528 hg graft -c
2529
2529
2530 - show the source of a grafted changeset::
2530 - show the source of a grafted changeset::
2531
2531
2532 hg log --debug -r tip
2532 hg log --debug -r tip
2533
2533
2534 Returns 0 on successful completion.
2534 Returns 0 on successful completion.
2535 '''
2535 '''
2536
2536
2537 if not opts.get('user') and opts.get('currentuser'):
2537 if not opts.get('user') and opts.get('currentuser'):
2538 opts['user'] = ui.username()
2538 opts['user'] = ui.username()
2539 if not opts.get('date') and opts.get('currentdate'):
2539 if not opts.get('date') and opts.get('currentdate'):
2540 opts['date'] = "%d %d" % util.makedate()
2540 opts['date'] = "%d %d" % util.makedate()
2541
2541
2542 editor = None
2542 editor = None
2543 if opts.get('edit'):
2543 if opts.get('edit'):
2544 editor = cmdutil.commitforceeditor
2544 editor = cmdutil.commitforceeditor
2545
2545
2546 cont = False
2546 cont = False
2547 if opts['continue']:
2547 if opts['continue']:
2548 cont = True
2548 cont = True
2549 if revs:
2549 if revs:
2550 raise util.Abort(_("can't specify --continue and revisions"))
2550 raise util.Abort(_("can't specify --continue and revisions"))
2551 # read in unfinished revisions
2551 # read in unfinished revisions
2552 try:
2552 try:
2553 nodes = repo.opener.read('graftstate').splitlines()
2553 nodes = repo.opener.read('graftstate').splitlines()
2554 revs = [repo[node].rev() for node in nodes]
2554 revs = [repo[node].rev() for node in nodes]
2555 except IOError, inst:
2555 except IOError, inst:
2556 if inst.errno != errno.ENOENT:
2556 if inst.errno != errno.ENOENT:
2557 raise
2557 raise
2558 raise util.Abort(_("no graft state found, can't continue"))
2558 raise util.Abort(_("no graft state found, can't continue"))
2559 else:
2559 else:
2560 cmdutil.bailifchanged(repo)
2560 cmdutil.bailifchanged(repo)
2561 if not revs:
2561 if not revs:
2562 raise util.Abort(_('no revisions specified'))
2562 raise util.Abort(_('no revisions specified'))
2563 revs = scmutil.revrange(repo, revs)
2563 revs = scmutil.revrange(repo, revs)
2564
2564
2565 # check for merges
2565 # check for merges
2566 for rev in repo.revs('%ld and merge()', revs):
2566 for rev in repo.revs('%ld and merge()', revs):
2567 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
2567 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
2568 revs.remove(rev)
2568 revs.remove(rev)
2569 if not revs:
2569 if not revs:
2570 return -1
2570 return -1
2571
2571
2572 # check for ancestors of dest branch
2572 # check for ancestors of dest branch
2573 for rev in repo.revs('::. and %ld', revs):
2573 for rev in repo.revs('::. and %ld', revs):
2574 ui.warn(_('skipping ancestor revision %s\n') % rev)
2574 ui.warn(_('skipping ancestor revision %s\n') % rev)
2575 revs.remove(rev)
2575 revs.remove(rev)
2576 if not revs:
2576 if not revs:
2577 return -1
2577 return -1
2578
2578
2579 # analyze revs for earlier grafts
2579 # analyze revs for earlier grafts
2580 ids = {}
2580 ids = {}
2581 for ctx in repo.set("%ld", revs):
2581 for ctx in repo.set("%ld", revs):
2582 ids[ctx.hex()] = ctx.rev()
2582 ids[ctx.hex()] = ctx.rev()
2583 n = ctx.extra().get('source')
2583 n = ctx.extra().get('source')
2584 if n:
2584 if n:
2585 ids[n] = ctx.rev()
2585 ids[n] = ctx.rev()
2586
2586
2587 # check ancestors for earlier grafts
2587 # check ancestors for earlier grafts
2588 ui.debug('scanning for duplicate grafts\n')
2588 ui.debug('scanning for duplicate grafts\n')
2589 for ctx in repo.set("::. - ::%ld", revs):
2589 for ctx in repo.set("::. - ::%ld", revs):
2590 n = ctx.extra().get('source')
2590 n = ctx.extra().get('source')
2591 if n in ids:
2591 if n in ids:
2592 r = repo[n].rev()
2592 r = repo[n].rev()
2593 if r in revs:
2593 if r in revs:
2594 ui.warn(_('skipping already grafted revision %s\n') % r)
2594 ui.warn(_('skipping already grafted revision %s\n') % r)
2595 revs.remove(r)
2595 revs.remove(r)
2596 elif ids[n] in revs:
2596 elif ids[n] in revs:
2597 ui.warn(_('skipping already grafted revision %s '
2597 ui.warn(_('skipping already grafted revision %s '
2598 '(same origin %d)\n') % (ids[n], r))
2598 '(same origin %d)\n') % (ids[n], r))
2599 revs.remove(ids[n])
2599 revs.remove(ids[n])
2600 elif ctx.hex() in ids:
2600 elif ctx.hex() in ids:
2601 r = ids[ctx.hex()]
2601 r = ids[ctx.hex()]
2602 ui.warn(_('skipping already grafted revision %s '
2602 ui.warn(_('skipping already grafted revision %s '
2603 '(was grafted from %d)\n') % (r, ctx.rev()))
2603 '(was grafted from %d)\n') % (r, ctx.rev()))
2604 revs.remove(r)
2604 revs.remove(r)
2605 if not revs:
2605 if not revs:
2606 return -1
2606 return -1
2607
2607
2608 for pos, ctx in enumerate(repo.set("%ld", revs)):
2608 for pos, ctx in enumerate(repo.set("%ld", revs)):
2609 current = repo['.']
2609 current = repo['.']
2610 ui.status(_('grafting revision %s\n') % ctx.rev())
2610 ui.status(_('grafting revision %s\n') % ctx.rev())
2611
2611
2612 # we don't merge the first commit when continuing
2612 # we don't merge the first commit when continuing
2613 if not cont:
2613 if not cont:
2614 # perform the graft merge with p1(rev) as 'ancestor'
2614 # perform the graft merge with p1(rev) as 'ancestor'
2615 try:
2615 try:
2616 # ui.forcemerge is an internal variable, do not document
2616 # ui.forcemerge is an internal variable, do not document
2617 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
2617 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
2618 stats = mergemod.update(repo, ctx.node(), True, True, False,
2618 stats = mergemod.update(repo, ctx.node(), True, True, False,
2619 ctx.p1().node())
2619 ctx.p1().node())
2620 finally:
2620 finally:
2621 ui.setconfig('ui', 'forcemerge', '')
2621 ui.setconfig('ui', 'forcemerge', '')
2622 # drop the second merge parent
2622 # drop the second merge parent
2623 repo.dirstate.setparents(current.node(), nullid)
2623 repo.dirstate.setparents(current.node(), nullid)
2624 repo.dirstate.write()
2624 repo.dirstate.write()
2625 # fix up dirstate for copies and renames
2625 # fix up dirstate for copies and renames
2626 cmdutil.duplicatecopies(repo, ctx.rev(), current.node(), nullid)
2626 cmdutil.duplicatecopies(repo, ctx.rev(), current.node(), nullid)
2627 # report any conflicts
2627 # report any conflicts
2628 if stats and stats[3] > 0:
2628 if stats and stats[3] > 0:
2629 # write out state for --continue
2629 # write out state for --continue
2630 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
2630 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
2631 repo.opener.write('graftstate', ''.join(nodelines))
2631 repo.opener.write('graftstate', ''.join(nodelines))
2632 raise util.Abort(
2632 raise util.Abort(
2633 _("unresolved conflicts, can't continue"),
2633 _("unresolved conflicts, can't continue"),
2634 hint=_('use hg resolve and hg graft --continue'))
2634 hint=_('use hg resolve and hg graft --continue'))
2635 else:
2635 else:
2636 cont = False
2636 cont = False
2637
2637
2638 # commit
2638 # commit
2639 source = ctx.extra().get('source')
2639 source = ctx.extra().get('source')
2640 if not source:
2640 if not source:
2641 source = ctx.hex()
2641 source = ctx.hex()
2642 extra = {'source': source}
2642 extra = {'source': source}
2643 user = ctx.user()
2643 user = ctx.user()
2644 if opts.get('user'):
2644 if opts.get('user'):
2645 user = opts['user']
2645 user = opts['user']
2646 date = ctx.date()
2646 date = ctx.date()
2647 if opts.get('date'):
2647 if opts.get('date'):
2648 date = opts['date']
2648 date = opts['date']
2649 repo.commit(text=ctx.description(), user=user,
2649 repo.commit(text=ctx.description(), user=user,
2650 date=date, extra=extra, editor=editor)
2650 date=date, extra=extra, editor=editor)
2651
2651
2652 # remove state when we complete successfully
2652 # remove state when we complete successfully
2653 if os.path.exists(repo.join('graftstate')):
2653 if os.path.exists(repo.join('graftstate')):
2654 util.unlinkpath(repo.join('graftstate'))
2654 util.unlinkpath(repo.join('graftstate'))
2655
2655
2656 return 0
2656 return 0
2657
2657
2658 @command('grep',
2658 @command('grep',
2659 [('0', 'print0', None, _('end fields with NUL')),
2659 [('0', 'print0', None, _('end fields with NUL')),
2660 ('', 'all', None, _('print all revisions that match')),
2660 ('', 'all', None, _('print all revisions that match')),
2661 ('a', 'text', None, _('treat all files as text')),
2661 ('a', 'text', None, _('treat all files as text')),
2662 ('f', 'follow', None,
2662 ('f', 'follow', None,
2663 _('follow changeset history,'
2663 _('follow changeset history,'
2664 ' or file history across copies and renames')),
2664 ' or file history across copies and renames')),
2665 ('i', 'ignore-case', None, _('ignore case when matching')),
2665 ('i', 'ignore-case', None, _('ignore case when matching')),
2666 ('l', 'files-with-matches', None,
2666 ('l', 'files-with-matches', None,
2667 _('print only filenames and revisions that match')),
2667 _('print only filenames and revisions that match')),
2668 ('n', 'line-number', None, _('print matching line numbers')),
2668 ('n', 'line-number', None, _('print matching line numbers')),
2669 ('r', 'rev', [],
2669 ('r', 'rev', [],
2670 _('only search files changed within revision range'), _('REV')),
2670 _('only search files changed within revision range'), _('REV')),
2671 ('u', 'user', None, _('list the author (long with -v)')),
2671 ('u', 'user', None, _('list the author (long with -v)')),
2672 ('d', 'date', None, _('list the date (short with -q)')),
2672 ('d', 'date', None, _('list the date (short with -q)')),
2673 ] + walkopts,
2673 ] + walkopts,
2674 _('[OPTION]... PATTERN [FILE]...'))
2674 _('[OPTION]... PATTERN [FILE]...'))
2675 def grep(ui, repo, pattern, *pats, **opts):
2675 def grep(ui, repo, pattern, *pats, **opts):
2676 """search for a pattern in specified files and revisions
2676 """search for a pattern in specified files and revisions
2677
2677
2678 Search revisions of files for a regular expression.
2678 Search revisions of files for a regular expression.
2679
2679
2680 This command behaves differently than Unix grep. It only accepts
2680 This command behaves differently than Unix grep. It only accepts
2681 Python/Perl regexps. It searches repository history, not the
2681 Python/Perl regexps. It searches repository history, not the
2682 working directory. It always prints the revision number in which a
2682 working directory. It always prints the revision number in which a
2683 match appears.
2683 match appears.
2684
2684
2685 By default, grep only prints output for the first revision of a
2685 By default, grep only prints output for the first revision of a
2686 file in which it finds a match. To get it to print every revision
2686 file in which it finds a match. To get it to print every revision
2687 that contains a change in match status ("-" for a match that
2687 that contains a change in match status ("-" for a match that
2688 becomes a non-match, or "+" for a non-match that becomes a match),
2688 becomes a non-match, or "+" for a non-match that becomes a match),
2689 use the --all flag.
2689 use the --all flag.
2690
2690
2691 Returns 0 if a match is found, 1 otherwise.
2691 Returns 0 if a match is found, 1 otherwise.
2692 """
2692 """
2693 reflags = 0
2693 reflags = 0
2694 if opts.get('ignore_case'):
2694 if opts.get('ignore_case'):
2695 reflags |= re.I
2695 reflags |= re.I
2696 try:
2696 try:
2697 regexp = re.compile(pattern, reflags)
2697 regexp = re.compile(pattern, reflags)
2698 except re.error, inst:
2698 except re.error, inst:
2699 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2699 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2700 return 1
2700 return 1
2701 sep, eol = ':', '\n'
2701 sep, eol = ':', '\n'
2702 if opts.get('print0'):
2702 if opts.get('print0'):
2703 sep = eol = '\0'
2703 sep = eol = '\0'
2704
2704
2705 getfile = util.lrucachefunc(repo.file)
2705 getfile = util.lrucachefunc(repo.file)
2706
2706
2707 def matchlines(body):
2707 def matchlines(body):
2708 begin = 0
2708 begin = 0
2709 linenum = 0
2709 linenum = 0
2710 while True:
2710 while True:
2711 match = regexp.search(body, begin)
2711 match = regexp.search(body, begin)
2712 if not match:
2712 if not match:
2713 break
2713 break
2714 mstart, mend = match.span()
2714 mstart, mend = match.span()
2715 linenum += body.count('\n', begin, mstart) + 1
2715 linenum += body.count('\n', begin, mstart) + 1
2716 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2716 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2717 begin = body.find('\n', mend) + 1 or len(body) + 1
2717 begin = body.find('\n', mend) + 1 or len(body) + 1
2718 lend = begin - 1
2718 lend = begin - 1
2719 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2719 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2720
2720
2721 class linestate(object):
2721 class linestate(object):
2722 def __init__(self, line, linenum, colstart, colend):
2722 def __init__(self, line, linenum, colstart, colend):
2723 self.line = line
2723 self.line = line
2724 self.linenum = linenum
2724 self.linenum = linenum
2725 self.colstart = colstart
2725 self.colstart = colstart
2726 self.colend = colend
2726 self.colend = colend
2727
2727
2728 def __hash__(self):
2728 def __hash__(self):
2729 return hash((self.linenum, self.line))
2729 return hash((self.linenum, self.line))
2730
2730
2731 def __eq__(self, other):
2731 def __eq__(self, other):
2732 return self.line == other.line
2732 return self.line == other.line
2733
2733
2734 matches = {}
2734 matches = {}
2735 copies = {}
2735 copies = {}
2736 def grepbody(fn, rev, body):
2736 def grepbody(fn, rev, body):
2737 matches[rev].setdefault(fn, [])
2737 matches[rev].setdefault(fn, [])
2738 m = matches[rev][fn]
2738 m = matches[rev][fn]
2739 for lnum, cstart, cend, line in matchlines(body):
2739 for lnum, cstart, cend, line in matchlines(body):
2740 s = linestate(line, lnum, cstart, cend)
2740 s = linestate(line, lnum, cstart, cend)
2741 m.append(s)
2741 m.append(s)
2742
2742
2743 def difflinestates(a, b):
2743 def difflinestates(a, b):
2744 sm = difflib.SequenceMatcher(None, a, b)
2744 sm = difflib.SequenceMatcher(None, a, b)
2745 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2745 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2746 if tag == 'insert':
2746 if tag == 'insert':
2747 for i in xrange(blo, bhi):
2747 for i in xrange(blo, bhi):
2748 yield ('+', b[i])
2748 yield ('+', b[i])
2749 elif tag == 'delete':
2749 elif tag == 'delete':
2750 for i in xrange(alo, ahi):
2750 for i in xrange(alo, ahi):
2751 yield ('-', a[i])
2751 yield ('-', a[i])
2752 elif tag == 'replace':
2752 elif tag == 'replace':
2753 for i in xrange(alo, ahi):
2753 for i in xrange(alo, ahi):
2754 yield ('-', a[i])
2754 yield ('-', a[i])
2755 for i in xrange(blo, bhi):
2755 for i in xrange(blo, bhi):
2756 yield ('+', b[i])
2756 yield ('+', b[i])
2757
2757
2758 def display(fn, ctx, pstates, states):
2758 def display(fn, ctx, pstates, states):
2759 rev = ctx.rev()
2759 rev = ctx.rev()
2760 datefunc = ui.quiet and util.shortdate or util.datestr
2760 datefunc = ui.quiet and util.shortdate or util.datestr
2761 found = False
2761 found = False
2762 filerevmatches = {}
2762 filerevmatches = {}
2763 def binary():
2763 def binary():
2764 flog = getfile(fn)
2764 flog = getfile(fn)
2765 return util.binary(flog.read(ctx.filenode(fn)))
2765 return util.binary(flog.read(ctx.filenode(fn)))
2766
2766
2767 if opts.get('all'):
2767 if opts.get('all'):
2768 iter = difflinestates(pstates, states)
2768 iter = difflinestates(pstates, states)
2769 else:
2769 else:
2770 iter = [('', l) for l in states]
2770 iter = [('', l) for l in states]
2771 for change, l in iter:
2771 for change, l in iter:
2772 cols = [fn, str(rev)]
2772 cols = [fn, str(rev)]
2773 before, match, after = None, None, None
2773 before, match, after = None, None, None
2774 if opts.get('line_number'):
2774 if opts.get('line_number'):
2775 cols.append(str(l.linenum))
2775 cols.append(str(l.linenum))
2776 if opts.get('all'):
2776 if opts.get('all'):
2777 cols.append(change)
2777 cols.append(change)
2778 if opts.get('user'):
2778 if opts.get('user'):
2779 cols.append(ui.shortuser(ctx.user()))
2779 cols.append(ui.shortuser(ctx.user()))
2780 if opts.get('date'):
2780 if opts.get('date'):
2781 cols.append(datefunc(ctx.date()))
2781 cols.append(datefunc(ctx.date()))
2782 if opts.get('files_with_matches'):
2782 if opts.get('files_with_matches'):
2783 c = (fn, rev)
2783 c = (fn, rev)
2784 if c in filerevmatches:
2784 if c in filerevmatches:
2785 continue
2785 continue
2786 filerevmatches[c] = 1
2786 filerevmatches[c] = 1
2787 else:
2787 else:
2788 before = l.line[:l.colstart]
2788 before = l.line[:l.colstart]
2789 match = l.line[l.colstart:l.colend]
2789 match = l.line[l.colstart:l.colend]
2790 after = l.line[l.colend:]
2790 after = l.line[l.colend:]
2791 ui.write(sep.join(cols))
2791 ui.write(sep.join(cols))
2792 if before is not None:
2792 if before is not None:
2793 if not opts.get('text') and binary():
2793 if not opts.get('text') and binary():
2794 ui.write(sep + " Binary file matches")
2794 ui.write(sep + " Binary file matches")
2795 else:
2795 else:
2796 ui.write(sep + before)
2796 ui.write(sep + before)
2797 ui.write(match, label='grep.match')
2797 ui.write(match, label='grep.match')
2798 ui.write(after)
2798 ui.write(after)
2799 ui.write(eol)
2799 ui.write(eol)
2800 found = True
2800 found = True
2801 return found
2801 return found
2802
2802
2803 skip = {}
2803 skip = {}
2804 revfiles = {}
2804 revfiles = {}
2805 matchfn = scmutil.match(repo[None], pats, opts)
2805 matchfn = scmutil.match(repo[None], pats, opts)
2806 found = False
2806 found = False
2807 follow = opts.get('follow')
2807 follow = opts.get('follow')
2808
2808
2809 def prep(ctx, fns):
2809 def prep(ctx, fns):
2810 rev = ctx.rev()
2810 rev = ctx.rev()
2811 pctx = ctx.p1()
2811 pctx = ctx.p1()
2812 parent = pctx.rev()
2812 parent = pctx.rev()
2813 matches.setdefault(rev, {})
2813 matches.setdefault(rev, {})
2814 matches.setdefault(parent, {})
2814 matches.setdefault(parent, {})
2815 files = revfiles.setdefault(rev, [])
2815 files = revfiles.setdefault(rev, [])
2816 for fn in fns:
2816 for fn in fns:
2817 flog = getfile(fn)
2817 flog = getfile(fn)
2818 try:
2818 try:
2819 fnode = ctx.filenode(fn)
2819 fnode = ctx.filenode(fn)
2820 except error.LookupError:
2820 except error.LookupError:
2821 continue
2821 continue
2822
2822
2823 copied = flog.renamed(fnode)
2823 copied = flog.renamed(fnode)
2824 copy = follow and copied and copied[0]
2824 copy = follow and copied and copied[0]
2825 if copy:
2825 if copy:
2826 copies.setdefault(rev, {})[fn] = copy
2826 copies.setdefault(rev, {})[fn] = copy
2827 if fn in skip:
2827 if fn in skip:
2828 if copy:
2828 if copy:
2829 skip[copy] = True
2829 skip[copy] = True
2830 continue
2830 continue
2831 files.append(fn)
2831 files.append(fn)
2832
2832
2833 if fn not in matches[rev]:
2833 if fn not in matches[rev]:
2834 grepbody(fn, rev, flog.read(fnode))
2834 grepbody(fn, rev, flog.read(fnode))
2835
2835
2836 pfn = copy or fn
2836 pfn = copy or fn
2837 if pfn not in matches[parent]:
2837 if pfn not in matches[parent]:
2838 try:
2838 try:
2839 fnode = pctx.filenode(pfn)
2839 fnode = pctx.filenode(pfn)
2840 grepbody(pfn, parent, flog.read(fnode))
2840 grepbody(pfn, parent, flog.read(fnode))
2841 except error.LookupError:
2841 except error.LookupError:
2842 pass
2842 pass
2843
2843
2844 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2844 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2845 rev = ctx.rev()
2845 rev = ctx.rev()
2846 parent = ctx.p1().rev()
2846 parent = ctx.p1().rev()
2847 for fn in sorted(revfiles.get(rev, [])):
2847 for fn in sorted(revfiles.get(rev, [])):
2848 states = matches[rev][fn]
2848 states = matches[rev][fn]
2849 copy = copies.get(rev, {}).get(fn)
2849 copy = copies.get(rev, {}).get(fn)
2850 if fn in skip:
2850 if fn in skip:
2851 if copy:
2851 if copy:
2852 skip[copy] = True
2852 skip[copy] = True
2853 continue
2853 continue
2854 pstates = matches.get(parent, {}).get(copy or fn, [])
2854 pstates = matches.get(parent, {}).get(copy or fn, [])
2855 if pstates or states:
2855 if pstates or states:
2856 r = display(fn, ctx, pstates, states)
2856 r = display(fn, ctx, pstates, states)
2857 found = found or r
2857 found = found or r
2858 if r and not opts.get('all'):
2858 if r and not opts.get('all'):
2859 skip[fn] = True
2859 skip[fn] = True
2860 if copy:
2860 if copy:
2861 skip[copy] = True
2861 skip[copy] = True
2862 del matches[rev]
2862 del matches[rev]
2863 del revfiles[rev]
2863 del revfiles[rev]
2864
2864
2865 return not found
2865 return not found
2866
2866
2867 @command('heads',
2867 @command('heads',
2868 [('r', 'rev', '',
2868 [('r', 'rev', '',
2869 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2869 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2870 ('t', 'topo', False, _('show topological heads only')),
2870 ('t', 'topo', False, _('show topological heads only')),
2871 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2871 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2872 ('c', 'closed', False, _('show normal and closed branch heads')),
2872 ('c', 'closed', False, _('show normal and closed branch heads')),
2873 ] + templateopts,
2873 ] + templateopts,
2874 _('[-ac] [-r STARTREV] [REV]...'))
2874 _('[-ac] [-r STARTREV] [REV]...'))
2875 def heads(ui, repo, *branchrevs, **opts):
2875 def heads(ui, repo, *branchrevs, **opts):
2876 """show current repository heads or show branch heads
2876 """show current repository heads or show branch heads
2877
2877
2878 With no arguments, show all repository branch heads.
2878 With no arguments, show all repository branch heads.
2879
2879
2880 Repository "heads" are changesets with no child changesets. They are
2880 Repository "heads" are changesets with no child changesets. They are
2881 where development generally takes place and are the usual targets
2881 where development generally takes place and are the usual targets
2882 for update and merge operations. Branch heads are changesets that have
2882 for update and merge operations. Branch heads are changesets that have
2883 no child changeset on the same branch.
2883 no child changeset on the same branch.
2884
2884
2885 If one or more REVs are given, only branch heads on the branches
2885 If one or more REVs are given, only branch heads on the branches
2886 associated with the specified changesets are shown. This means
2886 associated with the specified changesets are shown. This means
2887 that you can use :hg:`heads foo` to see the heads on a branch
2887 that you can use :hg:`heads foo` to see the heads on a branch
2888 named ``foo``.
2888 named ``foo``.
2889
2889
2890 If -c/--closed is specified, also show branch heads marked closed
2890 If -c/--closed is specified, also show branch heads marked closed
2891 (see :hg:`commit --close-branch`).
2891 (see :hg:`commit --close-branch`).
2892
2892
2893 If STARTREV is specified, only those heads that are descendants of
2893 If STARTREV is specified, only those heads that are descendants of
2894 STARTREV will be displayed.
2894 STARTREV will be displayed.
2895
2895
2896 If -t/--topo is specified, named branch mechanics will be ignored and only
2896 If -t/--topo is specified, named branch mechanics will be ignored and only
2897 changesets without children will be shown.
2897 changesets without children will be shown.
2898
2898
2899 Returns 0 if matching heads are found, 1 if not.
2899 Returns 0 if matching heads are found, 1 if not.
2900 """
2900 """
2901
2901
2902 start = None
2902 start = None
2903 if 'rev' in opts:
2903 if 'rev' in opts:
2904 start = scmutil.revsingle(repo, opts['rev'], None).node()
2904 start = scmutil.revsingle(repo, opts['rev'], None).node()
2905
2905
2906 if opts.get('topo'):
2906 if opts.get('topo'):
2907 heads = [repo[h] for h in repo.heads(start)]
2907 heads = [repo[h] for h in repo.heads(start)]
2908 else:
2908 else:
2909 heads = []
2909 heads = []
2910 for branch in repo.branchmap():
2910 for branch in repo.branchmap():
2911 heads += repo.branchheads(branch, start, opts.get('closed'))
2911 heads += repo.branchheads(branch, start, opts.get('closed'))
2912 heads = [repo[h] for h in heads]
2912 heads = [repo[h] for h in heads]
2913
2913
2914 if branchrevs:
2914 if branchrevs:
2915 branches = set(repo[br].branch() for br in branchrevs)
2915 branches = set(repo[br].branch() for br in branchrevs)
2916 heads = [h for h in heads if h.branch() in branches]
2916 heads = [h for h in heads if h.branch() in branches]
2917
2917
2918 if opts.get('active') and branchrevs:
2918 if opts.get('active') and branchrevs:
2919 dagheads = repo.heads(start)
2919 dagheads = repo.heads(start)
2920 heads = [h for h in heads if h.node() in dagheads]
2920 heads = [h for h in heads if h.node() in dagheads]
2921
2921
2922 if branchrevs:
2922 if branchrevs:
2923 haveheads = set(h.branch() for h in heads)
2923 haveheads = set(h.branch() for h in heads)
2924 if branches - haveheads:
2924 if branches - haveheads:
2925 headless = ', '.join(b for b in branches - haveheads)
2925 headless = ', '.join(b for b in branches - haveheads)
2926 msg = _('no open branch heads found on branches %s')
2926 msg = _('no open branch heads found on branches %s')
2927 if opts.get('rev'):
2927 if opts.get('rev'):
2928 msg += _(' (started at %s)' % opts['rev'])
2928 msg += _(' (started at %s)' % opts['rev'])
2929 ui.warn((msg + '\n') % headless)
2929 ui.warn((msg + '\n') % headless)
2930
2930
2931 if not heads:
2931 if not heads:
2932 return 1
2932 return 1
2933
2933
2934 heads = sorted(heads, key=lambda x: -x.rev())
2934 heads = sorted(heads, key=lambda x: -x.rev())
2935 displayer = cmdutil.show_changeset(ui, repo, opts)
2935 displayer = cmdutil.show_changeset(ui, repo, opts)
2936 for ctx in heads:
2936 for ctx in heads:
2937 displayer.show(ctx)
2937 displayer.show(ctx)
2938 displayer.close()
2938 displayer.close()
2939
2939
2940 @command('help',
2940 @command('help',
2941 [('e', 'extension', None, _('show only help for extensions')),
2941 [('e', 'extension', None, _('show only help for extensions')),
2942 ('c', 'command', None, _('show only help for commands'))],
2942 ('c', 'command', None, _('show only help for commands'))],
2943 _('[-ec] [TOPIC]'))
2943 _('[-ec] [TOPIC]'))
2944 def help_(ui, name=None, unknowncmd=False, full=True, **opts):
2944 def help_(ui, name=None, unknowncmd=False, full=True, **opts):
2945 """show help for a given topic or a help overview
2945 """show help for a given topic or a help overview
2946
2946
2947 With no arguments, print a list of commands with short help messages.
2947 With no arguments, print a list of commands with short help messages.
2948
2948
2949 Given a topic, extension, or command name, print help for that
2949 Given a topic, extension, or command name, print help for that
2950 topic.
2950 topic.
2951
2951
2952 Returns 0 if successful.
2952 Returns 0 if successful.
2953 """
2953 """
2954
2954
2955 textwidth = min(ui.termwidth(), 80) - 2
2955 textwidth = min(ui.termwidth(), 80) - 2
2956
2956
2957 def optrst(options):
2957 def optrst(options):
2958 data = []
2958 data = []
2959 multioccur = False
2959 multioccur = False
2960 for option in options:
2960 for option in options:
2961 if len(option) == 5:
2961 if len(option) == 5:
2962 shortopt, longopt, default, desc, optlabel = option
2962 shortopt, longopt, default, desc, optlabel = option
2963 else:
2963 else:
2964 shortopt, longopt, default, desc = option
2964 shortopt, longopt, default, desc = option
2965 optlabel = _("VALUE") # default label
2965 optlabel = _("VALUE") # default label
2966
2966
2967 if _("DEPRECATED") in desc and not ui.verbose:
2967 if _("DEPRECATED") in desc and not ui.verbose:
2968 continue
2968 continue
2969
2969
2970 so = ''
2970 so = ''
2971 if shortopt:
2971 if shortopt:
2972 so = '-' + shortopt
2972 so = '-' + shortopt
2973 lo = '--' + longopt
2973 lo = '--' + longopt
2974 if default:
2974 if default:
2975 desc += _(" (default: %s)") % default
2975 desc += _(" (default: %s)") % default
2976
2976
2977 if isinstance(default, list):
2977 if isinstance(default, list):
2978 lo += " %s [+]" % optlabel
2978 lo += " %s [+]" % optlabel
2979 multioccur = True
2979 multioccur = True
2980 elif (default is not None) and not isinstance(default, bool):
2980 elif (default is not None) and not isinstance(default, bool):
2981 lo += " %s" % optlabel
2981 lo += " %s" % optlabel
2982
2982
2983 data.append((so, lo, desc))
2983 data.append((so, lo, desc))
2984
2984
2985 rst = minirst.maketable(data, 1)
2985 rst = minirst.maketable(data, 1)
2986
2986
2987 if multioccur:
2987 if multioccur:
2988 rst += _("\n[+] marked option can be specified multiple times\n")
2988 rst += _("\n[+] marked option can be specified multiple times\n")
2989
2989
2990 return rst
2990 return rst
2991
2991
2992 # list all option lists
2992 # list all option lists
2993 def opttext(optlist, width):
2993 def opttext(optlist, width):
2994 rst = ''
2994 rst = ''
2995 if not optlist:
2995 if not optlist:
2996 return ''
2996 return ''
2997
2997
2998 for title, options in optlist:
2998 for title, options in optlist:
2999 rst += '\n%s\n' % title
2999 rst += '\n%s\n' % title
3000 if options:
3000 if options:
3001 rst += "\n"
3001 rst += "\n"
3002 rst += optrst(options)
3002 rst += optrst(options)
3003 rst += '\n'
3003 rst += '\n'
3004
3004
3005 return '\n' + minirst.format(rst, width)
3005 return '\n' + minirst.format(rst, width)
3006
3006
3007 def addglobalopts(optlist, aliases):
3007 def addglobalopts(optlist, aliases):
3008 if ui.quiet:
3008 if ui.quiet:
3009 return []
3009 return []
3010
3010
3011 if ui.verbose:
3011 if ui.verbose:
3012 optlist.append((_("global options:"), globalopts))
3012 optlist.append((_("global options:"), globalopts))
3013 if name == 'shortlist':
3013 if name == 'shortlist':
3014 optlist.append((_('use "hg help" for the full list '
3014 optlist.append((_('use "hg help" for the full list '
3015 'of commands'), ()))
3015 'of commands'), ()))
3016 else:
3016 else:
3017 if name == 'shortlist':
3017 if name == 'shortlist':
3018 msg = _('use "hg help" for the full list of commands '
3018 msg = _('use "hg help" for the full list of commands '
3019 'or "hg -v" for details')
3019 'or "hg -v" for details')
3020 elif name and not full:
3020 elif name and not full:
3021 msg = _('use "hg help %s" to show the full help text' % name)
3021 msg = _('use "hg help %s" to show the full help text' % name)
3022 elif aliases:
3022 elif aliases:
3023 msg = _('use "hg -v help%s" to show builtin aliases and '
3023 msg = _('use "hg -v help%s" to show builtin aliases and '
3024 'global options') % (name and " " + name or "")
3024 'global options') % (name and " " + name or "")
3025 else:
3025 else:
3026 msg = _('use "hg -v help %s" to show more info') % name
3026 msg = _('use "hg -v help %s" to show more info') % name
3027 optlist.append((msg, ()))
3027 optlist.append((msg, ()))
3028
3028
3029 def helpcmd(name):
3029 def helpcmd(name):
3030 try:
3030 try:
3031 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
3031 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
3032 except error.AmbiguousCommand, inst:
3032 except error.AmbiguousCommand, inst:
3033 # py3k fix: except vars can't be used outside the scope of the
3033 # py3k fix: except vars can't be used outside the scope of the
3034 # except block, nor can be used inside a lambda. python issue4617
3034 # except block, nor can be used inside a lambda. python issue4617
3035 prefix = inst.args[0]
3035 prefix = inst.args[0]
3036 select = lambda c: c.lstrip('^').startswith(prefix)
3036 select = lambda c: c.lstrip('^').startswith(prefix)
3037 helplist(select)
3037 helplist(select)
3038 return
3038 return
3039
3039
3040 # check if it's an invalid alias and display its error if it is
3040 # check if it's an invalid alias and display its error if it is
3041 if getattr(entry[0], 'badalias', False):
3041 if getattr(entry[0], 'badalias', False):
3042 if not unknowncmd:
3042 if not unknowncmd:
3043 entry[0](ui)
3043 entry[0](ui)
3044 return
3044 return
3045
3045
3046 rst = ""
3046 rst = ""
3047
3047
3048 # synopsis
3048 # synopsis
3049 if len(entry) > 2:
3049 if len(entry) > 2:
3050 if entry[2].startswith('hg'):
3050 if entry[2].startswith('hg'):
3051 rst += "%s\n" % entry[2]
3051 rst += "%s\n" % entry[2]
3052 else:
3052 else:
3053 rst += 'hg %s %s\n' % (aliases[0], entry[2])
3053 rst += 'hg %s %s\n' % (aliases[0], entry[2])
3054 else:
3054 else:
3055 rst += 'hg %s\n' % aliases[0]
3055 rst += 'hg %s\n' % aliases[0]
3056
3056
3057 # aliases
3057 # aliases
3058 if full and not ui.quiet and len(aliases) > 1:
3058 if full and not ui.quiet and len(aliases) > 1:
3059 rst += _("\naliases: %s\n") % ', '.join(aliases[1:])
3059 rst += _("\naliases: %s\n") % ', '.join(aliases[1:])
3060
3060
3061 # description
3061 # description
3062 doc = gettext(entry[0].__doc__)
3062 doc = gettext(entry[0].__doc__)
3063 if not doc:
3063 if not doc:
3064 doc = _("(no help text available)")
3064 doc = _("(no help text available)")
3065 if util.safehasattr(entry[0], 'definition'): # aliased command
3065 if util.safehasattr(entry[0], 'definition'): # aliased command
3066 if entry[0].definition.startswith('!'): # shell alias
3066 if entry[0].definition.startswith('!'): # shell alias
3067 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
3067 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
3068 else:
3068 else:
3069 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
3069 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
3070 if ui.quiet or not full:
3070 if ui.quiet or not full:
3071 doc = doc.splitlines()[0]
3071 doc = doc.splitlines()[0]
3072 rst += "\n" + doc + "\n"
3072 rst += "\n" + doc + "\n"
3073
3073
3074 # check if this command shadows a non-trivial (multi-line)
3074 # check if this command shadows a non-trivial (multi-line)
3075 # extension help text
3075 # extension help text
3076 try:
3076 try:
3077 mod = extensions.find(name)
3077 mod = extensions.find(name)
3078 doc = gettext(mod.__doc__) or ''
3078 doc = gettext(mod.__doc__) or ''
3079 if '\n' in doc.strip():
3079 if '\n' in doc.strip():
3080 msg = _('use "hg help -e %s" to show help for '
3080 msg = _('use "hg help -e %s" to show help for '
3081 'the %s extension') % (name, name)
3081 'the %s extension') % (name, name)
3082 rst += '\n%s\n' % msg
3082 rst += '\n%s\n' % msg
3083 except KeyError:
3083 except KeyError:
3084 pass
3084 pass
3085
3085
3086 # options
3086 # options
3087 if not ui.quiet and entry[1]:
3087 if not ui.quiet and entry[1]:
3088 rst += '\noptions:\n\n'
3088 rst += '\noptions:\n\n'
3089 rst += optrst(entry[1])
3089 rst += optrst(entry[1])
3090
3090
3091 if ui.verbose:
3091 if ui.verbose:
3092 rst += '\nglobal options:\n\n'
3092 rst += '\nglobal options:\n\n'
3093 rst += optrst(globalopts)
3093 rst += optrst(globalopts)
3094
3094
3095 keep = ui.verbose and ['verbose'] or []
3095 keep = ui.verbose and ['verbose'] or []
3096 formatted, pruned = minirst.format(rst, textwidth, keep=keep)
3096 formatted, pruned = minirst.format(rst, textwidth, keep=keep)
3097 ui.write(formatted)
3097 ui.write(formatted)
3098
3098
3099 if not ui.verbose:
3099 if not ui.verbose:
3100 if not full:
3100 if not full:
3101 ui.write(_('\nuse "hg help %s" to show the full help text\n')
3101 ui.write(_('\nuse "hg help %s" to show the full help text\n')
3102 % name)
3102 % name)
3103 elif not ui.quiet:
3103 elif not ui.quiet:
3104 ui.write(_('\nuse "hg -v help %s" to show more info\n') % name)
3104 ui.write(_('\nuse "hg -v help %s" to show more info\n') % name)
3105
3105
3106
3106
3107 def helplist(select=None):
3107 def helplist(select=None):
3108 # list of commands
3108 # list of commands
3109 if name == "shortlist":
3109 if name == "shortlist":
3110 header = _('basic commands:\n\n')
3110 header = _('basic commands:\n\n')
3111 else:
3111 else:
3112 header = _('list of commands:\n\n')
3112 header = _('list of commands:\n\n')
3113
3113
3114 h = {}
3114 h = {}
3115 cmds = {}
3115 cmds = {}
3116 for c, e in table.iteritems():
3116 for c, e in table.iteritems():
3117 f = c.split("|", 1)[0]
3117 f = c.split("|", 1)[0]
3118 if select and not select(f):
3118 if select and not select(f):
3119 continue
3119 continue
3120 if (not select and name != 'shortlist' and
3120 if (not select and name != 'shortlist' and
3121 e[0].__module__ != __name__):
3121 e[0].__module__ != __name__):
3122 continue
3122 continue
3123 if name == "shortlist" and not f.startswith("^"):
3123 if name == "shortlist" and not f.startswith("^"):
3124 continue
3124 continue
3125 f = f.lstrip("^")
3125 f = f.lstrip("^")
3126 if not ui.debugflag and f.startswith("debug"):
3126 if not ui.debugflag and f.startswith("debug"):
3127 continue
3127 continue
3128 doc = e[0].__doc__
3128 doc = e[0].__doc__
3129 if doc and 'DEPRECATED' in doc and not ui.verbose:
3129 if doc and 'DEPRECATED' in doc and not ui.verbose:
3130 continue
3130 continue
3131 doc = gettext(doc)
3131 doc = gettext(doc)
3132 if not doc:
3132 if not doc:
3133 doc = _("(no help text available)")
3133 doc = _("(no help text available)")
3134 h[f] = doc.splitlines()[0].rstrip()
3134 h[f] = doc.splitlines()[0].rstrip()
3135 cmds[f] = c.lstrip("^")
3135 cmds[f] = c.lstrip("^")
3136
3136
3137 if not h:
3137 if not h:
3138 ui.status(_('no commands defined\n'))
3138 ui.status(_('no commands defined\n'))
3139 return
3139 return
3140
3140
3141 ui.status(header)
3141 ui.status(header)
3142 fns = sorted(h)
3142 fns = sorted(h)
3143 m = max(map(len, fns))
3143 m = max(map(len, fns))
3144 for f in fns:
3144 for f in fns:
3145 if ui.verbose:
3145 if ui.verbose:
3146 commands = cmds[f].replace("|",", ")
3146 commands = cmds[f].replace("|",", ")
3147 ui.write(" %s:\n %s\n"%(commands, h[f]))
3147 ui.write(" %s:\n %s\n"%(commands, h[f]))
3148 else:
3148 else:
3149 ui.write('%s\n' % (util.wrap(h[f], textwidth,
3149 ui.write('%s\n' % (util.wrap(h[f], textwidth,
3150 initindent=' %-*s ' % (m, f),
3150 initindent=' %-*s ' % (m, f),
3151 hangindent=' ' * (m + 4))))
3151 hangindent=' ' * (m + 4))))
3152
3152
3153 if not name:
3153 if not name:
3154 text = help.listexts(_('enabled extensions:'), extensions.enabled())
3154 text = help.listexts(_('enabled extensions:'), extensions.enabled())
3155 if text:
3155 if text:
3156 ui.write("\n%s" % minirst.format(text, textwidth))
3156 ui.write("\n%s" % minirst.format(text, textwidth))
3157
3157
3158 ui.write(_("\nadditional help topics:\n\n"))
3158 ui.write(_("\nadditional help topics:\n\n"))
3159 topics = []
3159 topics = []
3160 for names, header, doc in help.helptable:
3160 for names, header, doc in help.helptable:
3161 topics.append((sorted(names, key=len, reverse=True)[0], header))
3161 topics.append((sorted(names, key=len, reverse=True)[0], header))
3162 topics_len = max([len(s[0]) for s in topics])
3162 topics_len = max([len(s[0]) for s in topics])
3163 for t, desc in topics:
3163 for t, desc in topics:
3164 ui.write(" %-*s %s\n" % (topics_len, t, desc))
3164 ui.write(" %-*s %s\n" % (topics_len, t, desc))
3165
3165
3166 optlist = []
3166 optlist = []
3167 addglobalopts(optlist, True)
3167 addglobalopts(optlist, True)
3168 ui.write(opttext(optlist, textwidth))
3168 ui.write(opttext(optlist, textwidth))
3169
3169
3170 def helptopic(name):
3170 def helptopic(name):
3171 for names, header, doc in help.helptable:
3171 for names, header, doc in help.helptable:
3172 if name in names:
3172 if name in names:
3173 break
3173 break
3174 else:
3174 else:
3175 raise error.UnknownCommand(name)
3175 raise error.UnknownCommand(name)
3176
3176
3177 # description
3177 # description
3178 if not doc:
3178 if not doc:
3179 doc = _("(no help text available)")
3179 doc = _("(no help text available)")
3180 if util.safehasattr(doc, '__call__'):
3180 if util.safehasattr(doc, '__call__'):
3181 doc = doc()
3181 doc = doc()
3182
3182
3183 ui.write("%s\n\n" % header)
3183 ui.write("%s\n\n" % header)
3184 ui.write("%s" % minirst.format(doc, textwidth, indent=4))
3184 ui.write("%s" % minirst.format(doc, textwidth, indent=4))
3185 try:
3185 try:
3186 cmdutil.findcmd(name, table)
3186 cmdutil.findcmd(name, table)
3187 ui.write(_('\nuse "hg help -c %s" to see help for '
3187 ui.write(_('\nuse "hg help -c %s" to see help for '
3188 'the %s command\n') % (name, name))
3188 'the %s command\n') % (name, name))
3189 except error.UnknownCommand:
3189 except error.UnknownCommand:
3190 pass
3190 pass
3191
3191
3192 def helpext(name):
3192 def helpext(name):
3193 try:
3193 try:
3194 mod = extensions.find(name)
3194 mod = extensions.find(name)
3195 doc = gettext(mod.__doc__) or _('no help text available')
3195 doc = gettext(mod.__doc__) or _('no help text available')
3196 except KeyError:
3196 except KeyError:
3197 mod = None
3197 mod = None
3198 doc = extensions.disabledext(name)
3198 doc = extensions.disabledext(name)
3199 if not doc:
3199 if not doc:
3200 raise error.UnknownCommand(name)
3200 raise error.UnknownCommand(name)
3201
3201
3202 if '\n' not in doc:
3202 if '\n' not in doc:
3203 head, tail = doc, ""
3203 head, tail = doc, ""
3204 else:
3204 else:
3205 head, tail = doc.split('\n', 1)
3205 head, tail = doc.split('\n', 1)
3206 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
3206 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
3207 if tail:
3207 if tail:
3208 ui.write(minirst.format(tail, textwidth))
3208 ui.write(minirst.format(tail, textwidth))
3209 ui.status('\n')
3209 ui.status('\n')
3210
3210
3211 if mod:
3211 if mod:
3212 try:
3212 try:
3213 ct = mod.cmdtable
3213 ct = mod.cmdtable
3214 except AttributeError:
3214 except AttributeError:
3215 ct = {}
3215 ct = {}
3216 modcmds = set([c.split('|', 1)[0] for c in ct])
3216 modcmds = set([c.split('|', 1)[0] for c in ct])
3217 helplist(modcmds.__contains__)
3217 helplist(modcmds.__contains__)
3218 else:
3218 else:
3219 ui.write(_('use "hg help extensions" for information on enabling '
3219 ui.write(_('use "hg help extensions" for information on enabling '
3220 'extensions\n'))
3220 'extensions\n'))
3221
3221
3222 def helpextcmd(name):
3222 def helpextcmd(name):
3223 cmd, ext, mod = extensions.disabledcmd(ui, name, ui.config('ui', 'strict'))
3223 cmd, ext, mod = extensions.disabledcmd(ui, name, ui.config('ui', 'strict'))
3224 doc = gettext(mod.__doc__).splitlines()[0]
3224 doc = gettext(mod.__doc__).splitlines()[0]
3225
3225
3226 msg = help.listexts(_("'%s' is provided by the following "
3226 msg = help.listexts(_("'%s' is provided by the following "
3227 "extension:") % cmd, {ext: doc}, indent=4)
3227 "extension:") % cmd, {ext: doc}, indent=4)
3228 ui.write(minirst.format(msg, textwidth))
3228 ui.write(minirst.format(msg, textwidth))
3229 ui.write('\n')
3229 ui.write('\n')
3230 ui.write(_('use "hg help extensions" for information on enabling '
3230 ui.write(_('use "hg help extensions" for information on enabling '
3231 'extensions\n'))
3231 'extensions\n'))
3232
3232
3233 if name and name != 'shortlist':
3233 if name and name != 'shortlist':
3234 i = None
3234 i = None
3235 if unknowncmd:
3235 if unknowncmd:
3236 queries = (helpextcmd,)
3236 queries = (helpextcmd,)
3237 elif opts.get('extension'):
3237 elif opts.get('extension'):
3238 queries = (helpext,)
3238 queries = (helpext,)
3239 elif opts.get('command'):
3239 elif opts.get('command'):
3240 queries = (helpcmd,)
3240 queries = (helpcmd,)
3241 else:
3241 else:
3242 queries = (helptopic, helpcmd, helpext, helpextcmd)
3242 queries = (helptopic, helpcmd, helpext, helpextcmd)
3243 for f in queries:
3243 for f in queries:
3244 try:
3244 try:
3245 f(name)
3245 f(name)
3246 i = None
3246 i = None
3247 break
3247 break
3248 except error.UnknownCommand, inst:
3248 except error.UnknownCommand, inst:
3249 i = inst
3249 i = inst
3250 if i:
3250 if i:
3251 raise i
3251 raise i
3252 else:
3252 else:
3253 # program name
3253 # program name
3254 ui.status(_("Mercurial Distributed SCM\n"))
3254 ui.status(_("Mercurial Distributed SCM\n"))
3255 ui.status('\n')
3255 ui.status('\n')
3256 helplist()
3256 helplist()
3257
3257
3258
3258
3259 @command('identify|id',
3259 @command('identify|id',
3260 [('r', 'rev', '',
3260 [('r', 'rev', '',
3261 _('identify the specified revision'), _('REV')),
3261 _('identify the specified revision'), _('REV')),
3262 ('n', 'num', None, _('show local revision number')),
3262 ('n', 'num', None, _('show local revision number')),
3263 ('i', 'id', None, _('show global revision id')),
3263 ('i', 'id', None, _('show global revision id')),
3264 ('b', 'branch', None, _('show branch')),
3264 ('b', 'branch', None, _('show branch')),
3265 ('t', 'tags', None, _('show tags')),
3265 ('t', 'tags', None, _('show tags')),
3266 ('B', 'bookmarks', None, _('show bookmarks')),
3266 ('B', 'bookmarks', None, _('show bookmarks')),
3267 ] + remoteopts,
3267 ] + remoteopts,
3268 _('[-nibtB] [-r REV] [SOURCE]'))
3268 _('[-nibtB] [-r REV] [SOURCE]'))
3269 def identify(ui, repo, source=None, rev=None,
3269 def identify(ui, repo, source=None, rev=None,
3270 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3270 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3271 """identify the working copy or specified revision
3271 """identify the working copy or specified revision
3272
3272
3273 Print a summary identifying the repository state at REV using one or
3273 Print a summary identifying the repository state at REV using one or
3274 two parent hash identifiers, followed by a "+" if the working
3274 two parent hash identifiers, followed by a "+" if the working
3275 directory has uncommitted changes, the branch name (if not default),
3275 directory has uncommitted changes, the branch name (if not default),
3276 a list of tags, and a list of bookmarks.
3276 a list of tags, and a list of bookmarks.
3277
3277
3278 When REV is not given, print a summary of the current state of the
3278 When REV is not given, print a summary of the current state of the
3279 repository.
3279 repository.
3280
3280
3281 Specifying a path to a repository root or Mercurial bundle will
3281 Specifying a path to a repository root or Mercurial bundle will
3282 cause lookup to operate on that repository/bundle.
3282 cause lookup to operate on that repository/bundle.
3283
3283
3284 .. container:: verbose
3284 .. container:: verbose
3285
3285
3286 Examples:
3286 Examples:
3287
3287
3288 - generate a build identifier for the working directory::
3288 - generate a build identifier for the working directory::
3289
3289
3290 hg id --id > build-id.dat
3290 hg id --id > build-id.dat
3291
3291
3292 - find the revision corresponding to a tag::
3292 - find the revision corresponding to a tag::
3293
3293
3294 hg id -n -r 1.3
3294 hg id -n -r 1.3
3295
3295
3296 - check the most recent revision of a remote repository::
3296 - check the most recent revision of a remote repository::
3297
3297
3298 hg id -r tip http://selenic.com/hg/
3298 hg id -r tip http://selenic.com/hg/
3299
3299
3300 Returns 0 if successful.
3300 Returns 0 if successful.
3301 """
3301 """
3302
3302
3303 if not repo and not source:
3303 if not repo and not source:
3304 raise util.Abort(_("there is no Mercurial repository here "
3304 raise util.Abort(_("there is no Mercurial repository here "
3305 "(.hg not found)"))
3305 "(.hg not found)"))
3306
3306
3307 hexfunc = ui.debugflag and hex or short
3307 hexfunc = ui.debugflag and hex or short
3308 default = not (num or id or branch or tags or bookmarks)
3308 default = not (num or id or branch or tags or bookmarks)
3309 output = []
3309 output = []
3310 revs = []
3310 revs = []
3311
3311
3312 if source:
3312 if source:
3313 source, branches = hg.parseurl(ui.expandpath(source))
3313 source, branches = hg.parseurl(ui.expandpath(source))
3314 repo = hg.peer(ui, opts, source)
3314 repo = hg.peer(ui, opts, source)
3315 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3315 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3316
3316
3317 if not repo.local():
3317 if not repo.local():
3318 if num or branch or tags:
3318 if num or branch or tags:
3319 raise util.Abort(
3319 raise util.Abort(
3320 _("can't query remote revision number, branch, or tags"))
3320 _("can't query remote revision number, branch, or tags"))
3321 if not rev and revs:
3321 if not rev and revs:
3322 rev = revs[0]
3322 rev = revs[0]
3323 if not rev:
3323 if not rev:
3324 rev = "tip"
3324 rev = "tip"
3325
3325
3326 remoterev = repo.lookup(rev)
3326 remoterev = repo.lookup(rev)
3327 if default or id:
3327 if default or id:
3328 output = [hexfunc(remoterev)]
3328 output = [hexfunc(remoterev)]
3329
3329
3330 def getbms():
3330 def getbms():
3331 bms = []
3331 bms = []
3332
3332
3333 if 'bookmarks' in repo.listkeys('namespaces'):
3333 if 'bookmarks' in repo.listkeys('namespaces'):
3334 hexremoterev = hex(remoterev)
3334 hexremoterev = hex(remoterev)
3335 bms = [bm for bm, bmr in repo.listkeys('bookmarks').iteritems()
3335 bms = [bm for bm, bmr in repo.listkeys('bookmarks').iteritems()
3336 if bmr == hexremoterev]
3336 if bmr == hexremoterev]
3337
3337
3338 return bms
3338 return bms
3339
3339
3340 if bookmarks:
3340 if bookmarks:
3341 output.extend(getbms())
3341 output.extend(getbms())
3342 elif default and not ui.quiet:
3342 elif default and not ui.quiet:
3343 # multiple bookmarks for a single parent separated by '/'
3343 # multiple bookmarks for a single parent separated by '/'
3344 bm = '/'.join(getbms())
3344 bm = '/'.join(getbms())
3345 if bm:
3345 if bm:
3346 output.append(bm)
3346 output.append(bm)
3347 else:
3347 else:
3348 if not rev:
3348 if not rev:
3349 ctx = repo[None]
3349 ctx = repo[None]
3350 parents = ctx.parents()
3350 parents = ctx.parents()
3351 changed = ""
3351 changed = ""
3352 if default or id or num:
3352 if default or id or num:
3353 changed = util.any(repo.status()) and "+" or ""
3353 changed = util.any(repo.status()) and "+" or ""
3354 if default or id:
3354 if default or id:
3355 output = ["%s%s" %
3355 output = ["%s%s" %
3356 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
3356 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
3357 if num:
3357 if num:
3358 output.append("%s%s" %
3358 output.append("%s%s" %
3359 ('+'.join([str(p.rev()) for p in parents]), changed))
3359 ('+'.join([str(p.rev()) for p in parents]), changed))
3360 else:
3360 else:
3361 ctx = scmutil.revsingle(repo, rev)
3361 ctx = scmutil.revsingle(repo, rev)
3362 if default or id:
3362 if default or id:
3363 output = [hexfunc(ctx.node())]
3363 output = [hexfunc(ctx.node())]
3364 if num:
3364 if num:
3365 output.append(str(ctx.rev()))
3365 output.append(str(ctx.rev()))
3366
3366
3367 if default and not ui.quiet:
3367 if default and not ui.quiet:
3368 b = ctx.branch()
3368 b = ctx.branch()
3369 if b != 'default':
3369 if b != 'default':
3370 output.append("(%s)" % b)
3370 output.append("(%s)" % b)
3371
3371
3372 # multiple tags for a single parent separated by '/'
3372 # multiple tags for a single parent separated by '/'
3373 t = '/'.join(ctx.tags())
3373 t = '/'.join(ctx.tags())
3374 if t:
3374 if t:
3375 output.append(t)
3375 output.append(t)
3376
3376
3377 # multiple bookmarks for a single parent separated by '/'
3377 # multiple bookmarks for a single parent separated by '/'
3378 bm = '/'.join(ctx.bookmarks())
3378 bm = '/'.join(ctx.bookmarks())
3379 if bm:
3379 if bm:
3380 output.append(bm)
3380 output.append(bm)
3381 else:
3381 else:
3382 if branch:
3382 if branch:
3383 output.append(ctx.branch())
3383 output.append(ctx.branch())
3384
3384
3385 if tags:
3385 if tags:
3386 output.extend(ctx.tags())
3386 output.extend(ctx.tags())
3387
3387
3388 if bookmarks:
3388 if bookmarks:
3389 output.extend(ctx.bookmarks())
3389 output.extend(ctx.bookmarks())
3390
3390
3391 ui.write("%s\n" % ' '.join(output))
3391 ui.write("%s\n" % ' '.join(output))
3392
3392
3393 @command('import|patch',
3393 @command('import|patch',
3394 [('p', 'strip', 1,
3394 [('p', 'strip', 1,
3395 _('directory strip option for patch. This has the same '
3395 _('directory strip option for patch. This has the same '
3396 'meaning as the corresponding patch option'), _('NUM')),
3396 'meaning as the corresponding patch option'), _('NUM')),
3397 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3397 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3398 ('e', 'edit', False, _('invoke editor on commit messages')),
3398 ('e', 'edit', False, _('invoke editor on commit messages')),
3399 ('f', 'force', None, _('skip check for outstanding uncommitted changes')),
3399 ('f', 'force', None, _('skip check for outstanding uncommitted changes')),
3400 ('', 'no-commit', None,
3400 ('', 'no-commit', None,
3401 _("don't commit, just update the working directory")),
3401 _("don't commit, just update the working directory")),
3402 ('', 'bypass', None,
3402 ('', 'bypass', None,
3403 _("apply patch without touching the working directory")),
3403 _("apply patch without touching the working directory")),
3404 ('', 'exact', None,
3404 ('', 'exact', None,
3405 _('apply patch to the nodes from which it was generated')),
3405 _('apply patch to the nodes from which it was generated')),
3406 ('', 'import-branch', None,
3406 ('', 'import-branch', None,
3407 _('use any branch information in patch (implied by --exact)'))] +
3407 _('use any branch information in patch (implied by --exact)'))] +
3408 commitopts + commitopts2 + similarityopts,
3408 commitopts + commitopts2 + similarityopts,
3409 _('[OPTION]... PATCH...'))
3409 _('[OPTION]... PATCH...'))
3410 def import_(ui, repo, patch1=None, *patches, **opts):
3410 def import_(ui, repo, patch1=None, *patches, **opts):
3411 """import an ordered set of patches
3411 """import an ordered set of patches
3412
3412
3413 Import a list of patches and commit them individually (unless
3413 Import a list of patches and commit them individually (unless
3414 --no-commit is specified).
3414 --no-commit is specified).
3415
3415
3416 If there are outstanding changes in the working directory, import
3416 If there are outstanding changes in the working directory, import
3417 will abort unless given the -f/--force flag.
3417 will abort unless given the -f/--force flag.
3418
3418
3419 You can import a patch straight from a mail message. Even patches
3419 You can import a patch straight from a mail message. Even patches
3420 as attachments work (to use the body part, it must have type
3420 as attachments work (to use the body part, it must have type
3421 text/plain or text/x-patch). From and Subject headers of email
3421 text/plain or text/x-patch). From and Subject headers of email
3422 message are used as default committer and commit message. All
3422 message are used as default committer and commit message. All
3423 text/plain body parts before first diff are added to commit
3423 text/plain body parts before first diff are added to commit
3424 message.
3424 message.
3425
3425
3426 If the imported patch was generated by :hg:`export`, user and
3426 If the imported patch was generated by :hg:`export`, user and
3427 description from patch override values from message headers and
3427 description from patch override values from message headers and
3428 body. Values given on command line with -m/--message and -u/--user
3428 body. Values given on command line with -m/--message and -u/--user
3429 override these.
3429 override these.
3430
3430
3431 If --exact is specified, import will set the working directory to
3431 If --exact is specified, import will set the working directory to
3432 the parent of each patch before applying it, and will abort if the
3432 the parent of each patch before applying it, and will abort if the
3433 resulting changeset has a different ID than the one recorded in
3433 resulting changeset has a different ID than the one recorded in
3434 the patch. This may happen due to character set problems or other
3434 the patch. This may happen due to character set problems or other
3435 deficiencies in the text patch format.
3435 deficiencies in the text patch format.
3436
3436
3437 Use --bypass to apply and commit patches directly to the
3437 Use --bypass to apply and commit patches directly to the
3438 repository, not touching the working directory. Without --exact,
3438 repository, not touching the working directory. Without --exact,
3439 patches will be applied on top of the working directory parent
3439 patches will be applied on top of the working directory parent
3440 revision.
3440 revision.
3441
3441
3442 With -s/--similarity, hg will attempt to discover renames and
3442 With -s/--similarity, hg will attempt to discover renames and
3443 copies in the patch in the same way as 'addremove'.
3443 copies in the patch in the same way as 'addremove'.
3444
3444
3445 To read a patch from standard input, use "-" as the patch name. If
3445 To read a patch from standard input, use "-" as the patch name. If
3446 a URL is specified, the patch will be downloaded from it.
3446 a URL is specified, the patch will be downloaded from it.
3447 See :hg:`help dates` for a list of formats valid for -d/--date.
3447 See :hg:`help dates` for a list of formats valid for -d/--date.
3448
3448
3449 .. container:: verbose
3449 .. container:: verbose
3450
3450
3451 Examples:
3451 Examples:
3452
3452
3453 - import a traditional patch from a website and detect renames::
3453 - import a traditional patch from a website and detect renames::
3454
3454
3455 hg import -s 80 http://example.com/bugfix.patch
3455 hg import -s 80 http://example.com/bugfix.patch
3456
3456
3457 - import a changeset from an hgweb server::
3457 - import a changeset from an hgweb server::
3458
3458
3459 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
3459 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
3460
3460
3461 - import all the patches in an Unix-style mbox::
3461 - import all the patches in an Unix-style mbox::
3462
3462
3463 hg import incoming-patches.mbox
3463 hg import incoming-patches.mbox
3464
3464
3465 - attempt to exactly restore an exported changeset (not always
3465 - attempt to exactly restore an exported changeset (not always
3466 possible)::
3466 possible)::
3467
3467
3468 hg import --exact proposed-fix.patch
3468 hg import --exact proposed-fix.patch
3469
3469
3470 Returns 0 on success.
3470 Returns 0 on success.
3471 """
3471 """
3472
3472
3473 if not patch1:
3473 if not patch1:
3474 raise util.Abort(_('need at least one patch to import'))
3474 raise util.Abort(_('need at least one patch to import'))
3475
3475
3476 patches = (patch1,) + patches
3476 patches = (patch1,) + patches
3477
3477
3478 date = opts.get('date')
3478 date = opts.get('date')
3479 if date:
3479 if date:
3480 opts['date'] = util.parsedate(date)
3480 opts['date'] = util.parsedate(date)
3481
3481
3482 editor = cmdutil.commiteditor
3482 editor = cmdutil.commiteditor
3483 if opts.get('edit'):
3483 if opts.get('edit'):
3484 editor = cmdutil.commitforceeditor
3484 editor = cmdutil.commitforceeditor
3485
3485
3486 update = not opts.get('bypass')
3486 update = not opts.get('bypass')
3487 if not update and opts.get('no_commit'):
3487 if not update and opts.get('no_commit'):
3488 raise util.Abort(_('cannot use --no-commit with --bypass'))
3488 raise util.Abort(_('cannot use --no-commit with --bypass'))
3489 try:
3489 try:
3490 sim = float(opts.get('similarity') or 0)
3490 sim = float(opts.get('similarity') or 0)
3491 except ValueError:
3491 except ValueError:
3492 raise util.Abort(_('similarity must be a number'))
3492 raise util.Abort(_('similarity must be a number'))
3493 if sim < 0 or sim > 100:
3493 if sim < 0 or sim > 100:
3494 raise util.Abort(_('similarity must be between 0 and 100'))
3494 raise util.Abort(_('similarity must be between 0 and 100'))
3495 if sim and not update:
3495 if sim and not update:
3496 raise util.Abort(_('cannot use --similarity with --bypass'))
3496 raise util.Abort(_('cannot use --similarity with --bypass'))
3497
3497
3498 if (opts.get('exact') or not opts.get('force')) and update:
3498 if (opts.get('exact') or not opts.get('force')) and update:
3499 cmdutil.bailifchanged(repo)
3499 cmdutil.bailifchanged(repo)
3500
3500
3501 base = opts["base"]
3501 base = opts["base"]
3502 strip = opts["strip"]
3502 strip = opts["strip"]
3503 wlock = lock = tr = None
3503 wlock = lock = tr = None
3504 msgs = []
3504 msgs = []
3505
3505
3506 def checkexact(repo, n, nodeid):
3506 def checkexact(repo, n, nodeid):
3507 if opts.get('exact') and hex(n) != nodeid:
3507 if opts.get('exact') and hex(n) != nodeid:
3508 repo.rollback()
3508 repo.rollback()
3509 raise util.Abort(_('patch is damaged or loses information'))
3509 raise util.Abort(_('patch is damaged or loses information'))
3510
3510
3511 def tryone(ui, hunk, parents):
3511 def tryone(ui, hunk, parents):
3512 tmpname, message, user, date, branch, nodeid, p1, p2 = \
3512 tmpname, message, user, date, branch, nodeid, p1, p2 = \
3513 patch.extract(ui, hunk)
3513 patch.extract(ui, hunk)
3514
3514
3515 if not tmpname:
3515 if not tmpname:
3516 return (None, None)
3516 return (None, None)
3517 msg = _('applied to working directory')
3517 msg = _('applied to working directory')
3518
3518
3519 try:
3519 try:
3520 cmdline_message = cmdutil.logmessage(ui, opts)
3520 cmdline_message = cmdutil.logmessage(ui, opts)
3521 if cmdline_message:
3521 if cmdline_message:
3522 # pickup the cmdline msg
3522 # pickup the cmdline msg
3523 message = cmdline_message
3523 message = cmdline_message
3524 elif message:
3524 elif message:
3525 # pickup the patch msg
3525 # pickup the patch msg
3526 message = message.strip()
3526 message = message.strip()
3527 else:
3527 else:
3528 # launch the editor
3528 # launch the editor
3529 message = None
3529 message = None
3530 ui.debug('message:\n%s\n' % message)
3530 ui.debug('message:\n%s\n' % message)
3531
3531
3532 if len(parents) == 1:
3532 if len(parents) == 1:
3533 parents.append(repo[nullid])
3533 parents.append(repo[nullid])
3534 if opts.get('exact'):
3534 if opts.get('exact'):
3535 if not nodeid or not p1:
3535 if not nodeid or not p1:
3536 raise util.Abort(_('not a Mercurial patch'))
3536 raise util.Abort(_('not a Mercurial patch'))
3537 p1 = repo[p1]
3537 p1 = repo[p1]
3538 p2 = repo[p2 or nullid]
3538 p2 = repo[p2 or nullid]
3539 elif p2:
3539 elif p2:
3540 try:
3540 try:
3541 p1 = repo[p1]
3541 p1 = repo[p1]
3542 p2 = repo[p2]
3542 p2 = repo[p2]
3543 # Without any options, consider p2 only if the
3543 # Without any options, consider p2 only if the
3544 # patch is being applied on top of the recorded
3544 # patch is being applied on top of the recorded
3545 # first parent.
3545 # first parent.
3546 if p1 != parents[0]:
3546 if p1 != parents[0]:
3547 p1 = parents[0]
3547 p1 = parents[0]
3548 p2 = repo[nullid]
3548 p2 = repo[nullid]
3549 except error.RepoError:
3549 except error.RepoError:
3550 p1, p2 = parents
3550 p1, p2 = parents
3551 else:
3551 else:
3552 p1, p2 = parents
3552 p1, p2 = parents
3553
3553
3554 n = None
3554 n = None
3555 if update:
3555 if update:
3556 if p1 != parents[0]:
3556 if p1 != parents[0]:
3557 hg.clean(repo, p1.node())
3557 hg.clean(repo, p1.node())
3558 if p2 != parents[1]:
3558 if p2 != parents[1]:
3559 repo.dirstate.setparents(p1.node(), p2.node())
3559 repo.dirstate.setparents(p1.node(), p2.node())
3560
3560
3561 if opts.get('exact') or opts.get('import_branch'):
3561 if opts.get('exact') or opts.get('import_branch'):
3562 repo.dirstate.setbranch(branch or 'default')
3562 repo.dirstate.setbranch(branch or 'default')
3563
3563
3564 files = set()
3564 files = set()
3565 patch.patch(ui, repo, tmpname, strip=strip, files=files,
3565 patch.patch(ui, repo, tmpname, strip=strip, files=files,
3566 eolmode=None, similarity=sim / 100.0)
3566 eolmode=None, similarity=sim / 100.0)
3567 files = list(files)
3567 files = list(files)
3568 if opts.get('no_commit'):
3568 if opts.get('no_commit'):
3569 if message:
3569 if message:
3570 msgs.append(message)
3570 msgs.append(message)
3571 else:
3571 else:
3572 if opts.get('exact') or p2:
3572 if opts.get('exact') or p2:
3573 # If you got here, you either use --force and know what
3573 # If you got here, you either use --force and know what
3574 # you are doing or used --exact or a merge patch while
3574 # you are doing or used --exact or a merge patch while
3575 # being updated to its first parent.
3575 # being updated to its first parent.
3576 m = None
3576 m = None
3577 else:
3577 else:
3578 m = scmutil.matchfiles(repo, files or [])
3578 m = scmutil.matchfiles(repo, files or [])
3579 n = repo.commit(message, opts.get('user') or user,
3579 n = repo.commit(message, opts.get('user') or user,
3580 opts.get('date') or date, match=m,
3580 opts.get('date') or date, match=m,
3581 editor=editor)
3581 editor=editor)
3582 checkexact(repo, n, nodeid)
3582 checkexact(repo, n, nodeid)
3583 else:
3583 else:
3584 if opts.get('exact') or opts.get('import_branch'):
3584 if opts.get('exact') or opts.get('import_branch'):
3585 branch = branch or 'default'
3585 branch = branch or 'default'
3586 else:
3586 else:
3587 branch = p1.branch()
3587 branch = p1.branch()
3588 store = patch.filestore()
3588 store = patch.filestore()
3589 try:
3589 try:
3590 files = set()
3590 files = set()
3591 try:
3591 try:
3592 patch.patchrepo(ui, repo, p1, store, tmpname, strip,
3592 patch.patchrepo(ui, repo, p1, store, tmpname, strip,
3593 files, eolmode=None)
3593 files, eolmode=None)
3594 except patch.PatchError, e:
3594 except patch.PatchError, e:
3595 raise util.Abort(str(e))
3595 raise util.Abort(str(e))
3596 memctx = patch.makememctx(repo, (p1.node(), p2.node()),
3596 memctx = patch.makememctx(repo, (p1.node(), p2.node()),
3597 message,
3597 message,
3598 opts.get('user') or user,
3598 opts.get('user') or user,
3599 opts.get('date') or date,
3599 opts.get('date') or date,
3600 branch, files, store,
3600 branch, files, store,
3601 editor=cmdutil.commiteditor)
3601 editor=cmdutil.commiteditor)
3602 repo.savecommitmessage(memctx.description())
3602 repo.savecommitmessage(memctx.description())
3603 n = memctx.commit()
3603 n = memctx.commit()
3604 checkexact(repo, n, nodeid)
3604 checkexact(repo, n, nodeid)
3605 finally:
3605 finally:
3606 store.close()
3606 store.close()
3607 if n:
3607 if n:
3608 # i18n: refers to a short changeset id
3608 # i18n: refers to a short changeset id
3609 msg = _('created %s') % short(n)
3609 msg = _('created %s') % short(n)
3610 return (msg, n)
3610 return (msg, n)
3611 finally:
3611 finally:
3612 os.unlink(tmpname)
3612 os.unlink(tmpname)
3613
3613
3614 try:
3614 try:
3615 try:
3615 try:
3616 wlock = repo.wlock()
3616 wlock = repo.wlock()
3617 lock = repo.lock()
3617 lock = repo.lock()
3618 tr = repo.transaction('import')
3618 tr = repo.transaction('import')
3619 parents = repo.parents()
3619 parents = repo.parents()
3620 for patchurl in patches:
3620 for patchurl in patches:
3621 if patchurl == '-':
3621 if patchurl == '-':
3622 ui.status(_('applying patch from stdin\n'))
3622 ui.status(_('applying patch from stdin\n'))
3623 patchfile = ui.fin
3623 patchfile = ui.fin
3624 patchurl = 'stdin' # for error message
3624 patchurl = 'stdin' # for error message
3625 else:
3625 else:
3626 patchurl = os.path.join(base, patchurl)
3626 patchurl = os.path.join(base, patchurl)
3627 ui.status(_('applying %s\n') % patchurl)
3627 ui.status(_('applying %s\n') % patchurl)
3628 patchfile = url.open(ui, patchurl)
3628 patchfile = url.open(ui, patchurl)
3629
3629
3630 haspatch = False
3630 haspatch = False
3631 for hunk in patch.split(patchfile):
3631 for hunk in patch.split(patchfile):
3632 (msg, node) = tryone(ui, hunk, parents)
3632 (msg, node) = tryone(ui, hunk, parents)
3633 if msg:
3633 if msg:
3634 haspatch = True
3634 haspatch = True
3635 ui.note(msg + '\n')
3635 ui.note(msg + '\n')
3636 if update or opts.get('exact'):
3636 if update or opts.get('exact'):
3637 parents = repo.parents()
3637 parents = repo.parents()
3638 else:
3638 else:
3639 parents = [repo[node]]
3639 parents = [repo[node]]
3640
3640
3641 if not haspatch:
3641 if not haspatch:
3642 raise util.Abort(_('%s: no diffs found') % patchurl)
3642 raise util.Abort(_('%s: no diffs found') % patchurl)
3643
3643
3644 tr.close()
3644 tr.close()
3645 if msgs:
3645 if msgs:
3646 repo.savecommitmessage('\n* * *\n'.join(msgs))
3646 repo.savecommitmessage('\n* * *\n'.join(msgs))
3647 except:
3647 except:
3648 # wlock.release() indirectly calls dirstate.write(): since
3648 # wlock.release() indirectly calls dirstate.write(): since
3649 # we're crashing, we do not want to change the working dir
3649 # we're crashing, we do not want to change the working dir
3650 # parent after all, so make sure it writes nothing
3650 # parent after all, so make sure it writes nothing
3651 repo.dirstate.invalidate()
3651 repo.dirstate.invalidate()
3652 raise
3652 raise
3653 finally:
3653 finally:
3654 if tr:
3654 if tr:
3655 tr.release()
3655 tr.release()
3656 release(lock, wlock)
3656 release(lock, wlock)
3657
3657
3658 @command('incoming|in',
3658 @command('incoming|in',
3659 [('f', 'force', None,
3659 [('f', 'force', None,
3660 _('run even if remote repository is unrelated')),
3660 _('run even if remote repository is unrelated')),
3661 ('n', 'newest-first', None, _('show newest record first')),
3661 ('n', 'newest-first', None, _('show newest record first')),
3662 ('', 'bundle', '',
3662 ('', 'bundle', '',
3663 _('file to store the bundles into'), _('FILE')),
3663 _('file to store the bundles into'), _('FILE')),
3664 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3664 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3665 ('B', 'bookmarks', False, _("compare bookmarks")),
3665 ('B', 'bookmarks', False, _("compare bookmarks")),
3666 ('b', 'branch', [],
3666 ('b', 'branch', [],
3667 _('a specific branch you would like to pull'), _('BRANCH')),
3667 _('a specific branch you would like to pull'), _('BRANCH')),
3668 ] + logopts + remoteopts + subrepoopts,
3668 ] + logopts + remoteopts + subrepoopts,
3669 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3669 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3670 def incoming(ui, repo, source="default", **opts):
3670 def incoming(ui, repo, source="default", **opts):
3671 """show new changesets found in source
3671 """show new changesets found in source
3672
3672
3673 Show new changesets found in the specified path/URL or the default
3673 Show new changesets found in the specified path/URL or the default
3674 pull location. These are the changesets that would have been pulled
3674 pull location. These are the changesets that would have been pulled
3675 if a pull at the time you issued this command.
3675 if a pull at the time you issued this command.
3676
3676
3677 For remote repository, using --bundle avoids downloading the
3677 For remote repository, using --bundle avoids downloading the
3678 changesets twice if the incoming is followed by a pull.
3678 changesets twice if the incoming is followed by a pull.
3679
3679
3680 See pull for valid source format details.
3680 See pull for valid source format details.
3681
3681
3682 Returns 0 if there are incoming changes, 1 otherwise.
3682 Returns 0 if there are incoming changes, 1 otherwise.
3683 """
3683 """
3684 if opts.get('bundle') and opts.get('subrepos'):
3684 if opts.get('bundle') and opts.get('subrepos'):
3685 raise util.Abort(_('cannot combine --bundle and --subrepos'))
3685 raise util.Abort(_('cannot combine --bundle and --subrepos'))
3686
3686
3687 if opts.get('bookmarks'):
3687 if opts.get('bookmarks'):
3688 source, branches = hg.parseurl(ui.expandpath(source),
3688 source, branches = hg.parseurl(ui.expandpath(source),
3689 opts.get('branch'))
3689 opts.get('branch'))
3690 other = hg.peer(repo, opts, source)
3690 other = hg.peer(repo, opts, source)
3691 if 'bookmarks' not in other.listkeys('namespaces'):
3691 if 'bookmarks' not in other.listkeys('namespaces'):
3692 ui.warn(_("remote doesn't support bookmarks\n"))
3692 ui.warn(_("remote doesn't support bookmarks\n"))
3693 return 0
3693 return 0
3694 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3694 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3695 return bookmarks.diff(ui, repo, other)
3695 return bookmarks.diff(ui, repo, other)
3696
3696
3697 repo._subtoppath = ui.expandpath(source)
3697 repo._subtoppath = ui.expandpath(source)
3698 try:
3698 try:
3699 return hg.incoming(ui, repo, source, opts)
3699 return hg.incoming(ui, repo, source, opts)
3700 finally:
3700 finally:
3701 del repo._subtoppath
3701 del repo._subtoppath
3702
3702
3703
3703
3704 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'))
3704 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'))
3705 def init(ui, dest=".", **opts):
3705 def init(ui, dest=".", **opts):
3706 """create a new repository in the given directory
3706 """create a new repository in the given directory
3707
3707
3708 Initialize a new repository in the given directory. If the given
3708 Initialize a new repository in the given directory. If the given
3709 directory does not exist, it will be created.
3709 directory does not exist, it will be created.
3710
3710
3711 If no directory is given, the current directory is used.
3711 If no directory is given, the current directory is used.
3712
3712
3713 It is possible to specify an ``ssh://`` URL as the destination.
3713 It is possible to specify an ``ssh://`` URL as the destination.
3714 See :hg:`help urls` for more information.
3714 See :hg:`help urls` for more information.
3715
3715
3716 Returns 0 on success.
3716 Returns 0 on success.
3717 """
3717 """
3718 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3718 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3719
3719
3720 @command('locate',
3720 @command('locate',
3721 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3721 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3722 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3722 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3723 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3723 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3724 ] + walkopts,
3724 ] + walkopts,
3725 _('[OPTION]... [PATTERN]...'))
3725 _('[OPTION]... [PATTERN]...'))
3726 def locate(ui, repo, *pats, **opts):
3726 def locate(ui, repo, *pats, **opts):
3727 """locate files matching specific patterns
3727 """locate files matching specific patterns
3728
3728
3729 Print files under Mercurial control in the working directory whose
3729 Print files under Mercurial control in the working directory whose
3730 names match the given patterns.
3730 names match the given patterns.
3731
3731
3732 By default, this command searches all directories in the working
3732 By default, this command searches all directories in the working
3733 directory. To search just the current directory and its
3733 directory. To search just the current directory and its
3734 subdirectories, use "--include .".
3734 subdirectories, use "--include .".
3735
3735
3736 If no patterns are given to match, this command prints the names
3736 If no patterns are given to match, this command prints the names
3737 of all files under Mercurial control in the working directory.
3737 of all files under Mercurial control in the working directory.
3738
3738
3739 If you want to feed the output of this command into the "xargs"
3739 If you want to feed the output of this command into the "xargs"
3740 command, use the -0 option to both this command and "xargs". This
3740 command, use the -0 option to both this command and "xargs". This
3741 will avoid the problem of "xargs" treating single filenames that
3741 will avoid the problem of "xargs" treating single filenames that
3742 contain whitespace as multiple filenames.
3742 contain whitespace as multiple filenames.
3743
3743
3744 Returns 0 if a match is found, 1 otherwise.
3744 Returns 0 if a match is found, 1 otherwise.
3745 """
3745 """
3746 end = opts.get('print0') and '\0' or '\n'
3746 end = opts.get('print0') and '\0' or '\n'
3747 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3747 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3748
3748
3749 ret = 1
3749 ret = 1
3750 m = scmutil.match(repo[rev], pats, opts, default='relglob')
3750 m = scmutil.match(repo[rev], pats, opts, default='relglob')
3751 m.bad = lambda x, y: False
3751 m.bad = lambda x, y: False
3752 for abs in repo[rev].walk(m):
3752 for abs in repo[rev].walk(m):
3753 if not rev and abs not in repo.dirstate:
3753 if not rev and abs not in repo.dirstate:
3754 continue
3754 continue
3755 if opts.get('fullpath'):
3755 if opts.get('fullpath'):
3756 ui.write(repo.wjoin(abs), end)
3756 ui.write(repo.wjoin(abs), end)
3757 else:
3757 else:
3758 ui.write(((pats and m.rel(abs)) or abs), end)
3758 ui.write(((pats and m.rel(abs)) or abs), end)
3759 ret = 0
3759 ret = 0
3760
3760
3761 return ret
3761 return ret
3762
3762
3763 @command('^log|history',
3763 @command('^log|history',
3764 [('f', 'follow', None,
3764 [('f', 'follow', None,
3765 _('follow changeset history, or file history across copies and renames')),
3765 _('follow changeset history, or file history across copies and renames')),
3766 ('', 'follow-first', None,
3766 ('', 'follow-first', None,
3767 _('only follow the first parent of merge changesets (DEPRECATED)')),
3767 _('only follow the first parent of merge changesets (DEPRECATED)')),
3768 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3768 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3769 ('C', 'copies', None, _('show copied files')),
3769 ('C', 'copies', None, _('show copied files')),
3770 ('k', 'keyword', [],
3770 ('k', 'keyword', [],
3771 _('do case-insensitive search for a given text'), _('TEXT')),
3771 _('do case-insensitive search for a given text'), _('TEXT')),
3772 ('r', 'rev', [], _('show the specified revision or range'), _('REV')),
3772 ('r', 'rev', [], _('show the specified revision or range'), _('REV')),
3773 ('', 'removed', None, _('include revisions where files were removed')),
3773 ('', 'removed', None, _('include revisions where files were removed')),
3774 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3774 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3775 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3775 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3776 ('', 'only-branch', [],
3776 ('', 'only-branch', [],
3777 _('show only changesets within the given named branch (DEPRECATED)'),
3777 _('show only changesets within the given named branch (DEPRECATED)'),
3778 _('BRANCH')),
3778 _('BRANCH')),
3779 ('b', 'branch', [],
3779 ('b', 'branch', [],
3780 _('show changesets within the given named branch'), _('BRANCH')),
3780 _('show changesets within the given named branch'), _('BRANCH')),
3781 ('P', 'prune', [],
3781 ('P', 'prune', [],
3782 _('do not display revision or any of its ancestors'), _('REV')),
3782 _('do not display revision or any of its ancestors'), _('REV')),
3783 ('', 'hidden', False, _('show hidden changesets (DEPRECATED)')),
3783 ('', 'hidden', False, _('show hidden changesets (DEPRECATED)')),
3784 ] + logopts + walkopts,
3784 ] + logopts + walkopts,
3785 _('[OPTION]... [FILE]'))
3785 _('[OPTION]... [FILE]'))
3786 def log(ui, repo, *pats, **opts):
3786 def log(ui, repo, *pats, **opts):
3787 """show revision history of entire repository or files
3787 """show revision history of entire repository or files
3788
3788
3789 Print the revision history of the specified files or the entire
3789 Print the revision history of the specified files or the entire
3790 project.
3790 project.
3791
3791
3792 If no revision range is specified, the default is ``tip:0`` unless
3792 If no revision range is specified, the default is ``tip:0`` unless
3793 --follow is set, in which case the working directory parent is
3793 --follow is set, in which case the working directory parent is
3794 used as the starting revision.
3794 used as the starting revision.
3795
3795
3796 File history is shown without following rename or copy history of
3796 File history is shown without following rename or copy history of
3797 files. Use -f/--follow with a filename to follow history across
3797 files. Use -f/--follow with a filename to follow history across
3798 renames and copies. --follow without a filename will only show
3798 renames and copies. --follow without a filename will only show
3799 ancestors or descendants of the starting revision.
3799 ancestors or descendants of the starting revision.
3800
3800
3801 By default this command prints revision number and changeset id,
3801 By default this command prints revision number and changeset id,
3802 tags, non-trivial parents, user, date and time, and a summary for
3802 tags, non-trivial parents, user, date and time, and a summary for
3803 each commit. When the -v/--verbose switch is used, the list of
3803 each commit. When the -v/--verbose switch is used, the list of
3804 changed files and full commit message are shown.
3804 changed files and full commit message are shown.
3805
3805
3806 .. note::
3806 .. note::
3807 log -p/--patch may generate unexpected diff output for merge
3807 log -p/--patch may generate unexpected diff output for merge
3808 changesets, as it will only compare the merge changeset against
3808 changesets, as it will only compare the merge changeset against
3809 its first parent. Also, only files different from BOTH parents
3809 its first parent. Also, only files different from BOTH parents
3810 will appear in files:.
3810 will appear in files:.
3811
3811
3812 .. note::
3812 .. note::
3813 for performance reasons, log FILE may omit duplicate changes
3813 for performance reasons, log FILE may omit duplicate changes
3814 made on branches and will not show deletions. To see all
3814 made on branches and will not show deletions. To see all
3815 changes including duplicates and deletions, use the --removed
3815 changes including duplicates and deletions, use the --removed
3816 switch.
3816 switch.
3817
3817
3818 .. container:: verbose
3818 .. container:: verbose
3819
3819
3820 Some examples:
3820 Some examples:
3821
3821
3822 - changesets with full descriptions and file lists::
3822 - changesets with full descriptions and file lists::
3823
3823
3824 hg log -v
3824 hg log -v
3825
3825
3826 - changesets ancestral to the working directory::
3826 - changesets ancestral to the working directory::
3827
3827
3828 hg log -f
3828 hg log -f
3829
3829
3830 - last 10 commits on the current branch::
3830 - last 10 commits on the current branch::
3831
3831
3832 hg log -l 10 -b .
3832 hg log -l 10 -b .
3833
3833
3834 - changesets showing all modifications of a file, including removals::
3834 - changesets showing all modifications of a file, including removals::
3835
3835
3836 hg log --removed file.c
3836 hg log --removed file.c
3837
3837
3838 - all changesets that touch a directory, with diffs, excluding merges::
3838 - all changesets that touch a directory, with diffs, excluding merges::
3839
3839
3840 hg log -Mp lib/
3840 hg log -Mp lib/
3841
3841
3842 - all revision numbers that match a keyword::
3842 - all revision numbers that match a keyword::
3843
3843
3844 hg log -k bug --template "{rev}\\n"
3844 hg log -k bug --template "{rev}\\n"
3845
3845
3846 - check if a given changeset is included is a tagged release::
3846 - check if a given changeset is included is a tagged release::
3847
3847
3848 hg log -r "a21ccf and ancestor(1.9)"
3848 hg log -r "a21ccf and ancestor(1.9)"
3849
3849
3850 - find all changesets by some user in a date range::
3850 - find all changesets by some user in a date range::
3851
3851
3852 hg log -k alice -d "may 2008 to jul 2008"
3852 hg log -k alice -d "may 2008 to jul 2008"
3853
3853
3854 - summary of all changesets after the last tag::
3854 - summary of all changesets after the last tag::
3855
3855
3856 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3856 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3857
3857
3858 See :hg:`help dates` for a list of formats valid for -d/--date.
3858 See :hg:`help dates` for a list of formats valid for -d/--date.
3859
3859
3860 See :hg:`help revisions` and :hg:`help revsets` for more about
3860 See :hg:`help revisions` and :hg:`help revsets` for more about
3861 specifying revisions.
3861 specifying revisions.
3862
3862
3863 Returns 0 on success.
3863 Returns 0 on success.
3864 """
3864 """
3865
3865
3866 matchfn = scmutil.match(repo[None], pats, opts)
3866 matchfn = scmutil.match(repo[None], pats, opts)
3867 limit = cmdutil.loglimit(opts)
3867 limit = cmdutil.loglimit(opts)
3868 count = 0
3868 count = 0
3869
3869
3870 endrev = None
3870 endrev = None
3871 if opts.get('copies') and opts.get('rev'):
3871 if opts.get('copies') and opts.get('rev'):
3872 endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1
3872 endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1
3873
3873
3874 df = False
3874 df = False
3875 if opts["date"]:
3875 if opts["date"]:
3876 df = util.matchdate(opts["date"])
3876 df = util.matchdate(opts["date"])
3877
3877
3878 branches = opts.get('branch', []) + opts.get('only_branch', [])
3878 branches = opts.get('branch', []) + opts.get('only_branch', [])
3879 opts['branch'] = [repo.lookupbranch(b) for b in branches]
3879 opts['branch'] = [repo.lookupbranch(b) for b in branches]
3880
3880
3881 displayer = cmdutil.show_changeset(ui, repo, opts, True)
3881 displayer = cmdutil.show_changeset(ui, repo, opts, True)
3882 def prep(ctx, fns):
3882 def prep(ctx, fns):
3883 rev = ctx.rev()
3883 rev = ctx.rev()
3884 parents = [p for p in repo.changelog.parentrevs(rev)
3884 parents = [p for p in repo.changelog.parentrevs(rev)
3885 if p != nullrev]
3885 if p != nullrev]
3886 if opts.get('no_merges') and len(parents) == 2:
3886 if opts.get('no_merges') and len(parents) == 2:
3887 return
3887 return
3888 if opts.get('only_merges') and len(parents) != 2:
3888 if opts.get('only_merges') and len(parents) != 2:
3889 return
3889 return
3890 if opts.get('branch') and ctx.branch() not in opts['branch']:
3890 if opts.get('branch') and ctx.branch() not in opts['branch']:
3891 return
3891 return
3892 if not opts.get('hidden') and ctx.hidden():
3892 if not opts.get('hidden') and ctx.hidden():
3893 return
3893 return
3894 if df and not df(ctx.date()[0]):
3894 if df and not df(ctx.date()[0]):
3895 return
3895 return
3896 if opts['user'] and not [k for k in opts['user']
3896 if opts['user'] and not [k for k in opts['user']
3897 if k.lower() in ctx.user().lower()]:
3897 if k.lower() in ctx.user().lower()]:
3898 return
3898 return
3899 if opts.get('keyword'):
3899 if opts.get('keyword'):
3900 for k in [kw.lower() for kw in opts['keyword']]:
3900 for k in [kw.lower() for kw in opts['keyword']]:
3901 if (k in ctx.user().lower() or
3901 if (k in ctx.user().lower() or
3902 k in ctx.description().lower() or
3902 k in ctx.description().lower() or
3903 k in " ".join(ctx.files()).lower()):
3903 k in " ".join(ctx.files()).lower()):
3904 break
3904 break
3905 else:
3905 else:
3906 return
3906 return
3907
3907
3908 copies = None
3908 copies = None
3909 if opts.get('copies') and rev:
3909 if opts.get('copies') and rev:
3910 copies = []
3910 copies = []
3911 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3911 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3912 for fn in ctx.files():
3912 for fn in ctx.files():
3913 rename = getrenamed(fn, rev)
3913 rename = getrenamed(fn, rev)
3914 if rename:
3914 if rename:
3915 copies.append((fn, rename[0]))
3915 copies.append((fn, rename[0]))
3916
3916
3917 revmatchfn = None
3917 revmatchfn = None
3918 if opts.get('patch') or opts.get('stat'):
3918 if opts.get('patch') or opts.get('stat'):
3919 if opts.get('follow') or opts.get('follow_first'):
3919 if opts.get('follow') or opts.get('follow_first'):
3920 # note: this might be wrong when following through merges
3920 # note: this might be wrong when following through merges
3921 revmatchfn = scmutil.match(repo[None], fns, default='path')
3921 revmatchfn = scmutil.match(repo[None], fns, default='path')
3922 else:
3922 else:
3923 revmatchfn = matchfn
3923 revmatchfn = matchfn
3924
3924
3925 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
3925 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
3926
3926
3927 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3927 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3928 if count == limit:
3928 if count == limit:
3929 break
3929 break
3930 if displayer.flush(ctx.rev()):
3930 if displayer.flush(ctx.rev()):
3931 count += 1
3931 count += 1
3932 displayer.close()
3932 displayer.close()
3933
3933
3934 @command('manifest',
3934 @command('manifest',
3935 [('r', 'rev', '', _('revision to display'), _('REV')),
3935 [('r', 'rev', '', _('revision to display'), _('REV')),
3936 ('', 'all', False, _("list files from all revisions"))],
3936 ('', 'all', False, _("list files from all revisions"))],
3937 _('[-r REV]'))
3937 _('[-r REV]'))
3938 def manifest(ui, repo, node=None, rev=None, **opts):
3938 def manifest(ui, repo, node=None, rev=None, **opts):
3939 """output the current or given revision of the project manifest
3939 """output the current or given revision of the project manifest
3940
3940
3941 Print a list of version controlled files for the given revision.
3941 Print a list of version controlled files for the given revision.
3942 If no revision is given, the first parent of the working directory
3942 If no revision is given, the first parent of the working directory
3943 is used, or the null revision if no revision is checked out.
3943 is used, or the null revision if no revision is checked out.
3944
3944
3945 With -v, print file permissions, symlink and executable bits.
3945 With -v, print file permissions, symlink and executable bits.
3946 With --debug, print file revision hashes.
3946 With --debug, print file revision hashes.
3947
3947
3948 If option --all is specified, the list of all files from all revisions
3948 If option --all is specified, the list of all files from all revisions
3949 is printed. This includes deleted and renamed files.
3949 is printed. This includes deleted and renamed files.
3950
3950
3951 Returns 0 on success.
3951 Returns 0 on success.
3952 """
3952 """
3953 if opts.get('all'):
3953 if opts.get('all'):
3954 if rev or node:
3954 if rev or node:
3955 raise util.Abort(_("can't specify a revision with --all"))
3955 raise util.Abort(_("can't specify a revision with --all"))
3956
3956
3957 res = []
3957 res = []
3958 prefix = "data/"
3958 prefix = "data/"
3959 suffix = ".i"
3959 suffix = ".i"
3960 plen = len(prefix)
3960 plen = len(prefix)
3961 slen = len(suffix)
3961 slen = len(suffix)
3962 lock = repo.lock()
3962 lock = repo.lock()
3963 try:
3963 try:
3964 for fn, b, size in repo.store.datafiles():
3964 for fn, b, size in repo.store.datafiles():
3965 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
3965 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
3966 res.append(fn[plen:-slen])
3966 res.append(fn[plen:-slen])
3967 finally:
3967 finally:
3968 lock.release()
3968 lock.release()
3969 for f in sorted(res):
3969 for f in sorted(res):
3970 ui.write("%s\n" % f)
3970 ui.write("%s\n" % f)
3971 return
3971 return
3972
3972
3973 if rev and node:
3973 if rev and node:
3974 raise util.Abort(_("please specify just one revision"))
3974 raise util.Abort(_("please specify just one revision"))
3975
3975
3976 if not node:
3976 if not node:
3977 node = rev
3977 node = rev
3978
3978
3979 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
3979 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
3980 ctx = scmutil.revsingle(repo, node)
3980 ctx = scmutil.revsingle(repo, node)
3981 for f in ctx:
3981 for f in ctx:
3982 if ui.debugflag:
3982 if ui.debugflag:
3983 ui.write("%40s " % hex(ctx.manifest()[f]))
3983 ui.write("%40s " % hex(ctx.manifest()[f]))
3984 if ui.verbose:
3984 if ui.verbose:
3985 ui.write(decor[ctx.flags(f)])
3985 ui.write(decor[ctx.flags(f)])
3986 ui.write("%s\n" % f)
3986 ui.write("%s\n" % f)
3987
3987
3988 @command('^merge',
3988 @command('^merge',
3989 [('f', 'force', None, _('force a merge with outstanding changes')),
3989 [('f', 'force', None, _('force a merge with outstanding changes')),
3990 ('r', 'rev', '', _('revision to merge'), _('REV')),
3990 ('r', 'rev', '', _('revision to merge'), _('REV')),
3991 ('P', 'preview', None,
3991 ('P', 'preview', None,
3992 _('review revisions to merge (no merge is performed)'))
3992 _('review revisions to merge (no merge is performed)'))
3993 ] + mergetoolopts,
3993 ] + mergetoolopts,
3994 _('[-P] [-f] [[-r] REV]'))
3994 _('[-P] [-f] [[-r] REV]'))
3995 def merge(ui, repo, node=None, **opts):
3995 def merge(ui, repo, node=None, **opts):
3996 """merge working directory with another revision
3996 """merge working directory with another revision
3997
3997
3998 The current working directory is updated with all changes made in
3998 The current working directory is updated with all changes made in
3999 the requested revision since the last common predecessor revision.
3999 the requested revision since the last common predecessor revision.
4000
4000
4001 Files that changed between either parent are marked as changed for
4001 Files that changed between either parent are marked as changed for
4002 the next commit and a commit must be performed before any further
4002 the next commit and a commit must be performed before any further
4003 updates to the repository are allowed. The next commit will have
4003 updates to the repository are allowed. The next commit will have
4004 two parents.
4004 two parents.
4005
4005
4006 ``--tool`` can be used to specify the merge tool used for file
4006 ``--tool`` can be used to specify the merge tool used for file
4007 merges. It overrides the HGMERGE environment variable and your
4007 merges. It overrides the HGMERGE environment variable and your
4008 configuration files. See :hg:`help merge-tools` for options.
4008 configuration files. See :hg:`help merge-tools` for options.
4009
4009
4010 If no revision is specified, the working directory's parent is a
4010 If no revision is specified, the working directory's parent is a
4011 head revision, and the current branch contains exactly one other
4011 head revision, and the current branch contains exactly one other
4012 head, the other head is merged with by default. Otherwise, an
4012 head, the other head is merged with by default. Otherwise, an
4013 explicit revision with which to merge with must be provided.
4013 explicit revision with which to merge with must be provided.
4014
4014
4015 :hg:`resolve` must be used to resolve unresolved files.
4015 :hg:`resolve` must be used to resolve unresolved files.
4016
4016
4017 To undo an uncommitted merge, use :hg:`update --clean .` which
4017 To undo an uncommitted merge, use :hg:`update --clean .` which
4018 will check out a clean copy of the original merge parent, losing
4018 will check out a clean copy of the original merge parent, losing
4019 all changes.
4019 all changes.
4020
4020
4021 Returns 0 on success, 1 if there are unresolved files.
4021 Returns 0 on success, 1 if there are unresolved files.
4022 """
4022 """
4023
4023
4024 if opts.get('rev') and node:
4024 if opts.get('rev') and node:
4025 raise util.Abort(_("please specify just one revision"))
4025 raise util.Abort(_("please specify just one revision"))
4026 if not node:
4026 if not node:
4027 node = opts.get('rev')
4027 node = opts.get('rev')
4028
4028
4029 if not node:
4029 if not node:
4030 branch = repo[None].branch()
4030 branch = repo[None].branch()
4031 bheads = repo.branchheads(branch)
4031 bheads = repo.branchheads(branch)
4032 if len(bheads) > 2:
4032 if len(bheads) > 2:
4033 raise util.Abort(_("branch '%s' has %d heads - "
4033 raise util.Abort(_("branch '%s' has %d heads - "
4034 "please merge with an explicit rev")
4034 "please merge with an explicit rev")
4035 % (branch, len(bheads)),
4035 % (branch, len(bheads)),
4036 hint=_("run 'hg heads .' to see heads"))
4036 hint=_("run 'hg heads .' to see heads"))
4037
4037
4038 parent = repo.dirstate.p1()
4038 parent = repo.dirstate.p1()
4039 if len(bheads) == 1:
4039 if len(bheads) == 1:
4040 if len(repo.heads()) > 1:
4040 if len(repo.heads()) > 1:
4041 raise util.Abort(_("branch '%s' has one head - "
4041 raise util.Abort(_("branch '%s' has one head - "
4042 "please merge with an explicit rev")
4042 "please merge with an explicit rev")
4043 % branch,
4043 % branch,
4044 hint=_("run 'hg heads' to see all heads"))
4044 hint=_("run 'hg heads' to see all heads"))
4045 msg = _('there is nothing to merge')
4045 msg = _('there is nothing to merge')
4046 if parent != repo.lookup(repo[None].branch()):
4046 if parent != repo.lookup(repo[None].branch()):
4047 msg = _('%s - use "hg update" instead') % msg
4047 msg = _('%s - use "hg update" instead') % msg
4048 raise util.Abort(msg)
4048 raise util.Abort(msg)
4049
4049
4050 if parent not in bheads:
4050 if parent not in bheads:
4051 raise util.Abort(_('working directory not at a head revision'),
4051 raise util.Abort(_('working directory not at a head revision'),
4052 hint=_("use 'hg update' or merge with an "
4052 hint=_("use 'hg update' or merge with an "
4053 "explicit revision"))
4053 "explicit revision"))
4054 node = parent == bheads[0] and bheads[-1] or bheads[0]
4054 node = parent == bheads[0] and bheads[-1] or bheads[0]
4055 else:
4055 else:
4056 node = scmutil.revsingle(repo, node).node()
4056 node = scmutil.revsingle(repo, node).node()
4057
4057
4058 if opts.get('preview'):
4058 if opts.get('preview'):
4059 # find nodes that are ancestors of p2 but not of p1
4059 # find nodes that are ancestors of p2 but not of p1
4060 p1 = repo.lookup('.')
4060 p1 = repo.lookup('.')
4061 p2 = repo.lookup(node)
4061 p2 = repo.lookup(node)
4062 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4062 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4063
4063
4064 displayer = cmdutil.show_changeset(ui, repo, opts)
4064 displayer = cmdutil.show_changeset(ui, repo, opts)
4065 for node in nodes:
4065 for node in nodes:
4066 displayer.show(repo[node])
4066 displayer.show(repo[node])
4067 displayer.close()
4067 displayer.close()
4068 return 0
4068 return 0
4069
4069
4070 try:
4070 try:
4071 # ui.forcemerge is an internal variable, do not document
4071 # ui.forcemerge is an internal variable, do not document
4072 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4072 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4073 return hg.merge(repo, node, force=opts.get('force'))
4073 return hg.merge(repo, node, force=opts.get('force'))
4074 finally:
4074 finally:
4075 ui.setconfig('ui', 'forcemerge', '')
4075 ui.setconfig('ui', 'forcemerge', '')
4076
4076
4077 @command('outgoing|out',
4077 @command('outgoing|out',
4078 [('f', 'force', None, _('run even when the destination is unrelated')),
4078 [('f', 'force', None, _('run even when the destination is unrelated')),
4079 ('r', 'rev', [],
4079 ('r', 'rev', [],
4080 _('a changeset intended to be included in the destination'), _('REV')),
4080 _('a changeset intended to be included in the destination'), _('REV')),
4081 ('n', 'newest-first', None, _('show newest record first')),
4081 ('n', 'newest-first', None, _('show newest record first')),
4082 ('B', 'bookmarks', False, _('compare bookmarks')),
4082 ('B', 'bookmarks', False, _('compare bookmarks')),
4083 ('b', 'branch', [], _('a specific branch you would like to push'),
4083 ('b', 'branch', [], _('a specific branch you would like to push'),
4084 _('BRANCH')),
4084 _('BRANCH')),
4085 ] + logopts + remoteopts + subrepoopts,
4085 ] + logopts + remoteopts + subrepoopts,
4086 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4086 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4087 def outgoing(ui, repo, dest=None, **opts):
4087 def outgoing(ui, repo, dest=None, **opts):
4088 """show changesets not found in the destination
4088 """show changesets not found in the destination
4089
4089
4090 Show changesets not found in the specified destination repository
4090 Show changesets not found in the specified destination repository
4091 or the default push location. These are the changesets that would
4091 or the default push location. These are the changesets that would
4092 be pushed if a push was requested.
4092 be pushed if a push was requested.
4093
4093
4094 See pull for details of valid destination formats.
4094 See pull for details of valid destination formats.
4095
4095
4096 Returns 0 if there are outgoing changes, 1 otherwise.
4096 Returns 0 if there are outgoing changes, 1 otherwise.
4097 """
4097 """
4098
4098
4099 if opts.get('bookmarks'):
4099 if opts.get('bookmarks'):
4100 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4100 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4101 dest, branches = hg.parseurl(dest, opts.get('branch'))
4101 dest, branches = hg.parseurl(dest, opts.get('branch'))
4102 other = hg.peer(repo, opts, dest)
4102 other = hg.peer(repo, opts, dest)
4103 if 'bookmarks' not in other.listkeys('namespaces'):
4103 if 'bookmarks' not in other.listkeys('namespaces'):
4104 ui.warn(_("remote doesn't support bookmarks\n"))
4104 ui.warn(_("remote doesn't support bookmarks\n"))
4105 return 0
4105 return 0
4106 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4106 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4107 return bookmarks.diff(ui, other, repo)
4107 return bookmarks.diff(ui, other, repo)
4108
4108
4109 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4109 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4110 try:
4110 try:
4111 return hg.outgoing(ui, repo, dest, opts)
4111 return hg.outgoing(ui, repo, dest, opts)
4112 finally:
4112 finally:
4113 del repo._subtoppath
4113 del repo._subtoppath
4114
4114
4115 @command('parents',
4115 @command('parents',
4116 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4116 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4117 ] + templateopts,
4117 ] + templateopts,
4118 _('[-r REV] [FILE]'))
4118 _('[-r REV] [FILE]'))
4119 def parents(ui, repo, file_=None, **opts):
4119 def parents(ui, repo, file_=None, **opts):
4120 """show the parents of the working directory or revision
4120 """show the parents of the working directory or revision
4121
4121
4122 Print the working directory's parent revisions. If a revision is
4122 Print the working directory's parent revisions. If a revision is
4123 given via -r/--rev, the parent of that revision will be printed.
4123 given via -r/--rev, the parent of that revision will be printed.
4124 If a file argument is given, the revision in which the file was
4124 If a file argument is given, the revision in which the file was
4125 last changed (before the working directory revision or the
4125 last changed (before the working directory revision or the
4126 argument to --rev if given) is printed.
4126 argument to --rev if given) is printed.
4127
4127
4128 Returns 0 on success.
4128 Returns 0 on success.
4129 """
4129 """
4130
4130
4131 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4131 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4132
4132
4133 if file_:
4133 if file_:
4134 m = scmutil.match(ctx, (file_,), opts)
4134 m = scmutil.match(ctx, (file_,), opts)
4135 if m.anypats() or len(m.files()) != 1:
4135 if m.anypats() or len(m.files()) != 1:
4136 raise util.Abort(_('can only specify an explicit filename'))
4136 raise util.Abort(_('can only specify an explicit filename'))
4137 file_ = m.files()[0]
4137 file_ = m.files()[0]
4138 filenodes = []
4138 filenodes = []
4139 for cp in ctx.parents():
4139 for cp in ctx.parents():
4140 if not cp:
4140 if not cp:
4141 continue
4141 continue
4142 try:
4142 try:
4143 filenodes.append(cp.filenode(file_))
4143 filenodes.append(cp.filenode(file_))
4144 except error.LookupError:
4144 except error.LookupError:
4145 pass
4145 pass
4146 if not filenodes:
4146 if not filenodes:
4147 raise util.Abort(_("'%s' not found in manifest!") % file_)
4147 raise util.Abort(_("'%s' not found in manifest!") % file_)
4148 fl = repo.file(file_)
4148 fl = repo.file(file_)
4149 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
4149 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
4150 else:
4150 else:
4151 p = [cp.node() for cp in ctx.parents()]
4151 p = [cp.node() for cp in ctx.parents()]
4152
4152
4153 displayer = cmdutil.show_changeset(ui, repo, opts)
4153 displayer = cmdutil.show_changeset(ui, repo, opts)
4154 for n in p:
4154 for n in p:
4155 if n != nullid:
4155 if n != nullid:
4156 displayer.show(repo[n])
4156 displayer.show(repo[n])
4157 displayer.close()
4157 displayer.close()
4158
4158
4159 @command('paths', [], _('[NAME]'))
4159 @command('paths', [], _('[NAME]'))
4160 def paths(ui, repo, search=None):
4160 def paths(ui, repo, search=None):
4161 """show aliases for remote repositories
4161 """show aliases for remote repositories
4162
4162
4163 Show definition of symbolic path name NAME. If no name is given,
4163 Show definition of symbolic path name NAME. If no name is given,
4164 show definition of all available names.
4164 show definition of all available names.
4165
4165
4166 Option -q/--quiet suppresses all output when searching for NAME
4166 Option -q/--quiet suppresses all output when searching for NAME
4167 and shows only the path names when listing all definitions.
4167 and shows only the path names when listing all definitions.
4168
4168
4169 Path names are defined in the [paths] section of your
4169 Path names are defined in the [paths] section of your
4170 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4170 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4171 repository, ``.hg/hgrc`` is used, too.
4171 repository, ``.hg/hgrc`` is used, too.
4172
4172
4173 The path names ``default`` and ``default-push`` have a special
4173 The path names ``default`` and ``default-push`` have a special
4174 meaning. When performing a push or pull operation, they are used
4174 meaning. When performing a push or pull operation, they are used
4175 as fallbacks if no location is specified on the command-line.
4175 as fallbacks if no location is specified on the command-line.
4176 When ``default-push`` is set, it will be used for push and
4176 When ``default-push`` is set, it will be used for push and
4177 ``default`` will be used for pull; otherwise ``default`` is used
4177 ``default`` will be used for pull; otherwise ``default`` is used
4178 as the fallback for both. When cloning a repository, the clone
4178 as the fallback for both. When cloning a repository, the clone
4179 source is written as ``default`` in ``.hg/hgrc``. Note that
4179 source is written as ``default`` in ``.hg/hgrc``. Note that
4180 ``default`` and ``default-push`` apply to all inbound (e.g.
4180 ``default`` and ``default-push`` apply to all inbound (e.g.
4181 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
4181 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
4182 :hg:`bundle`) operations.
4182 :hg:`bundle`) operations.
4183
4183
4184 See :hg:`help urls` for more information.
4184 See :hg:`help urls` for more information.
4185
4185
4186 Returns 0 on success.
4186 Returns 0 on success.
4187 """
4187 """
4188 if search:
4188 if search:
4189 for name, path in ui.configitems("paths"):
4189 for name, path in ui.configitems("paths"):
4190 if name == search:
4190 if name == search:
4191 ui.status("%s\n" % util.hidepassword(path))
4191 ui.status("%s\n" % util.hidepassword(path))
4192 return
4192 return
4193 if not ui.quiet:
4193 if not ui.quiet:
4194 ui.warn(_("not found!\n"))
4194 ui.warn(_("not found!\n"))
4195 return 1
4195 return 1
4196 else:
4196 else:
4197 for name, path in ui.configitems("paths"):
4197 for name, path in ui.configitems("paths"):
4198 if ui.quiet:
4198 if ui.quiet:
4199 ui.write("%s\n" % name)
4199 ui.write("%s\n" % name)
4200 else:
4200 else:
4201 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
4201 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
4202
4202
4203 def postincoming(ui, repo, modheads, optupdate, checkout):
4203 def postincoming(ui, repo, modheads, optupdate, checkout):
4204 if modheads == 0:
4204 if modheads == 0:
4205 return
4205 return
4206 if optupdate:
4206 if optupdate:
4207 try:
4207 try:
4208 return hg.update(repo, checkout)
4208 return hg.update(repo, checkout)
4209 except util.Abort, inst:
4209 except util.Abort, inst:
4210 ui.warn(_("not updating: %s\n" % str(inst)))
4210 ui.warn(_("not updating: %s\n" % str(inst)))
4211 return 0
4211 return 0
4212 if modheads > 1:
4212 if modheads > 1:
4213 currentbranchheads = len(repo.branchheads())
4213 currentbranchheads = len(repo.branchheads())
4214 if currentbranchheads == modheads:
4214 if currentbranchheads == modheads:
4215 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4215 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4216 elif currentbranchheads > 1:
4216 elif currentbranchheads > 1:
4217 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to merge)\n"))
4217 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to merge)\n"))
4218 else:
4218 else:
4219 ui.status(_("(run 'hg heads' to see heads)\n"))
4219 ui.status(_("(run 'hg heads' to see heads)\n"))
4220 else:
4220 else:
4221 ui.status(_("(run 'hg update' to get a working copy)\n"))
4221 ui.status(_("(run 'hg update' to get a working copy)\n"))
4222
4222
4223 @command('^pull',
4223 @command('^pull',
4224 [('u', 'update', None,
4224 [('u', 'update', None,
4225 _('update to new branch head if changesets were pulled')),
4225 _('update to new branch head if changesets were pulled')),
4226 ('f', 'force', None, _('run even when remote repository is unrelated')),
4226 ('f', 'force', None, _('run even when remote repository is unrelated')),
4227 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4227 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4228 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4228 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4229 ('b', 'branch', [], _('a specific branch you would like to pull'),
4229 ('b', 'branch', [], _('a specific branch you would like to pull'),
4230 _('BRANCH')),
4230 _('BRANCH')),
4231 ] + remoteopts,
4231 ] + remoteopts,
4232 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
4232 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
4233 def pull(ui, repo, source="default", **opts):
4233 def pull(ui, repo, source="default", **opts):
4234 """pull changes from the specified source
4234 """pull changes from the specified source
4235
4235
4236 Pull changes from a remote repository to a local one.
4236 Pull changes from a remote repository to a local one.
4237
4237
4238 This finds all changes from the repository at the specified path
4238 This finds all changes from the repository at the specified path
4239 or URL and adds them to a local repository (the current one unless
4239 or URL and adds them to a local repository (the current one unless
4240 -R is specified). By default, this does not update the copy of the
4240 -R is specified). By default, this does not update the copy of the
4241 project in the working directory.
4241 project in the working directory.
4242
4242
4243 Use :hg:`incoming` if you want to see what would have been added
4243 Use :hg:`incoming` if you want to see what would have been added
4244 by a pull at the time you issued this command. If you then decide
4244 by a pull at the time you issued this command. If you then decide
4245 to add those changes to the repository, you should use :hg:`pull
4245 to add those changes to the repository, you should use :hg:`pull
4246 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4246 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4247
4247
4248 If SOURCE is omitted, the 'default' path will be used.
4248 If SOURCE is omitted, the 'default' path will be used.
4249 See :hg:`help urls` for more information.
4249 See :hg:`help urls` for more information.
4250
4250
4251 Returns 0 on success, 1 if an update had unresolved files.
4251 Returns 0 on success, 1 if an update had unresolved files.
4252 """
4252 """
4253 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4253 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4254 other = hg.peer(repo, opts, source)
4254 other = hg.peer(repo, opts, source)
4255 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4255 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4256 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
4256 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
4257
4257
4258 if opts.get('bookmark'):
4258 if opts.get('bookmark'):
4259 if not revs:
4259 if not revs:
4260 revs = []
4260 revs = []
4261 rb = other.listkeys('bookmarks')
4261 rb = other.listkeys('bookmarks')
4262 for b in opts['bookmark']:
4262 for b in opts['bookmark']:
4263 if b not in rb:
4263 if b not in rb:
4264 raise util.Abort(_('remote bookmark %s not found!') % b)
4264 raise util.Abort(_('remote bookmark %s not found!') % b)
4265 revs.append(rb[b])
4265 revs.append(rb[b])
4266
4266
4267 if revs:
4267 if revs:
4268 try:
4268 try:
4269 revs = [other.lookup(rev) for rev in revs]
4269 revs = [other.lookup(rev) for rev in revs]
4270 except error.CapabilityError:
4270 except error.CapabilityError:
4271 err = _("other repository doesn't support revision lookup, "
4271 err = _("other repository doesn't support revision lookup, "
4272 "so a rev cannot be specified.")
4272 "so a rev cannot be specified.")
4273 raise util.Abort(err)
4273 raise util.Abort(err)
4274
4274
4275 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
4275 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
4276 bookmarks.updatefromremote(ui, repo, other)
4276 bookmarks.updatefromremote(ui, repo, other, source)
4277 if checkout:
4277 if checkout:
4278 checkout = str(repo.changelog.rev(other.lookup(checkout)))
4278 checkout = str(repo.changelog.rev(other.lookup(checkout)))
4279 repo._subtoppath = source
4279 repo._subtoppath = source
4280 try:
4280 try:
4281 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
4281 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
4282
4282
4283 finally:
4283 finally:
4284 del repo._subtoppath
4284 del repo._subtoppath
4285
4285
4286 # update specified bookmarks
4286 # update specified bookmarks
4287 if opts.get('bookmark'):
4287 if opts.get('bookmark'):
4288 for b in opts['bookmark']:
4288 for b in opts['bookmark']:
4289 # explicit pull overrides local bookmark if any
4289 # explicit pull overrides local bookmark if any
4290 ui.status(_("importing bookmark %s\n") % b)
4290 ui.status(_("importing bookmark %s\n") % b)
4291 repo._bookmarks[b] = repo[rb[b]].node()
4291 repo._bookmarks[b] = repo[rb[b]].node()
4292 bookmarks.write(repo)
4292 bookmarks.write(repo)
4293
4293
4294 return ret
4294 return ret
4295
4295
4296 @command('^push',
4296 @command('^push',
4297 [('f', 'force', None, _('force push')),
4297 [('f', 'force', None, _('force push')),
4298 ('r', 'rev', [],
4298 ('r', 'rev', [],
4299 _('a changeset intended to be included in the destination'),
4299 _('a changeset intended to be included in the destination'),
4300 _('REV')),
4300 _('REV')),
4301 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4301 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4302 ('b', 'branch', [],
4302 ('b', 'branch', [],
4303 _('a specific branch you would like to push'), _('BRANCH')),
4303 _('a specific branch you would like to push'), _('BRANCH')),
4304 ('', 'new-branch', False, _('allow pushing a new branch')),
4304 ('', 'new-branch', False, _('allow pushing a new branch')),
4305 ] + remoteopts,
4305 ] + remoteopts,
4306 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4306 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4307 def push(ui, repo, dest=None, **opts):
4307 def push(ui, repo, dest=None, **opts):
4308 """push changes to the specified destination
4308 """push changes to the specified destination
4309
4309
4310 Push changesets from the local repository to the specified
4310 Push changesets from the local repository to the specified
4311 destination.
4311 destination.
4312
4312
4313 This operation is symmetrical to pull: it is identical to a pull
4313 This operation is symmetrical to pull: it is identical to a pull
4314 in the destination repository from the current one.
4314 in the destination repository from the current one.
4315
4315
4316 By default, push will not allow creation of new heads at the
4316 By default, push will not allow creation of new heads at the
4317 destination, since multiple heads would make it unclear which head
4317 destination, since multiple heads would make it unclear which head
4318 to use. In this situation, it is recommended to pull and merge
4318 to use. In this situation, it is recommended to pull and merge
4319 before pushing.
4319 before pushing.
4320
4320
4321 Use --new-branch if you want to allow push to create a new named
4321 Use --new-branch if you want to allow push to create a new named
4322 branch that is not present at the destination. This allows you to
4322 branch that is not present at the destination. This allows you to
4323 only create a new branch without forcing other changes.
4323 only create a new branch without forcing other changes.
4324
4324
4325 Use -f/--force to override the default behavior and push all
4325 Use -f/--force to override the default behavior and push all
4326 changesets on all branches.
4326 changesets on all branches.
4327
4327
4328 If -r/--rev is used, the specified revision and all its ancestors
4328 If -r/--rev is used, the specified revision and all its ancestors
4329 will be pushed to the remote repository.
4329 will be pushed to the remote repository.
4330
4330
4331 Please see :hg:`help urls` for important details about ``ssh://``
4331 Please see :hg:`help urls` for important details about ``ssh://``
4332 URLs. If DESTINATION is omitted, a default path will be used.
4332 URLs. If DESTINATION is omitted, a default path will be used.
4333
4333
4334 Returns 0 if push was successful, 1 if nothing to push.
4334 Returns 0 if push was successful, 1 if nothing to push.
4335 """
4335 """
4336
4336
4337 if opts.get('bookmark'):
4337 if opts.get('bookmark'):
4338 for b in opts['bookmark']:
4338 for b in opts['bookmark']:
4339 # translate -B options to -r so changesets get pushed
4339 # translate -B options to -r so changesets get pushed
4340 if b in repo._bookmarks:
4340 if b in repo._bookmarks:
4341 opts.setdefault('rev', []).append(b)
4341 opts.setdefault('rev', []).append(b)
4342 else:
4342 else:
4343 # if we try to push a deleted bookmark, translate it to null
4343 # if we try to push a deleted bookmark, translate it to null
4344 # this lets simultaneous -r, -b options continue working
4344 # this lets simultaneous -r, -b options continue working
4345 opts.setdefault('rev', []).append("null")
4345 opts.setdefault('rev', []).append("null")
4346
4346
4347 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4347 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4348 dest, branches = hg.parseurl(dest, opts.get('branch'))
4348 dest, branches = hg.parseurl(dest, opts.get('branch'))
4349 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4349 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4350 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4350 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4351 other = hg.peer(repo, opts, dest)
4351 other = hg.peer(repo, opts, dest)
4352 if revs:
4352 if revs:
4353 revs = [repo.lookup(rev) for rev in revs]
4353 revs = [repo.lookup(rev) for rev in revs]
4354
4354
4355 repo._subtoppath = dest
4355 repo._subtoppath = dest
4356 try:
4356 try:
4357 # push subrepos depth-first for coherent ordering
4357 # push subrepos depth-first for coherent ordering
4358 c = repo['']
4358 c = repo['']
4359 subs = c.substate # only repos that are committed
4359 subs = c.substate # only repos that are committed
4360 for s in sorted(subs):
4360 for s in sorted(subs):
4361 if not c.sub(s).push(opts.get('force')):
4361 if not c.sub(s).push(opts.get('force')):
4362 return False
4362 return False
4363 finally:
4363 finally:
4364 del repo._subtoppath
4364 del repo._subtoppath
4365 result = repo.push(other, opts.get('force'), revs=revs,
4365 result = repo.push(other, opts.get('force'), revs=revs,
4366 newbranch=opts.get('new_branch'))
4366 newbranch=opts.get('new_branch'))
4367
4367
4368 result = (result == 0)
4368 result = (result == 0)
4369
4369
4370 if opts.get('bookmark'):
4370 if opts.get('bookmark'):
4371 rb = other.listkeys('bookmarks')
4371 rb = other.listkeys('bookmarks')
4372 for b in opts['bookmark']:
4372 for b in opts['bookmark']:
4373 # explicit push overrides remote bookmark if any
4373 # explicit push overrides remote bookmark if any
4374 if b in repo._bookmarks:
4374 if b in repo._bookmarks:
4375 ui.status(_("exporting bookmark %s\n") % b)
4375 ui.status(_("exporting bookmark %s\n") % b)
4376 new = repo[b].hex()
4376 new = repo[b].hex()
4377 elif b in rb:
4377 elif b in rb:
4378 ui.status(_("deleting remote bookmark %s\n") % b)
4378 ui.status(_("deleting remote bookmark %s\n") % b)
4379 new = '' # delete
4379 new = '' # delete
4380 else:
4380 else:
4381 ui.warn(_('bookmark %s does not exist on the local '
4381 ui.warn(_('bookmark %s does not exist on the local '
4382 'or remote repository!\n') % b)
4382 'or remote repository!\n') % b)
4383 return 2
4383 return 2
4384 old = rb.get(b, '')
4384 old = rb.get(b, '')
4385 r = other.pushkey('bookmarks', b, old, new)
4385 r = other.pushkey('bookmarks', b, old, new)
4386 if not r:
4386 if not r:
4387 ui.warn(_('updating bookmark %s failed!\n') % b)
4387 ui.warn(_('updating bookmark %s failed!\n') % b)
4388 if not result:
4388 if not result:
4389 result = 2
4389 result = 2
4390
4390
4391 return result
4391 return result
4392
4392
4393 @command('recover', [])
4393 @command('recover', [])
4394 def recover(ui, repo):
4394 def recover(ui, repo):
4395 """roll back an interrupted transaction
4395 """roll back an interrupted transaction
4396
4396
4397 Recover from an interrupted commit or pull.
4397 Recover from an interrupted commit or pull.
4398
4398
4399 This command tries to fix the repository status after an
4399 This command tries to fix the repository status after an
4400 interrupted operation. It should only be necessary when Mercurial
4400 interrupted operation. It should only be necessary when Mercurial
4401 suggests it.
4401 suggests it.
4402
4402
4403 Returns 0 if successful, 1 if nothing to recover or verify fails.
4403 Returns 0 if successful, 1 if nothing to recover or verify fails.
4404 """
4404 """
4405 if repo.recover():
4405 if repo.recover():
4406 return hg.verify(repo)
4406 return hg.verify(repo)
4407 return 1
4407 return 1
4408
4408
4409 @command('^remove|rm',
4409 @command('^remove|rm',
4410 [('A', 'after', None, _('record delete for missing files')),
4410 [('A', 'after', None, _('record delete for missing files')),
4411 ('f', 'force', None,
4411 ('f', 'force', None,
4412 _('remove (and delete) file even if added or modified')),
4412 _('remove (and delete) file even if added or modified')),
4413 ] + walkopts,
4413 ] + walkopts,
4414 _('[OPTION]... FILE...'))
4414 _('[OPTION]... FILE...'))
4415 def remove(ui, repo, *pats, **opts):
4415 def remove(ui, repo, *pats, **opts):
4416 """remove the specified files on the next commit
4416 """remove the specified files on the next commit
4417
4417
4418 Schedule the indicated files for removal from the current branch.
4418 Schedule the indicated files for removal from the current branch.
4419
4419
4420 This command schedules the files to be removed at the next commit.
4420 This command schedules the files to be removed at the next commit.
4421 To undo a remove before that, see :hg:`revert`. To undo added
4421 To undo a remove before that, see :hg:`revert`. To undo added
4422 files, see :hg:`forget`.
4422 files, see :hg:`forget`.
4423
4423
4424 .. container:: verbose
4424 .. container:: verbose
4425
4425
4426 -A/--after can be used to remove only files that have already
4426 -A/--after can be used to remove only files that have already
4427 been deleted, -f/--force can be used to force deletion, and -Af
4427 been deleted, -f/--force can be used to force deletion, and -Af
4428 can be used to remove files from the next revision without
4428 can be used to remove files from the next revision without
4429 deleting them from the working directory.
4429 deleting them from the working directory.
4430
4430
4431 The following table details the behavior of remove for different
4431 The following table details the behavior of remove for different
4432 file states (columns) and option combinations (rows). The file
4432 file states (columns) and option combinations (rows). The file
4433 states are Added [A], Clean [C], Modified [M] and Missing [!]
4433 states are Added [A], Clean [C], Modified [M] and Missing [!]
4434 (as reported by :hg:`status`). The actions are Warn, Remove
4434 (as reported by :hg:`status`). The actions are Warn, Remove
4435 (from branch) and Delete (from disk):
4435 (from branch) and Delete (from disk):
4436
4436
4437 ======= == == == ==
4437 ======= == == == ==
4438 A C M !
4438 A C M !
4439 ======= == == == ==
4439 ======= == == == ==
4440 none W RD W R
4440 none W RD W R
4441 -f R RD RD R
4441 -f R RD RD R
4442 -A W W W R
4442 -A W W W R
4443 -Af R R R R
4443 -Af R R R R
4444 ======= == == == ==
4444 ======= == == == ==
4445
4445
4446 Note that remove never deletes files in Added [A] state from the
4446 Note that remove never deletes files in Added [A] state from the
4447 working directory, not even if option --force is specified.
4447 working directory, not even if option --force is specified.
4448
4448
4449 Returns 0 on success, 1 if any warnings encountered.
4449 Returns 0 on success, 1 if any warnings encountered.
4450 """
4450 """
4451
4451
4452 ret = 0
4452 ret = 0
4453 after, force = opts.get('after'), opts.get('force')
4453 after, force = opts.get('after'), opts.get('force')
4454 if not pats and not after:
4454 if not pats and not after:
4455 raise util.Abort(_('no files specified'))
4455 raise util.Abort(_('no files specified'))
4456
4456
4457 m = scmutil.match(repo[None], pats, opts)
4457 m = scmutil.match(repo[None], pats, opts)
4458 s = repo.status(match=m, clean=True)
4458 s = repo.status(match=m, clean=True)
4459 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
4459 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
4460
4460
4461 for f in m.files():
4461 for f in m.files():
4462 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
4462 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
4463 if os.path.exists(m.rel(f)):
4463 if os.path.exists(m.rel(f)):
4464 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
4464 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
4465 ret = 1
4465 ret = 1
4466
4466
4467 if force:
4467 if force:
4468 list = modified + deleted + clean + added
4468 list = modified + deleted + clean + added
4469 elif after:
4469 elif after:
4470 list = deleted
4470 list = deleted
4471 for f in modified + added + clean:
4471 for f in modified + added + clean:
4472 ui.warn(_('not removing %s: file still exists (use -f'
4472 ui.warn(_('not removing %s: file still exists (use -f'
4473 ' to force removal)\n') % m.rel(f))
4473 ' to force removal)\n') % m.rel(f))
4474 ret = 1
4474 ret = 1
4475 else:
4475 else:
4476 list = deleted + clean
4476 list = deleted + clean
4477 for f in modified:
4477 for f in modified:
4478 ui.warn(_('not removing %s: file is modified (use -f'
4478 ui.warn(_('not removing %s: file is modified (use -f'
4479 ' to force removal)\n') % m.rel(f))
4479 ' to force removal)\n') % m.rel(f))
4480 ret = 1
4480 ret = 1
4481 for f in added:
4481 for f in added:
4482 ui.warn(_('not removing %s: file has been marked for add'
4482 ui.warn(_('not removing %s: file has been marked for add'
4483 ' (use forget to undo)\n') % m.rel(f))
4483 ' (use forget to undo)\n') % m.rel(f))
4484 ret = 1
4484 ret = 1
4485
4485
4486 for f in sorted(list):
4486 for f in sorted(list):
4487 if ui.verbose or not m.exact(f):
4487 if ui.verbose or not m.exact(f):
4488 ui.status(_('removing %s\n') % m.rel(f))
4488 ui.status(_('removing %s\n') % m.rel(f))
4489
4489
4490 wlock = repo.wlock()
4490 wlock = repo.wlock()
4491 try:
4491 try:
4492 if not after:
4492 if not after:
4493 for f in list:
4493 for f in list:
4494 if f in added:
4494 if f in added:
4495 continue # we never unlink added files on remove
4495 continue # we never unlink added files on remove
4496 try:
4496 try:
4497 util.unlinkpath(repo.wjoin(f))
4497 util.unlinkpath(repo.wjoin(f))
4498 except OSError, inst:
4498 except OSError, inst:
4499 if inst.errno != errno.ENOENT:
4499 if inst.errno != errno.ENOENT:
4500 raise
4500 raise
4501 repo[None].forget(list)
4501 repo[None].forget(list)
4502 finally:
4502 finally:
4503 wlock.release()
4503 wlock.release()
4504
4504
4505 return ret
4505 return ret
4506
4506
4507 @command('rename|move|mv',
4507 @command('rename|move|mv',
4508 [('A', 'after', None, _('record a rename that has already occurred')),
4508 [('A', 'after', None, _('record a rename that has already occurred')),
4509 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4509 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4510 ] + walkopts + dryrunopts,
4510 ] + walkopts + dryrunopts,
4511 _('[OPTION]... SOURCE... DEST'))
4511 _('[OPTION]... SOURCE... DEST'))
4512 def rename(ui, repo, *pats, **opts):
4512 def rename(ui, repo, *pats, **opts):
4513 """rename files; equivalent of copy + remove
4513 """rename files; equivalent of copy + remove
4514
4514
4515 Mark dest as copies of sources; mark sources for deletion. If dest
4515 Mark dest as copies of sources; mark sources for deletion. If dest
4516 is a directory, copies are put in that directory. If dest is a
4516 is a directory, copies are put in that directory. If dest is a
4517 file, there can only be one source.
4517 file, there can only be one source.
4518
4518
4519 By default, this command copies the contents of files as they
4519 By default, this command copies the contents of files as they
4520 exist in the working directory. If invoked with -A/--after, the
4520 exist in the working directory. If invoked with -A/--after, the
4521 operation is recorded, but no copying is performed.
4521 operation is recorded, but no copying is performed.
4522
4522
4523 This command takes effect at the next commit. To undo a rename
4523 This command takes effect at the next commit. To undo a rename
4524 before that, see :hg:`revert`.
4524 before that, see :hg:`revert`.
4525
4525
4526 Returns 0 on success, 1 if errors are encountered.
4526 Returns 0 on success, 1 if errors are encountered.
4527 """
4527 """
4528 wlock = repo.wlock(False)
4528 wlock = repo.wlock(False)
4529 try:
4529 try:
4530 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4530 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4531 finally:
4531 finally:
4532 wlock.release()
4532 wlock.release()
4533
4533
4534 @command('resolve',
4534 @command('resolve',
4535 [('a', 'all', None, _('select all unresolved files')),
4535 [('a', 'all', None, _('select all unresolved files')),
4536 ('l', 'list', None, _('list state of files needing merge')),
4536 ('l', 'list', None, _('list state of files needing merge')),
4537 ('m', 'mark', None, _('mark files as resolved')),
4537 ('m', 'mark', None, _('mark files as resolved')),
4538 ('u', 'unmark', None, _('mark files as unresolved')),
4538 ('u', 'unmark', None, _('mark files as unresolved')),
4539 ('n', 'no-status', None, _('hide status prefix'))]
4539 ('n', 'no-status', None, _('hide status prefix'))]
4540 + mergetoolopts + walkopts,
4540 + mergetoolopts + walkopts,
4541 _('[OPTION]... [FILE]...'))
4541 _('[OPTION]... [FILE]...'))
4542 def resolve(ui, repo, *pats, **opts):
4542 def resolve(ui, repo, *pats, **opts):
4543 """redo merges or set/view the merge status of files
4543 """redo merges or set/view the merge status of files
4544
4544
4545 Merges with unresolved conflicts are often the result of
4545 Merges with unresolved conflicts are often the result of
4546 non-interactive merging using the ``internal:merge`` configuration
4546 non-interactive merging using the ``internal:merge`` configuration
4547 setting, or a command-line merge tool like ``diff3``. The resolve
4547 setting, or a command-line merge tool like ``diff3``. The resolve
4548 command is used to manage the files involved in a merge, after
4548 command is used to manage the files involved in a merge, after
4549 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4549 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4550 working directory must have two parents).
4550 working directory must have two parents).
4551
4551
4552 The resolve command can be used in the following ways:
4552 The resolve command can be used in the following ways:
4553
4553
4554 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4554 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4555 files, discarding any previous merge attempts. Re-merging is not
4555 files, discarding any previous merge attempts. Re-merging is not
4556 performed for files already marked as resolved. Use ``--all/-a``
4556 performed for files already marked as resolved. Use ``--all/-a``
4557 to select all unresolved files. ``--tool`` can be used to specify
4557 to select all unresolved files. ``--tool`` can be used to specify
4558 the merge tool used for the given files. It overrides the HGMERGE
4558 the merge tool used for the given files. It overrides the HGMERGE
4559 environment variable and your configuration files. Previous file
4559 environment variable and your configuration files. Previous file
4560 contents are saved with a ``.orig`` suffix.
4560 contents are saved with a ``.orig`` suffix.
4561
4561
4562 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4562 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4563 (e.g. after having manually fixed-up the files). The default is
4563 (e.g. after having manually fixed-up the files). The default is
4564 to mark all unresolved files.
4564 to mark all unresolved files.
4565
4565
4566 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4566 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4567 default is to mark all resolved files.
4567 default is to mark all resolved files.
4568
4568
4569 - :hg:`resolve -l`: list files which had or still have conflicts.
4569 - :hg:`resolve -l`: list files which had or still have conflicts.
4570 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4570 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4571
4571
4572 Note that Mercurial will not let you commit files with unresolved
4572 Note that Mercurial will not let you commit files with unresolved
4573 merge conflicts. You must use :hg:`resolve -m ...` before you can
4573 merge conflicts. You must use :hg:`resolve -m ...` before you can
4574 commit after a conflicting merge.
4574 commit after a conflicting merge.
4575
4575
4576 Returns 0 on success, 1 if any files fail a resolve attempt.
4576 Returns 0 on success, 1 if any files fail a resolve attempt.
4577 """
4577 """
4578
4578
4579 all, mark, unmark, show, nostatus = \
4579 all, mark, unmark, show, nostatus = \
4580 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
4580 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
4581
4581
4582 if (show and (mark or unmark)) or (mark and unmark):
4582 if (show and (mark or unmark)) or (mark and unmark):
4583 raise util.Abort(_("too many options specified"))
4583 raise util.Abort(_("too many options specified"))
4584 if pats and all:
4584 if pats and all:
4585 raise util.Abort(_("can't specify --all and patterns"))
4585 raise util.Abort(_("can't specify --all and patterns"))
4586 if not (all or pats or show or mark or unmark):
4586 if not (all or pats or show or mark or unmark):
4587 raise util.Abort(_('no files or directories specified; '
4587 raise util.Abort(_('no files or directories specified; '
4588 'use --all to remerge all files'))
4588 'use --all to remerge all files'))
4589
4589
4590 ms = mergemod.mergestate(repo)
4590 ms = mergemod.mergestate(repo)
4591 m = scmutil.match(repo[None], pats, opts)
4591 m = scmutil.match(repo[None], pats, opts)
4592 ret = 0
4592 ret = 0
4593
4593
4594 for f in ms:
4594 for f in ms:
4595 if m(f):
4595 if m(f):
4596 if show:
4596 if show:
4597 if nostatus:
4597 if nostatus:
4598 ui.write("%s\n" % f)
4598 ui.write("%s\n" % f)
4599 else:
4599 else:
4600 ui.write("%s %s\n" % (ms[f].upper(), f),
4600 ui.write("%s %s\n" % (ms[f].upper(), f),
4601 label='resolve.' +
4601 label='resolve.' +
4602 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
4602 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
4603 elif mark:
4603 elif mark:
4604 ms.mark(f, "r")
4604 ms.mark(f, "r")
4605 elif unmark:
4605 elif unmark:
4606 ms.mark(f, "u")
4606 ms.mark(f, "u")
4607 else:
4607 else:
4608 wctx = repo[None]
4608 wctx = repo[None]
4609 mctx = wctx.parents()[-1]
4609 mctx = wctx.parents()[-1]
4610
4610
4611 # backup pre-resolve (merge uses .orig for its own purposes)
4611 # backup pre-resolve (merge uses .orig for its own purposes)
4612 a = repo.wjoin(f)
4612 a = repo.wjoin(f)
4613 util.copyfile(a, a + ".resolve")
4613 util.copyfile(a, a + ".resolve")
4614
4614
4615 try:
4615 try:
4616 # resolve file
4616 # resolve file
4617 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4617 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4618 if ms.resolve(f, wctx, mctx):
4618 if ms.resolve(f, wctx, mctx):
4619 ret = 1
4619 ret = 1
4620 finally:
4620 finally:
4621 ui.setconfig('ui', 'forcemerge', '')
4621 ui.setconfig('ui', 'forcemerge', '')
4622
4622
4623 # replace filemerge's .orig file with our resolve file
4623 # replace filemerge's .orig file with our resolve file
4624 util.rename(a + ".resolve", a + ".orig")
4624 util.rename(a + ".resolve", a + ".orig")
4625
4625
4626 ms.commit()
4626 ms.commit()
4627 return ret
4627 return ret
4628
4628
4629 @command('revert',
4629 @command('revert',
4630 [('a', 'all', None, _('revert all changes when no arguments given')),
4630 [('a', 'all', None, _('revert all changes when no arguments given')),
4631 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4631 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4632 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4632 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4633 ('C', 'no-backup', None, _('do not save backup copies of files')),
4633 ('C', 'no-backup', None, _('do not save backup copies of files')),
4634 ] + walkopts + dryrunopts,
4634 ] + walkopts + dryrunopts,
4635 _('[OPTION]... [-r REV] [NAME]...'))
4635 _('[OPTION]... [-r REV] [NAME]...'))
4636 def revert(ui, repo, *pats, **opts):
4636 def revert(ui, repo, *pats, **opts):
4637 """restore files to their checkout state
4637 """restore files to their checkout state
4638
4638
4639 .. note::
4639 .. note::
4640 To check out earlier revisions, you should use :hg:`update REV`.
4640 To check out earlier revisions, you should use :hg:`update REV`.
4641 To cancel a merge (and lose your changes), use :hg:`update --clean .`.
4641 To cancel a merge (and lose your changes), use :hg:`update --clean .`.
4642
4642
4643 With no revision specified, revert the specified files or directories
4643 With no revision specified, revert the specified files or directories
4644 to the contents they had in the parent of the working directory.
4644 to the contents they had in the parent of the working directory.
4645 This restores the contents of files to an unmodified
4645 This restores the contents of files to an unmodified
4646 state and unschedules adds, removes, copies, and renames. If the
4646 state and unschedules adds, removes, copies, and renames. If the
4647 working directory has two parents, you must explicitly specify a
4647 working directory has two parents, you must explicitly specify a
4648 revision.
4648 revision.
4649
4649
4650 Using the -r/--rev or -d/--date options, revert the given files or
4650 Using the -r/--rev or -d/--date options, revert the given files or
4651 directories to their states as of a specific revision. Because
4651 directories to their states as of a specific revision. Because
4652 revert does not change the working directory parents, this will
4652 revert does not change the working directory parents, this will
4653 cause these files to appear modified. This can be helpful to "back
4653 cause these files to appear modified. This can be helpful to "back
4654 out" some or all of an earlier change. See :hg:`backout` for a
4654 out" some or all of an earlier change. See :hg:`backout` for a
4655 related method.
4655 related method.
4656
4656
4657 Modified files are saved with a .orig suffix before reverting.
4657 Modified files are saved with a .orig suffix before reverting.
4658 To disable these backups, use --no-backup.
4658 To disable these backups, use --no-backup.
4659
4659
4660 See :hg:`help dates` for a list of formats valid for -d/--date.
4660 See :hg:`help dates` for a list of formats valid for -d/--date.
4661
4661
4662 Returns 0 on success.
4662 Returns 0 on success.
4663 """
4663 """
4664
4664
4665 if opts.get("date"):
4665 if opts.get("date"):
4666 if opts.get("rev"):
4666 if opts.get("rev"):
4667 raise util.Abort(_("you can't specify a revision and a date"))
4667 raise util.Abort(_("you can't specify a revision and a date"))
4668 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4668 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4669
4669
4670 parent, p2 = repo.dirstate.parents()
4670 parent, p2 = repo.dirstate.parents()
4671 if not opts.get('rev') and p2 != nullid:
4671 if not opts.get('rev') and p2 != nullid:
4672 # revert after merge is a trap for new users (issue2915)
4672 # revert after merge is a trap for new users (issue2915)
4673 raise util.Abort(_('uncommitted merge with no revision specified'),
4673 raise util.Abort(_('uncommitted merge with no revision specified'),
4674 hint=_('use "hg update" or see "hg help revert"'))
4674 hint=_('use "hg update" or see "hg help revert"'))
4675
4675
4676 ctx = scmutil.revsingle(repo, opts.get('rev'))
4676 ctx = scmutil.revsingle(repo, opts.get('rev'))
4677 node = ctx.node()
4677 node = ctx.node()
4678
4678
4679 if not pats and not opts.get('all'):
4679 if not pats and not opts.get('all'):
4680 msg = _("no files or directories specified")
4680 msg = _("no files or directories specified")
4681 if p2 != nullid:
4681 if p2 != nullid:
4682 hint = _("uncommitted merge, use --all to discard all changes,"
4682 hint = _("uncommitted merge, use --all to discard all changes,"
4683 " or 'hg update -C .' to abort the merge")
4683 " or 'hg update -C .' to abort the merge")
4684 raise util.Abort(msg, hint=hint)
4684 raise util.Abort(msg, hint=hint)
4685 dirty = util.any(repo.status())
4685 dirty = util.any(repo.status())
4686 if node != parent:
4686 if node != parent:
4687 if dirty:
4687 if dirty:
4688 hint = _("uncommitted changes, use --all to discard all"
4688 hint = _("uncommitted changes, use --all to discard all"
4689 " changes, or 'hg update %s' to update") % ctx.rev()
4689 " changes, or 'hg update %s' to update") % ctx.rev()
4690 else:
4690 else:
4691 hint = _("use --all to revert all files,"
4691 hint = _("use --all to revert all files,"
4692 " or 'hg update %s' to update") % ctx.rev()
4692 " or 'hg update %s' to update") % ctx.rev()
4693 elif dirty:
4693 elif dirty:
4694 hint = _("uncommitted changes, use --all to discard all changes")
4694 hint = _("uncommitted changes, use --all to discard all changes")
4695 else:
4695 else:
4696 hint = _("use --all to revert all files")
4696 hint = _("use --all to revert all files")
4697 raise util.Abort(msg, hint=hint)
4697 raise util.Abort(msg, hint=hint)
4698
4698
4699 mf = ctx.manifest()
4699 mf = ctx.manifest()
4700 if node == parent:
4700 if node == parent:
4701 pmf = mf
4701 pmf = mf
4702 else:
4702 else:
4703 pmf = None
4703 pmf = None
4704
4704
4705 # need all matching names in dirstate and manifest of target rev,
4705 # need all matching names in dirstate and manifest of target rev,
4706 # so have to walk both. do not print errors if files exist in one
4706 # so have to walk both. do not print errors if files exist in one
4707 # but not other.
4707 # but not other.
4708
4708
4709 names = {}
4709 names = {}
4710
4710
4711 wlock = repo.wlock()
4711 wlock = repo.wlock()
4712 try:
4712 try:
4713 # walk dirstate.
4713 # walk dirstate.
4714
4714
4715 m = scmutil.match(repo[None], pats, opts)
4715 m = scmutil.match(repo[None], pats, opts)
4716 m.bad = lambda x, y: False
4716 m.bad = lambda x, y: False
4717 for abs in repo.walk(m):
4717 for abs in repo.walk(m):
4718 names[abs] = m.rel(abs), m.exact(abs)
4718 names[abs] = m.rel(abs), m.exact(abs)
4719
4719
4720 # walk target manifest.
4720 # walk target manifest.
4721
4721
4722 def badfn(path, msg):
4722 def badfn(path, msg):
4723 if path in names:
4723 if path in names:
4724 return
4724 return
4725 if path in repo[node].substate:
4725 if path in repo[node].substate:
4726 ui.warn("%s: %s\n" % (m.rel(path),
4726 ui.warn("%s: %s\n" % (m.rel(path),
4727 'reverting subrepos is unsupported'))
4727 'reverting subrepos is unsupported'))
4728 return
4728 return
4729 path_ = path + '/'
4729 path_ = path + '/'
4730 for f in names:
4730 for f in names:
4731 if f.startswith(path_):
4731 if f.startswith(path_):
4732 return
4732 return
4733 ui.warn("%s: %s\n" % (m.rel(path), msg))
4733 ui.warn("%s: %s\n" % (m.rel(path), msg))
4734
4734
4735 m = scmutil.match(repo[node], pats, opts)
4735 m = scmutil.match(repo[node], pats, opts)
4736 m.bad = badfn
4736 m.bad = badfn
4737 for abs in repo[node].walk(m):
4737 for abs in repo[node].walk(m):
4738 if abs not in names:
4738 if abs not in names:
4739 names[abs] = m.rel(abs), m.exact(abs)
4739 names[abs] = m.rel(abs), m.exact(abs)
4740
4740
4741 m = scmutil.matchfiles(repo, names)
4741 m = scmutil.matchfiles(repo, names)
4742 changes = repo.status(match=m)[:4]
4742 changes = repo.status(match=m)[:4]
4743 modified, added, removed, deleted = map(set, changes)
4743 modified, added, removed, deleted = map(set, changes)
4744
4744
4745 # if f is a rename, also revert the source
4745 # if f is a rename, also revert the source
4746 cwd = repo.getcwd()
4746 cwd = repo.getcwd()
4747 for f in added:
4747 for f in added:
4748 src = repo.dirstate.copied(f)
4748 src = repo.dirstate.copied(f)
4749 if src and src not in names and repo.dirstate[src] == 'r':
4749 if src and src not in names and repo.dirstate[src] == 'r':
4750 removed.add(src)
4750 removed.add(src)
4751 names[src] = (repo.pathto(src, cwd), True)
4751 names[src] = (repo.pathto(src, cwd), True)
4752
4752
4753 def removeforget(abs):
4753 def removeforget(abs):
4754 if repo.dirstate[abs] == 'a':
4754 if repo.dirstate[abs] == 'a':
4755 return _('forgetting %s\n')
4755 return _('forgetting %s\n')
4756 return _('removing %s\n')
4756 return _('removing %s\n')
4757
4757
4758 revert = ([], _('reverting %s\n'))
4758 revert = ([], _('reverting %s\n'))
4759 add = ([], _('adding %s\n'))
4759 add = ([], _('adding %s\n'))
4760 remove = ([], removeforget)
4760 remove = ([], removeforget)
4761 undelete = ([], _('undeleting %s\n'))
4761 undelete = ([], _('undeleting %s\n'))
4762
4762
4763 disptable = (
4763 disptable = (
4764 # dispatch table:
4764 # dispatch table:
4765 # file state
4765 # file state
4766 # action if in target manifest
4766 # action if in target manifest
4767 # action if not in target manifest
4767 # action if not in target manifest
4768 # make backup if in target manifest
4768 # make backup if in target manifest
4769 # make backup if not in target manifest
4769 # make backup if not in target manifest
4770 (modified, revert, remove, True, True),
4770 (modified, revert, remove, True, True),
4771 (added, revert, remove, True, False),
4771 (added, revert, remove, True, False),
4772 (removed, undelete, None, False, False),
4772 (removed, undelete, None, False, False),
4773 (deleted, revert, remove, False, False),
4773 (deleted, revert, remove, False, False),
4774 )
4774 )
4775
4775
4776 for abs, (rel, exact) in sorted(names.items()):
4776 for abs, (rel, exact) in sorted(names.items()):
4777 mfentry = mf.get(abs)
4777 mfentry = mf.get(abs)
4778 target = repo.wjoin(abs)
4778 target = repo.wjoin(abs)
4779 def handle(xlist, dobackup):
4779 def handle(xlist, dobackup):
4780 xlist[0].append(abs)
4780 xlist[0].append(abs)
4781 if (dobackup and not opts.get('no_backup') and
4781 if (dobackup and not opts.get('no_backup') and
4782 os.path.lexists(target)):
4782 os.path.lexists(target)):
4783 bakname = "%s.orig" % rel
4783 bakname = "%s.orig" % rel
4784 ui.note(_('saving current version of %s as %s\n') %
4784 ui.note(_('saving current version of %s as %s\n') %
4785 (rel, bakname))
4785 (rel, bakname))
4786 if not opts.get('dry_run'):
4786 if not opts.get('dry_run'):
4787 util.rename(target, bakname)
4787 util.rename(target, bakname)
4788 if ui.verbose or not exact:
4788 if ui.verbose or not exact:
4789 msg = xlist[1]
4789 msg = xlist[1]
4790 if not isinstance(msg, basestring):
4790 if not isinstance(msg, basestring):
4791 msg = msg(abs)
4791 msg = msg(abs)
4792 ui.status(msg % rel)
4792 ui.status(msg % rel)
4793 for table, hitlist, misslist, backuphit, backupmiss in disptable:
4793 for table, hitlist, misslist, backuphit, backupmiss in disptable:
4794 if abs not in table:
4794 if abs not in table:
4795 continue
4795 continue
4796 # file has changed in dirstate
4796 # file has changed in dirstate
4797 if mfentry:
4797 if mfentry:
4798 handle(hitlist, backuphit)
4798 handle(hitlist, backuphit)
4799 elif misslist is not None:
4799 elif misslist is not None:
4800 handle(misslist, backupmiss)
4800 handle(misslist, backupmiss)
4801 break
4801 break
4802 else:
4802 else:
4803 if abs not in repo.dirstate:
4803 if abs not in repo.dirstate:
4804 if mfentry:
4804 if mfentry:
4805 handle(add, True)
4805 handle(add, True)
4806 elif exact:
4806 elif exact:
4807 ui.warn(_('file not managed: %s\n') % rel)
4807 ui.warn(_('file not managed: %s\n') % rel)
4808 continue
4808 continue
4809 # file has not changed in dirstate
4809 # file has not changed in dirstate
4810 if node == parent:
4810 if node == parent:
4811 if exact:
4811 if exact:
4812 ui.warn(_('no changes needed to %s\n') % rel)
4812 ui.warn(_('no changes needed to %s\n') % rel)
4813 continue
4813 continue
4814 if pmf is None:
4814 if pmf is None:
4815 # only need parent manifest in this unlikely case,
4815 # only need parent manifest in this unlikely case,
4816 # so do not read by default
4816 # so do not read by default
4817 pmf = repo[parent].manifest()
4817 pmf = repo[parent].manifest()
4818 if abs in pmf and mfentry:
4818 if abs in pmf and mfentry:
4819 # if version of file is same in parent and target
4819 # if version of file is same in parent and target
4820 # manifests, do nothing
4820 # manifests, do nothing
4821 if (pmf[abs] != mfentry or
4821 if (pmf[abs] != mfentry or
4822 pmf.flags(abs) != mf.flags(abs)):
4822 pmf.flags(abs) != mf.flags(abs)):
4823 handle(revert, False)
4823 handle(revert, False)
4824 else:
4824 else:
4825 handle(remove, False)
4825 handle(remove, False)
4826
4826
4827 if not opts.get('dry_run'):
4827 if not opts.get('dry_run'):
4828 def checkout(f):
4828 def checkout(f):
4829 fc = ctx[f]
4829 fc = ctx[f]
4830 repo.wwrite(f, fc.data(), fc.flags())
4830 repo.wwrite(f, fc.data(), fc.flags())
4831
4831
4832 audit_path = scmutil.pathauditor(repo.root)
4832 audit_path = scmutil.pathauditor(repo.root)
4833 for f in remove[0]:
4833 for f in remove[0]:
4834 if repo.dirstate[f] == 'a':
4834 if repo.dirstate[f] == 'a':
4835 repo.dirstate.drop(f)
4835 repo.dirstate.drop(f)
4836 continue
4836 continue
4837 audit_path(f)
4837 audit_path(f)
4838 try:
4838 try:
4839 util.unlinkpath(repo.wjoin(f))
4839 util.unlinkpath(repo.wjoin(f))
4840 except OSError:
4840 except OSError:
4841 pass
4841 pass
4842 repo.dirstate.remove(f)
4842 repo.dirstate.remove(f)
4843
4843
4844 normal = None
4844 normal = None
4845 if node == parent:
4845 if node == parent:
4846 # We're reverting to our parent. If possible, we'd like status
4846 # We're reverting to our parent. If possible, we'd like status
4847 # to report the file as clean. We have to use normallookup for
4847 # to report the file as clean. We have to use normallookup for
4848 # merges to avoid losing information about merged/dirty files.
4848 # merges to avoid losing information about merged/dirty files.
4849 if p2 != nullid:
4849 if p2 != nullid:
4850 normal = repo.dirstate.normallookup
4850 normal = repo.dirstate.normallookup
4851 else:
4851 else:
4852 normal = repo.dirstate.normal
4852 normal = repo.dirstate.normal
4853 for f in revert[0]:
4853 for f in revert[0]:
4854 checkout(f)
4854 checkout(f)
4855 if normal:
4855 if normal:
4856 normal(f)
4856 normal(f)
4857
4857
4858 for f in add[0]:
4858 for f in add[0]:
4859 checkout(f)
4859 checkout(f)
4860 repo.dirstate.add(f)
4860 repo.dirstate.add(f)
4861
4861
4862 normal = repo.dirstate.normallookup
4862 normal = repo.dirstate.normallookup
4863 if node == parent and p2 == nullid:
4863 if node == parent and p2 == nullid:
4864 normal = repo.dirstate.normal
4864 normal = repo.dirstate.normal
4865 for f in undelete[0]:
4865 for f in undelete[0]:
4866 checkout(f)
4866 checkout(f)
4867 normal(f)
4867 normal(f)
4868
4868
4869 finally:
4869 finally:
4870 wlock.release()
4870 wlock.release()
4871
4871
4872 @command('rollback', dryrunopts +
4872 @command('rollback', dryrunopts +
4873 [('f', 'force', False, _('ignore safety measures'))])
4873 [('f', 'force', False, _('ignore safety measures'))])
4874 def rollback(ui, repo, **opts):
4874 def rollback(ui, repo, **opts):
4875 """roll back the last transaction (dangerous)
4875 """roll back the last transaction (dangerous)
4876
4876
4877 This command should be used with care. There is only one level of
4877 This command should be used with care. There is only one level of
4878 rollback, and there is no way to undo a rollback. It will also
4878 rollback, and there is no way to undo a rollback. It will also
4879 restore the dirstate at the time of the last transaction, losing
4879 restore the dirstate at the time of the last transaction, losing
4880 any dirstate changes since that time. This command does not alter
4880 any dirstate changes since that time. This command does not alter
4881 the working directory.
4881 the working directory.
4882
4882
4883 Transactions are used to encapsulate the effects of all commands
4883 Transactions are used to encapsulate the effects of all commands
4884 that create new changesets or propagate existing changesets into a
4884 that create new changesets or propagate existing changesets into a
4885 repository. For example, the following commands are transactional,
4885 repository. For example, the following commands are transactional,
4886 and their effects can be rolled back:
4886 and their effects can be rolled back:
4887
4887
4888 - commit
4888 - commit
4889 - import
4889 - import
4890 - pull
4890 - pull
4891 - push (with this repository as the destination)
4891 - push (with this repository as the destination)
4892 - unbundle
4892 - unbundle
4893
4893
4894 It's possible to lose data with rollback: commit, update back to
4894 It's possible to lose data with rollback: commit, update back to
4895 an older changeset, and then rollback. The update removes the
4895 an older changeset, and then rollback. The update removes the
4896 changes you committed from the working directory, and rollback
4896 changes you committed from the working directory, and rollback
4897 removes them from history. To avoid data loss, you must pass
4897 removes them from history. To avoid data loss, you must pass
4898 --force in this case.
4898 --force in this case.
4899
4899
4900 This command is not intended for use on public repositories. Once
4900 This command is not intended for use on public repositories. Once
4901 changes are visible for pull by other users, rolling a transaction
4901 changes are visible for pull by other users, rolling a transaction
4902 back locally is ineffective (someone else may already have pulled
4902 back locally is ineffective (someone else may already have pulled
4903 the changes). Furthermore, a race is possible with readers of the
4903 the changes). Furthermore, a race is possible with readers of the
4904 repository; for example an in-progress pull from the repository
4904 repository; for example an in-progress pull from the repository
4905 may fail if a rollback is performed.
4905 may fail if a rollback is performed.
4906
4906
4907 Returns 0 on success, 1 if no rollback data is available.
4907 Returns 0 on success, 1 if no rollback data is available.
4908 """
4908 """
4909 return repo.rollback(dryrun=opts.get('dry_run'),
4909 return repo.rollback(dryrun=opts.get('dry_run'),
4910 force=opts.get('force'))
4910 force=opts.get('force'))
4911
4911
4912 @command('root', [])
4912 @command('root', [])
4913 def root(ui, repo):
4913 def root(ui, repo):
4914 """print the root (top) of the current working directory
4914 """print the root (top) of the current working directory
4915
4915
4916 Print the root directory of the current repository.
4916 Print the root directory of the current repository.
4917
4917
4918 Returns 0 on success.
4918 Returns 0 on success.
4919 """
4919 """
4920 ui.write(repo.root + "\n")
4920 ui.write(repo.root + "\n")
4921
4921
4922 @command('^serve',
4922 @command('^serve',
4923 [('A', 'accesslog', '', _('name of access log file to write to'),
4923 [('A', 'accesslog', '', _('name of access log file to write to'),
4924 _('FILE')),
4924 _('FILE')),
4925 ('d', 'daemon', None, _('run server in background')),
4925 ('d', 'daemon', None, _('run server in background')),
4926 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('NUM')),
4926 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('NUM')),
4927 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4927 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4928 # use string type, then we can check if something was passed
4928 # use string type, then we can check if something was passed
4929 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4929 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4930 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4930 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4931 _('ADDR')),
4931 _('ADDR')),
4932 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4932 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4933 _('PREFIX')),
4933 _('PREFIX')),
4934 ('n', 'name', '',
4934 ('n', 'name', '',
4935 _('name to show in web pages (default: working directory)'), _('NAME')),
4935 _('name to show in web pages (default: working directory)'), _('NAME')),
4936 ('', 'web-conf', '',
4936 ('', 'web-conf', '',
4937 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
4937 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
4938 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4938 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4939 _('FILE')),
4939 _('FILE')),
4940 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4940 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4941 ('', 'stdio', None, _('for remote clients')),
4941 ('', 'stdio', None, _('for remote clients')),
4942 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
4942 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
4943 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4943 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4944 ('', 'style', '', _('template style to use'), _('STYLE')),
4944 ('', 'style', '', _('template style to use'), _('STYLE')),
4945 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4945 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4946 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
4946 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
4947 _('[OPTION]...'))
4947 _('[OPTION]...'))
4948 def serve(ui, repo, **opts):
4948 def serve(ui, repo, **opts):
4949 """start stand-alone webserver
4949 """start stand-alone webserver
4950
4950
4951 Start a local HTTP repository browser and pull server. You can use
4951 Start a local HTTP repository browser and pull server. You can use
4952 this for ad-hoc sharing and browsing of repositories. It is
4952 this for ad-hoc sharing and browsing of repositories. It is
4953 recommended to use a real web server to serve a repository for
4953 recommended to use a real web server to serve a repository for
4954 longer periods of time.
4954 longer periods of time.
4955
4955
4956 Please note that the server does not implement access control.
4956 Please note that the server does not implement access control.
4957 This means that, by default, anybody can read from the server and
4957 This means that, by default, anybody can read from the server and
4958 nobody can write to it by default. Set the ``web.allow_push``
4958 nobody can write to it by default. Set the ``web.allow_push``
4959 option to ``*`` to allow everybody to push to the server. You
4959 option to ``*`` to allow everybody to push to the server. You
4960 should use a real web server if you need to authenticate users.
4960 should use a real web server if you need to authenticate users.
4961
4961
4962 By default, the server logs accesses to stdout and errors to
4962 By default, the server logs accesses to stdout and errors to
4963 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4963 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4964 files.
4964 files.
4965
4965
4966 To have the server choose a free port number to listen on, specify
4966 To have the server choose a free port number to listen on, specify
4967 a port number of 0; in this case, the server will print the port
4967 a port number of 0; in this case, the server will print the port
4968 number it uses.
4968 number it uses.
4969
4969
4970 Returns 0 on success.
4970 Returns 0 on success.
4971 """
4971 """
4972
4972
4973 if opts["stdio"] and opts["cmdserver"]:
4973 if opts["stdio"] and opts["cmdserver"]:
4974 raise util.Abort(_("cannot use --stdio with --cmdserver"))
4974 raise util.Abort(_("cannot use --stdio with --cmdserver"))
4975
4975
4976 def checkrepo():
4976 def checkrepo():
4977 if repo is None:
4977 if repo is None:
4978 raise error.RepoError(_("There is no Mercurial repository here"
4978 raise error.RepoError(_("There is no Mercurial repository here"
4979 " (.hg not found)"))
4979 " (.hg not found)"))
4980
4980
4981 if opts["stdio"]:
4981 if opts["stdio"]:
4982 checkrepo()
4982 checkrepo()
4983 s = sshserver.sshserver(ui, repo)
4983 s = sshserver.sshserver(ui, repo)
4984 s.serve_forever()
4984 s.serve_forever()
4985
4985
4986 if opts["cmdserver"]:
4986 if opts["cmdserver"]:
4987 checkrepo()
4987 checkrepo()
4988 s = commandserver.server(ui, repo, opts["cmdserver"])
4988 s = commandserver.server(ui, repo, opts["cmdserver"])
4989 return s.serve()
4989 return s.serve()
4990
4990
4991 # this way we can check if something was given in the command-line
4991 # this way we can check if something was given in the command-line
4992 if opts.get('port'):
4992 if opts.get('port'):
4993 opts['port'] = util.getport(opts.get('port'))
4993 opts['port'] = util.getport(opts.get('port'))
4994
4994
4995 baseui = repo and repo.baseui or ui
4995 baseui = repo and repo.baseui or ui
4996 optlist = ("name templates style address port prefix ipv6"
4996 optlist = ("name templates style address port prefix ipv6"
4997 " accesslog errorlog certificate encoding")
4997 " accesslog errorlog certificate encoding")
4998 for o in optlist.split():
4998 for o in optlist.split():
4999 val = opts.get(o, '')
4999 val = opts.get(o, '')
5000 if val in (None, ''): # should check against default options instead
5000 if val in (None, ''): # should check against default options instead
5001 continue
5001 continue
5002 baseui.setconfig("web", o, val)
5002 baseui.setconfig("web", o, val)
5003 if repo and repo.ui != baseui:
5003 if repo and repo.ui != baseui:
5004 repo.ui.setconfig("web", o, val)
5004 repo.ui.setconfig("web", o, val)
5005
5005
5006 o = opts.get('web_conf') or opts.get('webdir_conf')
5006 o = opts.get('web_conf') or opts.get('webdir_conf')
5007 if not o:
5007 if not o:
5008 if not repo:
5008 if not repo:
5009 raise error.RepoError(_("There is no Mercurial repository"
5009 raise error.RepoError(_("There is no Mercurial repository"
5010 " here (.hg not found)"))
5010 " here (.hg not found)"))
5011 o = repo.root
5011 o = repo.root
5012
5012
5013 app = hgweb.hgweb(o, baseui=ui)
5013 app = hgweb.hgweb(o, baseui=ui)
5014
5014
5015 class service(object):
5015 class service(object):
5016 def init(self):
5016 def init(self):
5017 util.setsignalhandler()
5017 util.setsignalhandler()
5018 self.httpd = hgweb.server.create_server(ui, app)
5018 self.httpd = hgweb.server.create_server(ui, app)
5019
5019
5020 if opts['port'] and not ui.verbose:
5020 if opts['port'] and not ui.verbose:
5021 return
5021 return
5022
5022
5023 if self.httpd.prefix:
5023 if self.httpd.prefix:
5024 prefix = self.httpd.prefix.strip('/') + '/'
5024 prefix = self.httpd.prefix.strip('/') + '/'
5025 else:
5025 else:
5026 prefix = ''
5026 prefix = ''
5027
5027
5028 port = ':%d' % self.httpd.port
5028 port = ':%d' % self.httpd.port
5029 if port == ':80':
5029 if port == ':80':
5030 port = ''
5030 port = ''
5031
5031
5032 bindaddr = self.httpd.addr
5032 bindaddr = self.httpd.addr
5033 if bindaddr == '0.0.0.0':
5033 if bindaddr == '0.0.0.0':
5034 bindaddr = '*'
5034 bindaddr = '*'
5035 elif ':' in bindaddr: # IPv6
5035 elif ':' in bindaddr: # IPv6
5036 bindaddr = '[%s]' % bindaddr
5036 bindaddr = '[%s]' % bindaddr
5037
5037
5038 fqaddr = self.httpd.fqaddr
5038 fqaddr = self.httpd.fqaddr
5039 if ':' in fqaddr:
5039 if ':' in fqaddr:
5040 fqaddr = '[%s]' % fqaddr
5040 fqaddr = '[%s]' % fqaddr
5041 if opts['port']:
5041 if opts['port']:
5042 write = ui.status
5042 write = ui.status
5043 else:
5043 else:
5044 write = ui.write
5044 write = ui.write
5045 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
5045 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
5046 (fqaddr, port, prefix, bindaddr, self.httpd.port))
5046 (fqaddr, port, prefix, bindaddr, self.httpd.port))
5047
5047
5048 def run(self):
5048 def run(self):
5049 self.httpd.serve_forever()
5049 self.httpd.serve_forever()
5050
5050
5051 service = service()
5051 service = service()
5052
5052
5053 cmdutil.service(opts, initfn=service.init, runfn=service.run)
5053 cmdutil.service(opts, initfn=service.init, runfn=service.run)
5054
5054
5055 @command('showconfig|debugconfig',
5055 @command('showconfig|debugconfig',
5056 [('u', 'untrusted', None, _('show untrusted configuration options'))],
5056 [('u', 'untrusted', None, _('show untrusted configuration options'))],
5057 _('[-u] [NAME]...'))
5057 _('[-u] [NAME]...'))
5058 def showconfig(ui, repo, *values, **opts):
5058 def showconfig(ui, repo, *values, **opts):
5059 """show combined config settings from all hgrc files
5059 """show combined config settings from all hgrc files
5060
5060
5061 With no arguments, print names and values of all config items.
5061 With no arguments, print names and values of all config items.
5062
5062
5063 With one argument of the form section.name, print just the value
5063 With one argument of the form section.name, print just the value
5064 of that config item.
5064 of that config item.
5065
5065
5066 With multiple arguments, print names and values of all config
5066 With multiple arguments, print names and values of all config
5067 items with matching section names.
5067 items with matching section names.
5068
5068
5069 With --debug, the source (filename and line number) is printed
5069 With --debug, the source (filename and line number) is printed
5070 for each config item.
5070 for each config item.
5071
5071
5072 Returns 0 on success.
5072 Returns 0 on success.
5073 """
5073 """
5074
5074
5075 for f in scmutil.rcpath():
5075 for f in scmutil.rcpath():
5076 ui.debug('read config from: %s\n' % f)
5076 ui.debug('read config from: %s\n' % f)
5077 untrusted = bool(opts.get('untrusted'))
5077 untrusted = bool(opts.get('untrusted'))
5078 if values:
5078 if values:
5079 sections = [v for v in values if '.' not in v]
5079 sections = [v for v in values if '.' not in v]
5080 items = [v for v in values if '.' in v]
5080 items = [v for v in values if '.' in v]
5081 if len(items) > 1 or items and sections:
5081 if len(items) > 1 or items and sections:
5082 raise util.Abort(_('only one config item permitted'))
5082 raise util.Abort(_('only one config item permitted'))
5083 for section, name, value in ui.walkconfig(untrusted=untrusted):
5083 for section, name, value in ui.walkconfig(untrusted=untrusted):
5084 value = str(value).replace('\n', '\\n')
5084 value = str(value).replace('\n', '\\n')
5085 sectname = section + '.' + name
5085 sectname = section + '.' + name
5086 if values:
5086 if values:
5087 for v in values:
5087 for v in values:
5088 if v == section:
5088 if v == section:
5089 ui.debug('%s: ' %
5089 ui.debug('%s: ' %
5090 ui.configsource(section, name, untrusted))
5090 ui.configsource(section, name, untrusted))
5091 ui.write('%s=%s\n' % (sectname, value))
5091 ui.write('%s=%s\n' % (sectname, value))
5092 elif v == sectname:
5092 elif v == sectname:
5093 ui.debug('%s: ' %
5093 ui.debug('%s: ' %
5094 ui.configsource(section, name, untrusted))
5094 ui.configsource(section, name, untrusted))
5095 ui.write(value, '\n')
5095 ui.write(value, '\n')
5096 else:
5096 else:
5097 ui.debug('%s: ' %
5097 ui.debug('%s: ' %
5098 ui.configsource(section, name, untrusted))
5098 ui.configsource(section, name, untrusted))
5099 ui.write('%s=%s\n' % (sectname, value))
5099 ui.write('%s=%s\n' % (sectname, value))
5100
5100
5101 @command('^status|st',
5101 @command('^status|st',
5102 [('A', 'all', None, _('show status of all files')),
5102 [('A', 'all', None, _('show status of all files')),
5103 ('m', 'modified', None, _('show only modified files')),
5103 ('m', 'modified', None, _('show only modified files')),
5104 ('a', 'added', None, _('show only added files')),
5104 ('a', 'added', None, _('show only added files')),
5105 ('r', 'removed', None, _('show only removed files')),
5105 ('r', 'removed', None, _('show only removed files')),
5106 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5106 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5107 ('c', 'clean', None, _('show only files without changes')),
5107 ('c', 'clean', None, _('show only files without changes')),
5108 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5108 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5109 ('i', 'ignored', None, _('show only ignored files')),
5109 ('i', 'ignored', None, _('show only ignored files')),
5110 ('n', 'no-status', None, _('hide status prefix')),
5110 ('n', 'no-status', None, _('hide status prefix')),
5111 ('C', 'copies', None, _('show source of copied files')),
5111 ('C', 'copies', None, _('show source of copied files')),
5112 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5112 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5113 ('', 'rev', [], _('show difference from revision'), _('REV')),
5113 ('', 'rev', [], _('show difference from revision'), _('REV')),
5114 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5114 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5115 ] + walkopts + subrepoopts,
5115 ] + walkopts + subrepoopts,
5116 _('[OPTION]... [FILE]...'))
5116 _('[OPTION]... [FILE]...'))
5117 def status(ui, repo, *pats, **opts):
5117 def status(ui, repo, *pats, **opts):
5118 """show changed files in the working directory
5118 """show changed files in the working directory
5119
5119
5120 Show status of files in the repository. If names are given, only
5120 Show status of files in the repository. If names are given, only
5121 files that match are shown. Files that are clean or ignored or
5121 files that match are shown. Files that are clean or ignored or
5122 the source of a copy/move operation, are not listed unless
5122 the source of a copy/move operation, are not listed unless
5123 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5123 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5124 Unless options described with "show only ..." are given, the
5124 Unless options described with "show only ..." are given, the
5125 options -mardu are used.
5125 options -mardu are used.
5126
5126
5127 Option -q/--quiet hides untracked (unknown and ignored) files
5127 Option -q/--quiet hides untracked (unknown and ignored) files
5128 unless explicitly requested with -u/--unknown or -i/--ignored.
5128 unless explicitly requested with -u/--unknown or -i/--ignored.
5129
5129
5130 .. note::
5130 .. note::
5131 status may appear to disagree with diff if permissions have
5131 status may appear to disagree with diff if permissions have
5132 changed or a merge has occurred. The standard diff format does
5132 changed or a merge has occurred. The standard diff format does
5133 not report permission changes and diff only reports changes
5133 not report permission changes and diff only reports changes
5134 relative to one merge parent.
5134 relative to one merge parent.
5135
5135
5136 If one revision is given, it is used as the base revision.
5136 If one revision is given, it is used as the base revision.
5137 If two revisions are given, the differences between them are
5137 If two revisions are given, the differences between them are
5138 shown. The --change option can also be used as a shortcut to list
5138 shown. The --change option can also be used as a shortcut to list
5139 the changed files of a revision from its first parent.
5139 the changed files of a revision from its first parent.
5140
5140
5141 The codes used to show the status of files are::
5141 The codes used to show the status of files are::
5142
5142
5143 M = modified
5143 M = modified
5144 A = added
5144 A = added
5145 R = removed
5145 R = removed
5146 C = clean
5146 C = clean
5147 ! = missing (deleted by non-hg command, but still tracked)
5147 ! = missing (deleted by non-hg command, but still tracked)
5148 ? = not tracked
5148 ? = not tracked
5149 I = ignored
5149 I = ignored
5150 = origin of the previous file listed as A (added)
5150 = origin of the previous file listed as A (added)
5151
5151
5152 .. container:: verbose
5152 .. container:: verbose
5153
5153
5154 Examples:
5154 Examples:
5155
5155
5156 - show changes in the working directory relative to a changeset:
5156 - show changes in the working directory relative to a changeset:
5157
5157
5158 hg status --rev 9353
5158 hg status --rev 9353
5159
5159
5160 - show all changes including copies in an existing changeset::
5160 - show all changes including copies in an existing changeset::
5161
5161
5162 hg status --copies --change 9353
5162 hg status --copies --change 9353
5163
5163
5164 - get a NUL separated list of added files, suitable for xargs::
5164 - get a NUL separated list of added files, suitable for xargs::
5165
5165
5166 hg status -an0
5166 hg status -an0
5167
5167
5168 Returns 0 on success.
5168 Returns 0 on success.
5169 """
5169 """
5170
5170
5171 revs = opts.get('rev')
5171 revs = opts.get('rev')
5172 change = opts.get('change')
5172 change = opts.get('change')
5173
5173
5174 if revs and change:
5174 if revs and change:
5175 msg = _('cannot specify --rev and --change at the same time')
5175 msg = _('cannot specify --rev and --change at the same time')
5176 raise util.Abort(msg)
5176 raise util.Abort(msg)
5177 elif change:
5177 elif change:
5178 node2 = scmutil.revsingle(repo, change, None).node()
5178 node2 = scmutil.revsingle(repo, change, None).node()
5179 node1 = repo[node2].p1().node()
5179 node1 = repo[node2].p1().node()
5180 else:
5180 else:
5181 node1, node2 = scmutil.revpair(repo, revs)
5181 node1, node2 = scmutil.revpair(repo, revs)
5182
5182
5183 cwd = (pats and repo.getcwd()) or ''
5183 cwd = (pats and repo.getcwd()) or ''
5184 end = opts.get('print0') and '\0' or '\n'
5184 end = opts.get('print0') and '\0' or '\n'
5185 copy = {}
5185 copy = {}
5186 states = 'modified added removed deleted unknown ignored clean'.split()
5186 states = 'modified added removed deleted unknown ignored clean'.split()
5187 show = [k for k in states if opts.get(k)]
5187 show = [k for k in states if opts.get(k)]
5188 if opts.get('all'):
5188 if opts.get('all'):
5189 show += ui.quiet and (states[:4] + ['clean']) or states
5189 show += ui.quiet and (states[:4] + ['clean']) or states
5190 if not show:
5190 if not show:
5191 show = ui.quiet and states[:4] or states[:5]
5191 show = ui.quiet and states[:4] or states[:5]
5192
5192
5193 stat = repo.status(node1, node2, scmutil.match(repo[node2], pats, opts),
5193 stat = repo.status(node1, node2, scmutil.match(repo[node2], pats, opts),
5194 'ignored' in show, 'clean' in show, 'unknown' in show,
5194 'ignored' in show, 'clean' in show, 'unknown' in show,
5195 opts.get('subrepos'))
5195 opts.get('subrepos'))
5196 changestates = zip(states, 'MAR!?IC', stat)
5196 changestates = zip(states, 'MAR!?IC', stat)
5197
5197
5198 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
5198 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
5199 ctxn = repo[nullid]
5199 ctxn = repo[nullid]
5200 ctx1 = repo[node1]
5200 ctx1 = repo[node1]
5201 ctx2 = repo[node2]
5201 ctx2 = repo[node2]
5202 added = stat[1]
5202 added = stat[1]
5203 if node2 is None:
5203 if node2 is None:
5204 added = stat[0] + stat[1] # merged?
5204 added = stat[0] + stat[1] # merged?
5205
5205
5206 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
5206 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
5207 if k in added:
5207 if k in added:
5208 copy[k] = v
5208 copy[k] = v
5209 elif v in added:
5209 elif v in added:
5210 copy[v] = k
5210 copy[v] = k
5211
5211
5212 for state, char, files in changestates:
5212 for state, char, files in changestates:
5213 if state in show:
5213 if state in show:
5214 format = "%s %%s%s" % (char, end)
5214 format = "%s %%s%s" % (char, end)
5215 if opts.get('no_status'):
5215 if opts.get('no_status'):
5216 format = "%%s%s" % end
5216 format = "%%s%s" % end
5217
5217
5218 for f in files:
5218 for f in files:
5219 ui.write(format % repo.pathto(f, cwd),
5219 ui.write(format % repo.pathto(f, cwd),
5220 label='status.' + state)
5220 label='status.' + state)
5221 if f in copy:
5221 if f in copy:
5222 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
5222 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
5223 label='status.copied')
5223 label='status.copied')
5224
5224
5225 @command('^summary|sum',
5225 @command('^summary|sum',
5226 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5226 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5227 def summary(ui, repo, **opts):
5227 def summary(ui, repo, **opts):
5228 """summarize working directory state
5228 """summarize working directory state
5229
5229
5230 This generates a brief summary of the working directory state,
5230 This generates a brief summary of the working directory state,
5231 including parents, branch, commit status, and available updates.
5231 including parents, branch, commit status, and available updates.
5232
5232
5233 With the --remote option, this will check the default paths for
5233 With the --remote option, this will check the default paths for
5234 incoming and outgoing changes. This can be time-consuming.
5234 incoming and outgoing changes. This can be time-consuming.
5235
5235
5236 Returns 0 on success.
5236 Returns 0 on success.
5237 """
5237 """
5238
5238
5239 ctx = repo[None]
5239 ctx = repo[None]
5240 parents = ctx.parents()
5240 parents = ctx.parents()
5241 pnode = parents[0].node()
5241 pnode = parents[0].node()
5242 marks = []
5242 marks = []
5243
5243
5244 for p in parents:
5244 for p in parents:
5245 # label with log.changeset (instead of log.parent) since this
5245 # label with log.changeset (instead of log.parent) since this
5246 # shows a working directory parent *changeset*:
5246 # shows a working directory parent *changeset*:
5247 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5247 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5248 label='log.changeset')
5248 label='log.changeset')
5249 ui.write(' '.join(p.tags()), label='log.tag')
5249 ui.write(' '.join(p.tags()), label='log.tag')
5250 if p.bookmarks():
5250 if p.bookmarks():
5251 marks.extend(p.bookmarks())
5251 marks.extend(p.bookmarks())
5252 if p.rev() == -1:
5252 if p.rev() == -1:
5253 if not len(repo):
5253 if not len(repo):
5254 ui.write(_(' (empty repository)'))
5254 ui.write(_(' (empty repository)'))
5255 else:
5255 else:
5256 ui.write(_(' (no revision checked out)'))
5256 ui.write(_(' (no revision checked out)'))
5257 ui.write('\n')
5257 ui.write('\n')
5258 if p.description():
5258 if p.description():
5259 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5259 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5260 label='log.summary')
5260 label='log.summary')
5261
5261
5262 branch = ctx.branch()
5262 branch = ctx.branch()
5263 bheads = repo.branchheads(branch)
5263 bheads = repo.branchheads(branch)
5264 m = _('branch: %s\n') % branch
5264 m = _('branch: %s\n') % branch
5265 if branch != 'default':
5265 if branch != 'default':
5266 ui.write(m, label='log.branch')
5266 ui.write(m, label='log.branch')
5267 else:
5267 else:
5268 ui.status(m, label='log.branch')
5268 ui.status(m, label='log.branch')
5269
5269
5270 if marks:
5270 if marks:
5271 current = repo._bookmarkcurrent
5271 current = repo._bookmarkcurrent
5272 ui.write(_('bookmarks:'), label='log.bookmark')
5272 ui.write(_('bookmarks:'), label='log.bookmark')
5273 if current is not None:
5273 if current is not None:
5274 try:
5274 try:
5275 marks.remove(current)
5275 marks.remove(current)
5276 ui.write(' *' + current, label='bookmarks.current')
5276 ui.write(' *' + current, label='bookmarks.current')
5277 except ValueError:
5277 except ValueError:
5278 # current bookmark not in parent ctx marks
5278 # current bookmark not in parent ctx marks
5279 pass
5279 pass
5280 for m in marks:
5280 for m in marks:
5281 ui.write(' ' + m, label='log.bookmark')
5281 ui.write(' ' + m, label='log.bookmark')
5282 ui.write('\n', label='log.bookmark')
5282 ui.write('\n', label='log.bookmark')
5283
5283
5284 st = list(repo.status(unknown=True))[:6]
5284 st = list(repo.status(unknown=True))[:6]
5285
5285
5286 c = repo.dirstate.copies()
5286 c = repo.dirstate.copies()
5287 copied, renamed = [], []
5287 copied, renamed = [], []
5288 for d, s in c.iteritems():
5288 for d, s in c.iteritems():
5289 if s in st[2]:
5289 if s in st[2]:
5290 st[2].remove(s)
5290 st[2].remove(s)
5291 renamed.append(d)
5291 renamed.append(d)
5292 else:
5292 else:
5293 copied.append(d)
5293 copied.append(d)
5294 if d in st[1]:
5294 if d in st[1]:
5295 st[1].remove(d)
5295 st[1].remove(d)
5296 st.insert(3, renamed)
5296 st.insert(3, renamed)
5297 st.insert(4, copied)
5297 st.insert(4, copied)
5298
5298
5299 ms = mergemod.mergestate(repo)
5299 ms = mergemod.mergestate(repo)
5300 st.append([f for f in ms if ms[f] == 'u'])
5300 st.append([f for f in ms if ms[f] == 'u'])
5301
5301
5302 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5302 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5303 st.append(subs)
5303 st.append(subs)
5304
5304
5305 labels = [ui.label(_('%d modified'), 'status.modified'),
5305 labels = [ui.label(_('%d modified'), 'status.modified'),
5306 ui.label(_('%d added'), 'status.added'),
5306 ui.label(_('%d added'), 'status.added'),
5307 ui.label(_('%d removed'), 'status.removed'),
5307 ui.label(_('%d removed'), 'status.removed'),
5308 ui.label(_('%d renamed'), 'status.copied'),
5308 ui.label(_('%d renamed'), 'status.copied'),
5309 ui.label(_('%d copied'), 'status.copied'),
5309 ui.label(_('%d copied'), 'status.copied'),
5310 ui.label(_('%d deleted'), 'status.deleted'),
5310 ui.label(_('%d deleted'), 'status.deleted'),
5311 ui.label(_('%d unknown'), 'status.unknown'),
5311 ui.label(_('%d unknown'), 'status.unknown'),
5312 ui.label(_('%d ignored'), 'status.ignored'),
5312 ui.label(_('%d ignored'), 'status.ignored'),
5313 ui.label(_('%d unresolved'), 'resolve.unresolved'),
5313 ui.label(_('%d unresolved'), 'resolve.unresolved'),
5314 ui.label(_('%d subrepos'), 'status.modified')]
5314 ui.label(_('%d subrepos'), 'status.modified')]
5315 t = []
5315 t = []
5316 for s, l in zip(st, labels):
5316 for s, l in zip(st, labels):
5317 if s:
5317 if s:
5318 t.append(l % len(s))
5318 t.append(l % len(s))
5319
5319
5320 t = ', '.join(t)
5320 t = ', '.join(t)
5321 cleanworkdir = False
5321 cleanworkdir = False
5322
5322
5323 if len(parents) > 1:
5323 if len(parents) > 1:
5324 t += _(' (merge)')
5324 t += _(' (merge)')
5325 elif branch != parents[0].branch():
5325 elif branch != parents[0].branch():
5326 t += _(' (new branch)')
5326 t += _(' (new branch)')
5327 elif (parents[0].extra().get('close') and
5327 elif (parents[0].extra().get('close') and
5328 pnode in repo.branchheads(branch, closed=True)):
5328 pnode in repo.branchheads(branch, closed=True)):
5329 t += _(' (head closed)')
5329 t += _(' (head closed)')
5330 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
5330 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
5331 t += _(' (clean)')
5331 t += _(' (clean)')
5332 cleanworkdir = True
5332 cleanworkdir = True
5333 elif pnode not in bheads:
5333 elif pnode not in bheads:
5334 t += _(' (new branch head)')
5334 t += _(' (new branch head)')
5335
5335
5336 if cleanworkdir:
5336 if cleanworkdir:
5337 ui.status(_('commit: %s\n') % t.strip())
5337 ui.status(_('commit: %s\n') % t.strip())
5338 else:
5338 else:
5339 ui.write(_('commit: %s\n') % t.strip())
5339 ui.write(_('commit: %s\n') % t.strip())
5340
5340
5341 # all ancestors of branch heads - all ancestors of parent = new csets
5341 # all ancestors of branch heads - all ancestors of parent = new csets
5342 new = [0] * len(repo)
5342 new = [0] * len(repo)
5343 cl = repo.changelog
5343 cl = repo.changelog
5344 for a in [cl.rev(n) for n in bheads]:
5344 for a in [cl.rev(n) for n in bheads]:
5345 new[a] = 1
5345 new[a] = 1
5346 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
5346 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
5347 new[a] = 1
5347 new[a] = 1
5348 for a in [p.rev() for p in parents]:
5348 for a in [p.rev() for p in parents]:
5349 if a >= 0:
5349 if a >= 0:
5350 new[a] = 0
5350 new[a] = 0
5351 for a in cl.ancestors(*[p.rev() for p in parents]):
5351 for a in cl.ancestors(*[p.rev() for p in parents]):
5352 new[a] = 0
5352 new[a] = 0
5353 new = sum(new)
5353 new = sum(new)
5354
5354
5355 if new == 0:
5355 if new == 0:
5356 ui.status(_('update: (current)\n'))
5356 ui.status(_('update: (current)\n'))
5357 elif pnode not in bheads:
5357 elif pnode not in bheads:
5358 ui.write(_('update: %d new changesets (update)\n') % new)
5358 ui.write(_('update: %d new changesets (update)\n') % new)
5359 else:
5359 else:
5360 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5360 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5361 (new, len(bheads)))
5361 (new, len(bheads)))
5362
5362
5363 if opts.get('remote'):
5363 if opts.get('remote'):
5364 t = []
5364 t = []
5365 source, branches = hg.parseurl(ui.expandpath('default'))
5365 source, branches = hg.parseurl(ui.expandpath('default'))
5366 other = hg.peer(repo, {}, source)
5366 other = hg.peer(repo, {}, source)
5367 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
5367 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
5368 ui.debug('comparing with %s\n' % util.hidepassword(source))
5368 ui.debug('comparing with %s\n' % util.hidepassword(source))
5369 repo.ui.pushbuffer()
5369 repo.ui.pushbuffer()
5370 commoninc = discovery.findcommonincoming(repo, other)
5370 commoninc = discovery.findcommonincoming(repo, other)
5371 _common, incoming, _rheads = commoninc
5371 _common, incoming, _rheads = commoninc
5372 repo.ui.popbuffer()
5372 repo.ui.popbuffer()
5373 if incoming:
5373 if incoming:
5374 t.append(_('1 or more incoming'))
5374 t.append(_('1 or more incoming'))
5375
5375
5376 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5376 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5377 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5377 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5378 if source != dest:
5378 if source != dest:
5379 other = hg.peer(repo, {}, dest)
5379 other = hg.peer(repo, {}, dest)
5380 commoninc = None
5380 commoninc = None
5381 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5381 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5382 repo.ui.pushbuffer()
5382 repo.ui.pushbuffer()
5383 common, outheads = discovery.findcommonoutgoing(repo, other,
5383 common, outheads = discovery.findcommonoutgoing(repo, other,
5384 commoninc=commoninc)
5384 commoninc=commoninc)
5385 repo.ui.popbuffer()
5385 repo.ui.popbuffer()
5386 o = repo.changelog.findmissing(common=common, heads=outheads)
5386 o = repo.changelog.findmissing(common=common, heads=outheads)
5387 if o:
5387 if o:
5388 t.append(_('%d outgoing') % len(o))
5388 t.append(_('%d outgoing') % len(o))
5389 if 'bookmarks' in other.listkeys('namespaces'):
5389 if 'bookmarks' in other.listkeys('namespaces'):
5390 lmarks = repo.listkeys('bookmarks')
5390 lmarks = repo.listkeys('bookmarks')
5391 rmarks = other.listkeys('bookmarks')
5391 rmarks = other.listkeys('bookmarks')
5392 diff = set(rmarks) - set(lmarks)
5392 diff = set(rmarks) - set(lmarks)
5393 if len(diff) > 0:
5393 if len(diff) > 0:
5394 t.append(_('%d incoming bookmarks') % len(diff))
5394 t.append(_('%d incoming bookmarks') % len(diff))
5395 diff = set(lmarks) - set(rmarks)
5395 diff = set(lmarks) - set(rmarks)
5396 if len(diff) > 0:
5396 if len(diff) > 0:
5397 t.append(_('%d outgoing bookmarks') % len(diff))
5397 t.append(_('%d outgoing bookmarks') % len(diff))
5398
5398
5399 if t:
5399 if t:
5400 ui.write(_('remote: %s\n') % (', '.join(t)))
5400 ui.write(_('remote: %s\n') % (', '.join(t)))
5401 else:
5401 else:
5402 ui.status(_('remote: (synced)\n'))
5402 ui.status(_('remote: (synced)\n'))
5403
5403
5404 @command('tag',
5404 @command('tag',
5405 [('f', 'force', None, _('force tag')),
5405 [('f', 'force', None, _('force tag')),
5406 ('l', 'local', None, _('make the tag local')),
5406 ('l', 'local', None, _('make the tag local')),
5407 ('r', 'rev', '', _('revision to tag'), _('REV')),
5407 ('r', 'rev', '', _('revision to tag'), _('REV')),
5408 ('', 'remove', None, _('remove a tag')),
5408 ('', 'remove', None, _('remove a tag')),
5409 # -l/--local is already there, commitopts cannot be used
5409 # -l/--local is already there, commitopts cannot be used
5410 ('e', 'edit', None, _('edit commit message')),
5410 ('e', 'edit', None, _('edit commit message')),
5411 ('m', 'message', '', _('use <text> as commit message'), _('TEXT')),
5411 ('m', 'message', '', _('use <text> as commit message'), _('TEXT')),
5412 ] + commitopts2,
5412 ] + commitopts2,
5413 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5413 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5414 def tag(ui, repo, name1, *names, **opts):
5414 def tag(ui, repo, name1, *names, **opts):
5415 """add one or more tags for the current or given revision
5415 """add one or more tags for the current or given revision
5416
5416
5417 Name a particular revision using <name>.
5417 Name a particular revision using <name>.
5418
5418
5419 Tags are used to name particular revisions of the repository and are
5419 Tags are used to name particular revisions of the repository and are
5420 very useful to compare different revisions, to go back to significant
5420 very useful to compare different revisions, to go back to significant
5421 earlier versions or to mark branch points as releases, etc. Changing
5421 earlier versions or to mark branch points as releases, etc. Changing
5422 an existing tag is normally disallowed; use -f/--force to override.
5422 an existing tag is normally disallowed; use -f/--force to override.
5423
5423
5424 If no revision is given, the parent of the working directory is
5424 If no revision is given, the parent of the working directory is
5425 used, or tip if no revision is checked out.
5425 used, or tip if no revision is checked out.
5426
5426
5427 To facilitate version control, distribution, and merging of tags,
5427 To facilitate version control, distribution, and merging of tags,
5428 they are stored as a file named ".hgtags" which is managed similarly
5428 they are stored as a file named ".hgtags" which is managed similarly
5429 to other project files and can be hand-edited if necessary. This
5429 to other project files and can be hand-edited if necessary. This
5430 also means that tagging creates a new commit. The file
5430 also means that tagging creates a new commit. The file
5431 ".hg/localtags" is used for local tags (not shared among
5431 ".hg/localtags" is used for local tags (not shared among
5432 repositories).
5432 repositories).
5433
5433
5434 Tag commits are usually made at the head of a branch. If the parent
5434 Tag commits are usually made at the head of a branch. If the parent
5435 of the working directory is not a branch head, :hg:`tag` aborts; use
5435 of the working directory is not a branch head, :hg:`tag` aborts; use
5436 -f/--force to force the tag commit to be based on a non-head
5436 -f/--force to force the tag commit to be based on a non-head
5437 changeset.
5437 changeset.
5438
5438
5439 See :hg:`help dates` for a list of formats valid for -d/--date.
5439 See :hg:`help dates` for a list of formats valid for -d/--date.
5440
5440
5441 Since tag names have priority over branch names during revision
5441 Since tag names have priority over branch names during revision
5442 lookup, using an existing branch name as a tag name is discouraged.
5442 lookup, using an existing branch name as a tag name is discouraged.
5443
5443
5444 Returns 0 on success.
5444 Returns 0 on success.
5445 """
5445 """
5446
5446
5447 rev_ = "."
5447 rev_ = "."
5448 names = [t.strip() for t in (name1,) + names]
5448 names = [t.strip() for t in (name1,) + names]
5449 if len(names) != len(set(names)):
5449 if len(names) != len(set(names)):
5450 raise util.Abort(_('tag names must be unique'))
5450 raise util.Abort(_('tag names must be unique'))
5451 for n in names:
5451 for n in names:
5452 if n in ['tip', '.', 'null']:
5452 if n in ['tip', '.', 'null']:
5453 raise util.Abort(_("the name '%s' is reserved") % n)
5453 raise util.Abort(_("the name '%s' is reserved") % n)
5454 if not n:
5454 if not n:
5455 raise util.Abort(_('tag names cannot consist entirely of whitespace'))
5455 raise util.Abort(_('tag names cannot consist entirely of whitespace'))
5456 if opts.get('rev') and opts.get('remove'):
5456 if opts.get('rev') and opts.get('remove'):
5457 raise util.Abort(_("--rev and --remove are incompatible"))
5457 raise util.Abort(_("--rev and --remove are incompatible"))
5458 if opts.get('rev'):
5458 if opts.get('rev'):
5459 rev_ = opts['rev']
5459 rev_ = opts['rev']
5460 message = opts.get('message')
5460 message = opts.get('message')
5461 if opts.get('remove'):
5461 if opts.get('remove'):
5462 expectedtype = opts.get('local') and 'local' or 'global'
5462 expectedtype = opts.get('local') and 'local' or 'global'
5463 for n in names:
5463 for n in names:
5464 if not repo.tagtype(n):
5464 if not repo.tagtype(n):
5465 raise util.Abort(_("tag '%s' does not exist") % n)
5465 raise util.Abort(_("tag '%s' does not exist") % n)
5466 if repo.tagtype(n) != expectedtype:
5466 if repo.tagtype(n) != expectedtype:
5467 if expectedtype == 'global':
5467 if expectedtype == 'global':
5468 raise util.Abort(_("tag '%s' is not a global tag") % n)
5468 raise util.Abort(_("tag '%s' is not a global tag") % n)
5469 else:
5469 else:
5470 raise util.Abort(_("tag '%s' is not a local tag") % n)
5470 raise util.Abort(_("tag '%s' is not a local tag") % n)
5471 rev_ = nullid
5471 rev_ = nullid
5472 if not message:
5472 if not message:
5473 # we don't translate commit messages
5473 # we don't translate commit messages
5474 message = 'Removed tag %s' % ', '.join(names)
5474 message = 'Removed tag %s' % ', '.join(names)
5475 elif not opts.get('force'):
5475 elif not opts.get('force'):
5476 for n in names:
5476 for n in names:
5477 if n in repo.tags():
5477 if n in repo.tags():
5478 raise util.Abort(_("tag '%s' already exists "
5478 raise util.Abort(_("tag '%s' already exists "
5479 "(use -f to force)") % n)
5479 "(use -f to force)") % n)
5480 if not opts.get('local'):
5480 if not opts.get('local'):
5481 p1, p2 = repo.dirstate.parents()
5481 p1, p2 = repo.dirstate.parents()
5482 if p2 != nullid:
5482 if p2 != nullid:
5483 raise util.Abort(_('uncommitted merge'))
5483 raise util.Abort(_('uncommitted merge'))
5484 bheads = repo.branchheads()
5484 bheads = repo.branchheads()
5485 if not opts.get('force') and bheads and p1 not in bheads:
5485 if not opts.get('force') and bheads and p1 not in bheads:
5486 raise util.Abort(_('not at a branch head (use -f to force)'))
5486 raise util.Abort(_('not at a branch head (use -f to force)'))
5487 r = scmutil.revsingle(repo, rev_).node()
5487 r = scmutil.revsingle(repo, rev_).node()
5488
5488
5489 if not message:
5489 if not message:
5490 # we don't translate commit messages
5490 # we don't translate commit messages
5491 message = ('Added tag %s for changeset %s' %
5491 message = ('Added tag %s for changeset %s' %
5492 (', '.join(names), short(r)))
5492 (', '.join(names), short(r)))
5493
5493
5494 date = opts.get('date')
5494 date = opts.get('date')
5495 if date:
5495 if date:
5496 date = util.parsedate(date)
5496 date = util.parsedate(date)
5497
5497
5498 if opts.get('edit'):
5498 if opts.get('edit'):
5499 message = ui.edit(message, ui.username())
5499 message = ui.edit(message, ui.username())
5500
5500
5501 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
5501 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
5502
5502
5503 @command('tags', [], '')
5503 @command('tags', [], '')
5504 def tags(ui, repo):
5504 def tags(ui, repo):
5505 """list repository tags
5505 """list repository tags
5506
5506
5507 This lists both regular and local tags. When the -v/--verbose
5507 This lists both regular and local tags. When the -v/--verbose
5508 switch is used, a third column "local" is printed for local tags.
5508 switch is used, a third column "local" is printed for local tags.
5509
5509
5510 Returns 0 on success.
5510 Returns 0 on success.
5511 """
5511 """
5512
5512
5513 hexfunc = ui.debugflag and hex or short
5513 hexfunc = ui.debugflag and hex or short
5514 tagtype = ""
5514 tagtype = ""
5515
5515
5516 for t, n in reversed(repo.tagslist()):
5516 for t, n in reversed(repo.tagslist()):
5517 if ui.quiet:
5517 if ui.quiet:
5518 ui.write("%s\n" % t, label='tags.normal')
5518 ui.write("%s\n" % t, label='tags.normal')
5519 continue
5519 continue
5520
5520
5521 hn = hexfunc(n)
5521 hn = hexfunc(n)
5522 r = "%5d:%s" % (repo.changelog.rev(n), hn)
5522 r = "%5d:%s" % (repo.changelog.rev(n), hn)
5523 rev = ui.label(r, 'log.changeset')
5523 rev = ui.label(r, 'log.changeset')
5524 spaces = " " * (30 - encoding.colwidth(t))
5524 spaces = " " * (30 - encoding.colwidth(t))
5525
5525
5526 tag = ui.label(t, 'tags.normal')
5526 tag = ui.label(t, 'tags.normal')
5527 if ui.verbose:
5527 if ui.verbose:
5528 if repo.tagtype(t) == 'local':
5528 if repo.tagtype(t) == 'local':
5529 tagtype = " local"
5529 tagtype = " local"
5530 tag = ui.label(t, 'tags.local')
5530 tag = ui.label(t, 'tags.local')
5531 else:
5531 else:
5532 tagtype = ""
5532 tagtype = ""
5533 ui.write("%s%s %s%s\n" % (tag, spaces, rev, tagtype))
5533 ui.write("%s%s %s%s\n" % (tag, spaces, rev, tagtype))
5534
5534
5535 @command('tip',
5535 @command('tip',
5536 [('p', 'patch', None, _('show patch')),
5536 [('p', 'patch', None, _('show patch')),
5537 ('g', 'git', None, _('use git extended diff format')),
5537 ('g', 'git', None, _('use git extended diff format')),
5538 ] + templateopts,
5538 ] + templateopts,
5539 _('[-p] [-g]'))
5539 _('[-p] [-g]'))
5540 def tip(ui, repo, **opts):
5540 def tip(ui, repo, **opts):
5541 """show the tip revision
5541 """show the tip revision
5542
5542
5543 The tip revision (usually just called the tip) is the changeset
5543 The tip revision (usually just called the tip) is the changeset
5544 most recently added to the repository (and therefore the most
5544 most recently added to the repository (and therefore the most
5545 recently changed head).
5545 recently changed head).
5546
5546
5547 If you have just made a commit, that commit will be the tip. If
5547 If you have just made a commit, that commit will be the tip. If
5548 you have just pulled changes from another repository, the tip of
5548 you have just pulled changes from another repository, the tip of
5549 that repository becomes the current tip. The "tip" tag is special
5549 that repository becomes the current tip. The "tip" tag is special
5550 and cannot be renamed or assigned to a different changeset.
5550 and cannot be renamed or assigned to a different changeset.
5551
5551
5552 Returns 0 on success.
5552 Returns 0 on success.
5553 """
5553 """
5554 displayer = cmdutil.show_changeset(ui, repo, opts)
5554 displayer = cmdutil.show_changeset(ui, repo, opts)
5555 displayer.show(repo[len(repo) - 1])
5555 displayer.show(repo[len(repo) - 1])
5556 displayer.close()
5556 displayer.close()
5557
5557
5558 @command('unbundle',
5558 @command('unbundle',
5559 [('u', 'update', None,
5559 [('u', 'update', None,
5560 _('update to new branch head if changesets were unbundled'))],
5560 _('update to new branch head if changesets were unbundled'))],
5561 _('[-u] FILE...'))
5561 _('[-u] FILE...'))
5562 def unbundle(ui, repo, fname1, *fnames, **opts):
5562 def unbundle(ui, repo, fname1, *fnames, **opts):
5563 """apply one or more changegroup files
5563 """apply one or more changegroup files
5564
5564
5565 Apply one or more compressed changegroup files generated by the
5565 Apply one or more compressed changegroup files generated by the
5566 bundle command.
5566 bundle command.
5567
5567
5568 Returns 0 on success, 1 if an update has unresolved files.
5568 Returns 0 on success, 1 if an update has unresolved files.
5569 """
5569 """
5570 fnames = (fname1,) + fnames
5570 fnames = (fname1,) + fnames
5571
5571
5572 lock = repo.lock()
5572 lock = repo.lock()
5573 wc = repo['.']
5573 wc = repo['.']
5574 try:
5574 try:
5575 for fname in fnames:
5575 for fname in fnames:
5576 f = url.open(ui, fname)
5576 f = url.open(ui, fname)
5577 gen = changegroup.readbundle(f, fname)
5577 gen = changegroup.readbundle(f, fname)
5578 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
5578 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
5579 bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
5579 bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
5580 finally:
5580 finally:
5581 lock.release()
5581 lock.release()
5582 return postincoming(ui, repo, modheads, opts.get('update'), None)
5582 return postincoming(ui, repo, modheads, opts.get('update'), None)
5583
5583
5584 @command('^update|up|checkout|co',
5584 @command('^update|up|checkout|co',
5585 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5585 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5586 ('c', 'check', None,
5586 ('c', 'check', None,
5587 _('update across branches if no uncommitted changes')),
5587 _('update across branches if no uncommitted changes')),
5588 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5588 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5589 ('r', 'rev', '', _('revision'), _('REV'))],
5589 ('r', 'rev', '', _('revision'), _('REV'))],
5590 _('[-c] [-C] [-d DATE] [[-r] REV]'))
5590 _('[-c] [-C] [-d DATE] [[-r] REV]'))
5591 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
5591 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
5592 """update working directory (or switch revisions)
5592 """update working directory (or switch revisions)
5593
5593
5594 Update the repository's working directory to the specified
5594 Update the repository's working directory to the specified
5595 changeset. If no changeset is specified, update to the tip of the
5595 changeset. If no changeset is specified, update to the tip of the
5596 current named branch.
5596 current named branch.
5597
5597
5598 If the changeset is not a descendant of the working directory's
5598 If the changeset is not a descendant of the working directory's
5599 parent, the update is aborted. With the -c/--check option, the
5599 parent, the update is aborted. With the -c/--check option, the
5600 working directory is checked for uncommitted changes; if none are
5600 working directory is checked for uncommitted changes; if none are
5601 found, the working directory is updated to the specified
5601 found, the working directory is updated to the specified
5602 changeset.
5602 changeset.
5603
5603
5604 Update sets the working directory's parent revison to the specified
5604 Update sets the working directory's parent revison to the specified
5605 changeset (see :hg:`help parents`).
5605 changeset (see :hg:`help parents`).
5606
5606
5607 The following rules apply when the working directory contains
5607 The following rules apply when the working directory contains
5608 uncommitted changes:
5608 uncommitted changes:
5609
5609
5610 1. If neither -c/--check nor -C/--clean is specified, and if
5610 1. If neither -c/--check nor -C/--clean is specified, and if
5611 the requested changeset is an ancestor or descendant of
5611 the requested changeset is an ancestor or descendant of
5612 the working directory's parent, the uncommitted changes
5612 the working directory's parent, the uncommitted changes
5613 are merged into the requested changeset and the merged
5613 are merged into the requested changeset and the merged
5614 result is left uncommitted. If the requested changeset is
5614 result is left uncommitted. If the requested changeset is
5615 not an ancestor or descendant (that is, it is on another
5615 not an ancestor or descendant (that is, it is on another
5616 branch), the update is aborted and the uncommitted changes
5616 branch), the update is aborted and the uncommitted changes
5617 are preserved.
5617 are preserved.
5618
5618
5619 2. With the -c/--check option, the update is aborted and the
5619 2. With the -c/--check option, the update is aborted and the
5620 uncommitted changes are preserved.
5620 uncommitted changes are preserved.
5621
5621
5622 3. With the -C/--clean option, uncommitted changes are discarded and
5622 3. With the -C/--clean option, uncommitted changes are discarded and
5623 the working directory is updated to the requested changeset.
5623 the working directory is updated to the requested changeset.
5624
5624
5625 Use null as the changeset to remove the working directory (like
5625 Use null as the changeset to remove the working directory (like
5626 :hg:`clone -U`).
5626 :hg:`clone -U`).
5627
5627
5628 If you want to revert just one file to an older revision, use
5628 If you want to revert just one file to an older revision, use
5629 :hg:`revert [-r REV] NAME`.
5629 :hg:`revert [-r REV] NAME`.
5630
5630
5631 See :hg:`help dates` for a list of formats valid for -d/--date.
5631 See :hg:`help dates` for a list of formats valid for -d/--date.
5632
5632
5633 Returns 0 on success, 1 if there are unresolved files.
5633 Returns 0 on success, 1 if there are unresolved files.
5634 """
5634 """
5635 if rev and node:
5635 if rev and node:
5636 raise util.Abort(_("please specify just one revision"))
5636 raise util.Abort(_("please specify just one revision"))
5637
5637
5638 if rev is None or rev == '':
5638 if rev is None or rev == '':
5639 rev = node
5639 rev = node
5640
5640
5641 # if we defined a bookmark, we have to remember the original bookmark name
5641 # if we defined a bookmark, we have to remember the original bookmark name
5642 brev = rev
5642 brev = rev
5643 rev = scmutil.revsingle(repo, rev, rev).rev()
5643 rev = scmutil.revsingle(repo, rev, rev).rev()
5644
5644
5645 if check and clean:
5645 if check and clean:
5646 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
5646 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
5647
5647
5648 if check:
5648 if check:
5649 # we could use dirty() but we can ignore merge and branch trivia
5649 # we could use dirty() but we can ignore merge and branch trivia
5650 c = repo[None]
5650 c = repo[None]
5651 if c.modified() or c.added() or c.removed():
5651 if c.modified() or c.added() or c.removed():
5652 raise util.Abort(_("uncommitted local changes"))
5652 raise util.Abort(_("uncommitted local changes"))
5653
5653
5654 if date:
5654 if date:
5655 if rev is not None:
5655 if rev is not None:
5656 raise util.Abort(_("you can't specify a revision and a date"))
5656 raise util.Abort(_("you can't specify a revision and a date"))
5657 rev = cmdutil.finddate(ui, repo, date)
5657 rev = cmdutil.finddate(ui, repo, date)
5658
5658
5659 if clean or check:
5659 if clean or check:
5660 ret = hg.clean(repo, rev)
5660 ret = hg.clean(repo, rev)
5661 else:
5661 else:
5662 ret = hg.update(repo, rev)
5662 ret = hg.update(repo, rev)
5663
5663
5664 if brev in repo._bookmarks:
5664 if brev in repo._bookmarks:
5665 bookmarks.setcurrent(repo, brev)
5665 bookmarks.setcurrent(repo, brev)
5666
5666
5667 return ret
5667 return ret
5668
5668
5669 @command('verify', [])
5669 @command('verify', [])
5670 def verify(ui, repo):
5670 def verify(ui, repo):
5671 """verify the integrity of the repository
5671 """verify the integrity of the repository
5672
5672
5673 Verify the integrity of the current repository.
5673 Verify the integrity of the current repository.
5674
5674
5675 This will perform an extensive check of the repository's
5675 This will perform an extensive check of the repository's
5676 integrity, validating the hashes and checksums of each entry in
5676 integrity, validating the hashes and checksums of each entry in
5677 the changelog, manifest, and tracked files, as well as the
5677 the changelog, manifest, and tracked files, as well as the
5678 integrity of their crosslinks and indices.
5678 integrity of their crosslinks and indices.
5679
5679
5680 Returns 0 on success, 1 if errors are encountered.
5680 Returns 0 on success, 1 if errors are encountered.
5681 """
5681 """
5682 return hg.verify(repo)
5682 return hg.verify(repo)
5683
5683
5684 @command('version', [])
5684 @command('version', [])
5685 def version_(ui):
5685 def version_(ui):
5686 """output version and copyright information"""
5686 """output version and copyright information"""
5687 ui.write(_("Mercurial Distributed SCM (version %s)\n")
5687 ui.write(_("Mercurial Distributed SCM (version %s)\n")
5688 % util.version())
5688 % util.version())
5689 ui.status(_(
5689 ui.status(_(
5690 "(see http://mercurial.selenic.com for more information)\n"
5690 "(see http://mercurial.selenic.com for more information)\n"
5691 "\nCopyright (C) 2005-2011 Matt Mackall and others\n"
5691 "\nCopyright (C) 2005-2011 Matt Mackall and others\n"
5692 "This is free software; see the source for copying conditions. "
5692 "This is free software; see the source for copying conditions. "
5693 "There is NO\nwarranty; "
5693 "There is NO\nwarranty; "
5694 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5694 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5695 ))
5695 ))
5696
5696
5697 norepo = ("clone init version help debugcommands debugcomplete"
5697 norepo = ("clone init version help debugcommands debugcomplete"
5698 " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
5698 " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
5699 " debugknown debuggetbundle debugbundle")
5699 " debugknown debuggetbundle debugbundle")
5700 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
5700 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
5701 " debugdata debugindex debugindexdot debugrevlog")
5701 " debugdata debugindex debugindexdot debugrevlog")
@@ -1,1142 +1,1143
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 l in ctx['.hgsubstate'].data().splitlines():
47 revision, path = l.split(" ", 1)
47 revision, path = l.split(" ", 1)
48 rev[path] = revision
48 rev[path] = revision
49 except IOError, err:
49 except IOError, err:
50 if err.errno != errno.ENOENT:
50 if err.errno != errno.ENOENT:
51 raise
51 raise
52
52
53 def remap(src):
53 def remap(src):
54 for pattern, repl in p.items('subpaths'):
54 for pattern, repl in p.items('subpaths'):
55 # Turn r'C:\foo\bar' into r'C:\\foo\\bar' since re.sub
55 # Turn r'C:\foo\bar' into r'C:\\foo\\bar' since re.sub
56 # does a string decode.
56 # does a string decode.
57 repl = repl.encode('string-escape')
57 repl = repl.encode('string-escape')
58 # However, we still want to allow back references to go
58 # However, we still want to allow back references to go
59 # through unharmed, so we turn r'\\1' into r'\1'. Again,
59 # through unharmed, so we turn r'\\1' into r'\1'. Again,
60 # extra escapes are needed because re.sub string decodes.
60 # extra escapes are needed because re.sub string decodes.
61 repl = re.sub(r'\\\\([0-9]+)', r'\\\1', repl)
61 repl = re.sub(r'\\\\([0-9]+)', r'\\\1', repl)
62 try:
62 try:
63 src = re.sub(pattern, repl, src, 1)
63 src = re.sub(pattern, repl, src, 1)
64 except re.error, e:
64 except re.error, e:
65 raise util.Abort(_("bad subrepository pattern in %s: %s")
65 raise util.Abort(_("bad subrepository pattern in %s: %s")
66 % (p.source('subpaths', pattern), e))
66 % (p.source('subpaths', pattern), e))
67 return src
67 return src
68
68
69 state = {}
69 state = {}
70 for path, src in p[''].items():
70 for path, src in p[''].items():
71 kind = 'hg'
71 kind = 'hg'
72 if src.startswith('['):
72 if src.startswith('['):
73 if ']' not in src:
73 if ']' not in src:
74 raise util.Abort(_('missing ] in subrepo source'))
74 raise util.Abort(_('missing ] in subrepo source'))
75 kind, src = src.split(']', 1)
75 kind, src = src.split(']', 1)
76 kind = kind[1:]
76 kind = kind[1:]
77 src = src.lstrip() # strip any extra whitespace after ']'
77 src = src.lstrip() # strip any extra whitespace after ']'
78
78
79 if not util.url(src).isabs():
79 if not util.url(src).isabs():
80 parent = _abssource(ctx._repo, abort=False)
80 parent = _abssource(ctx._repo, abort=False)
81 if parent:
81 if parent:
82 parent = util.url(parent)
82 parent = util.url(parent)
83 parent.path = posixpath.join(parent.path or '', src)
83 parent.path = posixpath.join(parent.path or '', src)
84 parent.path = posixpath.normpath(parent.path)
84 parent.path = posixpath.normpath(parent.path)
85 joined = str(parent)
85 joined = str(parent)
86 # Remap the full joined path and use it if it changes,
86 # Remap the full joined path and use it if it changes,
87 # else remap the original source.
87 # else remap the original source.
88 remapped = remap(joined)
88 remapped = remap(joined)
89 if remapped == joined:
89 if remapped == joined:
90 src = remap(src)
90 src = remap(src)
91 else:
91 else:
92 src = remapped
92 src = remapped
93
93
94 src = remap(src)
94 src = remap(src)
95 state[path] = (src.strip(), rev.get(path, ''), kind)
95 state[path] = (src.strip(), rev.get(path, ''), kind)
96
96
97 return state
97 return state
98
98
99 def writestate(repo, state):
99 def writestate(repo, state):
100 """rewrite .hgsubstate in (outer) repo with these subrepo states"""
100 """rewrite .hgsubstate in (outer) repo with these subrepo states"""
101 lines = ['%s %s\n' % (state[s][1], s) for s in sorted(state)]
101 lines = ['%s %s\n' % (state[s][1], s) for s in sorted(state)]
102 repo.wwrite('.hgsubstate', ''.join(lines), '')
102 repo.wwrite('.hgsubstate', ''.join(lines), '')
103
103
104 def submerge(repo, wctx, mctx, actx, overwrite):
104 def submerge(repo, wctx, mctx, actx, overwrite):
105 """delegated from merge.applyupdates: merging of .hgsubstate file
105 """delegated from merge.applyupdates: merging of .hgsubstate file
106 in working context, merging context and ancestor context"""
106 in working context, merging context and ancestor context"""
107 if mctx == actx: # backwards?
107 if mctx == actx: # backwards?
108 actx = wctx.p1()
108 actx = wctx.p1()
109 s1 = wctx.substate
109 s1 = wctx.substate
110 s2 = mctx.substate
110 s2 = mctx.substate
111 sa = actx.substate
111 sa = actx.substate
112 sm = {}
112 sm = {}
113
113
114 repo.ui.debug("subrepo merge %s %s %s\n" % (wctx, mctx, actx))
114 repo.ui.debug("subrepo merge %s %s %s\n" % (wctx, mctx, actx))
115
115
116 def debug(s, msg, r=""):
116 def debug(s, msg, r=""):
117 if r:
117 if r:
118 r = "%s:%s:%s" % r
118 r = "%s:%s:%s" % r
119 repo.ui.debug(" subrepo %s: %s %s\n" % (s, msg, r))
119 repo.ui.debug(" subrepo %s: %s %s\n" % (s, msg, r))
120
120
121 for s, l in s1.items():
121 for s, l in s1.items():
122 a = sa.get(s, nullstate)
122 a = sa.get(s, nullstate)
123 ld = l # local state with possible dirty flag for compares
123 ld = l # local state with possible dirty flag for compares
124 if wctx.sub(s).dirty():
124 if wctx.sub(s).dirty():
125 ld = (l[0], l[1] + "+")
125 ld = (l[0], l[1] + "+")
126 if wctx == actx: # overwrite
126 if wctx == actx: # overwrite
127 a = ld
127 a = ld
128
128
129 if s in s2:
129 if s in s2:
130 r = s2[s]
130 r = s2[s]
131 if ld == r or r == a: # no change or local is newer
131 if ld == r or r == a: # no change or local is newer
132 sm[s] = l
132 sm[s] = l
133 continue
133 continue
134 elif ld == a: # other side changed
134 elif ld == a: # other side changed
135 debug(s, "other changed, get", r)
135 debug(s, "other changed, get", r)
136 wctx.sub(s).get(r, overwrite)
136 wctx.sub(s).get(r, overwrite)
137 sm[s] = r
137 sm[s] = r
138 elif ld[0] != r[0]: # sources differ
138 elif ld[0] != r[0]: # sources differ
139 if repo.ui.promptchoice(
139 if repo.ui.promptchoice(
140 _(' subrepository sources for %s differ\n'
140 _(' subrepository sources for %s differ\n'
141 'use (l)ocal source (%s) or (r)emote source (%s)?')
141 'use (l)ocal source (%s) or (r)emote source (%s)?')
142 % (s, l[0], r[0]),
142 % (s, l[0], r[0]),
143 (_('&Local'), _('&Remote')), 0):
143 (_('&Local'), _('&Remote')), 0):
144 debug(s, "prompt changed, get", r)
144 debug(s, "prompt changed, get", r)
145 wctx.sub(s).get(r, overwrite)
145 wctx.sub(s).get(r, overwrite)
146 sm[s] = r
146 sm[s] = r
147 elif ld[1] == a[1]: # local side is unchanged
147 elif ld[1] == a[1]: # local side is unchanged
148 debug(s, "other side changed, get", r)
148 debug(s, "other side changed, get", r)
149 wctx.sub(s).get(r, overwrite)
149 wctx.sub(s).get(r, overwrite)
150 sm[s] = r
150 sm[s] = r
151 else:
151 else:
152 debug(s, "both sides changed, merge with", r)
152 debug(s, "both sides changed, merge with", r)
153 wctx.sub(s).merge(r)
153 wctx.sub(s).merge(r)
154 sm[s] = l
154 sm[s] = l
155 elif ld == a: # remote removed, local unchanged
155 elif ld == a: # remote removed, local unchanged
156 debug(s, "remote removed, remove")
156 debug(s, "remote removed, remove")
157 wctx.sub(s).remove()
157 wctx.sub(s).remove()
158 elif a == nullstate: # not present in remote or ancestor
158 elif a == nullstate: # not present in remote or ancestor
159 debug(s, "local added, keep")
159 debug(s, "local added, keep")
160 sm[s] = l
160 sm[s] = l
161 continue
161 continue
162 else:
162 else:
163 if repo.ui.promptchoice(
163 if repo.ui.promptchoice(
164 _(' local changed subrepository %s which remote removed\n'
164 _(' local changed subrepository %s which remote removed\n'
165 'use (c)hanged version or (d)elete?') % s,
165 'use (c)hanged version or (d)elete?') % s,
166 (_('&Changed'), _('&Delete')), 0):
166 (_('&Changed'), _('&Delete')), 0):
167 debug(s, "prompt remove")
167 debug(s, "prompt remove")
168 wctx.sub(s).remove()
168 wctx.sub(s).remove()
169
169
170 for s, r in sorted(s2.items()):
170 for s, r in sorted(s2.items()):
171 if s in s1:
171 if s in s1:
172 continue
172 continue
173 elif s not in sa:
173 elif s not in sa:
174 debug(s, "remote added, get", r)
174 debug(s, "remote added, get", r)
175 mctx.sub(s).get(r)
175 mctx.sub(s).get(r)
176 sm[s] = r
176 sm[s] = r
177 elif r != sa[s]:
177 elif r != sa[s]:
178 if repo.ui.promptchoice(
178 if repo.ui.promptchoice(
179 _(' remote changed subrepository %s which local removed\n'
179 _(' remote changed subrepository %s which local removed\n'
180 'use (c)hanged version or (d)elete?') % s,
180 'use (c)hanged version or (d)elete?') % s,
181 (_('&Changed'), _('&Delete')), 0) == 0:
181 (_('&Changed'), _('&Delete')), 0) == 0:
182 debug(s, "prompt recreate", r)
182 debug(s, "prompt recreate", r)
183 wctx.sub(s).get(r)
183 wctx.sub(s).get(r)
184 sm[s] = r
184 sm[s] = r
185
185
186 # record merged .hgsubstate
186 # record merged .hgsubstate
187 writestate(repo, sm)
187 writestate(repo, sm)
188
188
189 def _updateprompt(ui, sub, dirty, local, remote):
189 def _updateprompt(ui, sub, dirty, local, remote):
190 if dirty:
190 if dirty:
191 msg = (_(' subrepository sources for %s differ\n'
191 msg = (_(' subrepository sources for %s differ\n'
192 'use (l)ocal source (%s) or (r)emote source (%s)?\n')
192 'use (l)ocal source (%s) or (r)emote source (%s)?\n')
193 % (subrelpath(sub), local, remote))
193 % (subrelpath(sub), local, remote))
194 else:
194 else:
195 msg = (_(' subrepository sources for %s differ (in checked out version)\n'
195 msg = (_(' subrepository sources for %s differ (in checked out version)\n'
196 'use (l)ocal source (%s) or (r)emote source (%s)?\n')
196 'use (l)ocal source (%s) or (r)emote source (%s)?\n')
197 % (subrelpath(sub), local, remote))
197 % (subrelpath(sub), local, remote))
198 return ui.promptchoice(msg, (_('&Local'), _('&Remote')), 0)
198 return ui.promptchoice(msg, (_('&Local'), _('&Remote')), 0)
199
199
200 def reporelpath(repo):
200 def reporelpath(repo):
201 """return path to this (sub)repo as seen from outermost repo"""
201 """return path to this (sub)repo as seen from outermost repo"""
202 parent = repo
202 parent = repo
203 while util.safehasattr(parent, '_subparent'):
203 while util.safehasattr(parent, '_subparent'):
204 parent = parent._subparent
204 parent = parent._subparent
205 p = parent.root.rstrip(os.sep)
205 p = parent.root.rstrip(os.sep)
206 return repo.root[len(p) + 1:]
206 return repo.root[len(p) + 1:]
207
207
208 def subrelpath(sub):
208 def subrelpath(sub):
209 """return path to this subrepo as seen from outermost repo"""
209 """return path to this subrepo as seen from outermost repo"""
210 if util.safehasattr(sub, '_relpath'):
210 if util.safehasattr(sub, '_relpath'):
211 return sub._relpath
211 return sub._relpath
212 if not util.safehasattr(sub, '_repo'):
212 if not util.safehasattr(sub, '_repo'):
213 return sub._path
213 return sub._path
214 return reporelpath(sub._repo)
214 return reporelpath(sub._repo)
215
215
216 def _abssource(repo, push=False, abort=True):
216 def _abssource(repo, push=False, abort=True):
217 """return pull/push path of repo - either based on parent repo .hgsub info
217 """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."""
218 or on the top repo config. Abort or return None if no source found."""
219 if util.safehasattr(repo, '_subparent'):
219 if util.safehasattr(repo, '_subparent'):
220 source = util.url(repo._subsource)
220 source = util.url(repo._subsource)
221 if source.isabs():
221 if source.isabs():
222 return str(source)
222 return str(source)
223 source.path = posixpath.normpath(source.path)
223 source.path = posixpath.normpath(source.path)
224 parent = _abssource(repo._subparent, push, abort=False)
224 parent = _abssource(repo._subparent, push, abort=False)
225 if parent:
225 if parent:
226 parent = util.url(util.pconvert(parent))
226 parent = util.url(util.pconvert(parent))
227 parent.path = posixpath.join(parent.path or '', source.path)
227 parent.path = posixpath.join(parent.path or '', source.path)
228 parent.path = posixpath.normpath(parent.path)
228 parent.path = posixpath.normpath(parent.path)
229 return str(parent)
229 return str(parent)
230 else: # recursion reached top repo
230 else: # recursion reached top repo
231 if util.safehasattr(repo, '_subtoppath'):
231 if util.safehasattr(repo, '_subtoppath'):
232 return repo._subtoppath
232 return repo._subtoppath
233 if push and repo.ui.config('paths', 'default-push'):
233 if push and repo.ui.config('paths', 'default-push'):
234 return repo.ui.config('paths', 'default-push')
234 return repo.ui.config('paths', 'default-push')
235 if repo.ui.config('paths', 'default'):
235 if repo.ui.config('paths', 'default'):
236 return repo.ui.config('paths', 'default')
236 return repo.ui.config('paths', 'default')
237 if abort:
237 if abort:
238 raise util.Abort(_("default path for subrepository %s not found") %
238 raise util.Abort(_("default path for subrepository %s not found") %
239 reporelpath(repo))
239 reporelpath(repo))
240
240
241 def itersubrepos(ctx1, ctx2):
241 def itersubrepos(ctx1, ctx2):
242 """find subrepos in ctx1 or ctx2"""
242 """find subrepos in ctx1 or ctx2"""
243 # Create a (subpath, ctx) mapping where we prefer subpaths from
243 # Create a (subpath, ctx) mapping where we prefer subpaths from
244 # ctx1. The subpaths from ctx2 are important when the .hgsub file
244 # ctx1. The subpaths from ctx2 are important when the .hgsub file
245 # has been modified (in ctx2) but not yet committed (in ctx1).
245 # has been modified (in ctx2) but not yet committed (in ctx1).
246 subpaths = dict.fromkeys(ctx2.substate, ctx2)
246 subpaths = dict.fromkeys(ctx2.substate, ctx2)
247 subpaths.update(dict.fromkeys(ctx1.substate, ctx1))
247 subpaths.update(dict.fromkeys(ctx1.substate, ctx1))
248 for subpath, ctx in sorted(subpaths.iteritems()):
248 for subpath, ctx in sorted(subpaths.iteritems()):
249 yield subpath, ctx.sub(subpath)
249 yield subpath, ctx.sub(subpath)
250
250
251 def subrepo(ctx, path):
251 def subrepo(ctx, path):
252 """return instance of the right subrepo class for subrepo in path"""
252 """return instance of the right subrepo class for subrepo in path"""
253 # subrepo inherently violates our import layering rules
253 # subrepo inherently violates our import layering rules
254 # because it wants to make repo objects from deep inside the stack
254 # because it wants to make repo objects from deep inside the stack
255 # so we manually delay the circular imports to not break
255 # so we manually delay the circular imports to not break
256 # scripts that don't use our demand-loading
256 # scripts that don't use our demand-loading
257 global hg
257 global hg
258 import hg as h
258 import hg as h
259 hg = h
259 hg = h
260
260
261 scmutil.pathauditor(ctx._repo.root)(path)
261 scmutil.pathauditor(ctx._repo.root)(path)
262 state = ctx.substate.get(path, nullstate)
262 state = ctx.substate.get(path, nullstate)
263 if state[2] not in types:
263 if state[2] not in types:
264 raise util.Abort(_('unknown subrepo type %s') % state[2])
264 raise util.Abort(_('unknown subrepo type %s') % state[2])
265 return types[state[2]](ctx, path, state[:2])
265 return types[state[2]](ctx, path, state[:2])
266
266
267 # subrepo classes need to implement the following abstract class:
267 # subrepo classes need to implement the following abstract class:
268
268
269 class abstractsubrepo(object):
269 class abstractsubrepo(object):
270
270
271 def dirty(self, ignoreupdate=False):
271 def dirty(self, ignoreupdate=False):
272 """returns true if the dirstate of the subrepo is dirty or does not
272 """returns true if the dirstate of the subrepo is dirty or does not
273 match current stored state. If ignoreupdate is true, only check
273 match current stored state. If ignoreupdate is true, only check
274 whether the subrepo has uncommitted changes in its dirstate.
274 whether the subrepo has uncommitted changes in its dirstate.
275 """
275 """
276 raise NotImplementedError
276 raise NotImplementedError
277
277
278 def checknested(self, path):
278 def checknested(self, path):
279 """check if path is a subrepository within this repository"""
279 """check if path is a subrepository within this repository"""
280 return False
280 return False
281
281
282 def commit(self, text, user, date):
282 def commit(self, text, user, date):
283 """commit the current changes to the subrepo with the given
283 """commit the current changes to the subrepo with the given
284 log message. Use given user and date if possible. Return the
284 log message. Use given user and date if possible. Return the
285 new state of the subrepo.
285 new state of the subrepo.
286 """
286 """
287 raise NotImplementedError
287 raise NotImplementedError
288
288
289 def remove(self):
289 def remove(self):
290 """remove the subrepo
290 """remove the subrepo
291
291
292 (should verify the dirstate is not dirty first)
292 (should verify the dirstate is not dirty first)
293 """
293 """
294 raise NotImplementedError
294 raise NotImplementedError
295
295
296 def get(self, state, overwrite=False):
296 def get(self, state, overwrite=False):
297 """run whatever commands are needed to put the subrepo into
297 """run whatever commands are needed to put the subrepo into
298 this state
298 this state
299 """
299 """
300 raise NotImplementedError
300 raise NotImplementedError
301
301
302 def merge(self, state):
302 def merge(self, state):
303 """merge currently-saved state with the new state."""
303 """merge currently-saved state with the new state."""
304 raise NotImplementedError
304 raise NotImplementedError
305
305
306 def push(self, force):
306 def push(self, force):
307 """perform whatever action is analogous to 'hg push'
307 """perform whatever action is analogous to 'hg push'
308
308
309 This may be a no-op on some systems.
309 This may be a no-op on some systems.
310 """
310 """
311 raise NotImplementedError
311 raise NotImplementedError
312
312
313 def add(self, ui, match, dryrun, prefix):
313 def add(self, ui, match, dryrun, prefix):
314 return []
314 return []
315
315
316 def status(self, rev2, **opts):
316 def status(self, rev2, **opts):
317 return [], [], [], [], [], [], []
317 return [], [], [], [], [], [], []
318
318
319 def diff(self, diffopts, node2, match, prefix, **opts):
319 def diff(self, diffopts, node2, match, prefix, **opts):
320 pass
320 pass
321
321
322 def outgoing(self, ui, dest, opts):
322 def outgoing(self, ui, dest, opts):
323 return 1
323 return 1
324
324
325 def incoming(self, ui, source, opts):
325 def incoming(self, ui, source, opts):
326 return 1
326 return 1
327
327
328 def files(self):
328 def files(self):
329 """return filename iterator"""
329 """return filename iterator"""
330 raise NotImplementedError
330 raise NotImplementedError
331
331
332 def filedata(self, name):
332 def filedata(self, name):
333 """return file data"""
333 """return file data"""
334 raise NotImplementedError
334 raise NotImplementedError
335
335
336 def fileflags(self, name):
336 def fileflags(self, name):
337 """return file flags"""
337 """return file flags"""
338 return ''
338 return ''
339
339
340 def archive(self, ui, archiver, prefix):
340 def archive(self, ui, archiver, prefix):
341 files = self.files()
341 files = self.files()
342 total = len(files)
342 total = len(files)
343 relpath = subrelpath(self)
343 relpath = subrelpath(self)
344 ui.progress(_('archiving (%s)') % relpath, 0,
344 ui.progress(_('archiving (%s)') % relpath, 0,
345 unit=_('files'), total=total)
345 unit=_('files'), total=total)
346 for i, name in enumerate(files):
346 for i, name in enumerate(files):
347 flags = self.fileflags(name)
347 flags = self.fileflags(name)
348 mode = 'x' in flags and 0755 or 0644
348 mode = 'x' in flags and 0755 or 0644
349 symlink = 'l' in flags
349 symlink = 'l' in flags
350 archiver.addfile(os.path.join(prefix, self._path, name),
350 archiver.addfile(os.path.join(prefix, self._path, name),
351 mode, symlink, self.filedata(name))
351 mode, symlink, self.filedata(name))
352 ui.progress(_('archiving (%s)') % relpath, i + 1,
352 ui.progress(_('archiving (%s)') % relpath, i + 1,
353 unit=_('files'), total=total)
353 unit=_('files'), total=total)
354 ui.progress(_('archiving (%s)') % relpath, None)
354 ui.progress(_('archiving (%s)') % relpath, None)
355
355
356 def walk(self, match):
356 def walk(self, match):
357 '''
357 '''
358 walk recursively through the directory tree, finding all files
358 walk recursively through the directory tree, finding all files
359 matched by the match function
359 matched by the match function
360 '''
360 '''
361 pass
361 pass
362
362
363 def forget(self, files):
363 def forget(self, files):
364 pass
364 pass
365
365
366 class hgsubrepo(abstractsubrepo):
366 class hgsubrepo(abstractsubrepo):
367 def __init__(self, ctx, path, state):
367 def __init__(self, ctx, path, state):
368 self._path = path
368 self._path = path
369 self._state = state
369 self._state = state
370 r = ctx._repo
370 r = ctx._repo
371 root = r.wjoin(path)
371 root = r.wjoin(path)
372 create = False
372 create = False
373 if not os.path.exists(os.path.join(root, '.hg')):
373 if not os.path.exists(os.path.join(root, '.hg')):
374 create = True
374 create = True
375 util.makedirs(root)
375 util.makedirs(root)
376 self._repo = hg.repository(r.ui, root, create=create)
376 self._repo = hg.repository(r.ui, root, create=create)
377 self._initrepo(r, state[0], create)
377 self._initrepo(r, state[0], create)
378
378
379 def _initrepo(self, parentrepo, source, create):
379 def _initrepo(self, parentrepo, source, create):
380 self._repo._subparent = parentrepo
380 self._repo._subparent = parentrepo
381 self._repo._subsource = source
381 self._repo._subsource = source
382
382
383 if create:
383 if create:
384 fp = self._repo.opener("hgrc", "w", text=True)
384 fp = self._repo.opener("hgrc", "w", text=True)
385 fp.write('[paths]\n')
385 fp.write('[paths]\n')
386
386
387 def addpathconfig(key, value):
387 def addpathconfig(key, value):
388 if value:
388 if value:
389 fp.write('%s = %s\n' % (key, value))
389 fp.write('%s = %s\n' % (key, value))
390 self._repo.ui.setconfig('paths', key, value)
390 self._repo.ui.setconfig('paths', key, value)
391
391
392 defpath = _abssource(self._repo, abort=False)
392 defpath = _abssource(self._repo, abort=False)
393 defpushpath = _abssource(self._repo, True, abort=False)
393 defpushpath = _abssource(self._repo, True, abort=False)
394 addpathconfig('default', defpath)
394 addpathconfig('default', defpath)
395 if defpath != defpushpath:
395 if defpath != defpushpath:
396 addpathconfig('default-push', defpushpath)
396 addpathconfig('default-push', defpushpath)
397 fp.close()
397 fp.close()
398
398
399 def add(self, ui, match, dryrun, prefix):
399 def add(self, ui, match, dryrun, prefix):
400 return cmdutil.add(ui, self._repo, match, dryrun, True,
400 return cmdutil.add(ui, self._repo, match, dryrun, True,
401 os.path.join(prefix, self._path))
401 os.path.join(prefix, self._path))
402
402
403 def status(self, rev2, **opts):
403 def status(self, rev2, **opts):
404 try:
404 try:
405 rev1 = self._state[1]
405 rev1 = self._state[1]
406 ctx1 = self._repo[rev1]
406 ctx1 = self._repo[rev1]
407 ctx2 = self._repo[rev2]
407 ctx2 = self._repo[rev2]
408 return self._repo.status(ctx1, ctx2, **opts)
408 return self._repo.status(ctx1, ctx2, **opts)
409 except error.RepoLookupError, inst:
409 except error.RepoLookupError, inst:
410 self._repo.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
410 self._repo.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
411 % (inst, subrelpath(self)))
411 % (inst, subrelpath(self)))
412 return [], [], [], [], [], [], []
412 return [], [], [], [], [], [], []
413
413
414 def diff(self, diffopts, node2, match, prefix, **opts):
414 def diff(self, diffopts, node2, match, prefix, **opts):
415 try:
415 try:
416 node1 = node.bin(self._state[1])
416 node1 = node.bin(self._state[1])
417 # We currently expect node2 to come from substate and be
417 # We currently expect node2 to come from substate and be
418 # in hex format
418 # in hex format
419 if node2 is not None:
419 if node2 is not None:
420 node2 = node.bin(node2)
420 node2 = node.bin(node2)
421 cmdutil.diffordiffstat(self._repo.ui, self._repo, diffopts,
421 cmdutil.diffordiffstat(self._repo.ui, self._repo, diffopts,
422 node1, node2, match,
422 node1, node2, match,
423 prefix=os.path.join(prefix, self._path),
423 prefix=os.path.join(prefix, self._path),
424 listsubrepos=True, **opts)
424 listsubrepos=True, **opts)
425 except error.RepoLookupError, inst:
425 except error.RepoLookupError, inst:
426 self._repo.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
426 self._repo.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
427 % (inst, subrelpath(self)))
427 % (inst, subrelpath(self)))
428
428
429 def archive(self, ui, archiver, prefix):
429 def archive(self, ui, archiver, prefix):
430 self._get(self._state + ('hg',))
430 self._get(self._state + ('hg',))
431 abstractsubrepo.archive(self, ui, archiver, prefix)
431 abstractsubrepo.archive(self, ui, archiver, prefix)
432
432
433 rev = self._state[1]
433 rev = self._state[1]
434 ctx = self._repo[rev]
434 ctx = self._repo[rev]
435 for subpath in ctx.substate:
435 for subpath in ctx.substate:
436 s = subrepo(ctx, subpath)
436 s = subrepo(ctx, subpath)
437 s.archive(ui, archiver, os.path.join(prefix, self._path))
437 s.archive(ui, archiver, os.path.join(prefix, self._path))
438
438
439 def dirty(self, ignoreupdate=False):
439 def dirty(self, ignoreupdate=False):
440 r = self._state[1]
440 r = self._state[1]
441 if r == '' and not ignoreupdate: # no state recorded
441 if r == '' and not ignoreupdate: # no state recorded
442 return True
442 return True
443 w = self._repo[None]
443 w = self._repo[None]
444 if r != w.p1().hex() and not ignoreupdate:
444 if r != w.p1().hex() and not ignoreupdate:
445 # different version checked out
445 # different version checked out
446 return True
446 return True
447 return w.dirty() # working directory changed
447 return w.dirty() # working directory changed
448
448
449 def checknested(self, path):
449 def checknested(self, path):
450 return self._repo._checknested(self._repo.wjoin(path))
450 return self._repo._checknested(self._repo.wjoin(path))
451
451
452 def commit(self, text, user, date):
452 def commit(self, text, user, date):
453 # don't bother committing in the subrepo if it's only been
453 # don't bother committing in the subrepo if it's only been
454 # updated
454 # updated
455 if not self.dirty(True):
455 if not self.dirty(True):
456 return self._repo['.'].hex()
456 return self._repo['.'].hex()
457 self._repo.ui.debug("committing subrepo %s\n" % subrelpath(self))
457 self._repo.ui.debug("committing subrepo %s\n" % subrelpath(self))
458 n = self._repo.commit(text, user, date)
458 n = self._repo.commit(text, user, date)
459 if not n:
459 if not n:
460 return self._repo['.'].hex() # different version checked out
460 return self._repo['.'].hex() # different version checked out
461 return node.hex(n)
461 return node.hex(n)
462
462
463 def remove(self):
463 def remove(self):
464 # we can't fully delete the repository as it may contain
464 # we can't fully delete the repository as it may contain
465 # local-only history
465 # local-only history
466 self._repo.ui.note(_('removing subrepo %s\n') % subrelpath(self))
466 self._repo.ui.note(_('removing subrepo %s\n') % subrelpath(self))
467 hg.clean(self._repo, node.nullid, False)
467 hg.clean(self._repo, node.nullid, False)
468
468
469 def _get(self, state):
469 def _get(self, state):
470 source, revision, kind = state
470 source, revision, kind = state
471 if revision not in self._repo:
471 if revision not in self._repo:
472 self._repo._subsource = source
472 self._repo._subsource = source
473 srcurl = _abssource(self._repo)
473 srcurl = _abssource(self._repo)
474 other = hg.peer(self._repo.ui, {}, srcurl)
474 other = hg.peer(self._repo.ui, {}, srcurl)
475 if len(self._repo) == 0:
475 if len(self._repo) == 0:
476 self._repo.ui.status(_('cloning subrepo %s from %s\n')
476 self._repo.ui.status(_('cloning subrepo %s from %s\n')
477 % (subrelpath(self), srcurl))
477 % (subrelpath(self), srcurl))
478 parentrepo = self._repo._subparent
478 parentrepo = self._repo._subparent
479 shutil.rmtree(self._repo.path)
479 shutil.rmtree(self._repo.path)
480 other, self._repo = hg.clone(self._repo._subparent.ui, {}, other,
480 other, self._repo = hg.clone(self._repo._subparent.ui, {}, other,
481 self._repo.root, update=False)
481 self._repo.root, update=False)
482 self._initrepo(parentrepo, source, create=True)
482 self._initrepo(parentrepo, source, create=True)
483 else:
483 else:
484 self._repo.ui.status(_('pulling subrepo %s from %s\n')
484 self._repo.ui.status(_('pulling subrepo %s from %s\n')
485 % (subrelpath(self), srcurl))
485 % (subrelpath(self), srcurl))
486 self._repo.pull(other)
486 self._repo.pull(other)
487 bookmarks.updatefromremote(self._repo.ui, self._repo, other)
487 bookmarks.updatefromremote(self._repo.ui, self._repo, other,
488 srcurl)
488
489
489 def get(self, state, overwrite=False):
490 def get(self, state, overwrite=False):
490 self._get(state)
491 self._get(state)
491 source, revision, kind = state
492 source, revision, kind = state
492 self._repo.ui.debug("getting subrepo %s\n" % self._path)
493 self._repo.ui.debug("getting subrepo %s\n" % self._path)
493 hg.clean(self._repo, revision, False)
494 hg.clean(self._repo, revision, False)
494
495
495 def merge(self, state):
496 def merge(self, state):
496 self._get(state)
497 self._get(state)
497 cur = self._repo['.']
498 cur = self._repo['.']
498 dst = self._repo[state[1]]
499 dst = self._repo[state[1]]
499 anc = dst.ancestor(cur)
500 anc = dst.ancestor(cur)
500
501
501 def mergefunc():
502 def mergefunc():
502 if anc == cur:
503 if anc == cur:
503 self._repo.ui.debug("updating subrepo %s\n" % subrelpath(self))
504 self._repo.ui.debug("updating subrepo %s\n" % subrelpath(self))
504 hg.update(self._repo, state[1])
505 hg.update(self._repo, state[1])
505 elif anc == dst:
506 elif anc == dst:
506 self._repo.ui.debug("skipping subrepo %s\n" % subrelpath(self))
507 self._repo.ui.debug("skipping subrepo %s\n" % subrelpath(self))
507 else:
508 else:
508 self._repo.ui.debug("merging subrepo %s\n" % subrelpath(self))
509 self._repo.ui.debug("merging subrepo %s\n" % subrelpath(self))
509 hg.merge(self._repo, state[1], remind=False)
510 hg.merge(self._repo, state[1], remind=False)
510
511
511 wctx = self._repo[None]
512 wctx = self._repo[None]
512 if self.dirty():
513 if self.dirty():
513 if anc != dst:
514 if anc != dst:
514 if _updateprompt(self._repo.ui, self, wctx.dirty(), cur, dst):
515 if _updateprompt(self._repo.ui, self, wctx.dirty(), cur, dst):
515 mergefunc()
516 mergefunc()
516 else:
517 else:
517 mergefunc()
518 mergefunc()
518 else:
519 else:
519 mergefunc()
520 mergefunc()
520
521
521 def push(self, force):
522 def push(self, force):
522 # push subrepos depth-first for coherent ordering
523 # push subrepos depth-first for coherent ordering
523 c = self._repo['']
524 c = self._repo['']
524 subs = c.substate # only repos that are committed
525 subs = c.substate # only repos that are committed
525 for s in sorted(subs):
526 for s in sorted(subs):
526 if not c.sub(s).push(force):
527 if not c.sub(s).push(force):
527 return False
528 return False
528
529
529 dsturl = _abssource(self._repo, True)
530 dsturl = _abssource(self._repo, True)
530 self._repo.ui.status(_('pushing subrepo %s to %s\n') %
531 self._repo.ui.status(_('pushing subrepo %s to %s\n') %
531 (subrelpath(self), dsturl))
532 (subrelpath(self), dsturl))
532 other = hg.peer(self._repo.ui, {}, dsturl)
533 other = hg.peer(self._repo.ui, {}, dsturl)
533 return self._repo.push(other, force)
534 return self._repo.push(other, force)
534
535
535 def outgoing(self, ui, dest, opts):
536 def outgoing(self, ui, dest, opts):
536 return hg.outgoing(ui, self._repo, _abssource(self._repo, True), opts)
537 return hg.outgoing(ui, self._repo, _abssource(self._repo, True), opts)
537
538
538 def incoming(self, ui, source, opts):
539 def incoming(self, ui, source, opts):
539 return hg.incoming(ui, self._repo, _abssource(self._repo, False), opts)
540 return hg.incoming(ui, self._repo, _abssource(self._repo, False), opts)
540
541
541 def files(self):
542 def files(self):
542 rev = self._state[1]
543 rev = self._state[1]
543 ctx = self._repo[rev]
544 ctx = self._repo[rev]
544 return ctx.manifest()
545 return ctx.manifest()
545
546
546 def filedata(self, name):
547 def filedata(self, name):
547 rev = self._state[1]
548 rev = self._state[1]
548 return self._repo[rev][name].data()
549 return self._repo[rev][name].data()
549
550
550 def fileflags(self, name):
551 def fileflags(self, name):
551 rev = self._state[1]
552 rev = self._state[1]
552 ctx = self._repo[rev]
553 ctx = self._repo[rev]
553 return ctx.flags(name)
554 return ctx.flags(name)
554
555
555 def walk(self, match):
556 def walk(self, match):
556 ctx = self._repo[None]
557 ctx = self._repo[None]
557 return ctx.walk(match)
558 return ctx.walk(match)
558
559
559 def forget(self, files):
560 def forget(self, files):
560 ctx = self._repo[None]
561 ctx = self._repo[None]
561 ctx.forget(files)
562 ctx.forget(files)
562
563
563 class svnsubrepo(abstractsubrepo):
564 class svnsubrepo(abstractsubrepo):
564 def __init__(self, ctx, path, state):
565 def __init__(self, ctx, path, state):
565 self._path = path
566 self._path = path
566 self._state = state
567 self._state = state
567 self._ctx = ctx
568 self._ctx = ctx
568 self._ui = ctx._repo.ui
569 self._ui = ctx._repo.ui
569 self._exe = util.findexe('svn')
570 self._exe = util.findexe('svn')
570 if not self._exe:
571 if not self._exe:
571 raise util.Abort(_("'svn' executable not found for subrepo '%s'")
572 raise util.Abort(_("'svn' executable not found for subrepo '%s'")
572 % self._path)
573 % self._path)
573
574
574 def _svncommand(self, commands, filename='', failok=False):
575 def _svncommand(self, commands, filename='', failok=False):
575 cmd = [self._exe]
576 cmd = [self._exe]
576 extrakw = {}
577 extrakw = {}
577 if not self._ui.interactive():
578 if not self._ui.interactive():
578 # Making stdin be a pipe should prevent svn from behaving
579 # Making stdin be a pipe should prevent svn from behaving
579 # interactively even if we can't pass --non-interactive.
580 # interactively even if we can't pass --non-interactive.
580 extrakw['stdin'] = subprocess.PIPE
581 extrakw['stdin'] = subprocess.PIPE
581 # Starting in svn 1.5 --non-interactive is a global flag
582 # Starting in svn 1.5 --non-interactive is a global flag
582 # instead of being per-command, but we need to support 1.4 so
583 # instead of being per-command, but we need to support 1.4 so
583 # we have to be intelligent about what commands take
584 # we have to be intelligent about what commands take
584 # --non-interactive.
585 # --non-interactive.
585 if commands[0] in ('update', 'checkout', 'commit'):
586 if commands[0] in ('update', 'checkout', 'commit'):
586 cmd.append('--non-interactive')
587 cmd.append('--non-interactive')
587 cmd.extend(commands)
588 cmd.extend(commands)
588 if filename is not None:
589 if filename is not None:
589 path = os.path.join(self._ctx._repo.origroot, self._path, filename)
590 path = os.path.join(self._ctx._repo.origroot, self._path, filename)
590 cmd.append(path)
591 cmd.append(path)
591 env = dict(os.environ)
592 env = dict(os.environ)
592 # Avoid localized output, preserve current locale for everything else.
593 # Avoid localized output, preserve current locale for everything else.
593 env['LC_MESSAGES'] = 'C'
594 env['LC_MESSAGES'] = 'C'
594 p = subprocess.Popen(cmd, bufsize=-1, close_fds=util.closefds,
595 p = subprocess.Popen(cmd, bufsize=-1, close_fds=util.closefds,
595 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
596 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
596 universal_newlines=True, env=env, **extrakw)
597 universal_newlines=True, env=env, **extrakw)
597 stdout, stderr = p.communicate()
598 stdout, stderr = p.communicate()
598 stderr = stderr.strip()
599 stderr = stderr.strip()
599 if not failok:
600 if not failok:
600 if p.returncode:
601 if p.returncode:
601 raise util.Abort(stderr or 'exited with code %d' % p.returncode)
602 raise util.Abort(stderr or 'exited with code %d' % p.returncode)
602 if stderr:
603 if stderr:
603 self._ui.warn(stderr + '\n')
604 self._ui.warn(stderr + '\n')
604 return stdout, stderr
605 return stdout, stderr
605
606
606 @propertycache
607 @propertycache
607 def _svnversion(self):
608 def _svnversion(self):
608 output, err = self._svncommand(['--version'], filename=None)
609 output, err = self._svncommand(['--version'], filename=None)
609 m = re.search(r'^svn,\s+version\s+(\d+)\.(\d+)', output)
610 m = re.search(r'^svn,\s+version\s+(\d+)\.(\d+)', output)
610 if not m:
611 if not m:
611 raise util.Abort(_('cannot retrieve svn tool version'))
612 raise util.Abort(_('cannot retrieve svn tool version'))
612 return (int(m.group(1)), int(m.group(2)))
613 return (int(m.group(1)), int(m.group(2)))
613
614
614 def _wcrevs(self):
615 def _wcrevs(self):
615 # Get the working directory revision as well as the last
616 # Get the working directory revision as well as the last
616 # commit revision so we can compare the subrepo state with
617 # commit revision so we can compare the subrepo state with
617 # both. We used to store the working directory one.
618 # both. We used to store the working directory one.
618 output, err = self._svncommand(['info', '--xml'])
619 output, err = self._svncommand(['info', '--xml'])
619 doc = xml.dom.minidom.parseString(output)
620 doc = xml.dom.minidom.parseString(output)
620 entries = doc.getElementsByTagName('entry')
621 entries = doc.getElementsByTagName('entry')
621 lastrev, rev = '0', '0'
622 lastrev, rev = '0', '0'
622 if entries:
623 if entries:
623 rev = str(entries[0].getAttribute('revision')) or '0'
624 rev = str(entries[0].getAttribute('revision')) or '0'
624 commits = entries[0].getElementsByTagName('commit')
625 commits = entries[0].getElementsByTagName('commit')
625 if commits:
626 if commits:
626 lastrev = str(commits[0].getAttribute('revision')) or '0'
627 lastrev = str(commits[0].getAttribute('revision')) or '0'
627 return (lastrev, rev)
628 return (lastrev, rev)
628
629
629 def _wcrev(self):
630 def _wcrev(self):
630 return self._wcrevs()[0]
631 return self._wcrevs()[0]
631
632
632 def _wcchanged(self):
633 def _wcchanged(self):
633 """Return (changes, extchanges) where changes is True
634 """Return (changes, extchanges) where changes is True
634 if the working directory was changed, and extchanges is
635 if the working directory was changed, and extchanges is
635 True if any of these changes concern an external entry.
636 True if any of these changes concern an external entry.
636 """
637 """
637 output, err = self._svncommand(['status', '--xml'])
638 output, err = self._svncommand(['status', '--xml'])
638 externals, changes = [], []
639 externals, changes = [], []
639 doc = xml.dom.minidom.parseString(output)
640 doc = xml.dom.minidom.parseString(output)
640 for e in doc.getElementsByTagName('entry'):
641 for e in doc.getElementsByTagName('entry'):
641 s = e.getElementsByTagName('wc-status')
642 s = e.getElementsByTagName('wc-status')
642 if not s:
643 if not s:
643 continue
644 continue
644 item = s[0].getAttribute('item')
645 item = s[0].getAttribute('item')
645 props = s[0].getAttribute('props')
646 props = s[0].getAttribute('props')
646 path = e.getAttribute('path')
647 path = e.getAttribute('path')
647 if item == 'external':
648 if item == 'external':
648 externals.append(path)
649 externals.append(path)
649 if (item not in ('', 'normal', 'unversioned', 'external')
650 if (item not in ('', 'normal', 'unversioned', 'external')
650 or props not in ('', 'none', 'normal')):
651 or props not in ('', 'none', 'normal')):
651 changes.append(path)
652 changes.append(path)
652 for path in changes:
653 for path in changes:
653 for ext in externals:
654 for ext in externals:
654 if path == ext or path.startswith(ext + os.sep):
655 if path == ext or path.startswith(ext + os.sep):
655 return True, True
656 return True, True
656 return bool(changes), False
657 return bool(changes), False
657
658
658 def dirty(self, ignoreupdate=False):
659 def dirty(self, ignoreupdate=False):
659 if not self._wcchanged()[0]:
660 if not self._wcchanged()[0]:
660 if self._state[1] in self._wcrevs() or ignoreupdate:
661 if self._state[1] in self._wcrevs() or ignoreupdate:
661 return False
662 return False
662 return True
663 return True
663
664
664 def commit(self, text, user, date):
665 def commit(self, text, user, date):
665 # user and date are out of our hands since svn is centralized
666 # user and date are out of our hands since svn is centralized
666 changed, extchanged = self._wcchanged()
667 changed, extchanged = self._wcchanged()
667 if not changed:
668 if not changed:
668 return self._wcrev()
669 return self._wcrev()
669 if extchanged:
670 if extchanged:
670 # Do not try to commit externals
671 # Do not try to commit externals
671 raise util.Abort(_('cannot commit svn externals'))
672 raise util.Abort(_('cannot commit svn externals'))
672 commitinfo, err = self._svncommand(['commit', '-m', text])
673 commitinfo, err = self._svncommand(['commit', '-m', text])
673 self._ui.status(commitinfo)
674 self._ui.status(commitinfo)
674 newrev = re.search('Committed revision ([0-9]+).', commitinfo)
675 newrev = re.search('Committed revision ([0-9]+).', commitinfo)
675 if not newrev:
676 if not newrev:
676 raise util.Abort(commitinfo.splitlines()[-1])
677 raise util.Abort(commitinfo.splitlines()[-1])
677 newrev = newrev.groups()[0]
678 newrev = newrev.groups()[0]
678 self._ui.status(self._svncommand(['update', '-r', newrev])[0])
679 self._ui.status(self._svncommand(['update', '-r', newrev])[0])
679 return newrev
680 return newrev
680
681
681 def remove(self):
682 def remove(self):
682 if self.dirty():
683 if self.dirty():
683 self._ui.warn(_('not removing repo %s because '
684 self._ui.warn(_('not removing repo %s because '
684 'it has changes.\n' % self._path))
685 'it has changes.\n' % self._path))
685 return
686 return
686 self._ui.note(_('removing subrepo %s\n') % self._path)
687 self._ui.note(_('removing subrepo %s\n') % self._path)
687
688
688 def onerror(function, path, excinfo):
689 def onerror(function, path, excinfo):
689 if function is not os.remove:
690 if function is not os.remove:
690 raise
691 raise
691 # read-only files cannot be unlinked under Windows
692 # read-only files cannot be unlinked under Windows
692 s = os.stat(path)
693 s = os.stat(path)
693 if (s.st_mode & stat.S_IWRITE) != 0:
694 if (s.st_mode & stat.S_IWRITE) != 0:
694 raise
695 raise
695 os.chmod(path, stat.S_IMODE(s.st_mode) | stat.S_IWRITE)
696 os.chmod(path, stat.S_IMODE(s.st_mode) | stat.S_IWRITE)
696 os.remove(path)
697 os.remove(path)
697
698
698 path = self._ctx._repo.wjoin(self._path)
699 path = self._ctx._repo.wjoin(self._path)
699 shutil.rmtree(path, onerror=onerror)
700 shutil.rmtree(path, onerror=onerror)
700 try:
701 try:
701 os.removedirs(os.path.dirname(path))
702 os.removedirs(os.path.dirname(path))
702 except OSError:
703 except OSError:
703 pass
704 pass
704
705
705 def get(self, state, overwrite=False):
706 def get(self, state, overwrite=False):
706 if overwrite:
707 if overwrite:
707 self._svncommand(['revert', '--recursive'])
708 self._svncommand(['revert', '--recursive'])
708 args = ['checkout']
709 args = ['checkout']
709 if self._svnversion >= (1, 5):
710 if self._svnversion >= (1, 5):
710 args.append('--force')
711 args.append('--force')
711 # The revision must be specified at the end of the URL to properly
712 # The revision must be specified at the end of the URL to properly
712 # update to a directory which has since been deleted and recreated.
713 # update to a directory which has since been deleted and recreated.
713 args.append('%s@%s' % (state[0], state[1]))
714 args.append('%s@%s' % (state[0], state[1]))
714 status, err = self._svncommand(args, failok=True)
715 status, err = self._svncommand(args, failok=True)
715 if not re.search('Checked out revision [0-9]+.', status):
716 if not re.search('Checked out revision [0-9]+.', status):
716 if ('is already a working copy for a different URL' in err
717 if ('is already a working copy for a different URL' in err
717 and (self._wcchanged() == (False, False))):
718 and (self._wcchanged() == (False, False))):
718 # obstructed but clean working copy, so just blow it away.
719 # obstructed but clean working copy, so just blow it away.
719 self.remove()
720 self.remove()
720 self.get(state, overwrite=False)
721 self.get(state, overwrite=False)
721 return
722 return
722 raise util.Abort((status or err).splitlines()[-1])
723 raise util.Abort((status or err).splitlines()[-1])
723 self._ui.status(status)
724 self._ui.status(status)
724
725
725 def merge(self, state):
726 def merge(self, state):
726 old = self._state[1]
727 old = self._state[1]
727 new = state[1]
728 new = state[1]
728 if new != self._wcrev():
729 if new != self._wcrev():
729 dirty = old == self._wcrev() or self._wcchanged()[0]
730 dirty = old == self._wcrev() or self._wcchanged()[0]
730 if _updateprompt(self._ui, self, dirty, self._wcrev(), new):
731 if _updateprompt(self._ui, self, dirty, self._wcrev(), new):
731 self.get(state, False)
732 self.get(state, False)
732
733
733 def push(self, force):
734 def push(self, force):
734 # push is a no-op for SVN
735 # push is a no-op for SVN
735 return True
736 return True
736
737
737 def files(self):
738 def files(self):
738 output = self._svncommand(['list'])
739 output = self._svncommand(['list'])
739 # This works because svn forbids \n in filenames.
740 # This works because svn forbids \n in filenames.
740 return output.splitlines()
741 return output.splitlines()
741
742
742 def filedata(self, name):
743 def filedata(self, name):
743 return self._svncommand(['cat'], name)
744 return self._svncommand(['cat'], name)
744
745
745
746
746 class gitsubrepo(abstractsubrepo):
747 class gitsubrepo(abstractsubrepo):
747 def __init__(self, ctx, path, state):
748 def __init__(self, ctx, path, state):
748 # TODO add git version check.
749 # TODO add git version check.
749 self._state = state
750 self._state = state
750 self._ctx = ctx
751 self._ctx = ctx
751 self._path = path
752 self._path = path
752 self._relpath = os.path.join(reporelpath(ctx._repo), path)
753 self._relpath = os.path.join(reporelpath(ctx._repo), path)
753 self._abspath = ctx._repo.wjoin(path)
754 self._abspath = ctx._repo.wjoin(path)
754 self._subparent = ctx._repo
755 self._subparent = ctx._repo
755 self._ui = ctx._repo.ui
756 self._ui = ctx._repo.ui
756
757
757 def _gitcommand(self, commands, env=None, stream=False):
758 def _gitcommand(self, commands, env=None, stream=False):
758 return self._gitdir(commands, env=env, stream=stream)[0]
759 return self._gitdir(commands, env=env, stream=stream)[0]
759
760
760 def _gitdir(self, commands, env=None, stream=False):
761 def _gitdir(self, commands, env=None, stream=False):
761 return self._gitnodir(commands, env=env, stream=stream,
762 return self._gitnodir(commands, env=env, stream=stream,
762 cwd=self._abspath)
763 cwd=self._abspath)
763
764
764 def _gitnodir(self, commands, env=None, stream=False, cwd=None):
765 def _gitnodir(self, commands, env=None, stream=False, cwd=None):
765 """Calls the git command
766 """Calls the git command
766
767
767 The methods tries to call the git command. versions previor to 1.6.0
768 The methods tries to call the git command. versions previor to 1.6.0
768 are not supported and very probably fail.
769 are not supported and very probably fail.
769 """
770 """
770 self._ui.debug('%s: git %s\n' % (self._relpath, ' '.join(commands)))
771 self._ui.debug('%s: git %s\n' % (self._relpath, ' '.join(commands)))
771 # unless ui.quiet is set, print git's stderr,
772 # unless ui.quiet is set, print git's stderr,
772 # which is mostly progress and useful info
773 # which is mostly progress and useful info
773 errpipe = None
774 errpipe = None
774 if self._ui.quiet:
775 if self._ui.quiet:
775 errpipe = open(os.devnull, 'w')
776 errpipe = open(os.devnull, 'w')
776 p = subprocess.Popen(['git'] + commands, bufsize=-1, cwd=cwd, env=env,
777 p = subprocess.Popen(['git'] + commands, bufsize=-1, cwd=cwd, env=env,
777 close_fds=util.closefds,
778 close_fds=util.closefds,
778 stdout=subprocess.PIPE, stderr=errpipe)
779 stdout=subprocess.PIPE, stderr=errpipe)
779 if stream:
780 if stream:
780 return p.stdout, None
781 return p.stdout, None
781
782
782 retdata = p.stdout.read().strip()
783 retdata = p.stdout.read().strip()
783 # wait for the child to exit to avoid race condition.
784 # wait for the child to exit to avoid race condition.
784 p.wait()
785 p.wait()
785
786
786 if p.returncode != 0 and p.returncode != 1:
787 if p.returncode != 0 and p.returncode != 1:
787 # there are certain error codes that are ok
788 # there are certain error codes that are ok
788 command = commands[0]
789 command = commands[0]
789 if command in ('cat-file', 'symbolic-ref'):
790 if command in ('cat-file', 'symbolic-ref'):
790 return retdata, p.returncode
791 return retdata, p.returncode
791 # for all others, abort
792 # for all others, abort
792 raise util.Abort('git %s error %d in %s' %
793 raise util.Abort('git %s error %d in %s' %
793 (command, p.returncode, self._relpath))
794 (command, p.returncode, self._relpath))
794
795
795 return retdata, p.returncode
796 return retdata, p.returncode
796
797
797 def _gitmissing(self):
798 def _gitmissing(self):
798 return not os.path.exists(os.path.join(self._abspath, '.git'))
799 return not os.path.exists(os.path.join(self._abspath, '.git'))
799
800
800 def _gitstate(self):
801 def _gitstate(self):
801 return self._gitcommand(['rev-parse', 'HEAD'])
802 return self._gitcommand(['rev-parse', 'HEAD'])
802
803
803 def _gitcurrentbranch(self):
804 def _gitcurrentbranch(self):
804 current, err = self._gitdir(['symbolic-ref', 'HEAD', '--quiet'])
805 current, err = self._gitdir(['symbolic-ref', 'HEAD', '--quiet'])
805 if err:
806 if err:
806 current = None
807 current = None
807 return current
808 return current
808
809
809 def _gitremote(self, remote):
810 def _gitremote(self, remote):
810 out = self._gitcommand(['remote', 'show', '-n', remote])
811 out = self._gitcommand(['remote', 'show', '-n', remote])
811 line = out.split('\n')[1]
812 line = out.split('\n')[1]
812 i = line.index('URL: ') + len('URL: ')
813 i = line.index('URL: ') + len('URL: ')
813 return line[i:]
814 return line[i:]
814
815
815 def _githavelocally(self, revision):
816 def _githavelocally(self, revision):
816 out, code = self._gitdir(['cat-file', '-e', revision])
817 out, code = self._gitdir(['cat-file', '-e', revision])
817 return code == 0
818 return code == 0
818
819
819 def _gitisancestor(self, r1, r2):
820 def _gitisancestor(self, r1, r2):
820 base = self._gitcommand(['merge-base', r1, r2])
821 base = self._gitcommand(['merge-base', r1, r2])
821 return base == r1
822 return base == r1
822
823
823 def _gitisbare(self):
824 def _gitisbare(self):
824 return self._gitcommand(['config', '--bool', 'core.bare']) == 'true'
825 return self._gitcommand(['config', '--bool', 'core.bare']) == 'true'
825
826
826 def _gitupdatestat(self):
827 def _gitupdatestat(self):
827 """This must be run before git diff-index.
828 """This must be run before git diff-index.
828 diff-index only looks at changes to file stat;
829 diff-index only looks at changes to file stat;
829 this command looks at file contents and updates the stat."""
830 this command looks at file contents and updates the stat."""
830 self._gitcommand(['update-index', '-q', '--refresh'])
831 self._gitcommand(['update-index', '-q', '--refresh'])
831
832
832 def _gitbranchmap(self):
833 def _gitbranchmap(self):
833 '''returns 2 things:
834 '''returns 2 things:
834 a map from git branch to revision
835 a map from git branch to revision
835 a map from revision to branches'''
836 a map from revision to branches'''
836 branch2rev = {}
837 branch2rev = {}
837 rev2branch = {}
838 rev2branch = {}
838
839
839 out = self._gitcommand(['for-each-ref', '--format',
840 out = self._gitcommand(['for-each-ref', '--format',
840 '%(objectname) %(refname)'])
841 '%(objectname) %(refname)'])
841 for line in out.split('\n'):
842 for line in out.split('\n'):
842 revision, ref = line.split(' ')
843 revision, ref = line.split(' ')
843 if (not ref.startswith('refs/heads/') and
844 if (not ref.startswith('refs/heads/') and
844 not ref.startswith('refs/remotes/')):
845 not ref.startswith('refs/remotes/')):
845 continue
846 continue
846 if ref.startswith('refs/remotes/') and ref.endswith('/HEAD'):
847 if ref.startswith('refs/remotes/') and ref.endswith('/HEAD'):
847 continue # ignore remote/HEAD redirects
848 continue # ignore remote/HEAD redirects
848 branch2rev[ref] = revision
849 branch2rev[ref] = revision
849 rev2branch.setdefault(revision, []).append(ref)
850 rev2branch.setdefault(revision, []).append(ref)
850 return branch2rev, rev2branch
851 return branch2rev, rev2branch
851
852
852 def _gittracking(self, branches):
853 def _gittracking(self, branches):
853 'return map of remote branch to local tracking branch'
854 'return map of remote branch to local tracking branch'
854 # assumes no more than one local tracking branch for each remote
855 # assumes no more than one local tracking branch for each remote
855 tracking = {}
856 tracking = {}
856 for b in branches:
857 for b in branches:
857 if b.startswith('refs/remotes/'):
858 if b.startswith('refs/remotes/'):
858 continue
859 continue
859 bname = b.split('/', 2)[2]
860 bname = b.split('/', 2)[2]
860 remote = self._gitcommand(['config', 'branch.%s.remote' % bname])
861 remote = self._gitcommand(['config', 'branch.%s.remote' % bname])
861 if remote:
862 if remote:
862 ref = self._gitcommand(['config', 'branch.%s.merge' % bname])
863 ref = self._gitcommand(['config', 'branch.%s.merge' % bname])
863 tracking['refs/remotes/%s/%s' %
864 tracking['refs/remotes/%s/%s' %
864 (remote, ref.split('/', 2)[2])] = b
865 (remote, ref.split('/', 2)[2])] = b
865 return tracking
866 return tracking
866
867
867 def _abssource(self, source):
868 def _abssource(self, source):
868 if '://' not in source:
869 if '://' not in source:
869 # recognize the scp syntax as an absolute source
870 # recognize the scp syntax as an absolute source
870 colon = source.find(':')
871 colon = source.find(':')
871 if colon != -1 and '/' not in source[:colon]:
872 if colon != -1 and '/' not in source[:colon]:
872 return source
873 return source
873 self._subsource = source
874 self._subsource = source
874 return _abssource(self)
875 return _abssource(self)
875
876
876 def _fetch(self, source, revision):
877 def _fetch(self, source, revision):
877 if self._gitmissing():
878 if self._gitmissing():
878 source = self._abssource(source)
879 source = self._abssource(source)
879 self._ui.status(_('cloning subrepo %s from %s\n') %
880 self._ui.status(_('cloning subrepo %s from %s\n') %
880 (self._relpath, source))
881 (self._relpath, source))
881 self._gitnodir(['clone', source, self._abspath])
882 self._gitnodir(['clone', source, self._abspath])
882 if self._githavelocally(revision):
883 if self._githavelocally(revision):
883 return
884 return
884 self._ui.status(_('pulling subrepo %s from %s\n') %
885 self._ui.status(_('pulling subrepo %s from %s\n') %
885 (self._relpath, self._gitremote('origin')))
886 (self._relpath, self._gitremote('origin')))
886 # try only origin: the originally cloned repo
887 # try only origin: the originally cloned repo
887 self._gitcommand(['fetch'])
888 self._gitcommand(['fetch'])
888 if not self._githavelocally(revision):
889 if not self._githavelocally(revision):
889 raise util.Abort(_("revision %s does not exist in subrepo %s\n") %
890 raise util.Abort(_("revision %s does not exist in subrepo %s\n") %
890 (revision, self._relpath))
891 (revision, self._relpath))
891
892
892 def dirty(self, ignoreupdate=False):
893 def dirty(self, ignoreupdate=False):
893 if self._gitmissing():
894 if self._gitmissing():
894 return self._state[1] != ''
895 return self._state[1] != ''
895 if self._gitisbare():
896 if self._gitisbare():
896 return True
897 return True
897 if not ignoreupdate and self._state[1] != self._gitstate():
898 if not ignoreupdate and self._state[1] != self._gitstate():
898 # different version checked out
899 # different version checked out
899 return True
900 return True
900 # check for staged changes or modified files; ignore untracked files
901 # check for staged changes or modified files; ignore untracked files
901 self._gitupdatestat()
902 self._gitupdatestat()
902 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
903 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
903 return code == 1
904 return code == 1
904
905
905 def get(self, state, overwrite=False):
906 def get(self, state, overwrite=False):
906 source, revision, kind = state
907 source, revision, kind = state
907 if not revision:
908 if not revision:
908 self.remove()
909 self.remove()
909 return
910 return
910 self._fetch(source, revision)
911 self._fetch(source, revision)
911 # if the repo was set to be bare, unbare it
912 # if the repo was set to be bare, unbare it
912 if self._gitisbare():
913 if self._gitisbare():
913 self._gitcommand(['config', 'core.bare', 'false'])
914 self._gitcommand(['config', 'core.bare', 'false'])
914 if self._gitstate() == revision:
915 if self._gitstate() == revision:
915 self._gitcommand(['reset', '--hard', 'HEAD'])
916 self._gitcommand(['reset', '--hard', 'HEAD'])
916 return
917 return
917 elif self._gitstate() == revision:
918 elif self._gitstate() == revision:
918 if overwrite:
919 if overwrite:
919 # first reset the index to unmark new files for commit, because
920 # first reset the index to unmark new files for commit, because
920 # reset --hard will otherwise throw away files added for commit,
921 # reset --hard will otherwise throw away files added for commit,
921 # not just unmark them.
922 # not just unmark them.
922 self._gitcommand(['reset', 'HEAD'])
923 self._gitcommand(['reset', 'HEAD'])
923 self._gitcommand(['reset', '--hard', 'HEAD'])
924 self._gitcommand(['reset', '--hard', 'HEAD'])
924 return
925 return
925 branch2rev, rev2branch = self._gitbranchmap()
926 branch2rev, rev2branch = self._gitbranchmap()
926
927
927 def checkout(args):
928 def checkout(args):
928 cmd = ['checkout']
929 cmd = ['checkout']
929 if overwrite:
930 if overwrite:
930 # first reset the index to unmark new files for commit, because
931 # first reset the index to unmark new files for commit, because
931 # the -f option will otherwise throw away files added for
932 # the -f option will otherwise throw away files added for
932 # commit, not just unmark them.
933 # commit, not just unmark them.
933 self._gitcommand(['reset', 'HEAD'])
934 self._gitcommand(['reset', 'HEAD'])
934 cmd.append('-f')
935 cmd.append('-f')
935 self._gitcommand(cmd + args)
936 self._gitcommand(cmd + args)
936
937
937 def rawcheckout():
938 def rawcheckout():
938 # no branch to checkout, check it out with no branch
939 # no branch to checkout, check it out with no branch
939 self._ui.warn(_('checking out detached HEAD in subrepo %s\n') %
940 self._ui.warn(_('checking out detached HEAD in subrepo %s\n') %
940 self._relpath)
941 self._relpath)
941 self._ui.warn(_('check out a git branch if you intend '
942 self._ui.warn(_('check out a git branch if you intend '
942 'to make changes\n'))
943 'to make changes\n'))
943 checkout(['-q', revision])
944 checkout(['-q', revision])
944
945
945 if revision not in rev2branch:
946 if revision not in rev2branch:
946 rawcheckout()
947 rawcheckout()
947 return
948 return
948 branches = rev2branch[revision]
949 branches = rev2branch[revision]
949 firstlocalbranch = None
950 firstlocalbranch = None
950 for b in branches:
951 for b in branches:
951 if b == 'refs/heads/master':
952 if b == 'refs/heads/master':
952 # master trumps all other branches
953 # master trumps all other branches
953 checkout(['refs/heads/master'])
954 checkout(['refs/heads/master'])
954 return
955 return
955 if not firstlocalbranch and not b.startswith('refs/remotes/'):
956 if not firstlocalbranch and not b.startswith('refs/remotes/'):
956 firstlocalbranch = b
957 firstlocalbranch = b
957 if firstlocalbranch:
958 if firstlocalbranch:
958 checkout([firstlocalbranch])
959 checkout([firstlocalbranch])
959 return
960 return
960
961
961 tracking = self._gittracking(branch2rev.keys())
962 tracking = self._gittracking(branch2rev.keys())
962 # choose a remote branch already tracked if possible
963 # choose a remote branch already tracked if possible
963 remote = branches[0]
964 remote = branches[0]
964 if remote not in tracking:
965 if remote not in tracking:
965 for b in branches:
966 for b in branches:
966 if b in tracking:
967 if b in tracking:
967 remote = b
968 remote = b
968 break
969 break
969
970
970 if remote not in tracking:
971 if remote not in tracking:
971 # create a new local tracking branch
972 # create a new local tracking branch
972 local = remote.split('/', 2)[2]
973 local = remote.split('/', 2)[2]
973 checkout(['-b', local, remote])
974 checkout(['-b', local, remote])
974 elif self._gitisancestor(branch2rev[tracking[remote]], remote):
975 elif self._gitisancestor(branch2rev[tracking[remote]], remote):
975 # When updating to a tracked remote branch,
976 # When updating to a tracked remote branch,
976 # if the local tracking branch is downstream of it,
977 # if the local tracking branch is downstream of it,
977 # a normal `git pull` would have performed a "fast-forward merge"
978 # a normal `git pull` would have performed a "fast-forward merge"
978 # which is equivalent to updating the local branch to the remote.
979 # which is equivalent to updating the local branch to the remote.
979 # Since we are only looking at branching at update, we need to
980 # Since we are only looking at branching at update, we need to
980 # detect this situation and perform this action lazily.
981 # detect this situation and perform this action lazily.
981 if tracking[remote] != self._gitcurrentbranch():
982 if tracking[remote] != self._gitcurrentbranch():
982 checkout([tracking[remote]])
983 checkout([tracking[remote]])
983 self._gitcommand(['merge', '--ff', remote])
984 self._gitcommand(['merge', '--ff', remote])
984 else:
985 else:
985 # a real merge would be required, just checkout the revision
986 # a real merge would be required, just checkout the revision
986 rawcheckout()
987 rawcheckout()
987
988
988 def commit(self, text, user, date):
989 def commit(self, text, user, date):
989 if self._gitmissing():
990 if self._gitmissing():
990 raise util.Abort(_("subrepo %s is missing") % self._relpath)
991 raise util.Abort(_("subrepo %s is missing") % self._relpath)
991 cmd = ['commit', '-a', '-m', text]
992 cmd = ['commit', '-a', '-m', text]
992 env = os.environ.copy()
993 env = os.environ.copy()
993 if user:
994 if user:
994 cmd += ['--author', user]
995 cmd += ['--author', user]
995 if date:
996 if date:
996 # git's date parser silently ignores when seconds < 1e9
997 # git's date parser silently ignores when seconds < 1e9
997 # convert to ISO8601
998 # convert to ISO8601
998 env['GIT_AUTHOR_DATE'] = util.datestr(date,
999 env['GIT_AUTHOR_DATE'] = util.datestr(date,
999 '%Y-%m-%dT%H:%M:%S %1%2')
1000 '%Y-%m-%dT%H:%M:%S %1%2')
1000 self._gitcommand(cmd, env=env)
1001 self._gitcommand(cmd, env=env)
1001 # make sure commit works otherwise HEAD might not exist under certain
1002 # make sure commit works otherwise HEAD might not exist under certain
1002 # circumstances
1003 # circumstances
1003 return self._gitstate()
1004 return self._gitstate()
1004
1005
1005 def merge(self, state):
1006 def merge(self, state):
1006 source, revision, kind = state
1007 source, revision, kind = state
1007 self._fetch(source, revision)
1008 self._fetch(source, revision)
1008 base = self._gitcommand(['merge-base', revision, self._state[1]])
1009 base = self._gitcommand(['merge-base', revision, self._state[1]])
1009 self._gitupdatestat()
1010 self._gitupdatestat()
1010 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
1011 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
1011
1012
1012 def mergefunc():
1013 def mergefunc():
1013 if base == revision:
1014 if base == revision:
1014 self.get(state) # fast forward merge
1015 self.get(state) # fast forward merge
1015 elif base != self._state[1]:
1016 elif base != self._state[1]:
1016 self._gitcommand(['merge', '--no-commit', revision])
1017 self._gitcommand(['merge', '--no-commit', revision])
1017
1018
1018 if self.dirty():
1019 if self.dirty():
1019 if self._gitstate() != revision:
1020 if self._gitstate() != revision:
1020 dirty = self._gitstate() == self._state[1] or code != 0
1021 dirty = self._gitstate() == self._state[1] or code != 0
1021 if _updateprompt(self._ui, self, dirty,
1022 if _updateprompt(self._ui, self, dirty,
1022 self._state[1][:7], revision[:7]):
1023 self._state[1][:7], revision[:7]):
1023 mergefunc()
1024 mergefunc()
1024 else:
1025 else:
1025 mergefunc()
1026 mergefunc()
1026
1027
1027 def push(self, force):
1028 def push(self, force):
1028 if not self._state[1]:
1029 if not self._state[1]:
1029 return True
1030 return True
1030 if self._gitmissing():
1031 if self._gitmissing():
1031 raise util.Abort(_("subrepo %s is missing") % self._relpath)
1032 raise util.Abort(_("subrepo %s is missing") % self._relpath)
1032 # if a branch in origin contains the revision, nothing to do
1033 # if a branch in origin contains the revision, nothing to do
1033 branch2rev, rev2branch = self._gitbranchmap()
1034 branch2rev, rev2branch = self._gitbranchmap()
1034 if self._state[1] in rev2branch:
1035 if self._state[1] in rev2branch:
1035 for b in rev2branch[self._state[1]]:
1036 for b in rev2branch[self._state[1]]:
1036 if b.startswith('refs/remotes/origin/'):
1037 if b.startswith('refs/remotes/origin/'):
1037 return True
1038 return True
1038 for b, revision in branch2rev.iteritems():
1039 for b, revision in branch2rev.iteritems():
1039 if b.startswith('refs/remotes/origin/'):
1040 if b.startswith('refs/remotes/origin/'):
1040 if self._gitisancestor(self._state[1], revision):
1041 if self._gitisancestor(self._state[1], revision):
1041 return True
1042 return True
1042 # otherwise, try to push the currently checked out branch
1043 # otherwise, try to push the currently checked out branch
1043 cmd = ['push']
1044 cmd = ['push']
1044 if force:
1045 if force:
1045 cmd.append('--force')
1046 cmd.append('--force')
1046
1047
1047 current = self._gitcurrentbranch()
1048 current = self._gitcurrentbranch()
1048 if current:
1049 if current:
1049 # determine if the current branch is even useful
1050 # determine if the current branch is even useful
1050 if not self._gitisancestor(self._state[1], current):
1051 if not self._gitisancestor(self._state[1], current):
1051 self._ui.warn(_('unrelated git branch checked out '
1052 self._ui.warn(_('unrelated git branch checked out '
1052 'in subrepo %s\n') % self._relpath)
1053 'in subrepo %s\n') % self._relpath)
1053 return False
1054 return False
1054 self._ui.status(_('pushing branch %s of subrepo %s\n') %
1055 self._ui.status(_('pushing branch %s of subrepo %s\n') %
1055 (current.split('/', 2)[2], self._relpath))
1056 (current.split('/', 2)[2], self._relpath))
1056 self._gitcommand(cmd + ['origin', current])
1057 self._gitcommand(cmd + ['origin', current])
1057 return True
1058 return True
1058 else:
1059 else:
1059 self._ui.warn(_('no branch checked out in subrepo %s\n'
1060 self._ui.warn(_('no branch checked out in subrepo %s\n'
1060 'cannot push revision %s') %
1061 'cannot push revision %s') %
1061 (self._relpath, self._state[1]))
1062 (self._relpath, self._state[1]))
1062 return False
1063 return False
1063
1064
1064 def remove(self):
1065 def remove(self):
1065 if self._gitmissing():
1066 if self._gitmissing():
1066 return
1067 return
1067 if self.dirty():
1068 if self.dirty():
1068 self._ui.warn(_('not removing repo %s because '
1069 self._ui.warn(_('not removing repo %s because '
1069 'it has changes.\n') % self._relpath)
1070 'it has changes.\n') % self._relpath)
1070 return
1071 return
1071 # we can't fully delete the repository as it may contain
1072 # we can't fully delete the repository as it may contain
1072 # local-only history
1073 # local-only history
1073 self._ui.note(_('removing subrepo %s\n') % self._relpath)
1074 self._ui.note(_('removing subrepo %s\n') % self._relpath)
1074 self._gitcommand(['config', 'core.bare', 'true'])
1075 self._gitcommand(['config', 'core.bare', 'true'])
1075 for f in os.listdir(self._abspath):
1076 for f in os.listdir(self._abspath):
1076 if f == '.git':
1077 if f == '.git':
1077 continue
1078 continue
1078 path = os.path.join(self._abspath, f)
1079 path = os.path.join(self._abspath, f)
1079 if os.path.isdir(path) and not os.path.islink(path):
1080 if os.path.isdir(path) and not os.path.islink(path):
1080 shutil.rmtree(path)
1081 shutil.rmtree(path)
1081 else:
1082 else:
1082 os.remove(path)
1083 os.remove(path)
1083
1084
1084 def archive(self, ui, archiver, prefix):
1085 def archive(self, ui, archiver, prefix):
1085 source, revision = self._state
1086 source, revision = self._state
1086 if not revision:
1087 if not revision:
1087 return
1088 return
1088 self._fetch(source, revision)
1089 self._fetch(source, revision)
1089
1090
1090 # Parse git's native archive command.
1091 # Parse git's native archive command.
1091 # This should be much faster than manually traversing the trees
1092 # This should be much faster than manually traversing the trees
1092 # and objects with many subprocess calls.
1093 # and objects with many subprocess calls.
1093 tarstream = self._gitcommand(['archive', revision], stream=True)
1094 tarstream = self._gitcommand(['archive', revision], stream=True)
1094 tar = tarfile.open(fileobj=tarstream, mode='r|')
1095 tar = tarfile.open(fileobj=tarstream, mode='r|')
1095 relpath = subrelpath(self)
1096 relpath = subrelpath(self)
1096 ui.progress(_('archiving (%s)') % relpath, 0, unit=_('files'))
1097 ui.progress(_('archiving (%s)') % relpath, 0, unit=_('files'))
1097 for i, info in enumerate(tar):
1098 for i, info in enumerate(tar):
1098 if info.isdir():
1099 if info.isdir():
1099 continue
1100 continue
1100 if info.issym():
1101 if info.issym():
1101 data = info.linkname
1102 data = info.linkname
1102 else:
1103 else:
1103 data = tar.extractfile(info).read()
1104 data = tar.extractfile(info).read()
1104 archiver.addfile(os.path.join(prefix, self._path, info.name),
1105 archiver.addfile(os.path.join(prefix, self._path, info.name),
1105 info.mode, info.issym(), data)
1106 info.mode, info.issym(), data)
1106 ui.progress(_('archiving (%s)') % relpath, i + 1,
1107 ui.progress(_('archiving (%s)') % relpath, i + 1,
1107 unit=_('files'))
1108 unit=_('files'))
1108 ui.progress(_('archiving (%s)') % relpath, None)
1109 ui.progress(_('archiving (%s)') % relpath, None)
1109
1110
1110
1111
1111 def status(self, rev2, **opts):
1112 def status(self, rev2, **opts):
1112 rev1 = self._state[1]
1113 rev1 = self._state[1]
1113 if self._gitmissing() or not rev1:
1114 if self._gitmissing() or not rev1:
1114 # if the repo is missing, return no results
1115 # if the repo is missing, return no results
1115 return [], [], [], [], [], [], []
1116 return [], [], [], [], [], [], []
1116 modified, added, removed = [], [], []
1117 modified, added, removed = [], [], []
1117 self._gitupdatestat()
1118 self._gitupdatestat()
1118 if rev2:
1119 if rev2:
1119 command = ['diff-tree', rev1, rev2]
1120 command = ['diff-tree', rev1, rev2]
1120 else:
1121 else:
1121 command = ['diff-index', rev1]
1122 command = ['diff-index', rev1]
1122 out = self._gitcommand(command)
1123 out = self._gitcommand(command)
1123 for line in out.split('\n'):
1124 for line in out.split('\n'):
1124 tab = line.find('\t')
1125 tab = line.find('\t')
1125 if tab == -1:
1126 if tab == -1:
1126 continue
1127 continue
1127 status, f = line[tab - 1], line[tab + 1:]
1128 status, f = line[tab - 1], line[tab + 1:]
1128 if status == 'M':
1129 if status == 'M':
1129 modified.append(f)
1130 modified.append(f)
1130 elif status == 'A':
1131 elif status == 'A':
1131 added.append(f)
1132 added.append(f)
1132 elif status == 'D':
1133 elif status == 'D':
1133 removed.append(f)
1134 removed.append(f)
1134
1135
1135 deleted = unknown = ignored = clean = []
1136 deleted = unknown = ignored = clean = []
1136 return modified, added, removed, deleted, unknown, ignored, clean
1137 return modified, added, removed, deleted, unknown, ignored, clean
1137
1138
1138 types = {
1139 types = {
1139 'hg': hgsubrepo,
1140 'hg': hgsubrepo,
1140 'svn': svnsubrepo,
1141 'svn': svnsubrepo,
1141 'git': gitsubrepo,
1142 'git': gitsubrepo,
1142 }
1143 }
@@ -1,196 +1,196
1 $ "$TESTDIR/hghave" serve || exit 80
1 $ "$TESTDIR/hghave" serve || exit 80
2
2
3 initialize
3 initialize
4
4
5 $ hg init a
5 $ hg init a
6 $ cd a
6 $ cd a
7 $ echo 'test' > test
7 $ echo 'test' > test
8 $ hg commit -Am'test'
8 $ hg commit -Am'test'
9 adding test
9 adding test
10
10
11 set bookmarks
11 set bookmarks
12
12
13 $ hg bookmark X
13 $ hg bookmark X
14 $ hg bookmark Y
14 $ hg bookmark Y
15 $ hg bookmark Z
15 $ hg bookmark Z
16
16
17 import bookmark by name
17 import bookmark by name
18
18
19 $ hg init ../b
19 $ hg init ../b
20 $ cd ../b
20 $ cd ../b
21 $ hg book Y
21 $ hg book Y
22 $ hg book
22 $ hg book
23 * Y -1:000000000000
23 * Y -1:000000000000
24 $ hg pull ../a
24 $ hg pull ../a
25 pulling from ../a
25 pulling from ../a
26 requesting all changes
26 requesting all changes
27 adding changesets
27 adding changesets
28 adding manifests
28 adding manifests
29 adding file changes
29 adding file changes
30 added 1 changesets with 1 changes to 1 files
30 added 1 changesets with 1 changes to 1 files
31 updating bookmark Y
31 updating bookmark Y
32 (run 'hg update' to get a working copy)
32 (run 'hg update' to get a working copy)
33 $ hg bookmarks
33 $ hg bookmarks
34 Y 0:4e3505fd9583
34 Y 0:4e3505fd9583
35 $ hg debugpushkey ../a namespaces
35 $ hg debugpushkey ../a namespaces
36 bookmarks
36 bookmarks
37 namespaces
37 namespaces
38 $ hg debugpushkey ../a bookmarks
38 $ hg debugpushkey ../a bookmarks
39 Y 4e3505fd95835d721066b76e75dbb8cc554d7f77
39 Y 4e3505fd95835d721066b76e75dbb8cc554d7f77
40 X 4e3505fd95835d721066b76e75dbb8cc554d7f77
40 X 4e3505fd95835d721066b76e75dbb8cc554d7f77
41 Z 4e3505fd95835d721066b76e75dbb8cc554d7f77
41 Z 4e3505fd95835d721066b76e75dbb8cc554d7f77
42 $ hg pull -B X ../a
42 $ hg pull -B X ../a
43 pulling from ../a
43 pulling from ../a
44 no changes found
44 no changes found
45 importing bookmark X
45 importing bookmark X
46 $ hg bookmark
46 $ hg bookmark
47 X 0:4e3505fd9583
47 X 0:4e3505fd9583
48 Y 0:4e3505fd9583
48 Y 0:4e3505fd9583
49
49
50 export bookmark by name
50 export bookmark by name
51
51
52 $ hg bookmark W
52 $ hg bookmark W
53 $ hg bookmark foo
53 $ hg bookmark foo
54 $ hg bookmark foobar
54 $ hg bookmark foobar
55 $ hg push -B W ../a
55 $ hg push -B W ../a
56 pushing to ../a
56 pushing to ../a
57 searching for changes
57 searching for changes
58 no changes found
58 no changes found
59 exporting bookmark W
59 exporting bookmark W
60 $ hg -R ../a bookmarks
60 $ hg -R ../a bookmarks
61 W -1:000000000000
61 W -1:000000000000
62 X 0:4e3505fd9583
62 X 0:4e3505fd9583
63 Y 0:4e3505fd9583
63 Y 0:4e3505fd9583
64 * Z 0:4e3505fd9583
64 * Z 0:4e3505fd9583
65
65
66 delete a remote bookmark
66 delete a remote bookmark
67
67
68 $ hg book -d W
68 $ hg book -d W
69 $ hg push -B W ../a
69 $ hg push -B W ../a
70 pushing to ../a
70 pushing to ../a
71 searching for changes
71 searching for changes
72 no changes found
72 no changes found
73 deleting remote bookmark W
73 deleting remote bookmark W
74
74
75 push/pull name that doesn't exist
75 push/pull name that doesn't exist
76
76
77 $ hg push -B badname ../a
77 $ hg push -B badname ../a
78 pushing to ../a
78 pushing to ../a
79 searching for changes
79 searching for changes
80 no changes found
80 no changes found
81 bookmark badname does not exist on the local or remote repository!
81 bookmark badname does not exist on the local or remote repository!
82 [2]
82 [2]
83 $ hg pull -B anotherbadname ../a
83 $ hg pull -B anotherbadname ../a
84 pulling from ../a
84 pulling from ../a
85 abort: remote bookmark anotherbadname not found!
85 abort: remote bookmark anotherbadname not found!
86 [255]
86 [255]
87
87
88 divergent bookmarks
88 divergent bookmarks
89
89
90 $ cd ../a
90 $ cd ../a
91 $ echo c1 > f1
91 $ echo c1 > f1
92 $ hg ci -Am1
92 $ hg ci -Am1
93 adding f1
93 adding f1
94 $ hg book -f X
94 $ hg book -f X
95 $ hg book
95 $ hg book
96 * X 1:0d2164f0ce0d
96 * X 1:0d2164f0ce0d
97 Y 0:4e3505fd9583
97 Y 0:4e3505fd9583
98 Z 1:0d2164f0ce0d
98 Z 1:0d2164f0ce0d
99
99
100 $ cd ../b
100 $ cd ../b
101 $ hg up
101 $ hg up
102 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
102 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
103 $ echo c2 > f2
103 $ echo c2 > f2
104 $ hg ci -Am2
104 $ hg ci -Am2
105 adding f2
105 adding f2
106 $ hg book -f X
106 $ hg book -f X
107 $ hg book
107 $ hg book
108 * X 1:9b140be10808
108 * X 1:9b140be10808
109 Y 0:4e3505fd9583
109 Y 0:4e3505fd9583
110 foo -1:000000000000
110 foo -1:000000000000
111 foobar -1:000000000000
111 foobar -1:000000000000
112
112
113 $ hg pull ../a
113 $ hg pull --config paths.foo=../a foo
114 pulling from ../a
114 pulling from $TESTTMP/a
115 searching for changes
115 searching for changes
116 adding changesets
116 adding changesets
117 adding manifests
117 adding manifests
118 adding file changes
118 adding file changes
119 added 1 changesets with 1 changes to 1 files (+1 heads)
119 added 1 changesets with 1 changes to 1 files (+1 heads)
120 divergent bookmark X stored as X@1
120 divergent bookmark X stored as X@foo
121 (run 'hg heads' to see heads, 'hg merge' to merge)
121 (run 'hg heads' to see heads, 'hg merge' to merge)
122 $ hg book
122 $ hg book
123 * X 1:9b140be10808
123 * X 1:9b140be10808
124 X@1 2:0d2164f0ce0d
124 X@foo 2:0d2164f0ce0d
125 Y 0:4e3505fd9583
125 Y 0:4e3505fd9583
126 foo -1:000000000000
126 foo -1:000000000000
127 foobar -1:000000000000
127 foobar -1:000000000000
128 $ hg push -f ../a
128 $ hg push -f ../a
129 pushing to ../a
129 pushing to ../a
130 searching for changes
130 searching for changes
131 adding changesets
131 adding changesets
132 adding manifests
132 adding manifests
133 adding file changes
133 adding file changes
134 added 1 changesets with 1 changes to 1 files (+1 heads)
134 added 1 changesets with 1 changes to 1 files (+1 heads)
135 $ hg -R ../a book
135 $ hg -R ../a book
136 * X 1:0d2164f0ce0d
136 * X 1:0d2164f0ce0d
137 Y 0:4e3505fd9583
137 Y 0:4e3505fd9583
138 Z 1:0d2164f0ce0d
138 Z 1:0d2164f0ce0d
139
139
140 hgweb
140 hgweb
141
141
142 $ cat <<EOF > .hg/hgrc
142 $ cat <<EOF > .hg/hgrc
143 > [web]
143 > [web]
144 > push_ssl = false
144 > push_ssl = false
145 > allow_push = *
145 > allow_push = *
146 > EOF
146 > EOF
147
147
148 $ hg serve -p $HGPORT -d --pid-file=../hg.pid -E errors.log
148 $ hg serve -p $HGPORT -d --pid-file=../hg.pid -E errors.log
149 $ cat ../hg.pid >> $DAEMON_PIDS
149 $ cat ../hg.pid >> $DAEMON_PIDS
150 $ cd ../a
150 $ cd ../a
151
151
152 $ hg debugpushkey http://localhost:$HGPORT/ namespaces
152 $ hg debugpushkey http://localhost:$HGPORT/ namespaces
153 bookmarks
153 bookmarks
154 namespaces
154 namespaces
155 $ hg debugpushkey http://localhost:$HGPORT/ bookmarks
155 $ hg debugpushkey http://localhost:$HGPORT/ bookmarks
156 Y 4e3505fd95835d721066b76e75dbb8cc554d7f77
156 Y 4e3505fd95835d721066b76e75dbb8cc554d7f77
157 X 9b140be1080824d768c5a4691a564088eede71f9
157 X 9b140be1080824d768c5a4691a564088eede71f9
158 foo 0000000000000000000000000000000000000000
158 foo 0000000000000000000000000000000000000000
159 foobar 0000000000000000000000000000000000000000
159 foobar 0000000000000000000000000000000000000000
160 $ hg out -B http://localhost:$HGPORT/
160 $ hg out -B http://localhost:$HGPORT/
161 comparing with http://localhost:$HGPORT/
161 comparing with http://localhost:$HGPORT/
162 searching for changed bookmarks
162 searching for changed bookmarks
163 Z 0d2164f0ce0d
163 Z 0d2164f0ce0d
164 $ hg push -B Z http://localhost:$HGPORT/
164 $ hg push -B Z http://localhost:$HGPORT/
165 pushing to http://localhost:$HGPORT/
165 pushing to http://localhost:$HGPORT/
166 searching for changes
166 searching for changes
167 no changes found
167 no changes found
168 exporting bookmark Z
168 exporting bookmark Z
169 $ hg book -d Z
169 $ hg book -d Z
170 $ hg in -B http://localhost:$HGPORT/
170 $ hg in -B http://localhost:$HGPORT/
171 comparing with http://localhost:$HGPORT/
171 comparing with http://localhost:$HGPORT/
172 searching for changed bookmarks
172 searching for changed bookmarks
173 Z 0d2164f0ce0d
173 Z 0d2164f0ce0d
174 foo 000000000000
174 foo 000000000000
175 foobar 000000000000
175 foobar 000000000000
176 $ hg pull -B Z http://localhost:$HGPORT/
176 $ hg pull -B Z http://localhost:$HGPORT/
177 pulling from http://localhost:$HGPORT/
177 pulling from http://localhost:$HGPORT/
178 no changes found
178 no changes found
179 divergent bookmark X stored as X@1
179 divergent bookmark X stored as X@1
180 importing bookmark Z
180 importing bookmark Z
181 $ hg clone http://localhost:$HGPORT/ cloned-bookmarks
181 $ hg clone http://localhost:$HGPORT/ cloned-bookmarks
182 requesting all changes
182 requesting all changes
183 adding changesets
183 adding changesets
184 adding manifests
184 adding manifests
185 adding file changes
185 adding file changes
186 added 3 changesets with 3 changes to 3 files (+1 heads)
186 added 3 changesets with 3 changes to 3 files (+1 heads)
187 updating to branch default
187 updating to branch default
188 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
188 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
189 $ hg -R cloned-bookmarks bookmarks
189 $ hg -R cloned-bookmarks bookmarks
190 X 1:9b140be10808
190 X 1:9b140be10808
191 Y 0:4e3505fd9583
191 Y 0:4e3505fd9583
192 Z 2:0d2164f0ce0d
192 Z 2:0d2164f0ce0d
193 foo -1:000000000000
193 foo -1:000000000000
194 foobar -1:000000000000
194 foobar -1:000000000000
195
195
196 $ kill `cat ../hg.pid`
196 $ kill `cat ../hg.pid`
General Comments 0
You need to be logged in to leave comments. Login now