##// END OF EJS Templates
refactor options from cmdtable...
Benoit Boissinot -
r5147:c80af969 default
parent child Browse files
Show More
@@ -1,215 +1,214 b''
1 # extdiff.py - external diff program support for mercurial
1 # extdiff.py - external diff program support for mercurial
2 #
2 #
3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7 #
7 #
8 # The `extdiff' Mercurial extension allows you to use external programs
8 # The `extdiff' Mercurial extension allows you to use external programs
9 # to compare revisions, or revision with working dir. The external diff
9 # to compare revisions, or revision with working dir. The external diff
10 # programs are called with a configurable set of options and two
10 # programs are called with a configurable set of options and two
11 # non-option arguments: paths to directories containing snapshots of
11 # non-option arguments: paths to directories containing snapshots of
12 # files to compare.
12 # files to compare.
13 #
13 #
14 # To enable this extension:
14 # To enable this extension:
15 #
15 #
16 # [extensions]
16 # [extensions]
17 # hgext.extdiff =
17 # hgext.extdiff =
18 #
18 #
19 # The `extdiff' extension also allows to configure new diff commands, so
19 # The `extdiff' extension also allows to configure new diff commands, so
20 # you do not need to type "hg extdiff -p kdiff3" always.
20 # you do not need to type "hg extdiff -p kdiff3" always.
21 #
21 #
22 # [extdiff]
22 # [extdiff]
23 # # add new command that runs GNU diff(1) in 'context diff' mode
23 # # add new command that runs GNU diff(1) in 'context diff' mode
24 # cmd.cdiff = gdiff
24 # cmd.cdiff = gdiff
25 # opts.cdiff = -Nprc5
25 # opts.cdiff = -Nprc5
26
26
27 # # add new command called vdiff, runs kdiff3
27 # # add new command called vdiff, runs kdiff3
28 # cmd.vdiff = kdiff3
28 # cmd.vdiff = kdiff3
29
29
30 # # add new command called meld, runs meld (no need to name twice)
30 # # add new command called meld, runs meld (no need to name twice)
31 # cmd.meld =
31 # cmd.meld =
32
32
33 # # add new command called vimdiff, runs gvimdiff with DirDiff plugin
33 # # add new command called vimdiff, runs gvimdiff with DirDiff plugin
34 # #(see http://www.vim.org/scripts/script.php?script_id=102)
34 # #(see http://www.vim.org/scripts/script.php?script_id=102)
35 # # Non english user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
35 # # Non english user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
36 # # your .vimrc
36 # # your .vimrc
37 # cmd.vimdiff = gvim
37 # cmd.vimdiff = gvim
38 # opts.vimdiff = -f '+next' '+execute "DirDiff" argv(0) argv(1)'
38 # opts.vimdiff = -f '+next' '+execute "DirDiff" argv(0) argv(1)'
39 #
39 #
40 # Each custom diff commands can have two parts: a `cmd' and an `opts'
40 # Each custom diff commands can have two parts: a `cmd' and an `opts'
41 # part. The cmd.xxx option defines the name of an executable program
41 # part. The cmd.xxx option defines the name of an executable program
42 # that will be run, and opts.xxx defines a set of command-line options
42 # that will be run, and opts.xxx defines a set of command-line options
43 # which will be inserted to the command between the program name and
43 # which will be inserted to the command between the program name and
44 # the files/directories to diff (i.e. the cdiff example above).
44 # the files/directories to diff (i.e. the cdiff example above).
45 #
45 #
46 # You can use -I/-X and list of file or directory names like normal
46 # You can use -I/-X and list of file or directory names like normal
47 # "hg diff" command. The `extdiff' extension makes snapshots of only
47 # "hg diff" command. The `extdiff' extension makes snapshots of only
48 # needed files, so running the external diff program will actually be
48 # needed files, so running the external diff program will actually be
49 # pretty fast (at least faster than having to compare the entire tree).
49 # pretty fast (at least faster than having to compare the entire tree).
50
50
51 from mercurial.i18n import _
51 from mercurial.i18n import _
52 from mercurial.node import *
52 from mercurial.node import *
53 from mercurial import cmdutil, util
53 from mercurial import cmdutil, util, commands
54 import os, shutil, tempfile
54 import os, shutil, tempfile
55
55
56
56
57 def snapshot_node(ui, repo, files, node, tmproot):
57 def snapshot_node(ui, repo, files, node, tmproot):
58 '''snapshot files as of some revision'''
58 '''snapshot files as of some revision'''
59 mf = repo.changectx(node).manifest()
59 mf = repo.changectx(node).manifest()
60 dirname = os.path.basename(repo.root)
60 dirname = os.path.basename(repo.root)
61 if dirname == "":
61 if dirname == "":
62 dirname = "root"
62 dirname = "root"
63 dirname = '%s.%s' % (dirname, short(node))
63 dirname = '%s.%s' % (dirname, short(node))
64 base = os.path.join(tmproot, dirname)
64 base = os.path.join(tmproot, dirname)
65 os.mkdir(base)
65 os.mkdir(base)
66 ui.note(_('making snapshot of %d files from rev %s\n') %
66 ui.note(_('making snapshot of %d files from rev %s\n') %
67 (len(files), short(node)))
67 (len(files), short(node)))
68 for fn in files:
68 for fn in files:
69 if not fn in mf:
69 if not fn in mf:
70 # skipping new file after a merge ?
70 # skipping new file after a merge ?
71 continue
71 continue
72 wfn = util.pconvert(fn)
72 wfn = util.pconvert(fn)
73 ui.note(' %s\n' % wfn)
73 ui.note(' %s\n' % wfn)
74 dest = os.path.join(base, wfn)
74 dest = os.path.join(base, wfn)
75 destdir = os.path.dirname(dest)
75 destdir = os.path.dirname(dest)
76 if not os.path.isdir(destdir):
76 if not os.path.isdir(destdir):
77 os.makedirs(destdir)
77 os.makedirs(destdir)
78 data = repo.wwritedata(wfn, repo.file(wfn).read(mf[wfn]))
78 data = repo.wwritedata(wfn, repo.file(wfn).read(mf[wfn]))
79 open(dest, 'wb').write(data)
79 open(dest, 'wb').write(data)
80 return dirname
80 return dirname
81
81
82
82
83 def snapshot_wdir(ui, repo, files, tmproot):
83 def snapshot_wdir(ui, repo, files, tmproot):
84 '''snapshot files from working directory.
84 '''snapshot files from working directory.
85 if not using snapshot, -I/-X does not work and recursive diff
85 if not using snapshot, -I/-X does not work and recursive diff
86 in tools like kdiff3 and meld displays too many files.'''
86 in tools like kdiff3 and meld displays too many files.'''
87 dirname = os.path.basename(repo.root)
87 dirname = os.path.basename(repo.root)
88 if dirname == "":
88 if dirname == "":
89 dirname = "root"
89 dirname = "root"
90 base = os.path.join(tmproot, dirname)
90 base = os.path.join(tmproot, dirname)
91 os.mkdir(base)
91 os.mkdir(base)
92 ui.note(_('making snapshot of %d files from working dir\n') %
92 ui.note(_('making snapshot of %d files from working dir\n') %
93 (len(files)))
93 (len(files)))
94 for fn in files:
94 for fn in files:
95 wfn = util.pconvert(fn)
95 wfn = util.pconvert(fn)
96 ui.note(' %s\n' % wfn)
96 ui.note(' %s\n' % wfn)
97 dest = os.path.join(base, wfn)
97 dest = os.path.join(base, wfn)
98 destdir = os.path.dirname(dest)
98 destdir = os.path.dirname(dest)
99 if not os.path.isdir(destdir):
99 if not os.path.isdir(destdir):
100 os.makedirs(destdir)
100 os.makedirs(destdir)
101 fp = open(dest, 'wb')
101 fp = open(dest, 'wb')
102 for chunk in util.filechunkiter(repo.wopener(wfn)):
102 for chunk in util.filechunkiter(repo.wopener(wfn)):
103 fp.write(chunk)
103 fp.write(chunk)
104 return dirname
104 return dirname
105
105
106
106
107 def dodiff(ui, repo, diffcmd, diffopts, pats, opts):
107 def dodiff(ui, repo, diffcmd, diffopts, pats, opts):
108 node1, node2 = cmdutil.revpair(repo, opts['rev'])
108 node1, node2 = cmdutil.revpair(repo, opts['rev'])
109 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
109 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
110 modified, added, removed, deleted, unknown = repo.status(
110 modified, added, removed, deleted, unknown = repo.status(
111 node1, node2, files, match=matchfn)[:5]
111 node1, node2, files, match=matchfn)[:5]
112 if not (modified or added or removed):
112 if not (modified or added or removed):
113 return 0
113 return 0
114
114
115 tmproot = tempfile.mkdtemp(prefix='extdiff.')
115 tmproot = tempfile.mkdtemp(prefix='extdiff.')
116 dir2root = ''
116 dir2root = ''
117 try:
117 try:
118 # Always make a copy of node1
118 # Always make a copy of node1
119 dir1 = snapshot_node(ui, repo, modified + removed, node1, tmproot)
119 dir1 = snapshot_node(ui, repo, modified + removed, node1, tmproot)
120 changes = len(modified) + len(removed) + len(added)
120 changes = len(modified) + len(removed) + len(added)
121
121
122 # If node2 in not the wc or there is >1 change, copy it
122 # If node2 in not the wc or there is >1 change, copy it
123 if node2:
123 if node2:
124 dir2 = snapshot_node(ui, repo, modified + added, node2, tmproot)
124 dir2 = snapshot_node(ui, repo, modified + added, node2, tmproot)
125 elif changes > 1:
125 elif changes > 1:
126 dir2 = snapshot_wdir(ui, repo, modified + added, tmproot)
126 dir2 = snapshot_wdir(ui, repo, modified + added, tmproot)
127 else:
127 else:
128 # This lets the diff tool open the changed file directly
128 # This lets the diff tool open the changed file directly
129 dir2 = ''
129 dir2 = ''
130 dir2root = repo.root
130 dir2root = repo.root
131
131
132 # If only one change, diff the files instead of the directories
132 # If only one change, diff the files instead of the directories
133 if changes == 1 :
133 if changes == 1 :
134 if len(modified):
134 if len(modified):
135 dir1 = os.path.join(dir1, util.localpath(modified[0]))
135 dir1 = os.path.join(dir1, util.localpath(modified[0]))
136 dir2 = os.path.join(dir2root, dir2, util.localpath(modified[0]))
136 dir2 = os.path.join(dir2root, dir2, util.localpath(modified[0]))
137 elif len(removed) :
137 elif len(removed) :
138 dir1 = os.path.join(dir1, util.localpath(removed[0]))
138 dir1 = os.path.join(dir1, util.localpath(removed[0]))
139 dir2 = os.devnull
139 dir2 = os.devnull
140 else:
140 else:
141 dir1 = os.devnull
141 dir1 = os.devnull
142 dir2 = os.path.join(dir2root, dir2, util.localpath(added[0]))
142 dir2 = os.path.join(dir2root, dir2, util.localpath(added[0]))
143
143
144 cmdline = ('%s %s %s %s' %
144 cmdline = ('%s %s %s %s' %
145 (util.shellquote(diffcmd), ' '.join(diffopts),
145 (util.shellquote(diffcmd), ' '.join(diffopts),
146 util.shellquote(dir1), util.shellquote(dir2)))
146 util.shellquote(dir1), util.shellquote(dir2)))
147 ui.debug('running %r in %s\n' % (cmdline, tmproot))
147 ui.debug('running %r in %s\n' % (cmdline, tmproot))
148 util.system(cmdline, cwd=tmproot)
148 util.system(cmdline, cwd=tmproot)
149 return 1
149 return 1
150 finally:
150 finally:
151 ui.note(_('cleaning up temp directory\n'))
151 ui.note(_('cleaning up temp directory\n'))
152 shutil.rmtree(tmproot)
152 shutil.rmtree(tmproot)
153
153
154 def extdiff(ui, repo, *pats, **opts):
154 def extdiff(ui, repo, *pats, **opts):
155 '''use external program to diff repository (or selected files)
155 '''use external program to diff repository (or selected files)
156
156
157 Show differences between revisions for the specified files, using
157 Show differences between revisions for the specified files, using
158 an external program. The default program used is diff, with
158 an external program. The default program used is diff, with
159 default options "-Npru".
159 default options "-Npru".
160
160
161 To select a different program, use the -p option. The program
161 To select a different program, use the -p option. The program
162 will be passed the names of two directories to compare. To pass
162 will be passed the names of two directories to compare. To pass
163 additional options to the program, use the -o option. These will
163 additional options to the program, use the -o option. These will
164 be passed before the names of the directories to compare.
164 be passed before the names of the directories to compare.
165
165
166 When two revision arguments are given, then changes are
166 When two revision arguments are given, then changes are
167 shown between those revisions. If only one revision is
167 shown between those revisions. If only one revision is
168 specified then that revision is compared to the working
168 specified then that revision is compared to the working
169 directory, and, when no revisions are specified, the
169 directory, and, when no revisions are specified, the
170 working directory files are compared to its parent.'''
170 working directory files are compared to its parent.'''
171 program = opts['program'] or 'diff'
171 program = opts['program'] or 'diff'
172 if opts['program']:
172 if opts['program']:
173 option = opts['option']
173 option = opts['option']
174 else:
174 else:
175 option = opts['option'] or ['-Npru']
175 option = opts['option'] or ['-Npru']
176 return dodiff(ui, repo, program, option, pats, opts)
176 return dodiff(ui, repo, program, option, pats, opts)
177
177
178 cmdtable = {
178 cmdtable = {
179 "extdiff":
179 "extdiff":
180 (extdiff,
180 (extdiff,
181 [('p', 'program', '', _('comparison program to run')),
181 [('p', 'program', '', _('comparison program to run')),
182 ('o', 'option', [], _('pass option to comparison program')),
182 ('o', 'option', [], _('pass option to comparison program')),
183 ('r', 'rev', [], _('revision')),
183 ('r', 'rev', [], _('revision')),
184 ('I', 'include', [], _('include names matching the given patterns')),
184 ] + commands.walkopts,
185 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
186 _('hg extdiff [OPT]... [FILE]...')),
185 _('hg extdiff [OPT]... [FILE]...')),
187 }
186 }
188
187
189 def uisetup(ui):
188 def uisetup(ui):
190 for cmd, path in ui.configitems('extdiff'):
189 for cmd, path in ui.configitems('extdiff'):
191 if not cmd.startswith('cmd.'): continue
190 if not cmd.startswith('cmd.'): continue
192 cmd = cmd[4:]
191 cmd = cmd[4:]
193 if not path: path = cmd
192 if not path: path = cmd
194 diffopts = ui.config('extdiff', 'opts.' + cmd, '')
193 diffopts = ui.config('extdiff', 'opts.' + cmd, '')
195 diffopts = diffopts and [diffopts] or []
194 diffopts = diffopts and [diffopts] or []
196 def save(cmd, path, diffopts):
195 def save(cmd, path, diffopts):
197 '''use closure to save diff command to use'''
196 '''use closure to save diff command to use'''
198 def mydiff(ui, repo, *pats, **opts):
197 def mydiff(ui, repo, *pats, **opts):
199 return dodiff(ui, repo, path, diffopts, pats, opts)
198 return dodiff(ui, repo, path, diffopts, pats, opts)
200 mydiff.__doc__ = '''use %(path)r to diff repository (or selected files)
199 mydiff.__doc__ = '''use %(path)r to diff repository (or selected files)
201
200
202 Show differences between revisions for the specified
201 Show differences between revisions for the specified
203 files, using the %(path)r program.
202 files, using the %(path)r program.
204
203
205 When two revision arguments are given, then changes are
204 When two revision arguments are given, then changes are
206 shown between those revisions. If only one revision is
205 shown between those revisions. If only one revision is
207 specified then that revision is compared to the working
206 specified then that revision is compared to the working
208 directory, and, when no revisions are specified, the
207 directory, and, when no revisions are specified, the
209 working directory files are compared to its parent.''' % {
208 working directory files are compared to its parent.''' % {
210 'path': path,
209 'path': path,
211 }
210 }
212 return mydiff
211 return mydiff
213 cmdtable[cmd] = (save(cmd, path, diffopts),
212 cmdtable[cmd] = (save(cmd, path, diffopts),
214 cmdtable['extdiff'][1][1:],
213 cmdtable['extdiff'][1][1:],
215 _('hg %s [OPTION]... [FILE]...') % cmd)
214 _('hg %s [OPTION]... [FILE]...') % cmd)
@@ -1,98 +1,93 b''
1 # fetch.py - pull and merge remote changes
1 # fetch.py - pull and merge remote changes
2 #
2 #
3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from mercurial.i18n import _
8 from mercurial.i18n import _
9 from mercurial.node import *
9 from mercurial.node import *
10 from mercurial import commands, cmdutil, hg, node, util
10 from mercurial import commands, cmdutil, hg, node, util
11
11
12 def fetch(ui, repo, source='default', **opts):
12 def fetch(ui, repo, source='default', **opts):
13 '''Pull changes from a remote repository, merge new changes if needed.
13 '''Pull changes from a remote repository, merge new changes if needed.
14
14
15 This finds all changes from the repository at the specified path
15 This finds all changes from the repository at the specified path
16 or URL and adds them to the local repository.
16 or URL and adds them to the local repository.
17
17
18 If the pulled changes add a new head, the head is automatically
18 If the pulled changes add a new head, the head is automatically
19 merged, and the result of the merge is committed. Otherwise, the
19 merged, and the result of the merge is committed. Otherwise, the
20 working directory is updated.'''
20 working directory is updated.'''
21
21
22 def postincoming(other, modheads):
22 def postincoming(other, modheads):
23 if modheads == 0:
23 if modheads == 0:
24 return 0
24 return 0
25 if modheads == 1:
25 if modheads == 1:
26 return hg.clean(repo, repo.changelog.tip())
26 return hg.clean(repo, repo.changelog.tip())
27 newheads = repo.heads(parent)
27 newheads = repo.heads(parent)
28 newchildren = [n for n in repo.heads(parent) if n != parent]
28 newchildren = [n for n in repo.heads(parent) if n != parent]
29 newparent = parent
29 newparent = parent
30 if newchildren:
30 if newchildren:
31 newparent = newchildren[0]
31 newparent = newchildren[0]
32 hg.clean(repo, newparent)
32 hg.clean(repo, newparent)
33 newheads = [n for n in repo.heads() if n != newparent]
33 newheads = [n for n in repo.heads() if n != newparent]
34 err = False
34 err = False
35 if newheads:
35 if newheads:
36 ui.status(_('merging with new head %d:%s\n') %
36 ui.status(_('merging with new head %d:%s\n') %
37 (repo.changelog.rev(newheads[0]), short(newheads[0])))
37 (repo.changelog.rev(newheads[0]), short(newheads[0])))
38 err = hg.merge(repo, newheads[0], remind=False)
38 err = hg.merge(repo, newheads[0], remind=False)
39 if not err and len(newheads) > 1:
39 if not err and len(newheads) > 1:
40 ui.status(_('not merging with %d other new heads '
40 ui.status(_('not merging with %d other new heads '
41 '(use "hg heads" and "hg merge" to merge them)') %
41 '(use "hg heads" and "hg merge" to merge them)') %
42 (len(newheads) - 1))
42 (len(newheads) - 1))
43 if not err:
43 if not err:
44 mod, add, rem = repo.status()[:3]
44 mod, add, rem = repo.status()[:3]
45 message = (cmdutil.logmessage(opts) or
45 message = (cmdutil.logmessage(opts) or
46 (_('Automated merge with %s') % other.url()))
46 (_('Automated merge with %s') % other.url()))
47 n = repo.commit(mod + add + rem, message,
47 n = repo.commit(mod + add + rem, message,
48 opts['user'], opts['date'],
48 opts['user'], opts['date'],
49 force_editor=opts.get('force_editor'))
49 force_editor=opts.get('force_editor'))
50 ui.status(_('new changeset %d:%s merges remote changes '
50 ui.status(_('new changeset %d:%s merges remote changes '
51 'with local\n') % (repo.changelog.rev(n),
51 'with local\n') % (repo.changelog.rev(n),
52 short(n)))
52 short(n)))
53 def pull():
53 def pull():
54 cmdutil.setremoteconfig(ui, opts)
54 cmdutil.setremoteconfig(ui, opts)
55
55
56 other = hg.repository(ui, ui.expandpath(source))
56 other = hg.repository(ui, ui.expandpath(source))
57 ui.status(_('pulling from %s\n') % ui.expandpath(source))
57 ui.status(_('pulling from %s\n') % ui.expandpath(source))
58 revs = None
58 revs = None
59 if opts['rev'] and not other.local():
59 if opts['rev'] and not other.local():
60 raise util.Abort(_("fetch -r doesn't work for remote repositories yet"))
60 raise util.Abort(_("fetch -r doesn't work for remote repositories yet"))
61 elif opts['rev']:
61 elif opts['rev']:
62 revs = [other.lookup(rev) for rev in opts['rev']]
62 revs = [other.lookup(rev) for rev in opts['rev']]
63 modheads = repo.pull(other, heads=revs)
63 modheads = repo.pull(other, heads=revs)
64 return postincoming(other, modheads)
64 return postincoming(other, modheads)
65
65
66 parent, p2 = repo.dirstate.parents()
66 parent, p2 = repo.dirstate.parents()
67 if parent != repo.changelog.tip():
67 if parent != repo.changelog.tip():
68 raise util.Abort(_('working dir not at tip '
68 raise util.Abort(_('working dir not at tip '
69 '(use "hg update" to check out tip)'))
69 '(use "hg update" to check out tip)'))
70 if p2 != nullid:
70 if p2 != nullid:
71 raise util.Abort(_('outstanding uncommitted merge'))
71 raise util.Abort(_('outstanding uncommitted merge'))
72 wlock = lock = None
72 wlock = lock = None
73 try:
73 try:
74 wlock = repo.wlock()
74 wlock = repo.wlock()
75 lock = repo.lock()
75 lock = repo.lock()
76 mod, add, rem = repo.status()[:3]
76 mod, add, rem = repo.status()[:3]
77 if mod or add or rem:
77 if mod or add or rem:
78 raise util.Abort(_('outstanding uncommitted changes'))
78 raise util.Abort(_('outstanding uncommitted changes'))
79 if len(repo.heads()) > 1:
79 if len(repo.heads()) > 1:
80 raise util.Abort(_('multiple heads in this repository '
80 raise util.Abort(_('multiple heads in this repository '
81 '(use "hg heads" and "hg merge" to merge)'))
81 '(use "hg heads" and "hg merge" to merge)'))
82 return pull()
82 return pull()
83 finally:
83 finally:
84 del lock, wlock
84 del lock, wlock
85
85
86 cmdtable = {
86 cmdtable = {
87 'fetch':
87 'fetch':
88 (fetch,
88 (fetch,
89 [('e', 'ssh', '', _('specify ssh command to use')),
89 [('r', 'rev', [], _('a specific revision you would like to pull')),
90 ('m', 'message', '', _('use <text> as commit message')),
91 ('l', 'logfile', '', _('read the commit message from <file>')),
92 ('d', 'date', '', _('record datecode as commit date')),
93 ('u', 'user', '', _('record user as commiter')),
94 ('r', 'rev', [], _('a specific revision you would like to pull')),
95 ('f', 'force-editor', None, _('edit commit message')),
90 ('f', 'force-editor', None, _('edit commit message')),
96 ('', 'remotecmd', '', _('hg command to run on the remote side'))],
91 ] + commands.commitopts + commands.commitopts2 + commands.remoteopts,
97 _('hg fetch [SOURCE]')),
92 _('hg fetch [SOURCE]')),
98 }
93 }
@@ -1,280 +1,279 b''
1 # GnuPG signing extension for Mercurial
1 # GnuPG signing extension for Mercurial
2 #
2 #
3 # Copyright 2005, 2006 Benoit Boissinot <benoit.boissinot@ens-lyon.org>
3 # Copyright 2005, 2006 Benoit Boissinot <benoit.boissinot@ens-lyon.org>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 import os, tempfile, binascii
8 import os, tempfile, binascii
9 from mercurial import util
9 from mercurial import util, commands
10 from mercurial import node as hgnode
10 from mercurial import node as hgnode
11 from mercurial.i18n import _
11 from mercurial.i18n import _
12
12
13 class gpg:
13 class gpg:
14 def __init__(self, path, key=None):
14 def __init__(self, path, key=None):
15 self.path = path
15 self.path = path
16 self.key = (key and " --local-user \"%s\"" % key) or ""
16 self.key = (key and " --local-user \"%s\"" % key) or ""
17
17
18 def sign(self, data):
18 def sign(self, data):
19 gpgcmd = "%s --sign --detach-sign%s" % (self.path, self.key)
19 gpgcmd = "%s --sign --detach-sign%s" % (self.path, self.key)
20 return util.filter(data, gpgcmd)
20 return util.filter(data, gpgcmd)
21
21
22 def verify(self, data, sig):
22 def verify(self, data, sig):
23 """ returns of the good and bad signatures"""
23 """ returns of the good and bad signatures"""
24 sigfile = datafile = None
24 sigfile = datafile = None
25 try:
25 try:
26 # create temporary files
26 # create temporary files
27 fd, sigfile = tempfile.mkstemp(prefix="hg-gpg-", suffix=".sig")
27 fd, sigfile = tempfile.mkstemp(prefix="hg-gpg-", suffix=".sig")
28 fp = os.fdopen(fd, 'wb')
28 fp = os.fdopen(fd, 'wb')
29 fp.write(sig)
29 fp.write(sig)
30 fp.close()
30 fp.close()
31 fd, datafile = tempfile.mkstemp(prefix="hg-gpg-", suffix=".txt")
31 fd, datafile = tempfile.mkstemp(prefix="hg-gpg-", suffix=".txt")
32 fp = os.fdopen(fd, 'wb')
32 fp = os.fdopen(fd, 'wb')
33 fp.write(data)
33 fp.write(data)
34 fp.close()
34 fp.close()
35 gpgcmd = ("%s --logger-fd 1 --status-fd 1 --verify "
35 gpgcmd = ("%s --logger-fd 1 --status-fd 1 --verify "
36 "\"%s\" \"%s\"" % (self.path, sigfile, datafile))
36 "\"%s\" \"%s\"" % (self.path, sigfile, datafile))
37 ret = util.filter("", gpgcmd)
37 ret = util.filter("", gpgcmd)
38 finally:
38 finally:
39 for f in (sigfile, datafile):
39 for f in (sigfile, datafile):
40 try:
40 try:
41 if f: os.unlink(f)
41 if f: os.unlink(f)
42 except: pass
42 except: pass
43 keys = []
43 keys = []
44 key, fingerprint = None, None
44 key, fingerprint = None, None
45 err = ""
45 err = ""
46 for l in ret.splitlines():
46 for l in ret.splitlines():
47 # see DETAILS in the gnupg documentation
47 # see DETAILS in the gnupg documentation
48 # filter the logger output
48 # filter the logger output
49 if not l.startswith("[GNUPG:]"):
49 if not l.startswith("[GNUPG:]"):
50 continue
50 continue
51 l = l[9:]
51 l = l[9:]
52 if l.startswith("ERRSIG"):
52 if l.startswith("ERRSIG"):
53 err = _("error while verifying signature")
53 err = _("error while verifying signature")
54 break
54 break
55 elif l.startswith("VALIDSIG"):
55 elif l.startswith("VALIDSIG"):
56 # fingerprint of the primary key
56 # fingerprint of the primary key
57 fingerprint = l.split()[10]
57 fingerprint = l.split()[10]
58 elif (l.startswith("GOODSIG") or
58 elif (l.startswith("GOODSIG") or
59 l.startswith("EXPSIG") or
59 l.startswith("EXPSIG") or
60 l.startswith("EXPKEYSIG") or
60 l.startswith("EXPKEYSIG") or
61 l.startswith("BADSIG")):
61 l.startswith("BADSIG")):
62 if key is not None:
62 if key is not None:
63 keys.append(key + [fingerprint])
63 keys.append(key + [fingerprint])
64 key = l.split(" ", 2)
64 key = l.split(" ", 2)
65 fingerprint = None
65 fingerprint = None
66 if err:
66 if err:
67 return err, []
67 return err, []
68 if key is not None:
68 if key is not None:
69 keys.append(key + [fingerprint])
69 keys.append(key + [fingerprint])
70 return err, keys
70 return err, keys
71
71
72 def newgpg(ui, **opts):
72 def newgpg(ui, **opts):
73 """create a new gpg instance"""
73 """create a new gpg instance"""
74 gpgpath = ui.config("gpg", "cmd", "gpg")
74 gpgpath = ui.config("gpg", "cmd", "gpg")
75 gpgkey = opts.get('key')
75 gpgkey = opts.get('key')
76 if not gpgkey:
76 if not gpgkey:
77 gpgkey = ui.config("gpg", "key", None)
77 gpgkey = ui.config("gpg", "key", None)
78 return gpg(gpgpath, gpgkey)
78 return gpg(gpgpath, gpgkey)
79
79
80 def sigwalk(repo):
80 def sigwalk(repo):
81 """
81 """
82 walk over every sigs, yields a couple
82 walk over every sigs, yields a couple
83 ((node, version, sig), (filename, linenumber))
83 ((node, version, sig), (filename, linenumber))
84 """
84 """
85 def parsefile(fileiter, context):
85 def parsefile(fileiter, context):
86 ln = 1
86 ln = 1
87 for l in fileiter:
87 for l in fileiter:
88 if not l:
88 if not l:
89 continue
89 continue
90 yield (l.split(" ", 2), (context, ln))
90 yield (l.split(" ", 2), (context, ln))
91 ln +=1
91 ln +=1
92
92
93 fl = repo.file(".hgsigs")
93 fl = repo.file(".hgsigs")
94 h = fl.heads()
94 h = fl.heads()
95 h.reverse()
95 h.reverse()
96 # read the heads
96 # read the heads
97 for r in h:
97 for r in h:
98 fn = ".hgsigs|%s" % hgnode.short(r)
98 fn = ".hgsigs|%s" % hgnode.short(r)
99 for item in parsefile(fl.read(r).splitlines(), fn):
99 for item in parsefile(fl.read(r).splitlines(), fn):
100 yield item
100 yield item
101 try:
101 try:
102 # read local signatures
102 # read local signatures
103 fn = "localsigs"
103 fn = "localsigs"
104 for item in parsefile(repo.opener(fn), fn):
104 for item in parsefile(repo.opener(fn), fn):
105 yield item
105 yield item
106 except IOError:
106 except IOError:
107 pass
107 pass
108
108
109 def getkeys(ui, repo, mygpg, sigdata, context):
109 def getkeys(ui, repo, mygpg, sigdata, context):
110 """get the keys who signed a data"""
110 """get the keys who signed a data"""
111 fn, ln = context
111 fn, ln = context
112 node, version, sig = sigdata
112 node, version, sig = sigdata
113 prefix = "%s:%d" % (fn, ln)
113 prefix = "%s:%d" % (fn, ln)
114 node = hgnode.bin(node)
114 node = hgnode.bin(node)
115
115
116 data = node2txt(repo, node, version)
116 data = node2txt(repo, node, version)
117 sig = binascii.a2b_base64(sig)
117 sig = binascii.a2b_base64(sig)
118 err, keys = mygpg.verify(data, sig)
118 err, keys = mygpg.verify(data, sig)
119 if err:
119 if err:
120 ui.warn("%s:%d %s\n" % (fn, ln , err))
120 ui.warn("%s:%d %s\n" % (fn, ln , err))
121 return None
121 return None
122
122
123 validkeys = []
123 validkeys = []
124 # warn for expired key and/or sigs
124 # warn for expired key and/or sigs
125 for key in keys:
125 for key in keys:
126 if key[0] == "BADSIG":
126 if key[0] == "BADSIG":
127 ui.write(_("%s Bad signature from \"%s\"\n") % (prefix, key[2]))
127 ui.write(_("%s Bad signature from \"%s\"\n") % (prefix, key[2]))
128 continue
128 continue
129 if key[0] == "EXPSIG":
129 if key[0] == "EXPSIG":
130 ui.write(_("%s Note: Signature has expired"
130 ui.write(_("%s Note: Signature has expired"
131 " (signed by: \"%s\")\n") % (prefix, key[2]))
131 " (signed by: \"%s\")\n") % (prefix, key[2]))
132 elif key[0] == "EXPKEYSIG":
132 elif key[0] == "EXPKEYSIG":
133 ui.write(_("%s Note: This key has expired"
133 ui.write(_("%s Note: This key has expired"
134 " (signed by: \"%s\")\n") % (prefix, key[2]))
134 " (signed by: \"%s\")\n") % (prefix, key[2]))
135 validkeys.append((key[1], key[2], key[3]))
135 validkeys.append((key[1], key[2], key[3]))
136 return validkeys
136 return validkeys
137
137
138 def sigs(ui, repo):
138 def sigs(ui, repo):
139 """list signed changesets"""
139 """list signed changesets"""
140 mygpg = newgpg(ui)
140 mygpg = newgpg(ui)
141 revs = {}
141 revs = {}
142
142
143 for data, context in sigwalk(repo):
143 for data, context in sigwalk(repo):
144 node, version, sig = data
144 node, version, sig = data
145 fn, ln = context
145 fn, ln = context
146 try:
146 try:
147 n = repo.lookup(node)
147 n = repo.lookup(node)
148 except KeyError:
148 except KeyError:
149 ui.warn(_("%s:%d node does not exist\n") % (fn, ln))
149 ui.warn(_("%s:%d node does not exist\n") % (fn, ln))
150 continue
150 continue
151 r = repo.changelog.rev(n)
151 r = repo.changelog.rev(n)
152 keys = getkeys(ui, repo, mygpg, data, context)
152 keys = getkeys(ui, repo, mygpg, data, context)
153 if not keys:
153 if not keys:
154 continue
154 continue
155 revs.setdefault(r, [])
155 revs.setdefault(r, [])
156 revs[r].extend(keys)
156 revs[r].extend(keys)
157 nodes = list(revs)
157 nodes = list(revs)
158 nodes.reverse()
158 nodes.reverse()
159 for rev in nodes:
159 for rev in nodes:
160 for k in revs[rev]:
160 for k in revs[rev]:
161 r = "%5d:%s" % (rev, hgnode.hex(repo.changelog.node(rev)))
161 r = "%5d:%s" % (rev, hgnode.hex(repo.changelog.node(rev)))
162 ui.write("%-30s %s\n" % (keystr(ui, k), r))
162 ui.write("%-30s %s\n" % (keystr(ui, k), r))
163
163
164 def check(ui, repo, rev):
164 def check(ui, repo, rev):
165 """verify all the signatures there may be for a particular revision"""
165 """verify all the signatures there may be for a particular revision"""
166 mygpg = newgpg(ui)
166 mygpg = newgpg(ui)
167 rev = repo.lookup(rev)
167 rev = repo.lookup(rev)
168 hexrev = hgnode.hex(rev)
168 hexrev = hgnode.hex(rev)
169 keys = []
169 keys = []
170
170
171 for data, context in sigwalk(repo):
171 for data, context in sigwalk(repo):
172 node, version, sig = data
172 node, version, sig = data
173 if node == hexrev:
173 if node == hexrev:
174 k = getkeys(ui, repo, mygpg, data, context)
174 k = getkeys(ui, repo, mygpg, data, context)
175 if k:
175 if k:
176 keys.extend(k)
176 keys.extend(k)
177
177
178 if not keys:
178 if not keys:
179 ui.write(_("No valid signature for %s\n") % hgnode.short(rev))
179 ui.write(_("No valid signature for %s\n") % hgnode.short(rev))
180 return
180 return
181
181
182 # print summary
182 # print summary
183 ui.write("%s is signed by:\n" % hgnode.short(rev))
183 ui.write("%s is signed by:\n" % hgnode.short(rev))
184 for key in keys:
184 for key in keys:
185 ui.write(" %s\n" % keystr(ui, key))
185 ui.write(" %s\n" % keystr(ui, key))
186
186
187 def keystr(ui, key):
187 def keystr(ui, key):
188 """associate a string to a key (username, comment)"""
188 """associate a string to a key (username, comment)"""
189 keyid, user, fingerprint = key
189 keyid, user, fingerprint = key
190 comment = ui.config("gpg", fingerprint, None)
190 comment = ui.config("gpg", fingerprint, None)
191 if comment:
191 if comment:
192 return "%s (%s)" % (user, comment)
192 return "%s (%s)" % (user, comment)
193 else:
193 else:
194 return user
194 return user
195
195
196 def sign(ui, repo, *revs, **opts):
196 def sign(ui, repo, *revs, **opts):
197 """add a signature for the current or given revision
197 """add a signature for the current or given revision
198
198
199 If no revision is given, the parent of the working directory is used,
199 If no revision is given, the parent of the working directory is used,
200 or tip if no revision is checked out.
200 or tip if no revision is checked out.
201 """
201 """
202
202
203 mygpg = newgpg(ui, **opts)
203 mygpg = newgpg(ui, **opts)
204 sigver = "0"
204 sigver = "0"
205 sigmessage = ""
205 sigmessage = ""
206 if revs:
206 if revs:
207 nodes = [repo.lookup(n) for n in revs]
207 nodes = [repo.lookup(n) for n in revs]
208 else:
208 else:
209 nodes = [node for node in repo.dirstate.parents()
209 nodes = [node for node in repo.dirstate.parents()
210 if node != hgnode.nullid]
210 if node != hgnode.nullid]
211 if len(nodes) > 1:
211 if len(nodes) > 1:
212 raise util.Abort(_('uncommitted merge - please provide a '
212 raise util.Abort(_('uncommitted merge - please provide a '
213 'specific revision'))
213 'specific revision'))
214 if not nodes:
214 if not nodes:
215 nodes = [repo.changelog.tip()]
215 nodes = [repo.changelog.tip()]
216
216
217 for n in nodes:
217 for n in nodes:
218 hexnode = hgnode.hex(n)
218 hexnode = hgnode.hex(n)
219 ui.write("Signing %d:%s\n" % (repo.changelog.rev(n),
219 ui.write("Signing %d:%s\n" % (repo.changelog.rev(n),
220 hgnode.short(n)))
220 hgnode.short(n)))
221 # build data
221 # build data
222 data = node2txt(repo, n, sigver)
222 data = node2txt(repo, n, sigver)
223 sig = mygpg.sign(data)
223 sig = mygpg.sign(data)
224 if not sig:
224 if not sig:
225 raise util.Abort(_("Error while signing"))
225 raise util.Abort(_("Error while signing"))
226 sig = binascii.b2a_base64(sig)
226 sig = binascii.b2a_base64(sig)
227 sig = sig.replace("\n", "")
227 sig = sig.replace("\n", "")
228 sigmessage += "%s %s %s\n" % (hexnode, sigver, sig)
228 sigmessage += "%s %s %s\n" % (hexnode, sigver, sig)
229
229
230 # write it
230 # write it
231 if opts['local']:
231 if opts['local']:
232 repo.opener("localsigs", "ab").write(sigmessage)
232 repo.opener("localsigs", "ab").write(sigmessage)
233 return
233 return
234
234
235 for x in repo.status()[:5]:
235 for x in repo.status()[:5]:
236 if ".hgsigs" in x and not opts["force"]:
236 if ".hgsigs" in x and not opts["force"]:
237 raise util.Abort(_("working copy of .hgsigs is changed "
237 raise util.Abort(_("working copy of .hgsigs is changed "
238 "(please commit .hgsigs manually "
238 "(please commit .hgsigs manually "
239 "or use --force)"))
239 "or use --force)"))
240
240
241 repo.wfile(".hgsigs", "ab").write(sigmessage)
241 repo.wfile(".hgsigs", "ab").write(sigmessage)
242
242
243 if '.hgsigs' not in repo.dirstate:
243 if '.hgsigs' not in repo.dirstate:
244 repo.add([".hgsigs"])
244 repo.add([".hgsigs"])
245
245
246 if opts["no_commit"]:
246 if opts["no_commit"]:
247 return
247 return
248
248
249 message = opts['message']
249 message = opts['message']
250 if not message:
250 if not message:
251 message = "\n".join([_("Added signature for changeset %s")
251 message = "\n".join([_("Added signature for changeset %s")
252 % hgnode.hex(n)
252 % hgnode.hex(n)
253 for n in nodes])
253 for n in nodes])
254 try:
254 try:
255 repo.commit([".hgsigs"], message, opts['user'], opts['date'])
255 repo.commit([".hgsigs"], message, opts['user'], opts['date'])
256 except ValueError, inst:
256 except ValueError, inst:
257 raise util.Abort(str(inst))
257 raise util.Abort(str(inst))
258
258
259 def node2txt(repo, node, ver):
259 def node2txt(repo, node, ver):
260 """map a manifest into some text"""
260 """map a manifest into some text"""
261 if ver == "0":
261 if ver == "0":
262 return "%s\n" % hgnode.hex(node)
262 return "%s\n" % hgnode.hex(node)
263 else:
263 else:
264 raise util.Abort(_("unknown signature version"))
264 raise util.Abort(_("unknown signature version"))
265
265
266 cmdtable = {
266 cmdtable = {
267 "sign":
267 "sign":
268 (sign,
268 (sign,
269 [('l', 'local', None, _('make the signature local')),
269 [('l', 'local', None, _('make the signature local')),
270 ('f', 'force', None, _('sign even if the sigfile is modified')),
270 ('f', 'force', None, _('sign even if the sigfile is modified')),
271 ('', 'no-commit', None, _('do not commit the sigfile after signing')),
271 ('', 'no-commit', None, _('do not commit the sigfile after signing')),
272 ('k', 'key', '', _('the key id to sign with')),
272 ('m', 'message', '', _('commit message')),
273 ('m', 'message', '', _('commit message')),
273 ('d', 'date', '', _('date code')),
274 ] + commands.commitopts2,
274 ('u', 'user', '', _('user')),
275 ('k', 'key', '', _('the key id to sign with'))],
276 _('hg sign [OPTION]... [REVISION]...')),
275 _('hg sign [OPTION]... [REVISION]...')),
277 "sigcheck": (check, [], _('hg sigcheck REVISION')),
276 "sigcheck": (check, [], _('hg sigcheck REVISION')),
278 "sigs": (sigs, [], _('hg sigs')),
277 "sigs": (sigs, [], _('hg sigs')),
279 }
278 }
280
279
@@ -1,2250 +1,2243 b''
1 # queue.py - patch queues for mercurial
1 # queue.py - patch queues for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 '''patch management and development
8 '''patch management and development
9
9
10 This extension lets you work with a stack of patches in a Mercurial
10 This extension lets you work with a stack of patches in a Mercurial
11 repository. It manages two stacks of patches - all known patches, and
11 repository. It manages two stacks of patches - all known patches, and
12 applied patches (subset of known patches).
12 applied patches (subset of known patches).
13
13
14 Known patches are represented as patch files in the .hg/patches
14 Known patches are represented as patch files in the .hg/patches
15 directory. Applied patches are both patch files and changesets.
15 directory. Applied patches are both patch files and changesets.
16
16
17 Common tasks (use "hg help command" for more details):
17 Common tasks (use "hg help command" for more details):
18
18
19 prepare repository to work with patches qinit
19 prepare repository to work with patches qinit
20 create new patch qnew
20 create new patch qnew
21 import existing patch qimport
21 import existing patch qimport
22
22
23 print patch series qseries
23 print patch series qseries
24 print applied patches qapplied
24 print applied patches qapplied
25 print name of top applied patch qtop
25 print name of top applied patch qtop
26
26
27 add known patch to applied stack qpush
27 add known patch to applied stack qpush
28 remove patch from applied stack qpop
28 remove patch from applied stack qpop
29 refresh contents of top applied patch qrefresh
29 refresh contents of top applied patch qrefresh
30 '''
30 '''
31
31
32 from mercurial.i18n import _
32 from mercurial.i18n import _
33 from mercurial import commands, cmdutil, hg, patch, revlog, util
33 from mercurial import commands, cmdutil, hg, patch, revlog, util
34 from mercurial import repair
34 from mercurial import repair
35 import os, sys, re, errno
35 import os, sys, re, errno
36
36
37 commands.norepo += " qclone qversion"
37 commands.norepo += " qclone qversion"
38
38
39 # Patch names looks like unix-file names.
39 # Patch names looks like unix-file names.
40 # They must be joinable with queue directory and result in the patch path.
40 # They must be joinable with queue directory and result in the patch path.
41 normname = util.normpath
41 normname = util.normpath
42
42
43 class statusentry:
43 class statusentry:
44 def __init__(self, rev, name=None):
44 def __init__(self, rev, name=None):
45 if not name:
45 if not name:
46 fields = rev.split(':', 1)
46 fields = rev.split(':', 1)
47 if len(fields) == 2:
47 if len(fields) == 2:
48 self.rev, self.name = fields
48 self.rev, self.name = fields
49 else:
49 else:
50 self.rev, self.name = None, None
50 self.rev, self.name = None, None
51 else:
51 else:
52 self.rev, self.name = rev, name
52 self.rev, self.name = rev, name
53
53
54 def __str__(self):
54 def __str__(self):
55 return self.rev + ':' + self.name
55 return self.rev + ':' + self.name
56
56
57 class queue:
57 class queue:
58 def __init__(self, ui, path, patchdir=None):
58 def __init__(self, ui, path, patchdir=None):
59 self.basepath = path
59 self.basepath = path
60 self.path = patchdir or os.path.join(path, "patches")
60 self.path = patchdir or os.path.join(path, "patches")
61 self.opener = util.opener(self.path)
61 self.opener = util.opener(self.path)
62 self.ui = ui
62 self.ui = ui
63 self.applied = []
63 self.applied = []
64 self.full_series = []
64 self.full_series = []
65 self.applied_dirty = 0
65 self.applied_dirty = 0
66 self.series_dirty = 0
66 self.series_dirty = 0
67 self.series_path = "series"
67 self.series_path = "series"
68 self.status_path = "status"
68 self.status_path = "status"
69 self.guards_path = "guards"
69 self.guards_path = "guards"
70 self.active_guards = None
70 self.active_guards = None
71 self.guards_dirty = False
71 self.guards_dirty = False
72 self._diffopts = None
72 self._diffopts = None
73
73
74 if os.path.exists(self.join(self.series_path)):
74 if os.path.exists(self.join(self.series_path)):
75 self.full_series = self.opener(self.series_path).read().splitlines()
75 self.full_series = self.opener(self.series_path).read().splitlines()
76 self.parse_series()
76 self.parse_series()
77
77
78 if os.path.exists(self.join(self.status_path)):
78 if os.path.exists(self.join(self.status_path)):
79 lines = self.opener(self.status_path).read().splitlines()
79 lines = self.opener(self.status_path).read().splitlines()
80 self.applied = [statusentry(l) for l in lines]
80 self.applied = [statusentry(l) for l in lines]
81
81
82 def diffopts(self):
82 def diffopts(self):
83 if self._diffopts is None:
83 if self._diffopts is None:
84 self._diffopts = patch.diffopts(self.ui)
84 self._diffopts = patch.diffopts(self.ui)
85 return self._diffopts
85 return self._diffopts
86
86
87 def join(self, *p):
87 def join(self, *p):
88 return os.path.join(self.path, *p)
88 return os.path.join(self.path, *p)
89
89
90 def find_series(self, patch):
90 def find_series(self, patch):
91 pre = re.compile("(\s*)([^#]+)")
91 pre = re.compile("(\s*)([^#]+)")
92 index = 0
92 index = 0
93 for l in self.full_series:
93 for l in self.full_series:
94 m = pre.match(l)
94 m = pre.match(l)
95 if m:
95 if m:
96 s = m.group(2)
96 s = m.group(2)
97 s = s.rstrip()
97 s = s.rstrip()
98 if s == patch:
98 if s == patch:
99 return index
99 return index
100 index += 1
100 index += 1
101 return None
101 return None
102
102
103 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
103 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
104
104
105 def parse_series(self):
105 def parse_series(self):
106 self.series = []
106 self.series = []
107 self.series_guards = []
107 self.series_guards = []
108 for l in self.full_series:
108 for l in self.full_series:
109 h = l.find('#')
109 h = l.find('#')
110 if h == -1:
110 if h == -1:
111 patch = l
111 patch = l
112 comment = ''
112 comment = ''
113 elif h == 0:
113 elif h == 0:
114 continue
114 continue
115 else:
115 else:
116 patch = l[:h]
116 patch = l[:h]
117 comment = l[h:]
117 comment = l[h:]
118 patch = patch.strip()
118 patch = patch.strip()
119 if patch:
119 if patch:
120 if patch in self.series:
120 if patch in self.series:
121 raise util.Abort(_('%s appears more than once in %s') %
121 raise util.Abort(_('%s appears more than once in %s') %
122 (patch, self.join(self.series_path)))
122 (patch, self.join(self.series_path)))
123 self.series.append(patch)
123 self.series.append(patch)
124 self.series_guards.append(self.guard_re.findall(comment))
124 self.series_guards.append(self.guard_re.findall(comment))
125
125
126 def check_guard(self, guard):
126 def check_guard(self, guard):
127 bad_chars = '# \t\r\n\f'
127 bad_chars = '# \t\r\n\f'
128 first = guard[0]
128 first = guard[0]
129 for c in '-+':
129 for c in '-+':
130 if first == c:
130 if first == c:
131 return (_('guard %r starts with invalid character: %r') %
131 return (_('guard %r starts with invalid character: %r') %
132 (guard, c))
132 (guard, c))
133 for c in bad_chars:
133 for c in bad_chars:
134 if c in guard:
134 if c in guard:
135 return _('invalid character in guard %r: %r') % (guard, c)
135 return _('invalid character in guard %r: %r') % (guard, c)
136
136
137 def set_active(self, guards):
137 def set_active(self, guards):
138 for guard in guards:
138 for guard in guards:
139 bad = self.check_guard(guard)
139 bad = self.check_guard(guard)
140 if bad:
140 if bad:
141 raise util.Abort(bad)
141 raise util.Abort(bad)
142 guards = dict.fromkeys(guards).keys()
142 guards = dict.fromkeys(guards).keys()
143 guards.sort()
143 guards.sort()
144 self.ui.debug('active guards: %s\n' % ' '.join(guards))
144 self.ui.debug('active guards: %s\n' % ' '.join(guards))
145 self.active_guards = guards
145 self.active_guards = guards
146 self.guards_dirty = True
146 self.guards_dirty = True
147
147
148 def active(self):
148 def active(self):
149 if self.active_guards is None:
149 if self.active_guards is None:
150 self.active_guards = []
150 self.active_guards = []
151 try:
151 try:
152 guards = self.opener(self.guards_path).read().split()
152 guards = self.opener(self.guards_path).read().split()
153 except IOError, err:
153 except IOError, err:
154 if err.errno != errno.ENOENT: raise
154 if err.errno != errno.ENOENT: raise
155 guards = []
155 guards = []
156 for i, guard in enumerate(guards):
156 for i, guard in enumerate(guards):
157 bad = self.check_guard(guard)
157 bad = self.check_guard(guard)
158 if bad:
158 if bad:
159 self.ui.warn('%s:%d: %s\n' %
159 self.ui.warn('%s:%d: %s\n' %
160 (self.join(self.guards_path), i + 1, bad))
160 (self.join(self.guards_path), i + 1, bad))
161 else:
161 else:
162 self.active_guards.append(guard)
162 self.active_guards.append(guard)
163 return self.active_guards
163 return self.active_guards
164
164
165 def set_guards(self, idx, guards):
165 def set_guards(self, idx, guards):
166 for g in guards:
166 for g in guards:
167 if len(g) < 2:
167 if len(g) < 2:
168 raise util.Abort(_('guard %r too short') % g)
168 raise util.Abort(_('guard %r too short') % g)
169 if g[0] not in '-+':
169 if g[0] not in '-+':
170 raise util.Abort(_('guard %r starts with invalid char') % g)
170 raise util.Abort(_('guard %r starts with invalid char') % g)
171 bad = self.check_guard(g[1:])
171 bad = self.check_guard(g[1:])
172 if bad:
172 if bad:
173 raise util.Abort(bad)
173 raise util.Abort(bad)
174 drop = self.guard_re.sub('', self.full_series[idx])
174 drop = self.guard_re.sub('', self.full_series[idx])
175 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
175 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
176 self.parse_series()
176 self.parse_series()
177 self.series_dirty = True
177 self.series_dirty = True
178
178
179 def pushable(self, idx):
179 def pushable(self, idx):
180 if isinstance(idx, str):
180 if isinstance(idx, str):
181 idx = self.series.index(idx)
181 idx = self.series.index(idx)
182 patchguards = self.series_guards[idx]
182 patchguards = self.series_guards[idx]
183 if not patchguards:
183 if not patchguards:
184 return True, None
184 return True, None
185 default = False
185 default = False
186 guards = self.active()
186 guards = self.active()
187 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
187 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
188 if exactneg:
188 if exactneg:
189 return False, exactneg[0]
189 return False, exactneg[0]
190 pos = [g for g in patchguards if g[0] == '+']
190 pos = [g for g in patchguards if g[0] == '+']
191 exactpos = [g for g in pos if g[1:] in guards]
191 exactpos = [g for g in pos if g[1:] in guards]
192 if pos:
192 if pos:
193 if exactpos:
193 if exactpos:
194 return True, exactpos[0]
194 return True, exactpos[0]
195 return False, pos
195 return False, pos
196 return True, ''
196 return True, ''
197
197
198 def explain_pushable(self, idx, all_patches=False):
198 def explain_pushable(self, idx, all_patches=False):
199 write = all_patches and self.ui.write or self.ui.warn
199 write = all_patches and self.ui.write or self.ui.warn
200 if all_patches or self.ui.verbose:
200 if all_patches or self.ui.verbose:
201 if isinstance(idx, str):
201 if isinstance(idx, str):
202 idx = self.series.index(idx)
202 idx = self.series.index(idx)
203 pushable, why = self.pushable(idx)
203 pushable, why = self.pushable(idx)
204 if all_patches and pushable:
204 if all_patches and pushable:
205 if why is None:
205 if why is None:
206 write(_('allowing %s - no guards in effect\n') %
206 write(_('allowing %s - no guards in effect\n') %
207 self.series[idx])
207 self.series[idx])
208 else:
208 else:
209 if not why:
209 if not why:
210 write(_('allowing %s - no matching negative guards\n') %
210 write(_('allowing %s - no matching negative guards\n') %
211 self.series[idx])
211 self.series[idx])
212 else:
212 else:
213 write(_('allowing %s - guarded by %r\n') %
213 write(_('allowing %s - guarded by %r\n') %
214 (self.series[idx], why))
214 (self.series[idx], why))
215 if not pushable:
215 if not pushable:
216 if why:
216 if why:
217 write(_('skipping %s - guarded by %r\n') %
217 write(_('skipping %s - guarded by %r\n') %
218 (self.series[idx], why))
218 (self.series[idx], why))
219 else:
219 else:
220 write(_('skipping %s - no matching guards\n') %
220 write(_('skipping %s - no matching guards\n') %
221 self.series[idx])
221 self.series[idx])
222
222
223 def save_dirty(self):
223 def save_dirty(self):
224 def write_list(items, path):
224 def write_list(items, path):
225 fp = self.opener(path, 'w')
225 fp = self.opener(path, 'w')
226 for i in items:
226 for i in items:
227 print >> fp, i
227 print >> fp, i
228 fp.close()
228 fp.close()
229 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
229 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
230 if self.series_dirty: write_list(self.full_series, self.series_path)
230 if self.series_dirty: write_list(self.full_series, self.series_path)
231 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
231 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
232
232
233 def readheaders(self, patch):
233 def readheaders(self, patch):
234 def eatdiff(lines):
234 def eatdiff(lines):
235 while lines:
235 while lines:
236 l = lines[-1]
236 l = lines[-1]
237 if (l.startswith("diff -") or
237 if (l.startswith("diff -") or
238 l.startswith("Index:") or
238 l.startswith("Index:") or
239 l.startswith("===========")):
239 l.startswith("===========")):
240 del lines[-1]
240 del lines[-1]
241 else:
241 else:
242 break
242 break
243 def eatempty(lines):
243 def eatempty(lines):
244 while lines:
244 while lines:
245 l = lines[-1]
245 l = lines[-1]
246 if re.match('\s*$', l):
246 if re.match('\s*$', l):
247 del lines[-1]
247 del lines[-1]
248 else:
248 else:
249 break
249 break
250
250
251 pf = self.join(patch)
251 pf = self.join(patch)
252 message = []
252 message = []
253 comments = []
253 comments = []
254 user = None
254 user = None
255 date = None
255 date = None
256 format = None
256 format = None
257 subject = None
257 subject = None
258 diffstart = 0
258 diffstart = 0
259
259
260 for line in file(pf):
260 for line in file(pf):
261 line = line.rstrip()
261 line = line.rstrip()
262 if line.startswith('diff --git'):
262 if line.startswith('diff --git'):
263 diffstart = 2
263 diffstart = 2
264 break
264 break
265 if diffstart:
265 if diffstart:
266 if line.startswith('+++ '):
266 if line.startswith('+++ '):
267 diffstart = 2
267 diffstart = 2
268 break
268 break
269 if line.startswith("--- "):
269 if line.startswith("--- "):
270 diffstart = 1
270 diffstart = 1
271 continue
271 continue
272 elif format == "hgpatch":
272 elif format == "hgpatch":
273 # parse values when importing the result of an hg export
273 # parse values when importing the result of an hg export
274 if line.startswith("# User "):
274 if line.startswith("# User "):
275 user = line[7:]
275 user = line[7:]
276 elif line.startswith("# Date "):
276 elif line.startswith("# Date "):
277 date = line[7:]
277 date = line[7:]
278 elif not line.startswith("# ") and line:
278 elif not line.startswith("# ") and line:
279 message.append(line)
279 message.append(line)
280 format = None
280 format = None
281 elif line == '# HG changeset patch':
281 elif line == '# HG changeset patch':
282 format = "hgpatch"
282 format = "hgpatch"
283 elif (format != "tagdone" and (line.startswith("Subject: ") or
283 elif (format != "tagdone" and (line.startswith("Subject: ") or
284 line.startswith("subject: "))):
284 line.startswith("subject: "))):
285 subject = line[9:]
285 subject = line[9:]
286 format = "tag"
286 format = "tag"
287 elif (format != "tagdone" and (line.startswith("From: ") or
287 elif (format != "tagdone" and (line.startswith("From: ") or
288 line.startswith("from: "))):
288 line.startswith("from: "))):
289 user = line[6:]
289 user = line[6:]
290 format = "tag"
290 format = "tag"
291 elif format == "tag" and line == "":
291 elif format == "tag" and line == "":
292 # when looking for tags (subject: from: etc) they
292 # when looking for tags (subject: from: etc) they
293 # end once you find a blank line in the source
293 # end once you find a blank line in the source
294 format = "tagdone"
294 format = "tagdone"
295 elif message or line:
295 elif message or line:
296 message.append(line)
296 message.append(line)
297 comments.append(line)
297 comments.append(line)
298
298
299 eatdiff(message)
299 eatdiff(message)
300 eatdiff(comments)
300 eatdiff(comments)
301 eatempty(message)
301 eatempty(message)
302 eatempty(comments)
302 eatempty(comments)
303
303
304 # make sure message isn't empty
304 # make sure message isn't empty
305 if format and format.startswith("tag") and subject:
305 if format and format.startswith("tag") and subject:
306 message.insert(0, "")
306 message.insert(0, "")
307 message.insert(0, subject)
307 message.insert(0, subject)
308 return (message, comments, user, date, diffstart > 1)
308 return (message, comments, user, date, diffstart > 1)
309
309
310 def removeundo(self, repo):
310 def removeundo(self, repo):
311 undo = repo.sjoin('undo')
311 undo = repo.sjoin('undo')
312 if not os.path.exists(undo):
312 if not os.path.exists(undo):
313 return
313 return
314 try:
314 try:
315 os.unlink(undo)
315 os.unlink(undo)
316 except OSError, inst:
316 except OSError, inst:
317 self.ui.warn('error removing undo: %s\n' % str(inst))
317 self.ui.warn('error removing undo: %s\n' % str(inst))
318
318
319 def printdiff(self, repo, node1, node2=None, files=None,
319 def printdiff(self, repo, node1, node2=None, files=None,
320 fp=None, changes=None, opts={}):
320 fp=None, changes=None, opts={}):
321 fns, matchfn, anypats = cmdutil.matchpats(repo, files, opts)
321 fns, matchfn, anypats = cmdutil.matchpats(repo, files, opts)
322
322
323 patch.diff(repo, node1, node2, fns, match=matchfn,
323 patch.diff(repo, node1, node2, fns, match=matchfn,
324 fp=fp, changes=changes, opts=self.diffopts())
324 fp=fp, changes=changes, opts=self.diffopts())
325
325
326 def mergeone(self, repo, mergeq, head, patch, rev):
326 def mergeone(self, repo, mergeq, head, patch, rev):
327 # first try just applying the patch
327 # first try just applying the patch
328 (err, n) = self.apply(repo, [ patch ], update_status=False,
328 (err, n) = self.apply(repo, [ patch ], update_status=False,
329 strict=True, merge=rev)
329 strict=True, merge=rev)
330
330
331 if err == 0:
331 if err == 0:
332 return (err, n)
332 return (err, n)
333
333
334 if n is None:
334 if n is None:
335 raise util.Abort(_("apply failed for patch %s") % patch)
335 raise util.Abort(_("apply failed for patch %s") % patch)
336
336
337 self.ui.warn("patch didn't work out, merging %s\n" % patch)
337 self.ui.warn("patch didn't work out, merging %s\n" % patch)
338
338
339 # apply failed, strip away that rev and merge.
339 # apply failed, strip away that rev and merge.
340 hg.clean(repo, head)
340 hg.clean(repo, head)
341 self.strip(repo, n, update=False, backup='strip')
341 self.strip(repo, n, update=False, backup='strip')
342
342
343 ctx = repo.changectx(rev)
343 ctx = repo.changectx(rev)
344 ret = hg.merge(repo, rev)
344 ret = hg.merge(repo, rev)
345 if ret:
345 if ret:
346 raise util.Abort(_("update returned %d") % ret)
346 raise util.Abort(_("update returned %d") % ret)
347 n = repo.commit(None, ctx.description(), ctx.user(), force=1)
347 n = repo.commit(None, ctx.description(), ctx.user(), force=1)
348 if n == None:
348 if n == None:
349 raise util.Abort(_("repo commit failed"))
349 raise util.Abort(_("repo commit failed"))
350 try:
350 try:
351 message, comments, user, date, patchfound = mergeq.readheaders(patch)
351 message, comments, user, date, patchfound = mergeq.readheaders(patch)
352 except:
352 except:
353 raise util.Abort(_("unable to read %s") % patch)
353 raise util.Abort(_("unable to read %s") % patch)
354
354
355 patchf = self.opener(patch, "w")
355 patchf = self.opener(patch, "w")
356 if comments:
356 if comments:
357 comments = "\n".join(comments) + '\n\n'
357 comments = "\n".join(comments) + '\n\n'
358 patchf.write(comments)
358 patchf.write(comments)
359 self.printdiff(repo, head, n, fp=patchf)
359 self.printdiff(repo, head, n, fp=patchf)
360 patchf.close()
360 patchf.close()
361 self.removeundo(repo)
361 self.removeundo(repo)
362 return (0, n)
362 return (0, n)
363
363
364 def qparents(self, repo, rev=None):
364 def qparents(self, repo, rev=None):
365 if rev is None:
365 if rev is None:
366 (p1, p2) = repo.dirstate.parents()
366 (p1, p2) = repo.dirstate.parents()
367 if p2 == revlog.nullid:
367 if p2 == revlog.nullid:
368 return p1
368 return p1
369 if len(self.applied) == 0:
369 if len(self.applied) == 0:
370 return None
370 return None
371 return revlog.bin(self.applied[-1].rev)
371 return revlog.bin(self.applied[-1].rev)
372 pp = repo.changelog.parents(rev)
372 pp = repo.changelog.parents(rev)
373 if pp[1] != revlog.nullid:
373 if pp[1] != revlog.nullid:
374 arevs = [ x.rev for x in self.applied ]
374 arevs = [ x.rev for x in self.applied ]
375 p0 = revlog.hex(pp[0])
375 p0 = revlog.hex(pp[0])
376 p1 = revlog.hex(pp[1])
376 p1 = revlog.hex(pp[1])
377 if p0 in arevs:
377 if p0 in arevs:
378 return pp[0]
378 return pp[0]
379 if p1 in arevs:
379 if p1 in arevs:
380 return pp[1]
380 return pp[1]
381 return pp[0]
381 return pp[0]
382
382
383 def mergepatch(self, repo, mergeq, series):
383 def mergepatch(self, repo, mergeq, series):
384 if len(self.applied) == 0:
384 if len(self.applied) == 0:
385 # each of the patches merged in will have two parents. This
385 # each of the patches merged in will have two parents. This
386 # can confuse the qrefresh, qdiff, and strip code because it
386 # can confuse the qrefresh, qdiff, and strip code because it
387 # needs to know which parent is actually in the patch queue.
387 # needs to know which parent is actually in the patch queue.
388 # so, we insert a merge marker with only one parent. This way
388 # so, we insert a merge marker with only one parent. This way
389 # the first patch in the queue is never a merge patch
389 # the first patch in the queue is never a merge patch
390 #
390 #
391 pname = ".hg.patches.merge.marker"
391 pname = ".hg.patches.merge.marker"
392 n = repo.commit(None, '[mq]: merge marker', user=None, force=1)
392 n = repo.commit(None, '[mq]: merge marker', user=None, force=1)
393 self.removeundo(repo)
393 self.removeundo(repo)
394 self.applied.append(statusentry(revlog.hex(n), pname))
394 self.applied.append(statusentry(revlog.hex(n), pname))
395 self.applied_dirty = 1
395 self.applied_dirty = 1
396
396
397 head = self.qparents(repo)
397 head = self.qparents(repo)
398
398
399 for patch in series:
399 for patch in series:
400 patch = mergeq.lookup(patch, strict=True)
400 patch = mergeq.lookup(patch, strict=True)
401 if not patch:
401 if not patch:
402 self.ui.warn("patch %s does not exist\n" % patch)
402 self.ui.warn("patch %s does not exist\n" % patch)
403 return (1, None)
403 return (1, None)
404 pushable, reason = self.pushable(patch)
404 pushable, reason = self.pushable(patch)
405 if not pushable:
405 if not pushable:
406 self.explain_pushable(patch, all_patches=True)
406 self.explain_pushable(patch, all_patches=True)
407 continue
407 continue
408 info = mergeq.isapplied(patch)
408 info = mergeq.isapplied(patch)
409 if not info:
409 if not info:
410 self.ui.warn("patch %s is not applied\n" % patch)
410 self.ui.warn("patch %s is not applied\n" % patch)
411 return (1, None)
411 return (1, None)
412 rev = revlog.bin(info[1])
412 rev = revlog.bin(info[1])
413 (err, head) = self.mergeone(repo, mergeq, head, patch, rev)
413 (err, head) = self.mergeone(repo, mergeq, head, patch, rev)
414 if head:
414 if head:
415 self.applied.append(statusentry(revlog.hex(head), patch))
415 self.applied.append(statusentry(revlog.hex(head), patch))
416 self.applied_dirty = 1
416 self.applied_dirty = 1
417 if err:
417 if err:
418 return (err, head)
418 return (err, head)
419 self.save_dirty()
419 self.save_dirty()
420 return (0, head)
420 return (0, head)
421
421
422 def patch(self, repo, patchfile):
422 def patch(self, repo, patchfile):
423 '''Apply patchfile to the working directory.
423 '''Apply patchfile to the working directory.
424 patchfile: file name of patch'''
424 patchfile: file name of patch'''
425 files = {}
425 files = {}
426 try:
426 try:
427 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
427 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
428 files=files)
428 files=files)
429 except Exception, inst:
429 except Exception, inst:
430 self.ui.note(str(inst) + '\n')
430 self.ui.note(str(inst) + '\n')
431 if not self.ui.verbose:
431 if not self.ui.verbose:
432 self.ui.warn("patch failed, unable to continue (try -v)\n")
432 self.ui.warn("patch failed, unable to continue (try -v)\n")
433 return (False, files, False)
433 return (False, files, False)
434
434
435 return (True, files, fuzz)
435 return (True, files, fuzz)
436
436
437 def apply(self, repo, series, list=False, update_status=True,
437 def apply(self, repo, series, list=False, update_status=True,
438 strict=False, patchdir=None, merge=None, all_files={}):
438 strict=False, patchdir=None, merge=None, all_files={}):
439 wlock = lock = tr = None
439 wlock = lock = tr = None
440 try:
440 try:
441 wlock = repo.wlock()
441 wlock = repo.wlock()
442 lock = repo.lock()
442 lock = repo.lock()
443 tr = repo.transaction()
443 tr = repo.transaction()
444 try:
444 try:
445 ret = self._apply(repo, series, list, update_status,
445 ret = self._apply(repo, series, list, update_status,
446 strict, patchdir, merge, all_files=all_files)
446 strict, patchdir, merge, all_files=all_files)
447 tr.close()
447 tr.close()
448 self.save_dirty()
448 self.save_dirty()
449 return ret
449 return ret
450 except:
450 except:
451 try:
451 try:
452 tr.abort()
452 tr.abort()
453 finally:
453 finally:
454 repo.invalidate()
454 repo.invalidate()
455 repo.dirstate.invalidate()
455 repo.dirstate.invalidate()
456 raise
456 raise
457 finally:
457 finally:
458 del tr, lock, wlock
458 del tr, lock, wlock
459
459
460 def _apply(self, repo, series, list=False, update_status=True,
460 def _apply(self, repo, series, list=False, update_status=True,
461 strict=False, patchdir=None, merge=None, all_files={}):
461 strict=False, patchdir=None, merge=None, all_files={}):
462 # TODO unify with commands.py
462 # TODO unify with commands.py
463 if not patchdir:
463 if not patchdir:
464 patchdir = self.path
464 patchdir = self.path
465 err = 0
465 err = 0
466 n = None
466 n = None
467 for patchname in series:
467 for patchname in series:
468 pushable, reason = self.pushable(patchname)
468 pushable, reason = self.pushable(patchname)
469 if not pushable:
469 if not pushable:
470 self.explain_pushable(patchname, all_patches=True)
470 self.explain_pushable(patchname, all_patches=True)
471 continue
471 continue
472 self.ui.warn("applying %s\n" % patchname)
472 self.ui.warn("applying %s\n" % patchname)
473 pf = os.path.join(patchdir, patchname)
473 pf = os.path.join(patchdir, patchname)
474
474
475 try:
475 try:
476 message, comments, user, date, patchfound = self.readheaders(patchname)
476 message, comments, user, date, patchfound = self.readheaders(patchname)
477 except:
477 except:
478 self.ui.warn("Unable to read %s\n" % patchname)
478 self.ui.warn("Unable to read %s\n" % patchname)
479 err = 1
479 err = 1
480 break
480 break
481
481
482 if not message:
482 if not message:
483 message = "imported patch %s\n" % patchname
483 message = "imported patch %s\n" % patchname
484 else:
484 else:
485 if list:
485 if list:
486 message.append("\nimported patch %s" % patchname)
486 message.append("\nimported patch %s" % patchname)
487 message = '\n'.join(message)
487 message = '\n'.join(message)
488
488
489 (patcherr, files, fuzz) = self.patch(repo, pf)
489 (patcherr, files, fuzz) = self.patch(repo, pf)
490 all_files.update(files)
490 all_files.update(files)
491 patcherr = not patcherr
491 patcherr = not patcherr
492
492
493 if merge and files:
493 if merge and files:
494 # Mark as removed/merged and update dirstate parent info
494 # Mark as removed/merged and update dirstate parent info
495 removed = []
495 removed = []
496 merged = []
496 merged = []
497 for f in files:
497 for f in files:
498 if os.path.exists(repo.wjoin(f)):
498 if os.path.exists(repo.wjoin(f)):
499 merged.append(f)
499 merged.append(f)
500 else:
500 else:
501 removed.append(f)
501 removed.append(f)
502 for f in removed:
502 for f in removed:
503 repo.dirstate.remove(f)
503 repo.dirstate.remove(f)
504 for f in merged:
504 for f in merged:
505 repo.dirstate.merge(f)
505 repo.dirstate.merge(f)
506 p1, p2 = repo.dirstate.parents()
506 p1, p2 = repo.dirstate.parents()
507 repo.dirstate.setparents(p1, merge)
507 repo.dirstate.setparents(p1, merge)
508 files = patch.updatedir(self.ui, repo, files)
508 files = patch.updatedir(self.ui, repo, files)
509 n = repo.commit(files, message, user, date, force=1)
509 n = repo.commit(files, message, user, date, force=1)
510
510
511 if n == None:
511 if n == None:
512 raise util.Abort(_("repo commit failed"))
512 raise util.Abort(_("repo commit failed"))
513
513
514 if update_status:
514 if update_status:
515 self.applied.append(statusentry(revlog.hex(n), patchname))
515 self.applied.append(statusentry(revlog.hex(n), patchname))
516
516
517 if patcherr:
517 if patcherr:
518 if not patchfound:
518 if not patchfound:
519 self.ui.warn("patch %s is empty\n" % patchname)
519 self.ui.warn("patch %s is empty\n" % patchname)
520 err = 0
520 err = 0
521 else:
521 else:
522 self.ui.warn("patch failed, rejects left in working dir\n")
522 self.ui.warn("patch failed, rejects left in working dir\n")
523 err = 1
523 err = 1
524 break
524 break
525
525
526 if fuzz and strict:
526 if fuzz and strict:
527 self.ui.warn("fuzz found when applying patch, stopping\n")
527 self.ui.warn("fuzz found when applying patch, stopping\n")
528 err = 1
528 err = 1
529 break
529 break
530 self.removeundo(repo)
530 self.removeundo(repo)
531 return (err, n)
531 return (err, n)
532
532
533 def delete(self, repo, patches, opts):
533 def delete(self, repo, patches, opts):
534 if not patches and not opts.get('rev'):
534 if not patches and not opts.get('rev'):
535 raise util.Abort(_('qdelete requires at least one revision or '
535 raise util.Abort(_('qdelete requires at least one revision or '
536 'patch name'))
536 'patch name'))
537
537
538 realpatches = []
538 realpatches = []
539 for patch in patches:
539 for patch in patches:
540 patch = self.lookup(patch, strict=True)
540 patch = self.lookup(patch, strict=True)
541 info = self.isapplied(patch)
541 info = self.isapplied(patch)
542 if info:
542 if info:
543 raise util.Abort(_("cannot delete applied patch %s") % patch)
543 raise util.Abort(_("cannot delete applied patch %s") % patch)
544 if patch not in self.series:
544 if patch not in self.series:
545 raise util.Abort(_("patch %s not in series file") % patch)
545 raise util.Abort(_("patch %s not in series file") % patch)
546 realpatches.append(patch)
546 realpatches.append(patch)
547
547
548 appliedbase = 0
548 appliedbase = 0
549 if opts.get('rev'):
549 if opts.get('rev'):
550 if not self.applied:
550 if not self.applied:
551 raise util.Abort(_('no patches applied'))
551 raise util.Abort(_('no patches applied'))
552 revs = cmdutil.revrange(repo, opts['rev'])
552 revs = cmdutil.revrange(repo, opts['rev'])
553 if len(revs) > 1 and revs[0] > revs[1]:
553 if len(revs) > 1 and revs[0] > revs[1]:
554 revs.reverse()
554 revs.reverse()
555 for rev in revs:
555 for rev in revs:
556 if appliedbase >= len(self.applied):
556 if appliedbase >= len(self.applied):
557 raise util.Abort(_("revision %d is not managed") % rev)
557 raise util.Abort(_("revision %d is not managed") % rev)
558
558
559 base = revlog.bin(self.applied[appliedbase].rev)
559 base = revlog.bin(self.applied[appliedbase].rev)
560 node = repo.changelog.node(rev)
560 node = repo.changelog.node(rev)
561 if node != base:
561 if node != base:
562 raise util.Abort(_("cannot delete revision %d above "
562 raise util.Abort(_("cannot delete revision %d above "
563 "applied patches") % rev)
563 "applied patches") % rev)
564 realpatches.append(self.applied[appliedbase].name)
564 realpatches.append(self.applied[appliedbase].name)
565 appliedbase += 1
565 appliedbase += 1
566
566
567 if not opts.get('keep'):
567 if not opts.get('keep'):
568 r = self.qrepo()
568 r = self.qrepo()
569 if r:
569 if r:
570 r.remove(realpatches, True)
570 r.remove(realpatches, True)
571 else:
571 else:
572 for p in realpatches:
572 for p in realpatches:
573 os.unlink(self.join(p))
573 os.unlink(self.join(p))
574
574
575 if appliedbase:
575 if appliedbase:
576 del self.applied[:appliedbase]
576 del self.applied[:appliedbase]
577 self.applied_dirty = 1
577 self.applied_dirty = 1
578 indices = [self.find_series(p) for p in realpatches]
578 indices = [self.find_series(p) for p in realpatches]
579 indices.sort()
579 indices.sort()
580 for i in indices[-1::-1]:
580 for i in indices[-1::-1]:
581 del self.full_series[i]
581 del self.full_series[i]
582 self.parse_series()
582 self.parse_series()
583 self.series_dirty = 1
583 self.series_dirty = 1
584
584
585 def check_toppatch(self, repo):
585 def check_toppatch(self, repo):
586 if len(self.applied) > 0:
586 if len(self.applied) > 0:
587 top = revlog.bin(self.applied[-1].rev)
587 top = revlog.bin(self.applied[-1].rev)
588 pp = repo.dirstate.parents()
588 pp = repo.dirstate.parents()
589 if top not in pp:
589 if top not in pp:
590 raise util.Abort(_("queue top not at same revision as working directory"))
590 raise util.Abort(_("queue top not at same revision as working directory"))
591 return top
591 return top
592 return None
592 return None
593 def check_localchanges(self, repo, force=False, refresh=True):
593 def check_localchanges(self, repo, force=False, refresh=True):
594 m, a, r, d = repo.status()[:4]
594 m, a, r, d = repo.status()[:4]
595 if m or a or r or d:
595 if m or a or r or d:
596 if not force:
596 if not force:
597 if refresh:
597 if refresh:
598 raise util.Abort(_("local changes found, refresh first"))
598 raise util.Abort(_("local changes found, refresh first"))
599 else:
599 else:
600 raise util.Abort(_("local changes found"))
600 raise util.Abort(_("local changes found"))
601 return m, a, r, d
601 return m, a, r, d
602
602
603 def new(self, repo, patch, *pats, **opts):
603 def new(self, repo, patch, *pats, **opts):
604 msg = opts.get('msg')
604 msg = opts.get('msg')
605 force = opts.get('force')
605 force = opts.get('force')
606 if os.path.exists(self.join(patch)):
606 if os.path.exists(self.join(patch)):
607 raise util.Abort(_('patch "%s" already exists') % patch)
607 raise util.Abort(_('patch "%s" already exists') % patch)
608 if opts.get('include') or opts.get('exclude') or pats:
608 if opts.get('include') or opts.get('exclude') or pats:
609 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
609 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
610 m, a, r, d = repo.status(files=fns, match=match)[:4]
610 m, a, r, d = repo.status(files=fns, match=match)[:4]
611 else:
611 else:
612 m, a, r, d = self.check_localchanges(repo, force)
612 m, a, r, d = self.check_localchanges(repo, force)
613 commitfiles = m + a + r
613 commitfiles = m + a + r
614 self.check_toppatch(repo)
614 self.check_toppatch(repo)
615 wlock = repo.wlock()
615 wlock = repo.wlock()
616 try:
616 try:
617 insert = self.full_series_end()
617 insert = self.full_series_end()
618 if msg:
618 if msg:
619 n = repo.commit(commitfiles, msg, force=True)
619 n = repo.commit(commitfiles, msg, force=True)
620 else:
620 else:
621 n = repo.commit(commitfiles, "[mq]: %s" % patch, force=True)
621 n = repo.commit(commitfiles, "[mq]: %s" % patch, force=True)
622 if n == None:
622 if n == None:
623 raise util.Abort(_("repo commit failed"))
623 raise util.Abort(_("repo commit failed"))
624 self.full_series[insert:insert] = [patch]
624 self.full_series[insert:insert] = [patch]
625 self.applied.append(statusentry(revlog.hex(n), patch))
625 self.applied.append(statusentry(revlog.hex(n), patch))
626 self.parse_series()
626 self.parse_series()
627 self.series_dirty = 1
627 self.series_dirty = 1
628 self.applied_dirty = 1
628 self.applied_dirty = 1
629 p = self.opener(patch, "w")
629 p = self.opener(patch, "w")
630 if msg:
630 if msg:
631 msg = msg + "\n"
631 msg = msg + "\n"
632 p.write(msg)
632 p.write(msg)
633 p.close()
633 p.close()
634 wlock = None
634 wlock = None
635 r = self.qrepo()
635 r = self.qrepo()
636 if r: r.add([patch])
636 if r: r.add([patch])
637 if commitfiles:
637 if commitfiles:
638 self.refresh(repo, short=True, git=opts.get('git'))
638 self.refresh(repo, short=True, git=opts.get('git'))
639 self.removeundo(repo)
639 self.removeundo(repo)
640 finally:
640 finally:
641 del wlock
641 del wlock
642
642
643 def strip(self, repo, rev, update=True, backup="all"):
643 def strip(self, repo, rev, update=True, backup="all"):
644 wlock = lock = None
644 wlock = lock = None
645 try:
645 try:
646 wlock = repo.wlock()
646 wlock = repo.wlock()
647 lock = repo.lock()
647 lock = repo.lock()
648
648
649 if update:
649 if update:
650 self.check_localchanges(repo, refresh=False)
650 self.check_localchanges(repo, refresh=False)
651 urev = self.qparents(repo, rev)
651 urev = self.qparents(repo, rev)
652 hg.clean(repo, urev)
652 hg.clean(repo, urev)
653 repo.dirstate.write()
653 repo.dirstate.write()
654
654
655 self.removeundo(repo)
655 self.removeundo(repo)
656 repair.strip(self.ui, repo, rev, backup)
656 repair.strip(self.ui, repo, rev, backup)
657 finally:
657 finally:
658 del lock, wlock
658 del lock, wlock
659
659
660 def isapplied(self, patch):
660 def isapplied(self, patch):
661 """returns (index, rev, patch)"""
661 """returns (index, rev, patch)"""
662 for i in xrange(len(self.applied)):
662 for i in xrange(len(self.applied)):
663 a = self.applied[i]
663 a = self.applied[i]
664 if a.name == patch:
664 if a.name == patch:
665 return (i, a.rev, a.name)
665 return (i, a.rev, a.name)
666 return None
666 return None
667
667
668 # if the exact patch name does not exist, we try a few
668 # if the exact patch name does not exist, we try a few
669 # variations. If strict is passed, we try only #1
669 # variations. If strict is passed, we try only #1
670 #
670 #
671 # 1) a number to indicate an offset in the series file
671 # 1) a number to indicate an offset in the series file
672 # 2) a unique substring of the patch name was given
672 # 2) a unique substring of the patch name was given
673 # 3) patchname[-+]num to indicate an offset in the series file
673 # 3) patchname[-+]num to indicate an offset in the series file
674 def lookup(self, patch, strict=False):
674 def lookup(self, patch, strict=False):
675 patch = patch and str(patch)
675 patch = patch and str(patch)
676
676
677 def partial_name(s):
677 def partial_name(s):
678 if s in self.series:
678 if s in self.series:
679 return s
679 return s
680 matches = [x for x in self.series if s in x]
680 matches = [x for x in self.series if s in x]
681 if len(matches) > 1:
681 if len(matches) > 1:
682 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
682 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
683 for m in matches:
683 for m in matches:
684 self.ui.warn(' %s\n' % m)
684 self.ui.warn(' %s\n' % m)
685 return None
685 return None
686 if matches:
686 if matches:
687 return matches[0]
687 return matches[0]
688 if len(self.series) > 0 and len(self.applied) > 0:
688 if len(self.series) > 0 and len(self.applied) > 0:
689 if s == 'qtip':
689 if s == 'qtip':
690 return self.series[self.series_end(True)-1]
690 return self.series[self.series_end(True)-1]
691 if s == 'qbase':
691 if s == 'qbase':
692 return self.series[0]
692 return self.series[0]
693 return None
693 return None
694 if patch == None:
694 if patch == None:
695 return None
695 return None
696
696
697 # we don't want to return a partial match until we make
697 # we don't want to return a partial match until we make
698 # sure the file name passed in does not exist (checked below)
698 # sure the file name passed in does not exist (checked below)
699 res = partial_name(patch)
699 res = partial_name(patch)
700 if res and res == patch:
700 if res and res == patch:
701 return res
701 return res
702
702
703 if not os.path.isfile(self.join(patch)):
703 if not os.path.isfile(self.join(patch)):
704 try:
704 try:
705 sno = int(patch)
705 sno = int(patch)
706 except(ValueError, OverflowError):
706 except(ValueError, OverflowError):
707 pass
707 pass
708 else:
708 else:
709 if sno < len(self.series):
709 if sno < len(self.series):
710 return self.series[sno]
710 return self.series[sno]
711 if not strict:
711 if not strict:
712 # return any partial match made above
712 # return any partial match made above
713 if res:
713 if res:
714 return res
714 return res
715 minus = patch.rfind('-')
715 minus = patch.rfind('-')
716 if minus >= 0:
716 if minus >= 0:
717 res = partial_name(patch[:minus])
717 res = partial_name(patch[:minus])
718 if res:
718 if res:
719 i = self.series.index(res)
719 i = self.series.index(res)
720 try:
720 try:
721 off = int(patch[minus+1:] or 1)
721 off = int(patch[minus+1:] or 1)
722 except(ValueError, OverflowError):
722 except(ValueError, OverflowError):
723 pass
723 pass
724 else:
724 else:
725 if i - off >= 0:
725 if i - off >= 0:
726 return self.series[i - off]
726 return self.series[i - off]
727 plus = patch.rfind('+')
727 plus = patch.rfind('+')
728 if plus >= 0:
728 if plus >= 0:
729 res = partial_name(patch[:plus])
729 res = partial_name(patch[:plus])
730 if res:
730 if res:
731 i = self.series.index(res)
731 i = self.series.index(res)
732 try:
732 try:
733 off = int(patch[plus+1:] or 1)
733 off = int(patch[plus+1:] or 1)
734 except(ValueError, OverflowError):
734 except(ValueError, OverflowError):
735 pass
735 pass
736 else:
736 else:
737 if i + off < len(self.series):
737 if i + off < len(self.series):
738 return self.series[i + off]
738 return self.series[i + off]
739 raise util.Abort(_("patch %s not in series") % patch)
739 raise util.Abort(_("patch %s not in series") % patch)
740
740
741 def push(self, repo, patch=None, force=False, list=False,
741 def push(self, repo, patch=None, force=False, list=False,
742 mergeq=None):
742 mergeq=None):
743 wlock = repo.wlock()
743 wlock = repo.wlock()
744 try:
744 try:
745 patch = self.lookup(patch)
745 patch = self.lookup(patch)
746 # Suppose our series file is: A B C and the current 'top'
746 # Suppose our series file is: A B C and the current 'top'
747 # patch is B. qpush C should be performed (moving forward)
747 # patch is B. qpush C should be performed (moving forward)
748 # qpush B is a NOP (no change) qpush A is an error (can't
748 # qpush B is a NOP (no change) qpush A is an error (can't
749 # go backwards with qpush)
749 # go backwards with qpush)
750 if patch:
750 if patch:
751 info = self.isapplied(patch)
751 info = self.isapplied(patch)
752 if info:
752 if info:
753 if info[0] < len(self.applied) - 1:
753 if info[0] < len(self.applied) - 1:
754 raise util.Abort(
754 raise util.Abort(
755 _("cannot push to a previous patch: %s") % patch)
755 _("cannot push to a previous patch: %s") % patch)
756 if info[0] < len(self.series) - 1:
756 if info[0] < len(self.series) - 1:
757 self.ui.warn(
757 self.ui.warn(
758 _('qpush: %s is already at the top\n') % patch)
758 _('qpush: %s is already at the top\n') % patch)
759 else:
759 else:
760 self.ui.warn(_('all patches are currently applied\n'))
760 self.ui.warn(_('all patches are currently applied\n'))
761 return
761 return
762
762
763 # Following the above example, starting at 'top' of B:
763 # Following the above example, starting at 'top' of B:
764 # qpush should be performed (pushes C), but a subsequent
764 # qpush should be performed (pushes C), but a subsequent
765 # qpush without an argument is an error (nothing to
765 # qpush without an argument is an error (nothing to
766 # apply). This allows a loop of "...while hg qpush..." to
766 # apply). This allows a loop of "...while hg qpush..." to
767 # work as it detects an error when done
767 # work as it detects an error when done
768 if self.series_end() == len(self.series):
768 if self.series_end() == len(self.series):
769 self.ui.warn(_('patch series already fully applied\n'))
769 self.ui.warn(_('patch series already fully applied\n'))
770 return 1
770 return 1
771 if not force:
771 if not force:
772 self.check_localchanges(repo)
772 self.check_localchanges(repo)
773
773
774 self.applied_dirty = 1;
774 self.applied_dirty = 1;
775 start = self.series_end()
775 start = self.series_end()
776 if start > 0:
776 if start > 0:
777 self.check_toppatch(repo)
777 self.check_toppatch(repo)
778 if not patch:
778 if not patch:
779 patch = self.series[start]
779 patch = self.series[start]
780 end = start + 1
780 end = start + 1
781 else:
781 else:
782 end = self.series.index(patch, start) + 1
782 end = self.series.index(patch, start) + 1
783 s = self.series[start:end]
783 s = self.series[start:end]
784 all_files = {}
784 all_files = {}
785 try:
785 try:
786 if mergeq:
786 if mergeq:
787 ret = self.mergepatch(repo, mergeq, s)
787 ret = self.mergepatch(repo, mergeq, s)
788 else:
788 else:
789 ret = self.apply(repo, s, list, all_files=all_files)
789 ret = self.apply(repo, s, list, all_files=all_files)
790 except:
790 except:
791 self.ui.warn(_('cleaning up working directory...'))
791 self.ui.warn(_('cleaning up working directory...'))
792 node = repo.dirstate.parents()[0]
792 node = repo.dirstate.parents()[0]
793 hg.revert(repo, node, None)
793 hg.revert(repo, node, None)
794 unknown = repo.status()[4]
794 unknown = repo.status()[4]
795 # only remove unknown files that we know we touched or
795 # only remove unknown files that we know we touched or
796 # created while patching
796 # created while patching
797 for f in unknown:
797 for f in unknown:
798 if f in all_files:
798 if f in all_files:
799 util.unlink(repo.wjoin(f))
799 util.unlink(repo.wjoin(f))
800 self.ui.warn(_('done\n'))
800 self.ui.warn(_('done\n'))
801 raise
801 raise
802 top = self.applied[-1].name
802 top = self.applied[-1].name
803 if ret[0]:
803 if ret[0]:
804 self.ui.write(
804 self.ui.write(
805 "Errors during apply, please fix and refresh %s\n" % top)
805 "Errors during apply, please fix and refresh %s\n" % top)
806 else:
806 else:
807 self.ui.write("Now at: %s\n" % top)
807 self.ui.write("Now at: %s\n" % top)
808 return ret[0]
808 return ret[0]
809 finally:
809 finally:
810 del wlock
810 del wlock
811
811
812 def pop(self, repo, patch=None, force=False, update=True, all=False):
812 def pop(self, repo, patch=None, force=False, update=True, all=False):
813 def getfile(f, rev):
813 def getfile(f, rev):
814 t = repo.file(f).read(rev)
814 t = repo.file(f).read(rev)
815 repo.wfile(f, "w").write(t)
815 repo.wfile(f, "w").write(t)
816
816
817 wlock = repo.wlock()
817 wlock = repo.wlock()
818 try:
818 try:
819 if patch:
819 if patch:
820 # index, rev, patch
820 # index, rev, patch
821 info = self.isapplied(patch)
821 info = self.isapplied(patch)
822 if not info:
822 if not info:
823 patch = self.lookup(patch)
823 patch = self.lookup(patch)
824 info = self.isapplied(patch)
824 info = self.isapplied(patch)
825 if not info:
825 if not info:
826 raise util.Abort(_("patch %s is not applied") % patch)
826 raise util.Abort(_("patch %s is not applied") % patch)
827
827
828 if len(self.applied) == 0:
828 if len(self.applied) == 0:
829 # Allow qpop -a to work repeatedly,
829 # Allow qpop -a to work repeatedly,
830 # but not qpop without an argument
830 # but not qpop without an argument
831 self.ui.warn(_("no patches applied\n"))
831 self.ui.warn(_("no patches applied\n"))
832 return not all
832 return not all
833
833
834 if not update:
834 if not update:
835 parents = repo.dirstate.parents()
835 parents = repo.dirstate.parents()
836 rr = [ revlog.bin(x.rev) for x in self.applied ]
836 rr = [ revlog.bin(x.rev) for x in self.applied ]
837 for p in parents:
837 for p in parents:
838 if p in rr:
838 if p in rr:
839 self.ui.warn("qpop: forcing dirstate update\n")
839 self.ui.warn("qpop: forcing dirstate update\n")
840 update = True
840 update = True
841
841
842 if not force and update:
842 if not force and update:
843 self.check_localchanges(repo)
843 self.check_localchanges(repo)
844
844
845 self.applied_dirty = 1;
845 self.applied_dirty = 1;
846 end = len(self.applied)
846 end = len(self.applied)
847 if not patch:
847 if not patch:
848 if all:
848 if all:
849 popi = 0
849 popi = 0
850 else:
850 else:
851 popi = len(self.applied) - 1
851 popi = len(self.applied) - 1
852 else:
852 else:
853 popi = info[0] + 1
853 popi = info[0] + 1
854 if popi >= end:
854 if popi >= end:
855 self.ui.warn("qpop: %s is already at the top\n" % patch)
855 self.ui.warn("qpop: %s is already at the top\n" % patch)
856 return
856 return
857 info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name]
857 info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name]
858
858
859 start = info[0]
859 start = info[0]
860 rev = revlog.bin(info[1])
860 rev = revlog.bin(info[1])
861
861
862 # we know there are no local changes, so we can make a simplified
862 # we know there are no local changes, so we can make a simplified
863 # form of hg.update.
863 # form of hg.update.
864 if update:
864 if update:
865 top = self.check_toppatch(repo)
865 top = self.check_toppatch(repo)
866 qp = self.qparents(repo, rev)
866 qp = self.qparents(repo, rev)
867 changes = repo.changelog.read(qp)
867 changes = repo.changelog.read(qp)
868 mmap = repo.manifest.read(changes[0])
868 mmap = repo.manifest.read(changes[0])
869 m, a, r, d, u = repo.status(qp, top)[:5]
869 m, a, r, d, u = repo.status(qp, top)[:5]
870 if d:
870 if d:
871 raise util.Abort("deletions found between repo revs")
871 raise util.Abort("deletions found between repo revs")
872 for f in m:
872 for f in m:
873 getfile(f, mmap[f])
873 getfile(f, mmap[f])
874 for f in r:
874 for f in r:
875 getfile(f, mmap[f])
875 getfile(f, mmap[f])
876 util.set_exec(repo.wjoin(f), mmap.execf(f))
876 util.set_exec(repo.wjoin(f), mmap.execf(f))
877 for f in m + r:
877 for f in m + r:
878 repo.dirstate.normal(f)
878 repo.dirstate.normal(f)
879 for f in a:
879 for f in a:
880 try:
880 try:
881 os.unlink(repo.wjoin(f))
881 os.unlink(repo.wjoin(f))
882 except OSError, e:
882 except OSError, e:
883 if e.errno != errno.ENOENT:
883 if e.errno != errno.ENOENT:
884 raise
884 raise
885 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
885 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
886 except: pass
886 except: pass
887 repo.dirstate.forget(f)
887 repo.dirstate.forget(f)
888 repo.dirstate.setparents(qp, revlog.nullid)
888 repo.dirstate.setparents(qp, revlog.nullid)
889 self.strip(repo, rev, update=False, backup='strip')
889 self.strip(repo, rev, update=False, backup='strip')
890 del self.applied[start:end]
890 del self.applied[start:end]
891 if len(self.applied):
891 if len(self.applied):
892 self.ui.write("Now at: %s\n" % self.applied[-1].name)
892 self.ui.write("Now at: %s\n" % self.applied[-1].name)
893 else:
893 else:
894 self.ui.write("Patch queue now empty\n")
894 self.ui.write("Patch queue now empty\n")
895 finally:
895 finally:
896 del wlock
896 del wlock
897
897
898 def diff(self, repo, pats, opts):
898 def diff(self, repo, pats, opts):
899 top = self.check_toppatch(repo)
899 top = self.check_toppatch(repo)
900 if not top:
900 if not top:
901 self.ui.write("No patches applied\n")
901 self.ui.write("No patches applied\n")
902 return
902 return
903 qp = self.qparents(repo, top)
903 qp = self.qparents(repo, top)
904 if opts.get('git'):
904 if opts.get('git'):
905 self.diffopts().git = True
905 self.diffopts().git = True
906 self.printdiff(repo, qp, files=pats, opts=opts)
906 self.printdiff(repo, qp, files=pats, opts=opts)
907
907
908 def refresh(self, repo, pats=None, **opts):
908 def refresh(self, repo, pats=None, **opts):
909 if len(self.applied) == 0:
909 if len(self.applied) == 0:
910 self.ui.write("No patches applied\n")
910 self.ui.write("No patches applied\n")
911 return 1
911 return 1
912 wlock = repo.wlock()
912 wlock = repo.wlock()
913 try:
913 try:
914 self.check_toppatch(repo)
914 self.check_toppatch(repo)
915 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
915 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
916 top = revlog.bin(top)
916 top = revlog.bin(top)
917 cparents = repo.changelog.parents(top)
917 cparents = repo.changelog.parents(top)
918 patchparent = self.qparents(repo, top)
918 patchparent = self.qparents(repo, top)
919 message, comments, user, date, patchfound = self.readheaders(patchfn)
919 message, comments, user, date, patchfound = self.readheaders(patchfn)
920
920
921 patchf = self.opener(patchfn, 'r+')
921 patchf = self.opener(patchfn, 'r+')
922
922
923 # if the patch was a git patch, refresh it as a git patch
923 # if the patch was a git patch, refresh it as a git patch
924 for line in patchf:
924 for line in patchf:
925 if line.startswith('diff --git'):
925 if line.startswith('diff --git'):
926 self.diffopts().git = True
926 self.diffopts().git = True
927 break
927 break
928 patchf.seek(0)
928 patchf.seek(0)
929 patchf.truncate()
929 patchf.truncate()
930
930
931 msg = opts.get('msg', '').rstrip()
931 msg = opts.get('msg', '').rstrip()
932 if msg:
932 if msg:
933 if comments:
933 if comments:
934 # Remove existing message.
934 # Remove existing message.
935 ci = 0
935 ci = 0
936 subj = None
936 subj = None
937 for mi in xrange(len(message)):
937 for mi in xrange(len(message)):
938 if comments[ci].lower().startswith('subject: '):
938 if comments[ci].lower().startswith('subject: '):
939 subj = comments[ci][9:]
939 subj = comments[ci][9:]
940 while message[mi] != comments[ci] and message[mi] != subj:
940 while message[mi] != comments[ci] and message[mi] != subj:
941 ci += 1
941 ci += 1
942 del comments[ci]
942 del comments[ci]
943 comments.append(msg)
943 comments.append(msg)
944 if comments:
944 if comments:
945 comments = "\n".join(comments) + '\n\n'
945 comments = "\n".join(comments) + '\n\n'
946 patchf.write(comments)
946 patchf.write(comments)
947
947
948 if opts.get('git'):
948 if opts.get('git'):
949 self.diffopts().git = True
949 self.diffopts().git = True
950 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
950 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
951 tip = repo.changelog.tip()
951 tip = repo.changelog.tip()
952 if top == tip:
952 if top == tip:
953 # if the top of our patch queue is also the tip, there is an
953 # if the top of our patch queue is also the tip, there is an
954 # optimization here. We update the dirstate in place and strip
954 # optimization here. We update the dirstate in place and strip
955 # off the tip commit. Then just commit the current directory
955 # off the tip commit. Then just commit the current directory
956 # tree. We can also send repo.commit the list of files
956 # tree. We can also send repo.commit the list of files
957 # changed to speed up the diff
957 # changed to speed up the diff
958 #
958 #
959 # in short mode, we only diff the files included in the
959 # in short mode, we only diff the files included in the
960 # patch already
960 # patch already
961 #
961 #
962 # this should really read:
962 # this should really read:
963 # mm, dd, aa, aa2, uu = repo.status(tip, patchparent)[:5]
963 # mm, dd, aa, aa2, uu = repo.status(tip, patchparent)[:5]
964 # but we do it backwards to take advantage of manifest/chlog
964 # but we do it backwards to take advantage of manifest/chlog
965 # caching against the next repo.status call
965 # caching against the next repo.status call
966 #
966 #
967 mm, aa, dd, aa2, uu = repo.status(patchparent, tip)[:5]
967 mm, aa, dd, aa2, uu = repo.status(patchparent, tip)[:5]
968 changes = repo.changelog.read(tip)
968 changes = repo.changelog.read(tip)
969 man = repo.manifest.read(changes[0])
969 man = repo.manifest.read(changes[0])
970 aaa = aa[:]
970 aaa = aa[:]
971 if opts.get('short'):
971 if opts.get('short'):
972 filelist = mm + aa + dd
972 filelist = mm + aa + dd
973 match = dict.fromkeys(filelist).__contains__
973 match = dict.fromkeys(filelist).__contains__
974 else:
974 else:
975 filelist = None
975 filelist = None
976 match = util.always
976 match = util.always
977 m, a, r, d, u = repo.status(files=filelist, match=match)[:5]
977 m, a, r, d, u = repo.status(files=filelist, match=match)[:5]
978
978
979 # we might end up with files that were added between
979 # we might end up with files that were added between
980 # tip and the dirstate parent, but then changed in the
980 # tip and the dirstate parent, but then changed in the
981 # local dirstate. in this case, we want them to only
981 # local dirstate. in this case, we want them to only
982 # show up in the added section
982 # show up in the added section
983 for x in m:
983 for x in m:
984 if x not in aa:
984 if x not in aa:
985 mm.append(x)
985 mm.append(x)
986 # we might end up with files added by the local dirstate that
986 # we might end up with files added by the local dirstate that
987 # were deleted by the patch. In this case, they should only
987 # were deleted by the patch. In this case, they should only
988 # show up in the changed section.
988 # show up in the changed section.
989 for x in a:
989 for x in a:
990 if x in dd:
990 if x in dd:
991 del dd[dd.index(x)]
991 del dd[dd.index(x)]
992 mm.append(x)
992 mm.append(x)
993 else:
993 else:
994 aa.append(x)
994 aa.append(x)
995 # make sure any files deleted in the local dirstate
995 # make sure any files deleted in the local dirstate
996 # are not in the add or change column of the patch
996 # are not in the add or change column of the patch
997 forget = []
997 forget = []
998 for x in d + r:
998 for x in d + r:
999 if x in aa:
999 if x in aa:
1000 del aa[aa.index(x)]
1000 del aa[aa.index(x)]
1001 forget.append(x)
1001 forget.append(x)
1002 continue
1002 continue
1003 elif x in mm:
1003 elif x in mm:
1004 del mm[mm.index(x)]
1004 del mm[mm.index(x)]
1005 dd.append(x)
1005 dd.append(x)
1006
1006
1007 m = util.unique(mm)
1007 m = util.unique(mm)
1008 r = util.unique(dd)
1008 r = util.unique(dd)
1009 a = util.unique(aa)
1009 a = util.unique(aa)
1010 c = [filter(matchfn, l) for l in (m, a, r, [], u)]
1010 c = [filter(matchfn, l) for l in (m, a, r, [], u)]
1011 filelist = util.unique(c[0] + c[1] + c[2])
1011 filelist = util.unique(c[0] + c[1] + c[2])
1012 patch.diff(repo, patchparent, files=filelist, match=matchfn,
1012 patch.diff(repo, patchparent, files=filelist, match=matchfn,
1013 fp=patchf, changes=c, opts=self.diffopts())
1013 fp=patchf, changes=c, opts=self.diffopts())
1014 patchf.close()
1014 patchf.close()
1015
1015
1016 repo.dirstate.setparents(*cparents)
1016 repo.dirstate.setparents(*cparents)
1017 copies = {}
1017 copies = {}
1018 for dst in a:
1018 for dst in a:
1019 src = repo.dirstate.copied(dst)
1019 src = repo.dirstate.copied(dst)
1020 if src is None:
1020 if src is None:
1021 continue
1021 continue
1022 copies.setdefault(src, []).append(dst)
1022 copies.setdefault(src, []).append(dst)
1023 repo.dirstate.add(dst)
1023 repo.dirstate.add(dst)
1024 # remember the copies between patchparent and tip
1024 # remember the copies between patchparent and tip
1025 # this may be slow, so don't do it if we're not tracking copies
1025 # this may be slow, so don't do it if we're not tracking copies
1026 if self.diffopts().git:
1026 if self.diffopts().git:
1027 for dst in aaa:
1027 for dst in aaa:
1028 f = repo.file(dst)
1028 f = repo.file(dst)
1029 src = f.renamed(man[dst])
1029 src = f.renamed(man[dst])
1030 if src:
1030 if src:
1031 copies[src[0]] = copies.get(dst, [])
1031 copies[src[0]] = copies.get(dst, [])
1032 if dst in a:
1032 if dst in a:
1033 copies[src[0]].append(dst)
1033 copies[src[0]].append(dst)
1034 # we can't copy a file created by the patch itself
1034 # we can't copy a file created by the patch itself
1035 if dst in copies:
1035 if dst in copies:
1036 del copies[dst]
1036 del copies[dst]
1037 for src, dsts in copies.iteritems():
1037 for src, dsts in copies.iteritems():
1038 for dst in dsts:
1038 for dst in dsts:
1039 repo.dirstate.copy(src, dst)
1039 repo.dirstate.copy(src, dst)
1040 for f in r:
1040 for f in r:
1041 repo.dirstate.remove(f)
1041 repo.dirstate.remove(f)
1042 # if the patch excludes a modified file, mark that
1042 # if the patch excludes a modified file, mark that
1043 # file with mtime=0 so status can see it.
1043 # file with mtime=0 so status can see it.
1044 mm = []
1044 mm = []
1045 for i in xrange(len(m)-1, -1, -1):
1045 for i in xrange(len(m)-1, -1, -1):
1046 if not matchfn(m[i]):
1046 if not matchfn(m[i]):
1047 mm.append(m[i])
1047 mm.append(m[i])
1048 del m[i]
1048 del m[i]
1049 for f in m:
1049 for f in m:
1050 repo.dirstate.normal(f)
1050 repo.dirstate.normal(f)
1051 for f in mm:
1051 for f in mm:
1052 repo.dirstate.normaldirty(f)
1052 repo.dirstate.normaldirty(f)
1053 for f in forget:
1053 for f in forget:
1054 repo.dirstate.forget(f)
1054 repo.dirstate.forget(f)
1055
1055
1056 if not msg:
1056 if not msg:
1057 if not message:
1057 if not message:
1058 message = "[mq]: %s\n" % patchfn
1058 message = "[mq]: %s\n" % patchfn
1059 else:
1059 else:
1060 message = "\n".join(message)
1060 message = "\n".join(message)
1061 else:
1061 else:
1062 message = msg
1062 message = msg
1063
1063
1064 self.strip(repo, top, update=False,
1064 self.strip(repo, top, update=False,
1065 backup='strip')
1065 backup='strip')
1066 n = repo.commit(filelist, message, changes[1], match=matchfn,
1066 n = repo.commit(filelist, message, changes[1], match=matchfn,
1067 force=1)
1067 force=1)
1068 self.applied[-1] = statusentry(revlog.hex(n), patchfn)
1068 self.applied[-1] = statusentry(revlog.hex(n), patchfn)
1069 self.applied_dirty = 1
1069 self.applied_dirty = 1
1070 self.removeundo(repo)
1070 self.removeundo(repo)
1071 else:
1071 else:
1072 self.printdiff(repo, patchparent, fp=patchf)
1072 self.printdiff(repo, patchparent, fp=patchf)
1073 patchf.close()
1073 patchf.close()
1074 added = repo.status()[1]
1074 added = repo.status()[1]
1075 for a in added:
1075 for a in added:
1076 f = repo.wjoin(a)
1076 f = repo.wjoin(a)
1077 try:
1077 try:
1078 os.unlink(f)
1078 os.unlink(f)
1079 except OSError, e:
1079 except OSError, e:
1080 if e.errno != errno.ENOENT:
1080 if e.errno != errno.ENOENT:
1081 raise
1081 raise
1082 try: os.removedirs(os.path.dirname(f))
1082 try: os.removedirs(os.path.dirname(f))
1083 except: pass
1083 except: pass
1084 # forget the file copies in the dirstate
1084 # forget the file copies in the dirstate
1085 # push should readd the files later on
1085 # push should readd the files later on
1086 repo.dirstate.forget(a)
1086 repo.dirstate.forget(a)
1087 self.pop(repo, force=True)
1087 self.pop(repo, force=True)
1088 self.push(repo, force=True)
1088 self.push(repo, force=True)
1089 finally:
1089 finally:
1090 del wlock
1090 del wlock
1091
1091
1092 def init(self, repo, create=False):
1092 def init(self, repo, create=False):
1093 if not create and os.path.isdir(self.path):
1093 if not create and os.path.isdir(self.path):
1094 raise util.Abort(_("patch queue directory already exists"))
1094 raise util.Abort(_("patch queue directory already exists"))
1095 try:
1095 try:
1096 os.mkdir(self.path)
1096 os.mkdir(self.path)
1097 except OSError, inst:
1097 except OSError, inst:
1098 if inst.errno != errno.EEXIST or not create:
1098 if inst.errno != errno.EEXIST or not create:
1099 raise
1099 raise
1100 if create:
1100 if create:
1101 return self.qrepo(create=True)
1101 return self.qrepo(create=True)
1102
1102
1103 def unapplied(self, repo, patch=None):
1103 def unapplied(self, repo, patch=None):
1104 if patch and patch not in self.series:
1104 if patch and patch not in self.series:
1105 raise util.Abort(_("patch %s is not in series file") % patch)
1105 raise util.Abort(_("patch %s is not in series file") % patch)
1106 if not patch:
1106 if not patch:
1107 start = self.series_end()
1107 start = self.series_end()
1108 else:
1108 else:
1109 start = self.series.index(patch) + 1
1109 start = self.series.index(patch) + 1
1110 unapplied = []
1110 unapplied = []
1111 for i in xrange(start, len(self.series)):
1111 for i in xrange(start, len(self.series)):
1112 pushable, reason = self.pushable(i)
1112 pushable, reason = self.pushable(i)
1113 if pushable:
1113 if pushable:
1114 unapplied.append((i, self.series[i]))
1114 unapplied.append((i, self.series[i]))
1115 self.explain_pushable(i)
1115 self.explain_pushable(i)
1116 return unapplied
1116 return unapplied
1117
1117
1118 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1118 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1119 summary=False):
1119 summary=False):
1120 def displayname(patchname):
1120 def displayname(patchname):
1121 if summary:
1121 if summary:
1122 msg = self.readheaders(patchname)[0]
1122 msg = self.readheaders(patchname)[0]
1123 msg = msg and ': ' + msg[0] or ': '
1123 msg = msg and ': ' + msg[0] or ': '
1124 else:
1124 else:
1125 msg = ''
1125 msg = ''
1126 return '%s%s' % (patchname, msg)
1126 return '%s%s' % (patchname, msg)
1127
1127
1128 applied = dict.fromkeys([p.name for p in self.applied])
1128 applied = dict.fromkeys([p.name for p in self.applied])
1129 if length is None:
1129 if length is None:
1130 length = len(self.series) - start
1130 length = len(self.series) - start
1131 if not missing:
1131 if not missing:
1132 for i in xrange(start, start+length):
1132 for i in xrange(start, start+length):
1133 patch = self.series[i]
1133 patch = self.series[i]
1134 if patch in applied:
1134 if patch in applied:
1135 stat = 'A'
1135 stat = 'A'
1136 elif self.pushable(i)[0]:
1136 elif self.pushable(i)[0]:
1137 stat = 'U'
1137 stat = 'U'
1138 else:
1138 else:
1139 stat = 'G'
1139 stat = 'G'
1140 pfx = ''
1140 pfx = ''
1141 if self.ui.verbose:
1141 if self.ui.verbose:
1142 pfx = '%d %s ' % (i, stat)
1142 pfx = '%d %s ' % (i, stat)
1143 elif status and status != stat:
1143 elif status and status != stat:
1144 continue
1144 continue
1145 self.ui.write('%s%s\n' % (pfx, displayname(patch)))
1145 self.ui.write('%s%s\n' % (pfx, displayname(patch)))
1146 else:
1146 else:
1147 msng_list = []
1147 msng_list = []
1148 for root, dirs, files in os.walk(self.path):
1148 for root, dirs, files in os.walk(self.path):
1149 d = root[len(self.path) + 1:]
1149 d = root[len(self.path) + 1:]
1150 for f in files:
1150 for f in files:
1151 fl = os.path.join(d, f)
1151 fl = os.path.join(d, f)
1152 if (fl not in self.series and
1152 if (fl not in self.series and
1153 fl not in (self.status_path, self.series_path,
1153 fl not in (self.status_path, self.series_path,
1154 self.guards_path)
1154 self.guards_path)
1155 and not fl.startswith('.')):
1155 and not fl.startswith('.')):
1156 msng_list.append(fl)
1156 msng_list.append(fl)
1157 msng_list.sort()
1157 msng_list.sort()
1158 for x in msng_list:
1158 for x in msng_list:
1159 pfx = self.ui.verbose and ('D ') or ''
1159 pfx = self.ui.verbose and ('D ') or ''
1160 self.ui.write("%s%s\n" % (pfx, displayname(x)))
1160 self.ui.write("%s%s\n" % (pfx, displayname(x)))
1161
1161
1162 def issaveline(self, l):
1162 def issaveline(self, l):
1163 if l.name == '.hg.patches.save.line':
1163 if l.name == '.hg.patches.save.line':
1164 return True
1164 return True
1165
1165
1166 def qrepo(self, create=False):
1166 def qrepo(self, create=False):
1167 if create or os.path.isdir(self.join(".hg")):
1167 if create or os.path.isdir(self.join(".hg")):
1168 return hg.repository(self.ui, path=self.path, create=create)
1168 return hg.repository(self.ui, path=self.path, create=create)
1169
1169
1170 def restore(self, repo, rev, delete=None, qupdate=None):
1170 def restore(self, repo, rev, delete=None, qupdate=None):
1171 c = repo.changelog.read(rev)
1171 c = repo.changelog.read(rev)
1172 desc = c[4].strip()
1172 desc = c[4].strip()
1173 lines = desc.splitlines()
1173 lines = desc.splitlines()
1174 i = 0
1174 i = 0
1175 datastart = None
1175 datastart = None
1176 series = []
1176 series = []
1177 applied = []
1177 applied = []
1178 qpp = None
1178 qpp = None
1179 for i in xrange(0, len(lines)):
1179 for i in xrange(0, len(lines)):
1180 if lines[i] == 'Patch Data:':
1180 if lines[i] == 'Patch Data:':
1181 datastart = i + 1
1181 datastart = i + 1
1182 elif lines[i].startswith('Dirstate:'):
1182 elif lines[i].startswith('Dirstate:'):
1183 l = lines[i].rstrip()
1183 l = lines[i].rstrip()
1184 l = l[10:].split(' ')
1184 l = l[10:].split(' ')
1185 qpp = [ hg.bin(x) for x in l ]
1185 qpp = [ hg.bin(x) for x in l ]
1186 elif datastart != None:
1186 elif datastart != None:
1187 l = lines[i].rstrip()
1187 l = lines[i].rstrip()
1188 se = statusentry(l)
1188 se = statusentry(l)
1189 file_ = se.name
1189 file_ = se.name
1190 if se.rev:
1190 if se.rev:
1191 applied.append(se)
1191 applied.append(se)
1192 else:
1192 else:
1193 series.append(file_)
1193 series.append(file_)
1194 if datastart == None:
1194 if datastart == None:
1195 self.ui.warn("No saved patch data found\n")
1195 self.ui.warn("No saved patch data found\n")
1196 return 1
1196 return 1
1197 self.ui.warn("restoring status: %s\n" % lines[0])
1197 self.ui.warn("restoring status: %s\n" % lines[0])
1198 self.full_series = series
1198 self.full_series = series
1199 self.applied = applied
1199 self.applied = applied
1200 self.parse_series()
1200 self.parse_series()
1201 self.series_dirty = 1
1201 self.series_dirty = 1
1202 self.applied_dirty = 1
1202 self.applied_dirty = 1
1203 heads = repo.changelog.heads()
1203 heads = repo.changelog.heads()
1204 if delete:
1204 if delete:
1205 if rev not in heads:
1205 if rev not in heads:
1206 self.ui.warn("save entry has children, leaving it alone\n")
1206 self.ui.warn("save entry has children, leaving it alone\n")
1207 else:
1207 else:
1208 self.ui.warn("removing save entry %s\n" % hg.short(rev))
1208 self.ui.warn("removing save entry %s\n" % hg.short(rev))
1209 pp = repo.dirstate.parents()
1209 pp = repo.dirstate.parents()
1210 if rev in pp:
1210 if rev in pp:
1211 update = True
1211 update = True
1212 else:
1212 else:
1213 update = False
1213 update = False
1214 self.strip(repo, rev, update=update, backup='strip')
1214 self.strip(repo, rev, update=update, backup='strip')
1215 if qpp:
1215 if qpp:
1216 self.ui.warn("saved queue repository parents: %s %s\n" %
1216 self.ui.warn("saved queue repository parents: %s %s\n" %
1217 (hg.short(qpp[0]), hg.short(qpp[1])))
1217 (hg.short(qpp[0]), hg.short(qpp[1])))
1218 if qupdate:
1218 if qupdate:
1219 print "queue directory updating"
1219 print "queue directory updating"
1220 r = self.qrepo()
1220 r = self.qrepo()
1221 if not r:
1221 if not r:
1222 self.ui.warn("Unable to load queue repository\n")
1222 self.ui.warn("Unable to load queue repository\n")
1223 return 1
1223 return 1
1224 hg.clean(r, qpp[0])
1224 hg.clean(r, qpp[0])
1225
1225
1226 def save(self, repo, msg=None):
1226 def save(self, repo, msg=None):
1227 if len(self.applied) == 0:
1227 if len(self.applied) == 0:
1228 self.ui.warn("save: no patches applied, exiting\n")
1228 self.ui.warn("save: no patches applied, exiting\n")
1229 return 1
1229 return 1
1230 if self.issaveline(self.applied[-1]):
1230 if self.issaveline(self.applied[-1]):
1231 self.ui.warn("status is already saved\n")
1231 self.ui.warn("status is already saved\n")
1232 return 1
1232 return 1
1233
1233
1234 ar = [ ':' + x for x in self.full_series ]
1234 ar = [ ':' + x for x in self.full_series ]
1235 if not msg:
1235 if not msg:
1236 msg = "hg patches saved state"
1236 msg = "hg patches saved state"
1237 else:
1237 else:
1238 msg = "hg patches: " + msg.rstrip('\r\n')
1238 msg = "hg patches: " + msg.rstrip('\r\n')
1239 r = self.qrepo()
1239 r = self.qrepo()
1240 if r:
1240 if r:
1241 pp = r.dirstate.parents()
1241 pp = r.dirstate.parents()
1242 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
1242 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
1243 msg += "\n\nPatch Data:\n"
1243 msg += "\n\nPatch Data:\n"
1244 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1244 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1245 "\n".join(ar) + '\n' or "")
1245 "\n".join(ar) + '\n' or "")
1246 n = repo.commit(None, text, user=None, force=1)
1246 n = repo.commit(None, text, user=None, force=1)
1247 if not n:
1247 if not n:
1248 self.ui.warn("repo commit failed\n")
1248 self.ui.warn("repo commit failed\n")
1249 return 1
1249 return 1
1250 self.applied.append(statusentry(revlog.hex(n),'.hg.patches.save.line'))
1250 self.applied.append(statusentry(revlog.hex(n),'.hg.patches.save.line'))
1251 self.applied_dirty = 1
1251 self.applied_dirty = 1
1252 self.removeundo(repo)
1252 self.removeundo(repo)
1253
1253
1254 def full_series_end(self):
1254 def full_series_end(self):
1255 if len(self.applied) > 0:
1255 if len(self.applied) > 0:
1256 p = self.applied[-1].name
1256 p = self.applied[-1].name
1257 end = self.find_series(p)
1257 end = self.find_series(p)
1258 if end == None:
1258 if end == None:
1259 return len(self.full_series)
1259 return len(self.full_series)
1260 return end + 1
1260 return end + 1
1261 return 0
1261 return 0
1262
1262
1263 def series_end(self, all_patches=False):
1263 def series_end(self, all_patches=False):
1264 """If all_patches is False, return the index of the next pushable patch
1264 """If all_patches is False, return the index of the next pushable patch
1265 in the series, or the series length. If all_patches is True, return the
1265 in the series, or the series length. If all_patches is True, return the
1266 index of the first patch past the last applied one.
1266 index of the first patch past the last applied one.
1267 """
1267 """
1268 end = 0
1268 end = 0
1269 def next(start):
1269 def next(start):
1270 if all_patches:
1270 if all_patches:
1271 return start
1271 return start
1272 i = start
1272 i = start
1273 while i < len(self.series):
1273 while i < len(self.series):
1274 p, reason = self.pushable(i)
1274 p, reason = self.pushable(i)
1275 if p:
1275 if p:
1276 break
1276 break
1277 self.explain_pushable(i)
1277 self.explain_pushable(i)
1278 i += 1
1278 i += 1
1279 return i
1279 return i
1280 if len(self.applied) > 0:
1280 if len(self.applied) > 0:
1281 p = self.applied[-1].name
1281 p = self.applied[-1].name
1282 try:
1282 try:
1283 end = self.series.index(p)
1283 end = self.series.index(p)
1284 except ValueError:
1284 except ValueError:
1285 return 0
1285 return 0
1286 return next(end + 1)
1286 return next(end + 1)
1287 return next(end)
1287 return next(end)
1288
1288
1289 def appliedname(self, index):
1289 def appliedname(self, index):
1290 pname = self.applied[index].name
1290 pname = self.applied[index].name
1291 if not self.ui.verbose:
1291 if not self.ui.verbose:
1292 p = pname
1292 p = pname
1293 else:
1293 else:
1294 p = str(self.series.index(pname)) + " " + pname
1294 p = str(self.series.index(pname)) + " " + pname
1295 return p
1295 return p
1296
1296
1297 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1297 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1298 force=None, git=False):
1298 force=None, git=False):
1299 def checkseries(patchname):
1299 def checkseries(patchname):
1300 if patchname in self.series:
1300 if patchname in self.series:
1301 raise util.Abort(_('patch %s is already in the series file')
1301 raise util.Abort(_('patch %s is already in the series file')
1302 % patchname)
1302 % patchname)
1303 def checkfile(patchname):
1303 def checkfile(patchname):
1304 if not force and os.path.exists(self.join(patchname)):
1304 if not force and os.path.exists(self.join(patchname)):
1305 raise util.Abort(_('patch "%s" already exists')
1305 raise util.Abort(_('patch "%s" already exists')
1306 % patchname)
1306 % patchname)
1307
1307
1308 if rev:
1308 if rev:
1309 if files:
1309 if files:
1310 raise util.Abort(_('option "-r" not valid when importing '
1310 raise util.Abort(_('option "-r" not valid when importing '
1311 'files'))
1311 'files'))
1312 rev = cmdutil.revrange(repo, rev)
1312 rev = cmdutil.revrange(repo, rev)
1313 rev.sort(lambda x, y: cmp(y, x))
1313 rev.sort(lambda x, y: cmp(y, x))
1314 if (len(files) > 1 or len(rev) > 1) and patchname:
1314 if (len(files) > 1 or len(rev) > 1) and patchname:
1315 raise util.Abort(_('option "-n" not valid when importing multiple '
1315 raise util.Abort(_('option "-n" not valid when importing multiple '
1316 'patches'))
1316 'patches'))
1317 i = 0
1317 i = 0
1318 added = []
1318 added = []
1319 if rev:
1319 if rev:
1320 # If mq patches are applied, we can only import revisions
1320 # If mq patches are applied, we can only import revisions
1321 # that form a linear path to qbase.
1321 # that form a linear path to qbase.
1322 # Otherwise, they should form a linear path to a head.
1322 # Otherwise, they should form a linear path to a head.
1323 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1323 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1324 if len(heads) > 1:
1324 if len(heads) > 1:
1325 raise util.Abort(_('revision %d is the root of more than one '
1325 raise util.Abort(_('revision %d is the root of more than one '
1326 'branch') % rev[-1])
1326 'branch') % rev[-1])
1327 if self.applied:
1327 if self.applied:
1328 base = revlog.hex(repo.changelog.node(rev[0]))
1328 base = revlog.hex(repo.changelog.node(rev[0]))
1329 if base in [n.rev for n in self.applied]:
1329 if base in [n.rev for n in self.applied]:
1330 raise util.Abort(_('revision %d is already managed')
1330 raise util.Abort(_('revision %d is already managed')
1331 % rev[0])
1331 % rev[0])
1332 if heads != [revlog.bin(self.applied[-1].rev)]:
1332 if heads != [revlog.bin(self.applied[-1].rev)]:
1333 raise util.Abort(_('revision %d is not the parent of '
1333 raise util.Abort(_('revision %d is not the parent of '
1334 'the queue') % rev[0])
1334 'the queue') % rev[0])
1335 base = repo.changelog.rev(revlog.bin(self.applied[0].rev))
1335 base = repo.changelog.rev(revlog.bin(self.applied[0].rev))
1336 lastparent = repo.changelog.parentrevs(base)[0]
1336 lastparent = repo.changelog.parentrevs(base)[0]
1337 else:
1337 else:
1338 if heads != [repo.changelog.node(rev[0])]:
1338 if heads != [repo.changelog.node(rev[0])]:
1339 raise util.Abort(_('revision %d has unmanaged children')
1339 raise util.Abort(_('revision %d has unmanaged children')
1340 % rev[0])
1340 % rev[0])
1341 lastparent = None
1341 lastparent = None
1342
1342
1343 if git:
1343 if git:
1344 self.diffopts().git = True
1344 self.diffopts().git = True
1345
1345
1346 for r in rev:
1346 for r in rev:
1347 p1, p2 = repo.changelog.parentrevs(r)
1347 p1, p2 = repo.changelog.parentrevs(r)
1348 n = repo.changelog.node(r)
1348 n = repo.changelog.node(r)
1349 if p2 != revlog.nullrev:
1349 if p2 != revlog.nullrev:
1350 raise util.Abort(_('cannot import merge revision %d') % r)
1350 raise util.Abort(_('cannot import merge revision %d') % r)
1351 if lastparent and lastparent != r:
1351 if lastparent and lastparent != r:
1352 raise util.Abort(_('revision %d is not the parent of %d')
1352 raise util.Abort(_('revision %d is not the parent of %d')
1353 % (r, lastparent))
1353 % (r, lastparent))
1354 lastparent = p1
1354 lastparent = p1
1355
1355
1356 if not patchname:
1356 if not patchname:
1357 patchname = normname('%d.diff' % r)
1357 patchname = normname('%d.diff' % r)
1358 checkseries(patchname)
1358 checkseries(patchname)
1359 checkfile(patchname)
1359 checkfile(patchname)
1360 self.full_series.insert(0, patchname)
1360 self.full_series.insert(0, patchname)
1361
1361
1362 patchf = self.opener(patchname, "w")
1362 patchf = self.opener(patchname, "w")
1363 patch.export(repo, [n], fp=patchf, opts=self.diffopts())
1363 patch.export(repo, [n], fp=patchf, opts=self.diffopts())
1364 patchf.close()
1364 patchf.close()
1365
1365
1366 se = statusentry(revlog.hex(n), patchname)
1366 se = statusentry(revlog.hex(n), patchname)
1367 self.applied.insert(0, se)
1367 self.applied.insert(0, se)
1368
1368
1369 added.append(patchname)
1369 added.append(patchname)
1370 patchname = None
1370 patchname = None
1371 self.parse_series()
1371 self.parse_series()
1372 self.applied_dirty = 1
1372 self.applied_dirty = 1
1373
1373
1374 for filename in files:
1374 for filename in files:
1375 if existing:
1375 if existing:
1376 if filename == '-':
1376 if filename == '-':
1377 raise util.Abort(_('-e is incompatible with import from -'))
1377 raise util.Abort(_('-e is incompatible with import from -'))
1378 if not patchname:
1378 if not patchname:
1379 patchname = normname(filename)
1379 patchname = normname(filename)
1380 if not os.path.isfile(self.join(patchname)):
1380 if not os.path.isfile(self.join(patchname)):
1381 raise util.Abort(_("patch %s does not exist") % patchname)
1381 raise util.Abort(_("patch %s does not exist") % patchname)
1382 else:
1382 else:
1383 try:
1383 try:
1384 if filename == '-':
1384 if filename == '-':
1385 if not patchname:
1385 if not patchname:
1386 raise util.Abort(_('need --name to import a patch from -'))
1386 raise util.Abort(_('need --name to import a patch from -'))
1387 text = sys.stdin.read()
1387 text = sys.stdin.read()
1388 else:
1388 else:
1389 text = file(filename).read()
1389 text = file(filename).read()
1390 except IOError:
1390 except IOError:
1391 raise util.Abort(_("unable to read %s") % patchname)
1391 raise util.Abort(_("unable to read %s") % patchname)
1392 if not patchname:
1392 if not patchname:
1393 patchname = normname(os.path.basename(filename))
1393 patchname = normname(os.path.basename(filename))
1394 checkfile(patchname)
1394 checkfile(patchname)
1395 patchf = self.opener(patchname, "w")
1395 patchf = self.opener(patchname, "w")
1396 patchf.write(text)
1396 patchf.write(text)
1397 checkseries(patchname)
1397 checkseries(patchname)
1398 index = self.full_series_end() + i
1398 index = self.full_series_end() + i
1399 self.full_series[index:index] = [patchname]
1399 self.full_series[index:index] = [patchname]
1400 self.parse_series()
1400 self.parse_series()
1401 self.ui.warn("adding %s to series file\n" % patchname)
1401 self.ui.warn("adding %s to series file\n" % patchname)
1402 i += 1
1402 i += 1
1403 added.append(patchname)
1403 added.append(patchname)
1404 patchname = None
1404 patchname = None
1405 self.series_dirty = 1
1405 self.series_dirty = 1
1406 qrepo = self.qrepo()
1406 qrepo = self.qrepo()
1407 if qrepo:
1407 if qrepo:
1408 qrepo.add(added)
1408 qrepo.add(added)
1409
1409
1410 def delete(ui, repo, *patches, **opts):
1410 def delete(ui, repo, *patches, **opts):
1411 """remove patches from queue
1411 """remove patches from queue
1412
1412
1413 The patches must not be applied, unless they are arguments to
1413 The patches must not be applied, unless they are arguments to
1414 the --rev parameter. At least one patch or revision is required.
1414 the --rev parameter. At least one patch or revision is required.
1415
1415
1416 With --rev, mq will stop managing the named revisions (converting
1416 With --rev, mq will stop managing the named revisions (converting
1417 them to regular mercurial changesets). The patches must be applied
1417 them to regular mercurial changesets). The patches must be applied
1418 and at the base of the stack. This option is useful when the patches
1418 and at the base of the stack. This option is useful when the patches
1419 have been applied upstream.
1419 have been applied upstream.
1420
1420
1421 With --keep, the patch files are preserved in the patch directory."""
1421 With --keep, the patch files are preserved in the patch directory."""
1422 q = repo.mq
1422 q = repo.mq
1423 q.delete(repo, patches, opts)
1423 q.delete(repo, patches, opts)
1424 q.save_dirty()
1424 q.save_dirty()
1425 return 0
1425 return 0
1426
1426
1427 def applied(ui, repo, patch=None, **opts):
1427 def applied(ui, repo, patch=None, **opts):
1428 """print the patches already applied"""
1428 """print the patches already applied"""
1429 q = repo.mq
1429 q = repo.mq
1430 if patch:
1430 if patch:
1431 if patch not in q.series:
1431 if patch not in q.series:
1432 raise util.Abort(_("patch %s is not in series file") % patch)
1432 raise util.Abort(_("patch %s is not in series file") % patch)
1433 end = q.series.index(patch) + 1
1433 end = q.series.index(patch) + 1
1434 else:
1434 else:
1435 end = q.series_end(True)
1435 end = q.series_end(True)
1436 return q.qseries(repo, length=end, status='A', summary=opts.get('summary'))
1436 return q.qseries(repo, length=end, status='A', summary=opts.get('summary'))
1437
1437
1438 def unapplied(ui, repo, patch=None, **opts):
1438 def unapplied(ui, repo, patch=None, **opts):
1439 """print the patches not yet applied"""
1439 """print the patches not yet applied"""
1440 q = repo.mq
1440 q = repo.mq
1441 if patch:
1441 if patch:
1442 if patch not in q.series:
1442 if patch not in q.series:
1443 raise util.Abort(_("patch %s is not in series file") % patch)
1443 raise util.Abort(_("patch %s is not in series file") % patch)
1444 start = q.series.index(patch) + 1
1444 start = q.series.index(patch) + 1
1445 else:
1445 else:
1446 start = q.series_end(True)
1446 start = q.series_end(True)
1447 q.qseries(repo, start=start, status='U', summary=opts.get('summary'))
1447 q.qseries(repo, start=start, status='U', summary=opts.get('summary'))
1448
1448
1449 def qimport(ui, repo, *filename, **opts):
1449 def qimport(ui, repo, *filename, **opts):
1450 """import a patch
1450 """import a patch
1451
1451
1452 The patch will have the same name as its source file unless you
1452 The patch will have the same name as its source file unless you
1453 give it a new one with --name.
1453 give it a new one with --name.
1454
1454
1455 You can register an existing patch inside the patch directory
1455 You can register an existing patch inside the patch directory
1456 with the --existing flag.
1456 with the --existing flag.
1457
1457
1458 With --force, an existing patch of the same name will be overwritten.
1458 With --force, an existing patch of the same name will be overwritten.
1459
1459
1460 An existing changeset may be placed under mq control with --rev
1460 An existing changeset may be placed under mq control with --rev
1461 (e.g. qimport --rev tip -n patch will place tip under mq control).
1461 (e.g. qimport --rev tip -n patch will place tip under mq control).
1462 With --git, patches imported with --rev will use the git diff
1462 With --git, patches imported with --rev will use the git diff
1463 format.
1463 format.
1464 """
1464 """
1465 q = repo.mq
1465 q = repo.mq
1466 q.qimport(repo, filename, patchname=opts['name'],
1466 q.qimport(repo, filename, patchname=opts['name'],
1467 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1467 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1468 git=opts['git'])
1468 git=opts['git'])
1469 q.save_dirty()
1469 q.save_dirty()
1470 return 0
1470 return 0
1471
1471
1472 def init(ui, repo, **opts):
1472 def init(ui, repo, **opts):
1473 """init a new queue repository
1473 """init a new queue repository
1474
1474
1475 The queue repository is unversioned by default. If -c is
1475 The queue repository is unversioned by default. If -c is
1476 specified, qinit will create a separate nested repository
1476 specified, qinit will create a separate nested repository
1477 for patches (qinit -c may also be run later to convert
1477 for patches (qinit -c may also be run later to convert
1478 an unversioned patch repository into a versioned one).
1478 an unversioned patch repository into a versioned one).
1479 You can use qcommit to commit changes to this queue repository."""
1479 You can use qcommit to commit changes to this queue repository."""
1480 q = repo.mq
1480 q = repo.mq
1481 r = q.init(repo, create=opts['create_repo'])
1481 r = q.init(repo, create=opts['create_repo'])
1482 q.save_dirty()
1482 q.save_dirty()
1483 if r:
1483 if r:
1484 if not os.path.exists(r.wjoin('.hgignore')):
1484 if not os.path.exists(r.wjoin('.hgignore')):
1485 fp = r.wopener('.hgignore', 'w')
1485 fp = r.wopener('.hgignore', 'w')
1486 fp.write('syntax: glob\n')
1486 fp.write('syntax: glob\n')
1487 fp.write('status\n')
1487 fp.write('status\n')
1488 fp.write('guards\n')
1488 fp.write('guards\n')
1489 fp.close()
1489 fp.close()
1490 if not os.path.exists(r.wjoin('series')):
1490 if not os.path.exists(r.wjoin('series')):
1491 r.wopener('series', 'w').close()
1491 r.wopener('series', 'w').close()
1492 r.add(['.hgignore', 'series'])
1492 r.add(['.hgignore', 'series'])
1493 commands.add(ui, r)
1493 commands.add(ui, r)
1494 return 0
1494 return 0
1495
1495
1496 def clone(ui, source, dest=None, **opts):
1496 def clone(ui, source, dest=None, **opts):
1497 '''clone main and patch repository at same time
1497 '''clone main and patch repository at same time
1498
1498
1499 If source is local, destination will have no patches applied. If
1499 If source is local, destination will have no patches applied. If
1500 source is remote, this command can not check if patches are
1500 source is remote, this command can not check if patches are
1501 applied in source, so cannot guarantee that patches are not
1501 applied in source, so cannot guarantee that patches are not
1502 applied in destination. If you clone remote repository, be sure
1502 applied in destination. If you clone remote repository, be sure
1503 before that it has no patches applied.
1503 before that it has no patches applied.
1504
1504
1505 Source patch repository is looked for in <src>/.hg/patches by
1505 Source patch repository is looked for in <src>/.hg/patches by
1506 default. Use -p <url> to change.
1506 default. Use -p <url> to change.
1507
1507
1508 The patch directory must be a nested mercurial repository, as
1508 The patch directory must be a nested mercurial repository, as
1509 would be created by qinit -c.
1509 would be created by qinit -c.
1510 '''
1510 '''
1511 cmdutil.setremoteconfig(ui, opts)
1511 cmdutil.setremoteconfig(ui, opts)
1512 if dest is None:
1512 if dest is None:
1513 dest = hg.defaultdest(source)
1513 dest = hg.defaultdest(source)
1514 sr = hg.repository(ui, ui.expandpath(source))
1514 sr = hg.repository(ui, ui.expandpath(source))
1515 patchdir = opts['patches'] or (sr.url() + '/.hg/patches')
1515 patchdir = opts['patches'] or (sr.url() + '/.hg/patches')
1516 try:
1516 try:
1517 pr = hg.repository(ui, patchdir)
1517 pr = hg.repository(ui, patchdir)
1518 except hg.RepoError:
1518 except hg.RepoError:
1519 raise util.Abort(_('versioned patch repository not found'
1519 raise util.Abort(_('versioned patch repository not found'
1520 ' (see qinit -c)'))
1520 ' (see qinit -c)'))
1521 qbase, destrev = None, None
1521 qbase, destrev = None, None
1522 if sr.local():
1522 if sr.local():
1523 if sr.mq.applied:
1523 if sr.mq.applied:
1524 qbase = revlog.bin(sr.mq.applied[0].rev)
1524 qbase = revlog.bin(sr.mq.applied[0].rev)
1525 if not hg.islocal(dest):
1525 if not hg.islocal(dest):
1526 heads = dict.fromkeys(sr.heads())
1526 heads = dict.fromkeys(sr.heads())
1527 for h in sr.heads(qbase):
1527 for h in sr.heads(qbase):
1528 del heads[h]
1528 del heads[h]
1529 destrev = heads.keys()
1529 destrev = heads.keys()
1530 destrev.append(sr.changelog.parents(qbase)[0])
1530 destrev.append(sr.changelog.parents(qbase)[0])
1531 ui.note(_('cloning main repo\n'))
1531 ui.note(_('cloning main repo\n'))
1532 sr, dr = hg.clone(ui, sr.url(), dest,
1532 sr, dr = hg.clone(ui, sr.url(), dest,
1533 pull=opts['pull'],
1533 pull=opts['pull'],
1534 rev=destrev,
1534 rev=destrev,
1535 update=False,
1535 update=False,
1536 stream=opts['uncompressed'])
1536 stream=opts['uncompressed'])
1537 ui.note(_('cloning patch repo\n'))
1537 ui.note(_('cloning patch repo\n'))
1538 spr, dpr = hg.clone(ui, opts['patches'] or (sr.url() + '/.hg/patches'),
1538 spr, dpr = hg.clone(ui, opts['patches'] or (sr.url() + '/.hg/patches'),
1539 dr.url() + '/.hg/patches',
1539 dr.url() + '/.hg/patches',
1540 pull=opts['pull'],
1540 pull=opts['pull'],
1541 update=not opts['noupdate'],
1541 update=not opts['noupdate'],
1542 stream=opts['uncompressed'])
1542 stream=opts['uncompressed'])
1543 if dr.local():
1543 if dr.local():
1544 if qbase:
1544 if qbase:
1545 ui.note(_('stripping applied patches from destination repo\n'))
1545 ui.note(_('stripping applied patches from destination repo\n'))
1546 dr.mq.strip(dr, qbase, update=False, backup=None)
1546 dr.mq.strip(dr, qbase, update=False, backup=None)
1547 if not opts['noupdate']:
1547 if not opts['noupdate']:
1548 ui.note(_('updating destination repo\n'))
1548 ui.note(_('updating destination repo\n'))
1549 hg.update(dr, dr.changelog.tip())
1549 hg.update(dr, dr.changelog.tip())
1550
1550
1551 def commit(ui, repo, *pats, **opts):
1551 def commit(ui, repo, *pats, **opts):
1552 """commit changes in the queue repository"""
1552 """commit changes in the queue repository"""
1553 q = repo.mq
1553 q = repo.mq
1554 r = q.qrepo()
1554 r = q.qrepo()
1555 if not r: raise util.Abort('no queue repository')
1555 if not r: raise util.Abort('no queue repository')
1556 commands.commit(r.ui, r, *pats, **opts)
1556 commands.commit(r.ui, r, *pats, **opts)
1557
1557
1558 def series(ui, repo, **opts):
1558 def series(ui, repo, **opts):
1559 """print the entire series file"""
1559 """print the entire series file"""
1560 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1560 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1561 return 0
1561 return 0
1562
1562
1563 def top(ui, repo, **opts):
1563 def top(ui, repo, **opts):
1564 """print the name of the current patch"""
1564 """print the name of the current patch"""
1565 q = repo.mq
1565 q = repo.mq
1566 t = q.applied and q.series_end(True) or 0
1566 t = q.applied and q.series_end(True) or 0
1567 if t:
1567 if t:
1568 return q.qseries(repo, start=t-1, length=1, status='A',
1568 return q.qseries(repo, start=t-1, length=1, status='A',
1569 summary=opts.get('summary'))
1569 summary=opts.get('summary'))
1570 else:
1570 else:
1571 ui.write("No patches applied\n")
1571 ui.write("No patches applied\n")
1572 return 1
1572 return 1
1573
1573
1574 def next(ui, repo, **opts):
1574 def next(ui, repo, **opts):
1575 """print the name of the next patch"""
1575 """print the name of the next patch"""
1576 q = repo.mq
1576 q = repo.mq
1577 end = q.series_end()
1577 end = q.series_end()
1578 if end == len(q.series):
1578 if end == len(q.series):
1579 ui.write("All patches applied\n")
1579 ui.write("All patches applied\n")
1580 return 1
1580 return 1
1581 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1581 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1582
1582
1583 def prev(ui, repo, **opts):
1583 def prev(ui, repo, **opts):
1584 """print the name of the previous patch"""
1584 """print the name of the previous patch"""
1585 q = repo.mq
1585 q = repo.mq
1586 l = len(q.applied)
1586 l = len(q.applied)
1587 if l == 1:
1587 if l == 1:
1588 ui.write("Only one patch applied\n")
1588 ui.write("Only one patch applied\n")
1589 return 1
1589 return 1
1590 if not l:
1590 if not l:
1591 ui.write("No patches applied\n")
1591 ui.write("No patches applied\n")
1592 return 1
1592 return 1
1593 return q.qseries(repo, start=l-2, length=1, status='A',
1593 return q.qseries(repo, start=l-2, length=1, status='A',
1594 summary=opts.get('summary'))
1594 summary=opts.get('summary'))
1595
1595
1596 def new(ui, repo, patch, *args, **opts):
1596 def new(ui, repo, patch, *args, **opts):
1597 """create a new patch
1597 """create a new patch
1598
1598
1599 qnew creates a new patch on top of the currently-applied patch
1599 qnew creates a new patch on top of the currently-applied patch
1600 (if any). It will refuse to run if there are any outstanding
1600 (if any). It will refuse to run if there are any outstanding
1601 changes unless -f is specified, in which case the patch will
1601 changes unless -f is specified, in which case the patch will
1602 be initialised with them. You may also use -I, -X, and/or a list of
1602 be initialised with them. You may also use -I, -X, and/or a list of
1603 files after the patch name to add only changes to matching files
1603 files after the patch name to add only changes to matching files
1604 to the new patch, leaving the rest as uncommitted modifications.
1604 to the new patch, leaving the rest as uncommitted modifications.
1605
1605
1606 -e, -m or -l set the patch header as well as the commit message.
1606 -e, -m or -l set the patch header as well as the commit message.
1607 If none is specified, the patch header is empty and the
1607 If none is specified, the patch header is empty and the
1608 commit message is '[mq]: PATCH'"""
1608 commit message is '[mq]: PATCH'"""
1609 q = repo.mq
1609 q = repo.mq
1610 message = cmdutil.logmessage(opts)
1610 message = cmdutil.logmessage(opts)
1611 if opts['edit']:
1611 if opts['edit']:
1612 message = ui.edit(message, ui.username())
1612 message = ui.edit(message, ui.username())
1613 opts['msg'] = message
1613 opts['msg'] = message
1614 q.new(repo, patch, *args, **opts)
1614 q.new(repo, patch, *args, **opts)
1615 q.save_dirty()
1615 q.save_dirty()
1616 return 0
1616 return 0
1617
1617
1618 def refresh(ui, repo, *pats, **opts):
1618 def refresh(ui, repo, *pats, **opts):
1619 """update the current patch
1619 """update the current patch
1620
1620
1621 If any file patterns are provided, the refreshed patch will contain only
1621 If any file patterns are provided, the refreshed patch will contain only
1622 the modifications that match those patterns; the remaining modifications
1622 the modifications that match those patterns; the remaining modifications
1623 will remain in the working directory.
1623 will remain in the working directory.
1624
1624
1625 hg add/remove/copy/rename work as usual, though you might want to use
1625 hg add/remove/copy/rename work as usual, though you might want to use
1626 git-style patches (--git or [diff] git=1) to track copies and renames.
1626 git-style patches (--git or [diff] git=1) to track copies and renames.
1627 """
1627 """
1628 q = repo.mq
1628 q = repo.mq
1629 message = cmdutil.logmessage(opts)
1629 message = cmdutil.logmessage(opts)
1630 if opts['edit']:
1630 if opts['edit']:
1631 if message:
1631 if message:
1632 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1632 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1633 patch = q.applied[-1].name
1633 patch = q.applied[-1].name
1634 (message, comment, user, date, hasdiff) = q.readheaders(patch)
1634 (message, comment, user, date, hasdiff) = q.readheaders(patch)
1635 message = ui.edit('\n'.join(message), user or ui.username())
1635 message = ui.edit('\n'.join(message), user or ui.username())
1636 ret = q.refresh(repo, pats, msg=message, **opts)
1636 ret = q.refresh(repo, pats, msg=message, **opts)
1637 q.save_dirty()
1637 q.save_dirty()
1638 return ret
1638 return ret
1639
1639
1640 def diff(ui, repo, *pats, **opts):
1640 def diff(ui, repo, *pats, **opts):
1641 """diff of the current patch"""
1641 """diff of the current patch"""
1642 repo.mq.diff(repo, pats, opts)
1642 repo.mq.diff(repo, pats, opts)
1643 return 0
1643 return 0
1644
1644
1645 def fold(ui, repo, *files, **opts):
1645 def fold(ui, repo, *files, **opts):
1646 """fold the named patches into the current patch
1646 """fold the named patches into the current patch
1647
1647
1648 Patches must not yet be applied. Each patch will be successively
1648 Patches must not yet be applied. Each patch will be successively
1649 applied to the current patch in the order given. If all the
1649 applied to the current patch in the order given. If all the
1650 patches apply successfully, the current patch will be refreshed
1650 patches apply successfully, the current patch will be refreshed
1651 with the new cumulative patch, and the folded patches will
1651 with the new cumulative patch, and the folded patches will
1652 be deleted. With -k/--keep, the folded patch files will not
1652 be deleted. With -k/--keep, the folded patch files will not
1653 be removed afterwards.
1653 be removed afterwards.
1654
1654
1655 The header for each folded patch will be concatenated with
1655 The header for each folded patch will be concatenated with
1656 the current patch header, separated by a line of '* * *'."""
1656 the current patch header, separated by a line of '* * *'."""
1657
1657
1658 q = repo.mq
1658 q = repo.mq
1659
1659
1660 if not files:
1660 if not files:
1661 raise util.Abort(_('qfold requires at least one patch name'))
1661 raise util.Abort(_('qfold requires at least one patch name'))
1662 if not q.check_toppatch(repo):
1662 if not q.check_toppatch(repo):
1663 raise util.Abort(_('No patches applied'))
1663 raise util.Abort(_('No patches applied'))
1664
1664
1665 message = cmdutil.logmessage(opts)
1665 message = cmdutil.logmessage(opts)
1666 if opts['edit']:
1666 if opts['edit']:
1667 if message:
1667 if message:
1668 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1668 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1669
1669
1670 parent = q.lookup('qtip')
1670 parent = q.lookup('qtip')
1671 patches = []
1671 patches = []
1672 messages = []
1672 messages = []
1673 for f in files:
1673 for f in files:
1674 p = q.lookup(f)
1674 p = q.lookup(f)
1675 if p in patches or p == parent:
1675 if p in patches or p == parent:
1676 ui.warn(_('Skipping already folded patch %s') % p)
1676 ui.warn(_('Skipping already folded patch %s') % p)
1677 if q.isapplied(p):
1677 if q.isapplied(p):
1678 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
1678 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
1679 patches.append(p)
1679 patches.append(p)
1680
1680
1681 for p in patches:
1681 for p in patches:
1682 if not message:
1682 if not message:
1683 messages.append(q.readheaders(p)[0])
1683 messages.append(q.readheaders(p)[0])
1684 pf = q.join(p)
1684 pf = q.join(p)
1685 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1685 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1686 if not patchsuccess:
1686 if not patchsuccess:
1687 raise util.Abort(_('Error folding patch %s') % p)
1687 raise util.Abort(_('Error folding patch %s') % p)
1688 patch.updatedir(ui, repo, files)
1688 patch.updatedir(ui, repo, files)
1689
1689
1690 if not message:
1690 if not message:
1691 message, comments, user = q.readheaders(parent)[0:3]
1691 message, comments, user = q.readheaders(parent)[0:3]
1692 for msg in messages:
1692 for msg in messages:
1693 message.append('* * *')
1693 message.append('* * *')
1694 message.extend(msg)
1694 message.extend(msg)
1695 message = '\n'.join(message)
1695 message = '\n'.join(message)
1696
1696
1697 if opts['edit']:
1697 if opts['edit']:
1698 message = ui.edit(message, user or ui.username())
1698 message = ui.edit(message, user or ui.username())
1699
1699
1700 q.refresh(repo, msg=message)
1700 q.refresh(repo, msg=message)
1701 q.delete(repo, patches, opts)
1701 q.delete(repo, patches, opts)
1702 q.save_dirty()
1702 q.save_dirty()
1703
1703
1704 def goto(ui, repo, patch, **opts):
1704 def goto(ui, repo, patch, **opts):
1705 '''push or pop patches until named patch is at top of stack'''
1705 '''push or pop patches until named patch is at top of stack'''
1706 q = repo.mq
1706 q = repo.mq
1707 patch = q.lookup(patch)
1707 patch = q.lookup(patch)
1708 if q.isapplied(patch):
1708 if q.isapplied(patch):
1709 ret = q.pop(repo, patch, force=opts['force'])
1709 ret = q.pop(repo, patch, force=opts['force'])
1710 else:
1710 else:
1711 ret = q.push(repo, patch, force=opts['force'])
1711 ret = q.push(repo, patch, force=opts['force'])
1712 q.save_dirty()
1712 q.save_dirty()
1713 return ret
1713 return ret
1714
1714
1715 def guard(ui, repo, *args, **opts):
1715 def guard(ui, repo, *args, **opts):
1716 '''set or print guards for a patch
1716 '''set or print guards for a patch
1717
1717
1718 Guards control whether a patch can be pushed. A patch with no
1718 Guards control whether a patch can be pushed. A patch with no
1719 guards is always pushed. A patch with a positive guard ("+foo") is
1719 guards is always pushed. A patch with a positive guard ("+foo") is
1720 pushed only if the qselect command has activated it. A patch with
1720 pushed only if the qselect command has activated it. A patch with
1721 a negative guard ("-foo") is never pushed if the qselect command
1721 a negative guard ("-foo") is never pushed if the qselect command
1722 has activated it.
1722 has activated it.
1723
1723
1724 With no arguments, print the currently active guards.
1724 With no arguments, print the currently active guards.
1725 With arguments, set guards for the named patch.
1725 With arguments, set guards for the named patch.
1726
1726
1727 To set a negative guard "-foo" on topmost patch ("--" is needed so
1727 To set a negative guard "-foo" on topmost patch ("--" is needed so
1728 hg will not interpret "-foo" as an option):
1728 hg will not interpret "-foo" as an option):
1729 hg qguard -- -foo
1729 hg qguard -- -foo
1730
1730
1731 To set guards on another patch:
1731 To set guards on another patch:
1732 hg qguard other.patch +2.6.17 -stable
1732 hg qguard other.patch +2.6.17 -stable
1733 '''
1733 '''
1734 def status(idx):
1734 def status(idx):
1735 guards = q.series_guards[idx] or ['unguarded']
1735 guards = q.series_guards[idx] or ['unguarded']
1736 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
1736 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
1737 q = repo.mq
1737 q = repo.mq
1738 patch = None
1738 patch = None
1739 args = list(args)
1739 args = list(args)
1740 if opts['list']:
1740 if opts['list']:
1741 if args or opts['none']:
1741 if args or opts['none']:
1742 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
1742 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
1743 for i in xrange(len(q.series)):
1743 for i in xrange(len(q.series)):
1744 status(i)
1744 status(i)
1745 return
1745 return
1746 if not args or args[0][0:1] in '-+':
1746 if not args or args[0][0:1] in '-+':
1747 if not q.applied:
1747 if not q.applied:
1748 raise util.Abort(_('no patches applied'))
1748 raise util.Abort(_('no patches applied'))
1749 patch = q.applied[-1].name
1749 patch = q.applied[-1].name
1750 if patch is None and args[0][0:1] not in '-+':
1750 if patch is None and args[0][0:1] not in '-+':
1751 patch = args.pop(0)
1751 patch = args.pop(0)
1752 if patch is None:
1752 if patch is None:
1753 raise util.Abort(_('no patch to work with'))
1753 raise util.Abort(_('no patch to work with'))
1754 if args or opts['none']:
1754 if args or opts['none']:
1755 idx = q.find_series(patch)
1755 idx = q.find_series(patch)
1756 if idx is None:
1756 if idx is None:
1757 raise util.Abort(_('no patch named %s') % patch)
1757 raise util.Abort(_('no patch named %s') % patch)
1758 q.set_guards(idx, args)
1758 q.set_guards(idx, args)
1759 q.save_dirty()
1759 q.save_dirty()
1760 else:
1760 else:
1761 status(q.series.index(q.lookup(patch)))
1761 status(q.series.index(q.lookup(patch)))
1762
1762
1763 def header(ui, repo, patch=None):
1763 def header(ui, repo, patch=None):
1764 """Print the header of the topmost or specified patch"""
1764 """Print the header of the topmost or specified patch"""
1765 q = repo.mq
1765 q = repo.mq
1766
1766
1767 if patch:
1767 if patch:
1768 patch = q.lookup(patch)
1768 patch = q.lookup(patch)
1769 else:
1769 else:
1770 if not q.applied:
1770 if not q.applied:
1771 ui.write('No patches applied\n')
1771 ui.write('No patches applied\n')
1772 return 1
1772 return 1
1773 patch = q.lookup('qtip')
1773 patch = q.lookup('qtip')
1774 message = repo.mq.readheaders(patch)[0]
1774 message = repo.mq.readheaders(patch)[0]
1775
1775
1776 ui.write('\n'.join(message) + '\n')
1776 ui.write('\n'.join(message) + '\n')
1777
1777
1778 def lastsavename(path):
1778 def lastsavename(path):
1779 (directory, base) = os.path.split(path)
1779 (directory, base) = os.path.split(path)
1780 names = os.listdir(directory)
1780 names = os.listdir(directory)
1781 namere = re.compile("%s.([0-9]+)" % base)
1781 namere = re.compile("%s.([0-9]+)" % base)
1782 maxindex = None
1782 maxindex = None
1783 maxname = None
1783 maxname = None
1784 for f in names:
1784 for f in names:
1785 m = namere.match(f)
1785 m = namere.match(f)
1786 if m:
1786 if m:
1787 index = int(m.group(1))
1787 index = int(m.group(1))
1788 if maxindex == None or index > maxindex:
1788 if maxindex == None or index > maxindex:
1789 maxindex = index
1789 maxindex = index
1790 maxname = f
1790 maxname = f
1791 if maxname:
1791 if maxname:
1792 return (os.path.join(directory, maxname), maxindex)
1792 return (os.path.join(directory, maxname), maxindex)
1793 return (None, None)
1793 return (None, None)
1794
1794
1795 def savename(path):
1795 def savename(path):
1796 (last, index) = lastsavename(path)
1796 (last, index) = lastsavename(path)
1797 if last is None:
1797 if last is None:
1798 index = 0
1798 index = 0
1799 newpath = path + ".%d" % (index + 1)
1799 newpath = path + ".%d" % (index + 1)
1800 return newpath
1800 return newpath
1801
1801
1802 def push(ui, repo, patch=None, **opts):
1802 def push(ui, repo, patch=None, **opts):
1803 """push the next patch onto the stack"""
1803 """push the next patch onto the stack"""
1804 q = repo.mq
1804 q = repo.mq
1805 mergeq = None
1805 mergeq = None
1806
1806
1807 if opts['all']:
1807 if opts['all']:
1808 if not q.series:
1808 if not q.series:
1809 ui.warn(_('no patches in series\n'))
1809 ui.warn(_('no patches in series\n'))
1810 return 0
1810 return 0
1811 patch = q.series[-1]
1811 patch = q.series[-1]
1812 if opts['merge']:
1812 if opts['merge']:
1813 if opts['name']:
1813 if opts['name']:
1814 newpath = opts['name']
1814 newpath = opts['name']
1815 else:
1815 else:
1816 newpath, i = lastsavename(q.path)
1816 newpath, i = lastsavename(q.path)
1817 if not newpath:
1817 if not newpath:
1818 ui.warn("no saved queues found, please use -n\n")
1818 ui.warn("no saved queues found, please use -n\n")
1819 return 1
1819 return 1
1820 mergeq = queue(ui, repo.join(""), newpath)
1820 mergeq = queue(ui, repo.join(""), newpath)
1821 ui.warn("merging with queue at: %s\n" % mergeq.path)
1821 ui.warn("merging with queue at: %s\n" % mergeq.path)
1822 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1822 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1823 mergeq=mergeq)
1823 mergeq=mergeq)
1824 return ret
1824 return ret
1825
1825
1826 def pop(ui, repo, patch=None, **opts):
1826 def pop(ui, repo, patch=None, **opts):
1827 """pop the current patch off the stack"""
1827 """pop the current patch off the stack"""
1828 localupdate = True
1828 localupdate = True
1829 if opts['name']:
1829 if opts['name']:
1830 q = queue(ui, repo.join(""), repo.join(opts['name']))
1830 q = queue(ui, repo.join(""), repo.join(opts['name']))
1831 ui.warn('using patch queue: %s\n' % q.path)
1831 ui.warn('using patch queue: %s\n' % q.path)
1832 localupdate = False
1832 localupdate = False
1833 else:
1833 else:
1834 q = repo.mq
1834 q = repo.mq
1835 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
1835 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
1836 all=opts['all'])
1836 all=opts['all'])
1837 q.save_dirty()
1837 q.save_dirty()
1838 return ret
1838 return ret
1839
1839
1840 def rename(ui, repo, patch, name=None, **opts):
1840 def rename(ui, repo, patch, name=None, **opts):
1841 """rename a patch
1841 """rename a patch
1842
1842
1843 With one argument, renames the current patch to PATCH1.
1843 With one argument, renames the current patch to PATCH1.
1844 With two arguments, renames PATCH1 to PATCH2."""
1844 With two arguments, renames PATCH1 to PATCH2."""
1845
1845
1846 q = repo.mq
1846 q = repo.mq
1847
1847
1848 if not name:
1848 if not name:
1849 name = patch
1849 name = patch
1850 patch = None
1850 patch = None
1851
1851
1852 if patch:
1852 if patch:
1853 patch = q.lookup(patch)
1853 patch = q.lookup(patch)
1854 else:
1854 else:
1855 if not q.applied:
1855 if not q.applied:
1856 ui.write(_('No patches applied\n'))
1856 ui.write(_('No patches applied\n'))
1857 return
1857 return
1858 patch = q.lookup('qtip')
1858 patch = q.lookup('qtip')
1859 absdest = q.join(name)
1859 absdest = q.join(name)
1860 if os.path.isdir(absdest):
1860 if os.path.isdir(absdest):
1861 name = normname(os.path.join(name, os.path.basename(patch)))
1861 name = normname(os.path.join(name, os.path.basename(patch)))
1862 absdest = q.join(name)
1862 absdest = q.join(name)
1863 if os.path.exists(absdest):
1863 if os.path.exists(absdest):
1864 raise util.Abort(_('%s already exists') % absdest)
1864 raise util.Abort(_('%s already exists') % absdest)
1865
1865
1866 if name in q.series:
1866 if name in q.series:
1867 raise util.Abort(_('A patch named %s already exists in the series file') % name)
1867 raise util.Abort(_('A patch named %s already exists in the series file') % name)
1868
1868
1869 if ui.verbose:
1869 if ui.verbose:
1870 ui.write('Renaming %s to %s\n' % (patch, name))
1870 ui.write('Renaming %s to %s\n' % (patch, name))
1871 i = q.find_series(patch)
1871 i = q.find_series(patch)
1872 guards = q.guard_re.findall(q.full_series[i])
1872 guards = q.guard_re.findall(q.full_series[i])
1873 q.full_series[i] = name + ''.join([' #' + g for g in guards])
1873 q.full_series[i] = name + ''.join([' #' + g for g in guards])
1874 q.parse_series()
1874 q.parse_series()
1875 q.series_dirty = 1
1875 q.series_dirty = 1
1876
1876
1877 info = q.isapplied(patch)
1877 info = q.isapplied(patch)
1878 if info:
1878 if info:
1879 q.applied[info[0]] = statusentry(info[1], name)
1879 q.applied[info[0]] = statusentry(info[1], name)
1880 q.applied_dirty = 1
1880 q.applied_dirty = 1
1881
1881
1882 util.rename(q.join(patch), absdest)
1882 util.rename(q.join(patch), absdest)
1883 r = q.qrepo()
1883 r = q.qrepo()
1884 if r:
1884 if r:
1885 wlock = r.wlock()
1885 wlock = r.wlock()
1886 try:
1886 try:
1887 if r.dirstate[name] == 'r':
1887 if r.dirstate[name] == 'r':
1888 r.undelete([name])
1888 r.undelete([name])
1889 r.copy(patch, name)
1889 r.copy(patch, name)
1890 r.remove([patch], False)
1890 r.remove([patch], False)
1891 finally:
1891 finally:
1892 del wlock
1892 del wlock
1893
1893
1894 q.save_dirty()
1894 q.save_dirty()
1895
1895
1896 def restore(ui, repo, rev, **opts):
1896 def restore(ui, repo, rev, **opts):
1897 """restore the queue state saved by a rev"""
1897 """restore the queue state saved by a rev"""
1898 rev = repo.lookup(rev)
1898 rev = repo.lookup(rev)
1899 q = repo.mq
1899 q = repo.mq
1900 q.restore(repo, rev, delete=opts['delete'],
1900 q.restore(repo, rev, delete=opts['delete'],
1901 qupdate=opts['update'])
1901 qupdate=opts['update'])
1902 q.save_dirty()
1902 q.save_dirty()
1903 return 0
1903 return 0
1904
1904
1905 def save(ui, repo, **opts):
1905 def save(ui, repo, **opts):
1906 """save current queue state"""
1906 """save current queue state"""
1907 q = repo.mq
1907 q = repo.mq
1908 message = cmdutil.logmessage(opts)
1908 message = cmdutil.logmessage(opts)
1909 ret = q.save(repo, msg=message)
1909 ret = q.save(repo, msg=message)
1910 if ret:
1910 if ret:
1911 return ret
1911 return ret
1912 q.save_dirty()
1912 q.save_dirty()
1913 if opts['copy']:
1913 if opts['copy']:
1914 path = q.path
1914 path = q.path
1915 if opts['name']:
1915 if opts['name']:
1916 newpath = os.path.join(q.basepath, opts['name'])
1916 newpath = os.path.join(q.basepath, opts['name'])
1917 if os.path.exists(newpath):
1917 if os.path.exists(newpath):
1918 if not os.path.isdir(newpath):
1918 if not os.path.isdir(newpath):
1919 raise util.Abort(_('destination %s exists and is not '
1919 raise util.Abort(_('destination %s exists and is not '
1920 'a directory') % newpath)
1920 'a directory') % newpath)
1921 if not opts['force']:
1921 if not opts['force']:
1922 raise util.Abort(_('destination %s exists, '
1922 raise util.Abort(_('destination %s exists, '
1923 'use -f to force') % newpath)
1923 'use -f to force') % newpath)
1924 else:
1924 else:
1925 newpath = savename(path)
1925 newpath = savename(path)
1926 ui.warn("copy %s to %s\n" % (path, newpath))
1926 ui.warn("copy %s to %s\n" % (path, newpath))
1927 util.copyfiles(path, newpath)
1927 util.copyfiles(path, newpath)
1928 if opts['empty']:
1928 if opts['empty']:
1929 try:
1929 try:
1930 os.unlink(q.join(q.status_path))
1930 os.unlink(q.join(q.status_path))
1931 except:
1931 except:
1932 pass
1932 pass
1933 return 0
1933 return 0
1934
1934
1935 def strip(ui, repo, rev, **opts):
1935 def strip(ui, repo, rev, **opts):
1936 """strip a revision and all later revs on the same branch"""
1936 """strip a revision and all later revs on the same branch"""
1937 rev = repo.lookup(rev)
1937 rev = repo.lookup(rev)
1938 backup = 'all'
1938 backup = 'all'
1939 if opts['backup']:
1939 if opts['backup']:
1940 backup = 'strip'
1940 backup = 'strip'
1941 elif opts['nobackup']:
1941 elif opts['nobackup']:
1942 backup = 'none'
1942 backup = 'none'
1943 update = repo.dirstate.parents()[0] != revlog.nullid
1943 update = repo.dirstate.parents()[0] != revlog.nullid
1944 repo.mq.strip(repo, rev, backup=backup, update=update)
1944 repo.mq.strip(repo, rev, backup=backup, update=update)
1945 return 0
1945 return 0
1946
1946
1947 def select(ui, repo, *args, **opts):
1947 def select(ui, repo, *args, **opts):
1948 '''set or print guarded patches to push
1948 '''set or print guarded patches to push
1949
1949
1950 Use the qguard command to set or print guards on patch, then use
1950 Use the qguard command to set or print guards on patch, then use
1951 qselect to tell mq which guards to use. A patch will be pushed if it
1951 qselect to tell mq which guards to use. A patch will be pushed if it
1952 has no guards or any positive guards match the currently selected guard,
1952 has no guards or any positive guards match the currently selected guard,
1953 but will not be pushed if any negative guards match the current guard.
1953 but will not be pushed if any negative guards match the current guard.
1954 For example:
1954 For example:
1955
1955
1956 qguard foo.patch -stable (negative guard)
1956 qguard foo.patch -stable (negative guard)
1957 qguard bar.patch +stable (positive guard)
1957 qguard bar.patch +stable (positive guard)
1958 qselect stable
1958 qselect stable
1959
1959
1960 This activates the "stable" guard. mq will skip foo.patch (because
1960 This activates the "stable" guard. mq will skip foo.patch (because
1961 it has a negative match) but push bar.patch (because it
1961 it has a negative match) but push bar.patch (because it
1962 has a positive match).
1962 has a positive match).
1963
1963
1964 With no arguments, prints the currently active guards.
1964 With no arguments, prints the currently active guards.
1965 With one argument, sets the active guard.
1965 With one argument, sets the active guard.
1966
1966
1967 Use -n/--none to deactivate guards (no other arguments needed).
1967 Use -n/--none to deactivate guards (no other arguments needed).
1968 When no guards are active, patches with positive guards are skipped
1968 When no guards are active, patches with positive guards are skipped
1969 and patches with negative guards are pushed.
1969 and patches with negative guards are pushed.
1970
1970
1971 qselect can change the guards on applied patches. It does not pop
1971 qselect can change the guards on applied patches. It does not pop
1972 guarded patches by default. Use --pop to pop back to the last applied
1972 guarded patches by default. Use --pop to pop back to the last applied
1973 patch that is not guarded. Use --reapply (which implies --pop) to push
1973 patch that is not guarded. Use --reapply (which implies --pop) to push
1974 back to the current patch afterwards, but skip guarded patches.
1974 back to the current patch afterwards, but skip guarded patches.
1975
1975
1976 Use -s/--series to print a list of all guards in the series file (no
1976 Use -s/--series to print a list of all guards in the series file (no
1977 other arguments needed). Use -v for more information.'''
1977 other arguments needed). Use -v for more information.'''
1978
1978
1979 q = repo.mq
1979 q = repo.mq
1980 guards = q.active()
1980 guards = q.active()
1981 if args or opts['none']:
1981 if args or opts['none']:
1982 old_unapplied = q.unapplied(repo)
1982 old_unapplied = q.unapplied(repo)
1983 old_guarded = [i for i in xrange(len(q.applied)) if
1983 old_guarded = [i for i in xrange(len(q.applied)) if
1984 not q.pushable(i)[0]]
1984 not q.pushable(i)[0]]
1985 q.set_active(args)
1985 q.set_active(args)
1986 q.save_dirty()
1986 q.save_dirty()
1987 if not args:
1987 if not args:
1988 ui.status(_('guards deactivated\n'))
1988 ui.status(_('guards deactivated\n'))
1989 if not opts['pop'] and not opts['reapply']:
1989 if not opts['pop'] and not opts['reapply']:
1990 unapplied = q.unapplied(repo)
1990 unapplied = q.unapplied(repo)
1991 guarded = [i for i in xrange(len(q.applied))
1991 guarded = [i for i in xrange(len(q.applied))
1992 if not q.pushable(i)[0]]
1992 if not q.pushable(i)[0]]
1993 if len(unapplied) != len(old_unapplied):
1993 if len(unapplied) != len(old_unapplied):
1994 ui.status(_('number of unguarded, unapplied patches has '
1994 ui.status(_('number of unguarded, unapplied patches has '
1995 'changed from %d to %d\n') %
1995 'changed from %d to %d\n') %
1996 (len(old_unapplied), len(unapplied)))
1996 (len(old_unapplied), len(unapplied)))
1997 if len(guarded) != len(old_guarded):
1997 if len(guarded) != len(old_guarded):
1998 ui.status(_('number of guarded, applied patches has changed '
1998 ui.status(_('number of guarded, applied patches has changed '
1999 'from %d to %d\n') %
1999 'from %d to %d\n') %
2000 (len(old_guarded), len(guarded)))
2000 (len(old_guarded), len(guarded)))
2001 elif opts['series']:
2001 elif opts['series']:
2002 guards = {}
2002 guards = {}
2003 noguards = 0
2003 noguards = 0
2004 for gs in q.series_guards:
2004 for gs in q.series_guards:
2005 if not gs:
2005 if not gs:
2006 noguards += 1
2006 noguards += 1
2007 for g in gs:
2007 for g in gs:
2008 guards.setdefault(g, 0)
2008 guards.setdefault(g, 0)
2009 guards[g] += 1
2009 guards[g] += 1
2010 if ui.verbose:
2010 if ui.verbose:
2011 guards['NONE'] = noguards
2011 guards['NONE'] = noguards
2012 guards = guards.items()
2012 guards = guards.items()
2013 guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
2013 guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
2014 if guards:
2014 if guards:
2015 ui.note(_('guards in series file:\n'))
2015 ui.note(_('guards in series file:\n'))
2016 for guard, count in guards:
2016 for guard, count in guards:
2017 ui.note('%2d ' % count)
2017 ui.note('%2d ' % count)
2018 ui.write(guard, '\n')
2018 ui.write(guard, '\n')
2019 else:
2019 else:
2020 ui.note(_('no guards in series file\n'))
2020 ui.note(_('no guards in series file\n'))
2021 else:
2021 else:
2022 if guards:
2022 if guards:
2023 ui.note(_('active guards:\n'))
2023 ui.note(_('active guards:\n'))
2024 for g in guards:
2024 for g in guards:
2025 ui.write(g, '\n')
2025 ui.write(g, '\n')
2026 else:
2026 else:
2027 ui.write(_('no active guards\n'))
2027 ui.write(_('no active guards\n'))
2028 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2028 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2029 popped = False
2029 popped = False
2030 if opts['pop'] or opts['reapply']:
2030 if opts['pop'] or opts['reapply']:
2031 for i in xrange(len(q.applied)):
2031 for i in xrange(len(q.applied)):
2032 pushable, reason = q.pushable(i)
2032 pushable, reason = q.pushable(i)
2033 if not pushable:
2033 if not pushable:
2034 ui.status(_('popping guarded patches\n'))
2034 ui.status(_('popping guarded patches\n'))
2035 popped = True
2035 popped = True
2036 if i == 0:
2036 if i == 0:
2037 q.pop(repo, all=True)
2037 q.pop(repo, all=True)
2038 else:
2038 else:
2039 q.pop(repo, i-1)
2039 q.pop(repo, i-1)
2040 break
2040 break
2041 if popped:
2041 if popped:
2042 try:
2042 try:
2043 if reapply:
2043 if reapply:
2044 ui.status(_('reapplying unguarded patches\n'))
2044 ui.status(_('reapplying unguarded patches\n'))
2045 q.push(repo, reapply)
2045 q.push(repo, reapply)
2046 finally:
2046 finally:
2047 q.save_dirty()
2047 q.save_dirty()
2048
2048
2049 def reposetup(ui, repo):
2049 def reposetup(ui, repo):
2050 class mqrepo(repo.__class__):
2050 class mqrepo(repo.__class__):
2051 def abort_if_wdir_patched(self, errmsg, force=False):
2051 def abort_if_wdir_patched(self, errmsg, force=False):
2052 if self.mq.applied and not force:
2052 if self.mq.applied and not force:
2053 parent = revlog.hex(self.dirstate.parents()[0])
2053 parent = revlog.hex(self.dirstate.parents()[0])
2054 if parent in [s.rev for s in self.mq.applied]:
2054 if parent in [s.rev for s in self.mq.applied]:
2055 raise util.Abort(errmsg)
2055 raise util.Abort(errmsg)
2056
2056
2057 def commit(self, *args, **opts):
2057 def commit(self, *args, **opts):
2058 if len(args) >= 6:
2058 if len(args) >= 6:
2059 force = args[5]
2059 force = args[5]
2060 else:
2060 else:
2061 force = opts.get('force')
2061 force = opts.get('force')
2062 self.abort_if_wdir_patched(
2062 self.abort_if_wdir_patched(
2063 _('cannot commit over an applied mq patch'),
2063 _('cannot commit over an applied mq patch'),
2064 force)
2064 force)
2065
2065
2066 return super(mqrepo, self).commit(*args, **opts)
2066 return super(mqrepo, self).commit(*args, **opts)
2067
2067
2068 def push(self, remote, force=False, revs=None):
2068 def push(self, remote, force=False, revs=None):
2069 if self.mq.applied and not force and not revs:
2069 if self.mq.applied and not force and not revs:
2070 raise util.Abort(_('source has mq patches applied'))
2070 raise util.Abort(_('source has mq patches applied'))
2071 return super(mqrepo, self).push(remote, force, revs)
2071 return super(mqrepo, self).push(remote, force, revs)
2072
2072
2073 def tags(self):
2073 def tags(self):
2074 if self.tagscache:
2074 if self.tagscache:
2075 return self.tagscache
2075 return self.tagscache
2076
2076
2077 tagscache = super(mqrepo, self).tags()
2077 tagscache = super(mqrepo, self).tags()
2078
2078
2079 q = self.mq
2079 q = self.mq
2080 if not q.applied:
2080 if not q.applied:
2081 return tagscache
2081 return tagscache
2082
2082
2083 mqtags = [(revlog.bin(patch.rev), patch.name) for patch in q.applied]
2083 mqtags = [(revlog.bin(patch.rev), patch.name) for patch in q.applied]
2084 mqtags.append((mqtags[-1][0], 'qtip'))
2084 mqtags.append((mqtags[-1][0], 'qtip'))
2085 mqtags.append((mqtags[0][0], 'qbase'))
2085 mqtags.append((mqtags[0][0], 'qbase'))
2086 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2086 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2087 for patch in mqtags:
2087 for patch in mqtags:
2088 if patch[1] in tagscache:
2088 if patch[1] in tagscache:
2089 self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
2089 self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
2090 else:
2090 else:
2091 tagscache[patch[1]] = patch[0]
2091 tagscache[patch[1]] = patch[0]
2092
2092
2093 return tagscache
2093 return tagscache
2094
2094
2095 def _branchtags(self):
2095 def _branchtags(self):
2096 q = self.mq
2096 q = self.mq
2097 if not q.applied:
2097 if not q.applied:
2098 return super(mqrepo, self)._branchtags()
2098 return super(mqrepo, self)._branchtags()
2099
2099
2100 self.branchcache = {} # avoid recursion in changectx
2100 self.branchcache = {} # avoid recursion in changectx
2101 cl = self.changelog
2101 cl = self.changelog
2102 partial, last, lrev = self._readbranchcache()
2102 partial, last, lrev = self._readbranchcache()
2103
2103
2104 qbase = cl.rev(revlog.bin(q.applied[0].rev))
2104 qbase = cl.rev(revlog.bin(q.applied[0].rev))
2105 start = lrev + 1
2105 start = lrev + 1
2106 if start < qbase:
2106 if start < qbase:
2107 # update the cache (excluding the patches) and save it
2107 # update the cache (excluding the patches) and save it
2108 self._updatebranchcache(partial, lrev+1, qbase)
2108 self._updatebranchcache(partial, lrev+1, qbase)
2109 self._writebranchcache(partial, cl.node(qbase-1), qbase-1)
2109 self._writebranchcache(partial, cl.node(qbase-1), qbase-1)
2110 start = qbase
2110 start = qbase
2111 # if start = qbase, the cache is as updated as it should be.
2111 # if start = qbase, the cache is as updated as it should be.
2112 # if start > qbase, the cache includes (part of) the patches.
2112 # if start > qbase, the cache includes (part of) the patches.
2113 # we might as well use it, but we won't save it.
2113 # we might as well use it, but we won't save it.
2114
2114
2115 # update the cache up to the tip
2115 # update the cache up to the tip
2116 self._updatebranchcache(partial, start, cl.count())
2116 self._updatebranchcache(partial, start, cl.count())
2117
2117
2118 return partial
2118 return partial
2119
2119
2120 if repo.local():
2120 if repo.local():
2121 repo.__class__ = mqrepo
2121 repo.__class__ = mqrepo
2122 repo.mq = queue(ui, repo.join(""))
2122 repo.mq = queue(ui, repo.join(""))
2123
2123
2124 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2124 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2125
2125
2126 cmdtable = {
2126 cmdtable = {
2127 "qapplied": (applied, [] + seriesopts, _('hg qapplied [-s] [PATCH]')),
2127 "qapplied": (applied, [] + seriesopts, _('hg qapplied [-s] [PATCH]')),
2128 "qclone":
2128 "qclone":
2129 (clone,
2129 (clone,
2130 [('', 'pull', None, _('use pull protocol to copy metadata')),
2130 [('', 'pull', None, _('use pull protocol to copy metadata')),
2131 ('U', 'noupdate', None, _('do not update the new working directories')),
2131 ('U', 'noupdate', None, _('do not update the new working directories')),
2132 ('', 'uncompressed', None,
2132 ('', 'uncompressed', None,
2133 _('use uncompressed transfer (fast over LAN)')),
2133 _('use uncompressed transfer (fast over LAN)')),
2134 ('e', 'ssh', '', _('specify ssh command to use')),
2135 ('p', 'patches', '', _('location of source patch repo')),
2134 ('p', 'patches', '', _('location of source patch repo')),
2136 ('', 'remotecmd', '',
2135 ] + commands.remoteopts,
2137 _('specify hg command to run on the remote side'))],
2138 _('hg qclone [OPTION]... SOURCE [DEST]')),
2136 _('hg qclone [OPTION]... SOURCE [DEST]')),
2139 "qcommit|qci":
2137 "qcommit|qci":
2140 (commit,
2138 (commit,
2141 commands.table["^commit|ci"][1],
2139 commands.table["^commit|ci"][1],
2142 _('hg qcommit [OPTION]... [FILE]...')),
2140 _('hg qcommit [OPTION]... [FILE]...')),
2143 "^qdiff":
2141 "^qdiff":
2144 (diff,
2142 (diff,
2145 [('g', 'git', None, _('use git extended diff format')),
2143 [('g', 'git', None, _('use git extended diff format')),
2146 ('I', 'include', [], _('include names matching the given patterns')),
2144 ] + commands.walkopts,
2147 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2148 _('hg qdiff [-I] [-X] [-g] [FILE]...')),
2145 _('hg qdiff [-I] [-X] [-g] [FILE]...')),
2149 "qdelete|qremove|qrm":
2146 "qdelete|qremove|qrm":
2150 (delete,
2147 (delete,
2151 [('k', 'keep', None, _('keep patch file')),
2148 [('k', 'keep', None, _('keep patch file')),
2152 ('r', 'rev', [], _('stop managing a revision'))],
2149 ('r', 'rev', [], _('stop managing a revision'))],
2153 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2150 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2154 'qfold':
2151 'qfold':
2155 (fold,
2152 (fold,
2156 [('e', 'edit', None, _('edit patch header')),
2153 [('e', 'edit', None, _('edit patch header')),
2157 ('k', 'keep', None, _('keep folded patch files')),
2154 ('k', 'keep', None, _('keep folded patch files')),
2158 ] + commands.commitopts,
2155 ] + commands.commitopts,
2159 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2156 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2160 'qgoto':
2157 'qgoto':
2161 (goto,
2158 (goto,
2162 [('f', 'force', None, _('overwrite any local changes'))],
2159 [('f', 'force', None, _('overwrite any local changes'))],
2163 _('hg qgoto [OPTION]... PATCH')),
2160 _('hg qgoto [OPTION]... PATCH')),
2164 'qguard':
2161 'qguard':
2165 (guard,
2162 (guard,
2166 [('l', 'list', None, _('list all patches and guards')),
2163 [('l', 'list', None, _('list all patches and guards')),
2167 ('n', 'none', None, _('drop all guards'))],
2164 ('n', 'none', None, _('drop all guards'))],
2168 _('hg qguard [-l] [-n] [PATCH] [+GUARD]... [-GUARD]...')),
2165 _('hg qguard [-l] [-n] [PATCH] [+GUARD]... [-GUARD]...')),
2169 'qheader': (header, [], _('hg qheader [PATCH]')),
2166 'qheader': (header, [], _('hg qheader [PATCH]')),
2170 "^qimport":
2167 "^qimport":
2171 (qimport,
2168 (qimport,
2172 [('e', 'existing', None, 'import file in patch dir'),
2169 [('e', 'existing', None, 'import file in patch dir'),
2173 ('n', 'name', '', 'patch file name'),
2170 ('n', 'name', '', 'patch file name'),
2174 ('f', 'force', None, 'overwrite existing files'),
2171 ('f', 'force', None, 'overwrite existing files'),
2175 ('r', 'rev', [], 'place existing revisions under mq control'),
2172 ('r', 'rev', [], 'place existing revisions under mq control'),
2176 ('g', 'git', None, _('use git extended diff format'))],
2173 ('g', 'git', None, _('use git extended diff format'))],
2177 _('hg qimport [-e] [-n NAME] [-f] [-g] [-r REV]... FILE...')),
2174 _('hg qimport [-e] [-n NAME] [-f] [-g] [-r REV]... FILE...')),
2178 "^qinit":
2175 "^qinit":
2179 (init,
2176 (init,
2180 [('c', 'create-repo', None, 'create queue repository')],
2177 [('c', 'create-repo', None, 'create queue repository')],
2181 _('hg qinit [-c]')),
2178 _('hg qinit [-c]')),
2182 "qnew":
2179 "qnew":
2183 (new,
2180 (new,
2184 [('e', 'edit', None, _('edit commit message')),
2181 [('e', 'edit', None, _('edit commit message')),
2185 ('f', 'force', None, _('import uncommitted changes into patch')),
2182 ('f', 'force', None, _('import uncommitted changes into patch')),
2186 ('g', 'git', None, _('use git extended diff format')),
2183 ('g', 'git', None, _('use git extended diff format')),
2187 ('I', 'include', [], _('include names matching the given patterns')),
2184 ] + commands.walkopts + commands.commitopts,
2188 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2189 ] + commands.commitopts,
2190 _('hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH [FILE]...')),
2185 _('hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH [FILE]...')),
2191 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2186 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2192 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2187 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2193 "^qpop":
2188 "^qpop":
2194 (pop,
2189 (pop,
2195 [('a', 'all', None, _('pop all patches')),
2190 [('a', 'all', None, _('pop all patches')),
2196 ('n', 'name', '', _('queue name to pop')),
2191 ('n', 'name', '', _('queue name to pop')),
2197 ('f', 'force', None, _('forget any local changes'))],
2192 ('f', 'force', None, _('forget any local changes'))],
2198 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2193 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2199 "^qpush":
2194 "^qpush":
2200 (push,
2195 (push,
2201 [('f', 'force', None, _('apply if the patch has rejects')),
2196 [('f', 'force', None, _('apply if the patch has rejects')),
2202 ('l', 'list', None, _('list patch name in commit text')),
2197 ('l', 'list', None, _('list patch name in commit text')),
2203 ('a', 'all', None, _('apply all patches')),
2198 ('a', 'all', None, _('apply all patches')),
2204 ('m', 'merge', None, _('merge from another queue')),
2199 ('m', 'merge', None, _('merge from another queue')),
2205 ('n', 'name', '', _('merge queue name'))],
2200 ('n', 'name', '', _('merge queue name'))],
2206 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]')),
2201 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]')),
2207 "^qrefresh":
2202 "^qrefresh":
2208 (refresh,
2203 (refresh,
2209 [('e', 'edit', None, _('edit commit message')),
2204 [('e', 'edit', None, _('edit commit message')),
2210 ('g', 'git', None, _('use git extended diff format')),
2205 ('g', 'git', None, _('use git extended diff format')),
2211 ('s', 'short', None, _('refresh only files already in the patch')),
2206 ('s', 'short', None, _('refresh only files already in the patch')),
2212 ('I', 'include', [], _('include names matching the given patterns')),
2207 ] + commands.walkopts + commands.commitopts,
2213 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2214 ] + commands.commitopts,
2215 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2208 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2216 'qrename|qmv':
2209 'qrename|qmv':
2217 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2210 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2218 "qrestore":
2211 "qrestore":
2219 (restore,
2212 (restore,
2220 [('d', 'delete', None, _('delete save entry')),
2213 [('d', 'delete', None, _('delete save entry')),
2221 ('u', 'update', None, _('update queue working dir'))],
2214 ('u', 'update', None, _('update queue working dir'))],
2222 _('hg qrestore [-d] [-u] REV')),
2215 _('hg qrestore [-d] [-u] REV')),
2223 "qsave":
2216 "qsave":
2224 (save,
2217 (save,
2225 [('c', 'copy', None, _('copy patch directory')),
2218 [('c', 'copy', None, _('copy patch directory')),
2226 ('n', 'name', '', _('copy directory name')),
2219 ('n', 'name', '', _('copy directory name')),
2227 ('e', 'empty', None, _('clear queue status file')),
2220 ('e', 'empty', None, _('clear queue status file')),
2228 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2221 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2229 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2222 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2230 "qselect":
2223 "qselect":
2231 (select,
2224 (select,
2232 [('n', 'none', None, _('disable all guards')),
2225 [('n', 'none', None, _('disable all guards')),
2233 ('s', 'series', None, _('list all guards in series file')),
2226 ('s', 'series', None, _('list all guards in series file')),
2234 ('', 'pop', None, _('pop to before first guarded applied patch')),
2227 ('', 'pop', None, _('pop to before first guarded applied patch')),
2235 ('', 'reapply', None, _('pop, then reapply patches'))],
2228 ('', 'reapply', None, _('pop, then reapply patches'))],
2236 _('hg qselect [OPTION]... [GUARD]...')),
2229 _('hg qselect [OPTION]... [GUARD]...')),
2237 "qseries":
2230 "qseries":
2238 (series,
2231 (series,
2239 [('m', 'missing', None, _('print patches not in series')),
2232 [('m', 'missing', None, _('print patches not in series')),
2240 ] + seriesopts,
2233 ] + seriesopts,
2241 _('hg qseries [-ms]')),
2234 _('hg qseries [-ms]')),
2242 "^strip":
2235 "^strip":
2243 (strip,
2236 (strip,
2244 [('f', 'force', None, _('force multi-head removal')),
2237 [('f', 'force', None, _('force multi-head removal')),
2245 ('b', 'backup', None, _('bundle unrelated changesets')),
2238 ('b', 'backup', None, _('bundle unrelated changesets')),
2246 ('n', 'nobackup', None, _('no backups'))],
2239 ('n', 'nobackup', None, _('no backups'))],
2247 _('hg strip [-f] [-b] [-n] REV')),
2240 _('hg strip [-f] [-b] [-n] REV')),
2248 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
2241 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
2249 "qunapplied": (unapplied, [] + seriesopts, _('hg qunapplied [-s] [PATCH]')),
2242 "qunapplied": (unapplied, [] + seriesopts, _('hg qunapplied [-s] [PATCH]')),
2250 }
2243 }
@@ -1,168 +1,167 b''
1 # Copyright (C) 2006 - Marco Barisione <marco@barisione.org>
1 # Copyright (C) 2006 - Marco Barisione <marco@barisione.org>
2 #
2 #
3 # This is a small extension for Mercurial (http://www.selenic.com/mercurial)
3 # This is a small extension for Mercurial (http://www.selenic.com/mercurial)
4 # that removes files not known to mercurial
4 # that removes files not known to mercurial
5 #
5 #
6 # This program was inspired by the "cvspurge" script contained in CVS utilities
6 # This program was inspired by the "cvspurge" script contained in CVS utilities
7 # (http://www.red-bean.com/cvsutils/).
7 # (http://www.red-bean.com/cvsutils/).
8 #
8 #
9 # To enable the "purge" extension put these lines in your ~/.hgrc:
9 # To enable the "purge" extension put these lines in your ~/.hgrc:
10 # [extensions]
10 # [extensions]
11 # hgext.purge =
11 # hgext.purge =
12 #
12 #
13 # For help on the usage of "hg purge" use:
13 # For help on the usage of "hg purge" use:
14 # hg help purge
14 # hg help purge
15 #
15 #
16 # This program is free software; you can redistribute it and/or modify
16 # This program is free software; you can redistribute it and/or modify
17 # it under the terms of the GNU General Public License as published by
17 # it under the terms of the GNU General Public License as published by
18 # the Free Software Foundation; either version 2 of the License, or
18 # the Free Software Foundation; either version 2 of the License, or
19 # (at your option) any later version.
19 # (at your option) any later version.
20 #
20 #
21 # This program is distributed in the hope that it will be useful,
21 # This program is distributed in the hope that it will be useful,
22 # but WITHOUT ANY WARRANTY; without even the implied warranty of
22 # but WITHOUT ANY WARRANTY; without even the implied warranty of
23 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 # GNU General Public License for more details.
24 # GNU General Public License for more details.
25 #
25 #
26 # You should have received a copy of the GNU General Public License
26 # You should have received a copy of the GNU General Public License
27 # along with this program; if not, write to the Free Software
27 # along with this program; if not, write to the Free Software
28 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
28 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29
29
30 from mercurial import hg, util
30 from mercurial import hg, util, commands
31 from mercurial.i18n import _
31 from mercurial.i18n import _
32 import os
32 import os
33
33
34 def dopurge(ui, repo, dirs=None, act=True, ignored=False,
34 def dopurge(ui, repo, dirs=None, act=True, ignored=False,
35 abort_on_err=False, eol='\n',
35 abort_on_err=False, eol='\n',
36 force=False, include=None, exclude=None):
36 force=False, include=None, exclude=None):
37 def error(msg):
37 def error(msg):
38 if abort_on_err:
38 if abort_on_err:
39 raise util.Abort(msg)
39 raise util.Abort(msg)
40 else:
40 else:
41 ui.warn(_('warning: %s\n') % msg)
41 ui.warn(_('warning: %s\n') % msg)
42
42
43 def remove(remove_func, name):
43 def remove(remove_func, name):
44 if act:
44 if act:
45 try:
45 try:
46 remove_func(os.path.join(repo.root, name))
46 remove_func(os.path.join(repo.root, name))
47 except OSError, e:
47 except OSError, e:
48 error(_('%s cannot be removed') % name)
48 error(_('%s cannot be removed') % name)
49 else:
49 else:
50 ui.write('%s%s' % (name, eol))
50 ui.write('%s%s' % (name, eol))
51
51
52 directories = []
52 directories = []
53 files = []
53 files = []
54 missing = []
54 missing = []
55 roots, match, anypats = util.cmdmatcher(repo.root, repo.getcwd(), dirs,
55 roots, match, anypats = util.cmdmatcher(repo.root, repo.getcwd(), dirs,
56 include, exclude)
56 include, exclude)
57 for src, f, st in repo.dirstate.statwalk(files=roots, match=match,
57 for src, f, st in repo.dirstate.statwalk(files=roots, match=match,
58 ignored=ignored, directories=True):
58 ignored=ignored, directories=True):
59 if src == 'd':
59 if src == 'd':
60 directories.append(f)
60 directories.append(f)
61 elif src == 'm':
61 elif src == 'm':
62 missing.append(f)
62 missing.append(f)
63 elif src == 'f' and f not in repo.dirstate:
63 elif src == 'f' and f not in repo.dirstate:
64 files.append(f)
64 files.append(f)
65
65
66 _check_missing(ui, repo, missing, force)
66 _check_missing(ui, repo, missing, force)
67
67
68 directories.sort()
68 directories.sort()
69
69
70 for f in files:
70 for f in files:
71 if f not in repo.dirstate:
71 if f not in repo.dirstate:
72 ui.note(_('Removing file %s\n') % f)
72 ui.note(_('Removing file %s\n') % f)
73 remove(os.remove, f)
73 remove(os.remove, f)
74
74
75 for f in directories[::-1]:
75 for f in directories[::-1]:
76 if match(f) and not os.listdir(repo.wjoin(f)):
76 if match(f) and not os.listdir(repo.wjoin(f)):
77 ui.note(_('Removing directory %s\n') % f)
77 ui.note(_('Removing directory %s\n') % f)
78 remove(os.rmdir, f)
78 remove(os.rmdir, f)
79
79
80 def _check_missing(ui, repo, missing, force=False):
80 def _check_missing(ui, repo, missing, force=False):
81 """Abort if there is the chance of having problems with name-mangling fs
81 """Abort if there is the chance of having problems with name-mangling fs
82
82
83 In a name mangling filesystem (e.g. a case insensitive one)
83 In a name mangling filesystem (e.g. a case insensitive one)
84 dirstate.walk() can yield filenames different from the ones
84 dirstate.walk() can yield filenames different from the ones
85 stored in the dirstate. This already confuses the status and
85 stored in the dirstate. This already confuses the status and
86 add commands, but with purge this may cause data loss.
86 add commands, but with purge this may cause data loss.
87
87
88 To prevent this, _check_missing will abort if there are missing
88 To prevent this, _check_missing will abort if there are missing
89 files. The force option will let the user skip the check if he
89 files. The force option will let the user skip the check if he
90 knows it is safe.
90 knows it is safe.
91
91
92 Even with the force option this function will check if any of the
92 Even with the force option this function will check if any of the
93 missing files is still available in the working dir: if so there
93 missing files is still available in the working dir: if so there
94 may be some problem with the underlying filesystem, so it
94 may be some problem with the underlying filesystem, so it
95 aborts unconditionally."""
95 aborts unconditionally."""
96
96
97 found = [f for f in missing if util.lexists(repo.wjoin(f))]
97 found = [f for f in missing if util.lexists(repo.wjoin(f))]
98
98
99 if found:
99 if found:
100 if not ui.quiet:
100 if not ui.quiet:
101 ui.warn(_("The following tracked files weren't listed by the "
101 ui.warn(_("The following tracked files weren't listed by the "
102 "filesystem, but could still be found:\n"))
102 "filesystem, but could still be found:\n"))
103 for f in found:
103 for f in found:
104 ui.warn("%s\n" % f)
104 ui.warn("%s\n" % f)
105 if util.checkfolding(repo.path):
105 if util.checkfolding(repo.path):
106 ui.warn(_("This is probably due to a case-insensitive "
106 ui.warn(_("This is probably due to a case-insensitive "
107 "filesystem\n"))
107 "filesystem\n"))
108 raise util.Abort(_("purging on name mangling filesystems is not "
108 raise util.Abort(_("purging on name mangling filesystems is not "
109 "yet fully supported"))
109 "yet fully supported"))
110
110
111 if missing and not force:
111 if missing and not force:
112 raise util.Abort(_("there are missing files in the working dir and "
112 raise util.Abort(_("there are missing files in the working dir and "
113 "purge still has problems with them due to name "
113 "purge still has problems with them due to name "
114 "mangling filesystems. "
114 "mangling filesystems. "
115 "Use --force if you know what you are doing"))
115 "Use --force if you know what you are doing"))
116
116
117
117
118 def purge(ui, repo, *dirs, **opts):
118 def purge(ui, repo, *dirs, **opts):
119 '''removes files not tracked by mercurial
119 '''removes files not tracked by mercurial
120
120
121 Delete files not known to mercurial, this is useful to test local and
121 Delete files not known to mercurial, this is useful to test local and
122 uncommitted changes in the otherwise clean source tree.
122 uncommitted changes in the otherwise clean source tree.
123
123
124 This means that purge will delete:
124 This means that purge will delete:
125 - Unknown files: files marked with "?" by "hg status"
125 - Unknown files: files marked with "?" by "hg status"
126 - Ignored files: files usually ignored by Mercurial because they match
126 - Ignored files: files usually ignored by Mercurial because they match
127 a pattern in a ".hgignore" file
127 a pattern in a ".hgignore" file
128 - Empty directories: in fact Mercurial ignores directories unless they
128 - Empty directories: in fact Mercurial ignores directories unless they
129 contain files under source control managment
129 contain files under source control managment
130 But it will leave untouched:
130 But it will leave untouched:
131 - Unmodified tracked files
131 - Unmodified tracked files
132 - Modified tracked files
132 - Modified tracked files
133 - New files added to the repository (with "hg add")
133 - New files added to the repository (with "hg add")
134
134
135 If directories are given on the command line, only files in these
135 If directories are given on the command line, only files in these
136 directories are considered.
136 directories are considered.
137
137
138 Be careful with purge, you could irreversibly delete some files you
138 Be careful with purge, you could irreversibly delete some files you
139 forgot to add to the repository. If you only want to print the list of
139 forgot to add to the repository. If you only want to print the list of
140 files that this program would delete use the --print option.
140 files that this program would delete use the --print option.
141 '''
141 '''
142 act = not opts['print']
142 act = not opts['print']
143 ignored = bool(opts['all'])
143 ignored = bool(opts['all'])
144 abort_on_err = bool(opts['abort_on_err'])
144 abort_on_err = bool(opts['abort_on_err'])
145 eol = opts['print0'] and '\0' or '\n'
145 eol = opts['print0'] and '\0' or '\n'
146 if eol == '\0':
146 if eol == '\0':
147 # --print0 implies --print
147 # --print0 implies --print
148 act = False
148 act = False
149 force = bool(opts['force'])
149 force = bool(opts['force'])
150 include = opts['include']
150 include = opts['include']
151 exclude = opts['exclude']
151 exclude = opts['exclude']
152 dopurge(ui, repo, dirs, act, ignored, abort_on_err,
152 dopurge(ui, repo, dirs, act, ignored, abort_on_err,
153 eol, force, include, exclude)
153 eol, force, include, exclude)
154
154
155
155
156 cmdtable = {
156 cmdtable = {
157 'purge|clean':
157 'purge|clean':
158 (purge,
158 (purge,
159 [('a', 'abort-on-err', None, _('abort if an error occurs')),
159 [('a', 'abort-on-err', None, _('abort if an error occurs')),
160 ('', 'all', None, _('purge ignored files too')),
160 ('', 'all', None, _('purge ignored files too')),
161 ('f', 'force', None, _('purge even when missing files are detected')),
161 ('f', 'force', None, _('purge even when missing files are detected')),
162 ('p', 'print', None, _('print the file names instead of deleting them')),
162 ('p', 'print', None, _('print the file names instead of deleting them')),
163 ('0', 'print0', None, _('end filenames with NUL, for use with xargs'
163 ('0', 'print0', None, _('end filenames with NUL, for use with xargs'
164 ' (implies -p)')),
164 ' (implies -p)')),
165 ('I', 'include', [], _('include names matching the given patterns')),
165 ] + commands.walkopts,
166 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
167 _('hg purge [OPTION]... [DIR]...'))
166 _('hg purge [OPTION]... [DIR]...'))
168 }
167 }
@@ -1,382 +1,380 b''
1 # record.py
1 # record.py
2 #
2 #
3 # Copyright 2007 Bryan O'Sullivan <bos@serpentine.com>
3 # Copyright 2007 Bryan O'Sullivan <bos@serpentine.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of
5 # This software may be used and distributed according to the terms of
6 # the GNU General Public License, incorporated herein by reference.
6 # the GNU General Public License, incorporated herein by reference.
7
7
8 '''interactive change selection during commit'''
8 '''interactive change selection during commit'''
9
9
10 from mercurial.i18n import _
10 from mercurial.i18n import _
11 from mercurial import cmdutil, commands, cmdutil, hg, mdiff, patch, revlog
11 from mercurial import cmdutil, commands, cmdutil, hg, mdiff, patch, revlog
12 from mercurial import util
12 from mercurial import util
13 import copy, cStringIO, errno, operator, os, re, shutil, tempfile
13 import copy, cStringIO, errno, operator, os, re, shutil, tempfile
14
14
15 lines_re = re.compile(r'@@ -(\d+),(\d+) \+(\d+),(\d+) @@\s*(.*)')
15 lines_re = re.compile(r'@@ -(\d+),(\d+) \+(\d+),(\d+) @@\s*(.*)')
16
16
17 def scanpatch(fp):
17 def scanpatch(fp):
18 lr = patch.linereader(fp)
18 lr = patch.linereader(fp)
19
19
20 def scanwhile(first, p):
20 def scanwhile(first, p):
21 lines = [first]
21 lines = [first]
22 while True:
22 while True:
23 line = lr.readline()
23 line = lr.readline()
24 if not line:
24 if not line:
25 break
25 break
26 if p(line):
26 if p(line):
27 lines.append(line)
27 lines.append(line)
28 else:
28 else:
29 lr.push(line)
29 lr.push(line)
30 break
30 break
31 return lines
31 return lines
32
32
33 while True:
33 while True:
34 line = lr.readline()
34 line = lr.readline()
35 if not line:
35 if not line:
36 break
36 break
37 if line.startswith('diff --git a/'):
37 if line.startswith('diff --git a/'):
38 def notheader(line):
38 def notheader(line):
39 s = line.split(None, 1)
39 s = line.split(None, 1)
40 return not s or s[0] not in ('---', 'diff')
40 return not s or s[0] not in ('---', 'diff')
41 header = scanwhile(line, notheader)
41 header = scanwhile(line, notheader)
42 fromfile = lr.readline()
42 fromfile = lr.readline()
43 if fromfile.startswith('---'):
43 if fromfile.startswith('---'):
44 tofile = lr.readline()
44 tofile = lr.readline()
45 header += [fromfile, tofile]
45 header += [fromfile, tofile]
46 else:
46 else:
47 lr.push(fromfile)
47 lr.push(fromfile)
48 yield 'file', header
48 yield 'file', header
49 elif line[0] == ' ':
49 elif line[0] == ' ':
50 yield 'context', scanwhile(line, lambda l: l[0] in ' \\')
50 yield 'context', scanwhile(line, lambda l: l[0] in ' \\')
51 elif line[0] in '-+':
51 elif line[0] in '-+':
52 yield 'hunk', scanwhile(line, lambda l: l[0] in '-+\\')
52 yield 'hunk', scanwhile(line, lambda l: l[0] in '-+\\')
53 else:
53 else:
54 m = lines_re.match(line)
54 m = lines_re.match(line)
55 if m:
55 if m:
56 yield 'range', m.groups()
56 yield 'range', m.groups()
57 else:
57 else:
58 raise patch.PatchError('unknown patch content: %r' % line)
58 raise patch.PatchError('unknown patch content: %r' % line)
59
59
60 class header(object):
60 class header(object):
61 diff_re = re.compile('diff --git a/(.*) b/(.*)$')
61 diff_re = re.compile('diff --git a/(.*) b/(.*)$')
62 allhunks_re = re.compile('(?:index|new file|deleted file) ')
62 allhunks_re = re.compile('(?:index|new file|deleted file) ')
63 pretty_re = re.compile('(?:new file|deleted file) ')
63 pretty_re = re.compile('(?:new file|deleted file) ')
64 special_re = re.compile('(?:index|new|deleted|copy|rename) ')
64 special_re = re.compile('(?:index|new|deleted|copy|rename) ')
65
65
66 def __init__(self, header):
66 def __init__(self, header):
67 self.header = header
67 self.header = header
68 self.hunks = []
68 self.hunks = []
69
69
70 def binary(self):
70 def binary(self):
71 for h in self.header:
71 for h in self.header:
72 if h.startswith('index '):
72 if h.startswith('index '):
73 return True
73 return True
74
74
75 def pretty(self, fp):
75 def pretty(self, fp):
76 for h in self.header:
76 for h in self.header:
77 if h.startswith('index '):
77 if h.startswith('index '):
78 fp.write(_('this modifies a binary file (all or nothing)\n'))
78 fp.write(_('this modifies a binary file (all or nothing)\n'))
79 break
79 break
80 if self.pretty_re.match(h):
80 if self.pretty_re.match(h):
81 fp.write(h)
81 fp.write(h)
82 if self.binary():
82 if self.binary():
83 fp.write(_('this is a binary file\n'))
83 fp.write(_('this is a binary file\n'))
84 break
84 break
85 if h.startswith('---'):
85 if h.startswith('---'):
86 fp.write(_('%d hunks, %d lines changed\n') %
86 fp.write(_('%d hunks, %d lines changed\n') %
87 (len(self.hunks),
87 (len(self.hunks),
88 sum([h.added + h.removed for h in self.hunks])))
88 sum([h.added + h.removed for h in self.hunks])))
89 break
89 break
90 fp.write(h)
90 fp.write(h)
91
91
92 def write(self, fp):
92 def write(self, fp):
93 fp.write(''.join(self.header))
93 fp.write(''.join(self.header))
94
94
95 def allhunks(self):
95 def allhunks(self):
96 for h in self.header:
96 for h in self.header:
97 if self.allhunks_re.match(h):
97 if self.allhunks_re.match(h):
98 return True
98 return True
99
99
100 def files(self):
100 def files(self):
101 fromfile, tofile = self.diff_re.match(self.header[0]).groups()
101 fromfile, tofile = self.diff_re.match(self.header[0]).groups()
102 if fromfile == tofile:
102 if fromfile == tofile:
103 return [fromfile]
103 return [fromfile]
104 return [fromfile, tofile]
104 return [fromfile, tofile]
105
105
106 def filename(self):
106 def filename(self):
107 return self.files()[-1]
107 return self.files()[-1]
108
108
109 def __repr__(self):
109 def __repr__(self):
110 return '<header %s>' % (' '.join(map(repr, self.files())))
110 return '<header %s>' % (' '.join(map(repr, self.files())))
111
111
112 def special(self):
112 def special(self):
113 for h in self.header:
113 for h in self.header:
114 if self.special_re.match(h):
114 if self.special_re.match(h):
115 return True
115 return True
116
116
117 def countchanges(hunk):
117 def countchanges(hunk):
118 add = len([h for h in hunk if h[0] == '+'])
118 add = len([h for h in hunk if h[0] == '+'])
119 rem = len([h for h in hunk if h[0] == '-'])
119 rem = len([h for h in hunk if h[0] == '-'])
120 return add, rem
120 return add, rem
121
121
122 class hunk(object):
122 class hunk(object):
123 maxcontext = 3
123 maxcontext = 3
124
124
125 def __init__(self, header, fromline, toline, proc, before, hunk, after):
125 def __init__(self, header, fromline, toline, proc, before, hunk, after):
126 def trimcontext(number, lines):
126 def trimcontext(number, lines):
127 delta = len(lines) - self.maxcontext
127 delta = len(lines) - self.maxcontext
128 if False and delta > 0:
128 if False and delta > 0:
129 return number + delta, lines[:self.maxcontext]
129 return number + delta, lines[:self.maxcontext]
130 return number, lines
130 return number, lines
131
131
132 self.header = header
132 self.header = header
133 self.fromline, self.before = trimcontext(fromline, before)
133 self.fromline, self.before = trimcontext(fromline, before)
134 self.toline, self.after = trimcontext(toline, after)
134 self.toline, self.after = trimcontext(toline, after)
135 self.proc = proc
135 self.proc = proc
136 self.hunk = hunk
136 self.hunk = hunk
137 self.added, self.removed = countchanges(self.hunk)
137 self.added, self.removed = countchanges(self.hunk)
138
138
139 def write(self, fp):
139 def write(self, fp):
140 delta = len(self.before) + len(self.after)
140 delta = len(self.before) + len(self.after)
141 fromlen = delta + self.removed
141 fromlen = delta + self.removed
142 tolen = delta + self.added
142 tolen = delta + self.added
143 fp.write('@@ -%d,%d +%d,%d @@%s\n' %
143 fp.write('@@ -%d,%d +%d,%d @@%s\n' %
144 (self.fromline, fromlen, self.toline, tolen,
144 (self.fromline, fromlen, self.toline, tolen,
145 self.proc and (' ' + self.proc)))
145 self.proc and (' ' + self.proc)))
146 fp.write(''.join(self.before + self.hunk + self.after))
146 fp.write(''.join(self.before + self.hunk + self.after))
147
147
148 pretty = write
148 pretty = write
149
149
150 def filename(self):
150 def filename(self):
151 return self.header.filename()
151 return self.header.filename()
152
152
153 def __repr__(self):
153 def __repr__(self):
154 return '<hunk %r@%d>' % (self.filename(), self.fromline)
154 return '<hunk %r@%d>' % (self.filename(), self.fromline)
155
155
156 def parsepatch(fp):
156 def parsepatch(fp):
157 class parser(object):
157 class parser(object):
158 def __init__(self):
158 def __init__(self):
159 self.fromline = 0
159 self.fromline = 0
160 self.toline = 0
160 self.toline = 0
161 self.proc = ''
161 self.proc = ''
162 self.header = None
162 self.header = None
163 self.context = []
163 self.context = []
164 self.before = []
164 self.before = []
165 self.hunk = []
165 self.hunk = []
166 self.stream = []
166 self.stream = []
167
167
168 def addrange(self, (fromstart, fromend, tostart, toend, proc)):
168 def addrange(self, (fromstart, fromend, tostart, toend, proc)):
169 self.fromline = int(fromstart)
169 self.fromline = int(fromstart)
170 self.toline = int(tostart)
170 self.toline = int(tostart)
171 self.proc = proc
171 self.proc = proc
172
172
173 def addcontext(self, context):
173 def addcontext(self, context):
174 if self.hunk:
174 if self.hunk:
175 h = hunk(self.header, self.fromline, self.toline, self.proc,
175 h = hunk(self.header, self.fromline, self.toline, self.proc,
176 self.before, self.hunk, context)
176 self.before, self.hunk, context)
177 self.header.hunks.append(h)
177 self.header.hunks.append(h)
178 self.stream.append(h)
178 self.stream.append(h)
179 self.fromline += len(self.before) + h.removed
179 self.fromline += len(self.before) + h.removed
180 self.toline += len(self.before) + h.added
180 self.toline += len(self.before) + h.added
181 self.before = []
181 self.before = []
182 self.hunk = []
182 self.hunk = []
183 self.proc = ''
183 self.proc = ''
184 self.context = context
184 self.context = context
185
185
186 def addhunk(self, hunk):
186 def addhunk(self, hunk):
187 if self.context:
187 if self.context:
188 self.before = self.context
188 self.before = self.context
189 self.context = []
189 self.context = []
190 self.hunk = data
190 self.hunk = data
191
191
192 def newfile(self, hdr):
192 def newfile(self, hdr):
193 self.addcontext([])
193 self.addcontext([])
194 h = header(hdr)
194 h = header(hdr)
195 self.stream.append(h)
195 self.stream.append(h)
196 self.header = h
196 self.header = h
197
197
198 def finished(self):
198 def finished(self):
199 self.addcontext([])
199 self.addcontext([])
200 return self.stream
200 return self.stream
201
201
202 transitions = {
202 transitions = {
203 'file': {'context': addcontext,
203 'file': {'context': addcontext,
204 'file': newfile,
204 'file': newfile,
205 'hunk': addhunk,
205 'hunk': addhunk,
206 'range': addrange},
206 'range': addrange},
207 'context': {'file': newfile,
207 'context': {'file': newfile,
208 'hunk': addhunk,
208 'hunk': addhunk,
209 'range': addrange},
209 'range': addrange},
210 'hunk': {'context': addcontext,
210 'hunk': {'context': addcontext,
211 'file': newfile,
211 'file': newfile,
212 'range': addrange},
212 'range': addrange},
213 'range': {'context': addcontext,
213 'range': {'context': addcontext,
214 'hunk': addhunk},
214 'hunk': addhunk},
215 }
215 }
216
216
217 p = parser()
217 p = parser()
218
218
219 state = 'context'
219 state = 'context'
220 for newstate, data in scanpatch(fp):
220 for newstate, data in scanpatch(fp):
221 try:
221 try:
222 p.transitions[state][newstate](p, data)
222 p.transitions[state][newstate](p, data)
223 except KeyError:
223 except KeyError:
224 raise patch.PatchError('unhandled transition: %s -> %s' %
224 raise patch.PatchError('unhandled transition: %s -> %s' %
225 (state, newstate))
225 (state, newstate))
226 state = newstate
226 state = newstate
227 return p.finished()
227 return p.finished()
228
228
229 def filterpatch(ui, chunks):
229 def filterpatch(ui, chunks):
230 chunks = list(chunks)
230 chunks = list(chunks)
231 chunks.reverse()
231 chunks.reverse()
232 seen = {}
232 seen = {}
233 def consumefile():
233 def consumefile():
234 consumed = []
234 consumed = []
235 while chunks:
235 while chunks:
236 if isinstance(chunks[-1], header):
236 if isinstance(chunks[-1], header):
237 break
237 break
238 else:
238 else:
239 consumed.append(chunks.pop())
239 consumed.append(chunks.pop())
240 return consumed
240 return consumed
241 resp = None
241 resp = None
242 applied = {}
242 applied = {}
243 while chunks:
243 while chunks:
244 chunk = chunks.pop()
244 chunk = chunks.pop()
245 if isinstance(chunk, header):
245 if isinstance(chunk, header):
246 fixoffset = 0
246 fixoffset = 0
247 hdr = ''.join(chunk.header)
247 hdr = ''.join(chunk.header)
248 if hdr in seen:
248 if hdr in seen:
249 consumefile()
249 consumefile()
250 continue
250 continue
251 seen[hdr] = True
251 seen[hdr] = True
252 if not resp:
252 if not resp:
253 chunk.pretty(ui)
253 chunk.pretty(ui)
254 r = resp or ui.prompt(_('record changes to %s? [y]es [n]o') %
254 r = resp or ui.prompt(_('record changes to %s? [y]es [n]o') %
255 _(' and ').join(map(repr, chunk.files())),
255 _(' and ').join(map(repr, chunk.files())),
256 '(?:|[yYnNqQaA])$') or 'y'
256 '(?:|[yYnNqQaA])$') or 'y'
257 if r in 'aA':
257 if r in 'aA':
258 r = 'y'
258 r = 'y'
259 resp = 'y'
259 resp = 'y'
260 if r in 'qQ':
260 if r in 'qQ':
261 raise util.Abort(_('user quit'))
261 raise util.Abort(_('user quit'))
262 if r in 'yY':
262 if r in 'yY':
263 applied[chunk.filename()] = [chunk]
263 applied[chunk.filename()] = [chunk]
264 if chunk.allhunks():
264 if chunk.allhunks():
265 applied[chunk.filename()] += consumefile()
265 applied[chunk.filename()] += consumefile()
266 else:
266 else:
267 consumefile()
267 consumefile()
268 else:
268 else:
269 if not resp:
269 if not resp:
270 chunk.pretty(ui)
270 chunk.pretty(ui)
271 r = resp or ui.prompt(_('record this change to %r? [y]es [n]o') %
271 r = resp or ui.prompt(_('record this change to %r? [y]es [n]o') %
272 chunk.filename(), '(?:|[yYnNqQaA])$') or 'y'
272 chunk.filename(), '(?:|[yYnNqQaA])$') or 'y'
273 if r in 'aA':
273 if r in 'aA':
274 r = 'y'
274 r = 'y'
275 resp = 'y'
275 resp = 'y'
276 if r in 'qQ':
276 if r in 'qQ':
277 raise util.Abort(_('user quit'))
277 raise util.Abort(_('user quit'))
278 if r in 'yY':
278 if r in 'yY':
279 if fixoffset:
279 if fixoffset:
280 chunk = copy.copy(chunk)
280 chunk = copy.copy(chunk)
281 chunk.toline += fixoffset
281 chunk.toline += fixoffset
282 applied[chunk.filename()].append(chunk)
282 applied[chunk.filename()].append(chunk)
283 else:
283 else:
284 fixoffset += chunk.removed - chunk.added
284 fixoffset += chunk.removed - chunk.added
285 return reduce(operator.add, [h for h in applied.itervalues()
285 return reduce(operator.add, [h for h in applied.itervalues()
286 if h[0].special() or len(h) > 1], [])
286 if h[0].special() or len(h) > 1], [])
287
287
288 def record(ui, repo, *pats, **opts):
288 def record(ui, repo, *pats, **opts):
289 '''interactively select changes to commit'''
289 '''interactively select changes to commit'''
290
290
291 if not ui.interactive:
291 if not ui.interactive:
292 raise util.Abort(_('running non-interactively, use commit instead'))
292 raise util.Abort(_('running non-interactively, use commit instead'))
293
293
294 def recordfunc(ui, repo, files, message, match, opts):
294 def recordfunc(ui, repo, files, message, match, opts):
295 if files:
295 if files:
296 changes = None
296 changes = None
297 else:
297 else:
298 changes = repo.status(files=files, match=match)[:5]
298 changes = repo.status(files=files, match=match)[:5]
299 modified, added, removed = changes[:3]
299 modified, added, removed = changes[:3]
300 files = modified + added + removed
300 files = modified + added + removed
301 diffopts = mdiff.diffopts(git=True, nodates=True)
301 diffopts = mdiff.diffopts(git=True, nodates=True)
302 fp = cStringIO.StringIO()
302 fp = cStringIO.StringIO()
303 patch.diff(repo, repo.dirstate.parents()[0], files=files,
303 patch.diff(repo, repo.dirstate.parents()[0], files=files,
304 match=match, changes=changes, opts=diffopts, fp=fp)
304 match=match, changes=changes, opts=diffopts, fp=fp)
305 fp.seek(0)
305 fp.seek(0)
306
306
307 chunks = filterpatch(ui, parsepatch(fp))
307 chunks = filterpatch(ui, parsepatch(fp))
308 del fp
308 del fp
309
309
310 contenders = {}
310 contenders = {}
311 for h in chunks:
311 for h in chunks:
312 try: contenders.update(dict.fromkeys(h.files()))
312 try: contenders.update(dict.fromkeys(h.files()))
313 except AttributeError: pass
313 except AttributeError: pass
314
314
315 newfiles = [f for f in files if f in contenders]
315 newfiles = [f for f in files if f in contenders]
316
316
317 if not newfiles:
317 if not newfiles:
318 ui.status(_('no changes to record\n'))
318 ui.status(_('no changes to record\n'))
319 return 0
319 return 0
320
320
321 if changes is None:
321 if changes is None:
322 changes = repo.status(files=newfiles, match=match)[:5]
322 changes = repo.status(files=newfiles, match=match)[:5]
323 modified = dict.fromkeys(changes[0])
323 modified = dict.fromkeys(changes[0])
324
324
325 backups = {}
325 backups = {}
326 backupdir = repo.join('record-backups')
326 backupdir = repo.join('record-backups')
327 try:
327 try:
328 os.mkdir(backupdir)
328 os.mkdir(backupdir)
329 except OSError, err:
329 except OSError, err:
330 if err.errno != errno.EEXIST:
330 if err.errno != errno.EEXIST:
331 raise
331 raise
332 try:
332 try:
333 for f in newfiles:
333 for f in newfiles:
334 if f not in modified:
334 if f not in modified:
335 continue
335 continue
336 fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
336 fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
337 dir=backupdir)
337 dir=backupdir)
338 os.close(fd)
338 os.close(fd)
339 ui.debug('backup %r as %r\n' % (f, tmpname))
339 ui.debug('backup %r as %r\n' % (f, tmpname))
340 util.copyfile(repo.wjoin(f), tmpname)
340 util.copyfile(repo.wjoin(f), tmpname)
341 backups[f] = tmpname
341 backups[f] = tmpname
342
342
343 fp = cStringIO.StringIO()
343 fp = cStringIO.StringIO()
344 for c in chunks:
344 for c in chunks:
345 if c.filename() in backups:
345 if c.filename() in backups:
346 c.write(fp)
346 c.write(fp)
347 dopatch = fp.tell()
347 dopatch = fp.tell()
348 fp.seek(0)
348 fp.seek(0)
349
349
350 if backups:
350 if backups:
351 hg.revert(repo, repo.dirstate.parents()[0], backups.has_key)
351 hg.revert(repo, repo.dirstate.parents()[0], backups.has_key)
352
352
353 if dopatch:
353 if dopatch:
354 ui.debug('applying patch\n')
354 ui.debug('applying patch\n')
355 ui.debug(fp.getvalue())
355 ui.debug(fp.getvalue())
356 patch.internalpatch(fp, ui, 1, repo.root)
356 patch.internalpatch(fp, ui, 1, repo.root)
357 del fp
357 del fp
358
358
359 repo.commit(newfiles, message, opts['user'], opts['date'], match,
359 repo.commit(newfiles, message, opts['user'], opts['date'], match,
360 force_editor=opts.get('force_editor'))
360 force_editor=opts.get('force_editor'))
361 return 0
361 return 0
362 finally:
362 finally:
363 try:
363 try:
364 for realname, tmpname in backups.iteritems():
364 for realname, tmpname in backups.iteritems():
365 ui.debug('restoring %r to %r\n' % (tmpname, realname))
365 ui.debug('restoring %r to %r\n' % (tmpname, realname))
366 util.copyfile(tmpname, repo.wjoin(realname))
366 util.copyfile(tmpname, repo.wjoin(realname))
367 os.unlink(tmpname)
367 os.unlink(tmpname)
368 os.rmdir(backupdir)
368 os.rmdir(backupdir)
369 except OSError:
369 except OSError:
370 pass
370 pass
371 return cmdutil.commit(ui, repo, recordfunc, pats, opts)
371 return cmdutil.commit(ui, repo, recordfunc, pats, opts)
372
372
373 cmdtable = {
373 cmdtable = {
374 "record":
374 "record":
375 (record,
375 (record,
376 [('A', 'addremove', None,
376 [('A', 'addremove', None,
377 _('mark new/missing files as added/removed before committing')),
377 _('mark new/missing files as added/removed before committing')),
378 ('d', 'date', '', _('record datecode as commit date')),
378 ] + commands.walkopts + commands.commitopts + commands.commitopts2,
379 ('u', 'user', '', _('record user as commiter')),
380 ] + commands.walkopts + commands.commitopts,
381 _('hg record [OPTION]... [FILE]...')),
379 _('hg record [OPTION]... [FILE]...')),
382 }
380 }
@@ -1,3141 +1,3140 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 import demandimport; demandimport.enable()
8 import demandimport; demandimport.enable()
9 from node import *
9 from node import *
10 from i18n import _
10 from i18n import _
11 import os, re, sys, urllib
11 import os, re, sys, urllib
12 import ui, hg, util, revlog, bundlerepo, extensions
12 import ui, hg, util, revlog, bundlerepo, extensions
13 import difflib, patch, time, help, mdiff, tempfile
13 import difflib, patch, time, help, mdiff, tempfile
14 import errno, version, socket
14 import errno, version, socket
15 import archival, changegroup, cmdutil, hgweb.server, sshserver
15 import archival, changegroup, cmdutil, hgweb.server, sshserver
16
16
17 # Commands start here, listed alphabetically
17 # Commands start here, listed alphabetically
18
18
19 def add(ui, repo, *pats, **opts):
19 def add(ui, repo, *pats, **opts):
20 """add the specified files on the next commit
20 """add the specified files on the next commit
21
21
22 Schedule files to be version controlled and added to the repository.
22 Schedule files to be version controlled and added to the repository.
23
23
24 The files will be added to the repository at the next commit. To
24 The files will be added to the repository at the next commit. To
25 undo an add before that, see hg revert.
25 undo an add before that, see hg revert.
26
26
27 If no names are given, add all files in the repository.
27 If no names are given, add all files in the repository.
28 """
28 """
29
29
30 names = []
30 names = []
31 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
31 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
32 if exact:
32 if exact:
33 if ui.verbose:
33 if ui.verbose:
34 ui.status(_('adding %s\n') % rel)
34 ui.status(_('adding %s\n') % rel)
35 names.append(abs)
35 names.append(abs)
36 elif abs not in repo.dirstate:
36 elif abs not in repo.dirstate:
37 ui.status(_('adding %s\n') % rel)
37 ui.status(_('adding %s\n') % rel)
38 names.append(abs)
38 names.append(abs)
39 if not opts.get('dry_run'):
39 if not opts.get('dry_run'):
40 repo.add(names)
40 repo.add(names)
41
41
42 def addremove(ui, repo, *pats, **opts):
42 def addremove(ui, repo, *pats, **opts):
43 """add all new files, delete all missing files
43 """add all new files, delete all missing files
44
44
45 Add all new files and remove all missing files from the repository.
45 Add all new files and remove all missing files from the repository.
46
46
47 New files are ignored if they match any of the patterns in .hgignore. As
47 New files are ignored if they match any of the patterns in .hgignore. As
48 with add, these changes take effect at the next commit.
48 with add, these changes take effect at the next commit.
49
49
50 Use the -s option to detect renamed files. With a parameter > 0,
50 Use the -s option to detect renamed files. With a parameter > 0,
51 this compares every removed file with every added file and records
51 this compares every removed file with every added file and records
52 those similar enough as renames. This option takes a percentage
52 those similar enough as renames. This option takes a percentage
53 between 0 (disabled) and 100 (files must be identical) as its
53 between 0 (disabled) and 100 (files must be identical) as its
54 parameter. Detecting renamed files this way can be expensive.
54 parameter. Detecting renamed files this way can be expensive.
55 """
55 """
56 try:
56 try:
57 sim = float(opts.get('similarity') or 0)
57 sim = float(opts.get('similarity') or 0)
58 except ValueError:
58 except ValueError:
59 raise util.Abort(_('similarity must be a number'))
59 raise util.Abort(_('similarity must be a number'))
60 if sim < 0 or sim > 100:
60 if sim < 0 or sim > 100:
61 raise util.Abort(_('similarity must be between 0 and 100'))
61 raise util.Abort(_('similarity must be between 0 and 100'))
62 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
62 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
63
63
64 def annotate(ui, repo, *pats, **opts):
64 def annotate(ui, repo, *pats, **opts):
65 """show changeset information per file line
65 """show changeset information per file line
66
66
67 List changes in files, showing the revision id responsible for each line
67 List changes in files, showing the revision id responsible for each line
68
68
69 This command is useful to discover who did a change or when a change took
69 This command is useful to discover who did a change or when a change took
70 place.
70 place.
71
71
72 Without the -a option, annotate will avoid processing files it
72 Without the -a option, annotate will avoid processing files it
73 detects as binary. With -a, annotate will generate an annotation
73 detects as binary. With -a, annotate will generate an annotation
74 anyway, probably with undesirable results.
74 anyway, probably with undesirable results.
75 """
75 """
76 getdate = util.cachefunc(lambda x: util.datestr(x[0].date()))
76 getdate = util.cachefunc(lambda x: util.datestr(x[0].date()))
77
77
78 if not pats:
78 if not pats:
79 raise util.Abort(_('at least one file name or pattern required'))
79 raise util.Abort(_('at least one file name or pattern required'))
80
80
81 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
81 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
82 ('number', lambda x: str(x[0].rev())),
82 ('number', lambda x: str(x[0].rev())),
83 ('changeset', lambda x: short(x[0].node())),
83 ('changeset', lambda x: short(x[0].node())),
84 ('date', getdate),
84 ('date', getdate),
85 ('follow', lambda x: x[0].path()),
85 ('follow', lambda x: x[0].path()),
86 ]
86 ]
87
87
88 if (not opts['user'] and not opts['changeset'] and not opts['date']
88 if (not opts['user'] and not opts['changeset'] and not opts['date']
89 and not opts['follow']):
89 and not opts['follow']):
90 opts['number'] = 1
90 opts['number'] = 1
91
91
92 linenumber = opts.get('line_number') is not None
92 linenumber = opts.get('line_number') is not None
93 if (linenumber and (not opts['changeset']) and (not opts['number'])):
93 if (linenumber and (not opts['changeset']) and (not opts['number'])):
94 raise util.Abort(_('at least one of -n/-c is required for -l'))
94 raise util.Abort(_('at least one of -n/-c is required for -l'))
95
95
96 funcmap = [func for op, func in opmap if opts.get(op)]
96 funcmap = [func for op, func in opmap if opts.get(op)]
97 if linenumber:
97 if linenumber:
98 lastfunc = funcmap[-1]
98 lastfunc = funcmap[-1]
99 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
99 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
100
100
101 ctx = repo.changectx(opts['rev'])
101 ctx = repo.changectx(opts['rev'])
102
102
103 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
103 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
104 node=ctx.node()):
104 node=ctx.node()):
105 fctx = ctx.filectx(abs)
105 fctx = ctx.filectx(abs)
106 if not opts['text'] and util.binary(fctx.data()):
106 if not opts['text'] and util.binary(fctx.data()):
107 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
107 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
108 continue
108 continue
109
109
110 lines = fctx.annotate(follow=opts.get('follow'),
110 lines = fctx.annotate(follow=opts.get('follow'),
111 linenumber=linenumber)
111 linenumber=linenumber)
112 pieces = []
112 pieces = []
113
113
114 for f in funcmap:
114 for f in funcmap:
115 l = [f(n) for n, dummy in lines]
115 l = [f(n) for n, dummy in lines]
116 if l:
116 if l:
117 m = max(map(len, l))
117 m = max(map(len, l))
118 pieces.append(["%*s" % (m, x) for x in l])
118 pieces.append(["%*s" % (m, x) for x in l])
119
119
120 if pieces:
120 if pieces:
121 for p, l in zip(zip(*pieces), lines):
121 for p, l in zip(zip(*pieces), lines):
122 ui.write("%s: %s" % (" ".join(p), l[1]))
122 ui.write("%s: %s" % (" ".join(p), l[1]))
123
123
124 def archive(ui, repo, dest, **opts):
124 def archive(ui, repo, dest, **opts):
125 '''create unversioned archive of a repository revision
125 '''create unversioned archive of a repository revision
126
126
127 By default, the revision used is the parent of the working
127 By default, the revision used is the parent of the working
128 directory; use "-r" to specify a different revision.
128 directory; use "-r" to specify a different revision.
129
129
130 To specify the type of archive to create, use "-t". Valid
130 To specify the type of archive to create, use "-t". Valid
131 types are:
131 types are:
132
132
133 "files" (default): a directory full of files
133 "files" (default): a directory full of files
134 "tar": tar archive, uncompressed
134 "tar": tar archive, uncompressed
135 "tbz2": tar archive, compressed using bzip2
135 "tbz2": tar archive, compressed using bzip2
136 "tgz": tar archive, compressed using gzip
136 "tgz": tar archive, compressed using gzip
137 "uzip": zip archive, uncompressed
137 "uzip": zip archive, uncompressed
138 "zip": zip archive, compressed using deflate
138 "zip": zip archive, compressed using deflate
139
139
140 The exact name of the destination archive or directory is given
140 The exact name of the destination archive or directory is given
141 using a format string; see "hg help export" for details.
141 using a format string; see "hg help export" for details.
142
142
143 Each member added to an archive file has a directory prefix
143 Each member added to an archive file has a directory prefix
144 prepended. Use "-p" to specify a format string for the prefix.
144 prepended. Use "-p" to specify a format string for the prefix.
145 The default is the basename of the archive, with suffixes removed.
145 The default is the basename of the archive, with suffixes removed.
146 '''
146 '''
147
147
148 ctx = repo.changectx(opts['rev'])
148 ctx = repo.changectx(opts['rev'])
149 if not ctx:
149 if not ctx:
150 raise util.Abort(_('repository has no revisions'))
150 raise util.Abort(_('repository has no revisions'))
151 node = ctx.node()
151 node = ctx.node()
152 dest = cmdutil.make_filename(repo, dest, node)
152 dest = cmdutil.make_filename(repo, dest, node)
153 if os.path.realpath(dest) == repo.root:
153 if os.path.realpath(dest) == repo.root:
154 raise util.Abort(_('repository root cannot be destination'))
154 raise util.Abort(_('repository root cannot be destination'))
155 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
155 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
156 kind = opts.get('type') or 'files'
156 kind = opts.get('type') or 'files'
157 prefix = opts['prefix']
157 prefix = opts['prefix']
158 if dest == '-':
158 if dest == '-':
159 if kind == 'files':
159 if kind == 'files':
160 raise util.Abort(_('cannot archive plain files to stdout'))
160 raise util.Abort(_('cannot archive plain files to stdout'))
161 dest = sys.stdout
161 dest = sys.stdout
162 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
162 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
163 prefix = cmdutil.make_filename(repo, prefix, node)
163 prefix = cmdutil.make_filename(repo, prefix, node)
164 archival.archive(repo, dest, node, kind, not opts['no_decode'],
164 archival.archive(repo, dest, node, kind, not opts['no_decode'],
165 matchfn, prefix)
165 matchfn, prefix)
166
166
167 def backout(ui, repo, node=None, rev=None, **opts):
167 def backout(ui, repo, node=None, rev=None, **opts):
168 '''reverse effect of earlier changeset
168 '''reverse effect of earlier changeset
169
169
170 Commit the backed out changes as a new changeset. The new
170 Commit the backed out changes as a new changeset. The new
171 changeset is a child of the backed out changeset.
171 changeset is a child of the backed out changeset.
172
172
173 If you back out a changeset other than the tip, a new head is
173 If you back out a changeset other than the tip, a new head is
174 created. This head is the parent of the working directory. If
174 created. This head is the parent of the working directory. If
175 you back out an old changeset, your working directory will appear
175 you back out an old changeset, your working directory will appear
176 old after the backout. You should merge the backout changeset
176 old after the backout. You should merge the backout changeset
177 with another head.
177 with another head.
178
178
179 The --merge option remembers the parent of the working directory
179 The --merge option remembers the parent of the working directory
180 before starting the backout, then merges the new head with that
180 before starting the backout, then merges the new head with that
181 changeset afterwards. This saves you from doing the merge by
181 changeset afterwards. This saves you from doing the merge by
182 hand. The result of this merge is not committed, as for a normal
182 hand. The result of this merge is not committed, as for a normal
183 merge.'''
183 merge.'''
184 if rev and node:
184 if rev and node:
185 raise util.Abort(_("please specify just one revision"))
185 raise util.Abort(_("please specify just one revision"))
186
186
187 if not rev:
187 if not rev:
188 rev = node
188 rev = node
189
189
190 if not rev:
190 if not rev:
191 raise util.Abort(_("please specify a revision to backout"))
191 raise util.Abort(_("please specify a revision to backout"))
192
192
193 cmdutil.bail_if_changed(repo)
193 cmdutil.bail_if_changed(repo)
194 op1, op2 = repo.dirstate.parents()
194 op1, op2 = repo.dirstate.parents()
195 if op2 != nullid:
195 if op2 != nullid:
196 raise util.Abort(_('outstanding uncommitted merge'))
196 raise util.Abort(_('outstanding uncommitted merge'))
197 node = repo.lookup(rev)
197 node = repo.lookup(rev)
198 p1, p2 = repo.changelog.parents(node)
198 p1, p2 = repo.changelog.parents(node)
199 if p1 == nullid:
199 if p1 == nullid:
200 raise util.Abort(_('cannot back out a change with no parents'))
200 raise util.Abort(_('cannot back out a change with no parents'))
201 if p2 != nullid:
201 if p2 != nullid:
202 if not opts['parent']:
202 if not opts['parent']:
203 raise util.Abort(_('cannot back out a merge changeset without '
203 raise util.Abort(_('cannot back out a merge changeset without '
204 '--parent'))
204 '--parent'))
205 p = repo.lookup(opts['parent'])
205 p = repo.lookup(opts['parent'])
206 if p not in (p1, p2):
206 if p not in (p1, p2):
207 raise util.Abort(_('%s is not a parent of %s') %
207 raise util.Abort(_('%s is not a parent of %s') %
208 (short(p), short(node)))
208 (short(p), short(node)))
209 parent = p
209 parent = p
210 else:
210 else:
211 if opts['parent']:
211 if opts['parent']:
212 raise util.Abort(_('cannot use --parent on non-merge changeset'))
212 raise util.Abort(_('cannot use --parent on non-merge changeset'))
213 parent = p1
213 parent = p1
214 hg.clean(repo, node, show_stats=False)
214 hg.clean(repo, node, show_stats=False)
215 revert_opts = opts.copy()
215 revert_opts = opts.copy()
216 revert_opts['date'] = None
216 revert_opts['date'] = None
217 revert_opts['all'] = True
217 revert_opts['all'] = True
218 revert_opts['rev'] = hex(parent)
218 revert_opts['rev'] = hex(parent)
219 revert(ui, repo, **revert_opts)
219 revert(ui, repo, **revert_opts)
220 commit_opts = opts.copy()
220 commit_opts = opts.copy()
221 commit_opts['addremove'] = False
221 commit_opts['addremove'] = False
222 if not commit_opts['message'] and not commit_opts['logfile']:
222 if not commit_opts['message'] and not commit_opts['logfile']:
223 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
223 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
224 commit_opts['force_editor'] = True
224 commit_opts['force_editor'] = True
225 commit(ui, repo, **commit_opts)
225 commit(ui, repo, **commit_opts)
226 def nice(node):
226 def nice(node):
227 return '%d:%s' % (repo.changelog.rev(node), short(node))
227 return '%d:%s' % (repo.changelog.rev(node), short(node))
228 ui.status(_('changeset %s backs out changeset %s\n') %
228 ui.status(_('changeset %s backs out changeset %s\n') %
229 (nice(repo.changelog.tip()), nice(node)))
229 (nice(repo.changelog.tip()), nice(node)))
230 if op1 != node:
230 if op1 != node:
231 if opts['merge']:
231 if opts['merge']:
232 ui.status(_('merging with changeset %s\n') % nice(op1))
232 ui.status(_('merging with changeset %s\n') % nice(op1))
233 hg.merge(repo, hex(op1))
233 hg.merge(repo, hex(op1))
234 else:
234 else:
235 ui.status(_('the backout changeset is a new head - '
235 ui.status(_('the backout changeset is a new head - '
236 'do not forget to merge\n'))
236 'do not forget to merge\n'))
237 ui.status(_('(use "backout --merge" '
237 ui.status(_('(use "backout --merge" '
238 'if you want to auto-merge)\n'))
238 'if you want to auto-merge)\n'))
239
239
240 def branch(ui, repo, label=None, **opts):
240 def branch(ui, repo, label=None, **opts):
241 """set or show the current branch name
241 """set or show the current branch name
242
242
243 With no argument, show the current branch name. With one argument,
243 With no argument, show the current branch name. With one argument,
244 set the working directory branch name (the branch does not exist in
244 set the working directory branch name (the branch does not exist in
245 the repository until the next commit).
245 the repository until the next commit).
246
246
247 Unless --force is specified, branch will not let you set a
247 Unless --force is specified, branch will not let you set a
248 branch name that shadows an existing branch.
248 branch name that shadows an existing branch.
249 """
249 """
250
250
251 if label:
251 if label:
252 if not opts.get('force') and label in repo.branchtags():
252 if not opts.get('force') and label in repo.branchtags():
253 if label not in [p.branch() for p in repo.workingctx().parents()]:
253 if label not in [p.branch() for p in repo.workingctx().parents()]:
254 raise util.Abort(_('a branch of the same name already exists'
254 raise util.Abort(_('a branch of the same name already exists'
255 ' (use --force to override)'))
255 ' (use --force to override)'))
256 repo.dirstate.setbranch(util.fromlocal(label))
256 repo.dirstate.setbranch(util.fromlocal(label))
257 ui.status(_('marked working directory as branch %s\n') % label)
257 ui.status(_('marked working directory as branch %s\n') % label)
258 else:
258 else:
259 ui.write("%s\n" % util.tolocal(repo.dirstate.branch()))
259 ui.write("%s\n" % util.tolocal(repo.dirstate.branch()))
260
260
261 def branches(ui, repo, active=False):
261 def branches(ui, repo, active=False):
262 """list repository named branches
262 """list repository named branches
263
263
264 List the repository's named branches, indicating which ones are
264 List the repository's named branches, indicating which ones are
265 inactive. If active is specified, only show active branches.
265 inactive. If active is specified, only show active branches.
266
266
267 A branch is considered active if it contains unmerged heads.
267 A branch is considered active if it contains unmerged heads.
268 """
268 """
269 b = repo.branchtags()
269 b = repo.branchtags()
270 heads = dict.fromkeys(repo.heads(), 1)
270 heads = dict.fromkeys(repo.heads(), 1)
271 l = [((n in heads), repo.changelog.rev(n), n, t) for t, n in b.items()]
271 l = [((n in heads), repo.changelog.rev(n), n, t) for t, n in b.items()]
272 l.sort()
272 l.sort()
273 l.reverse()
273 l.reverse()
274 for ishead, r, n, t in l:
274 for ishead, r, n, t in l:
275 if active and not ishead:
275 if active and not ishead:
276 # If we're only displaying active branches, abort the loop on
276 # If we're only displaying active branches, abort the loop on
277 # encountering the first inactive head
277 # encountering the first inactive head
278 break
278 break
279 else:
279 else:
280 hexfunc = ui.debugflag and hex or short
280 hexfunc = ui.debugflag and hex or short
281 if ui.quiet:
281 if ui.quiet:
282 ui.write("%s\n" % t)
282 ui.write("%s\n" % t)
283 else:
283 else:
284 spaces = " " * (30 - util.locallen(t))
284 spaces = " " * (30 - util.locallen(t))
285 # The code only gets here if inactive branches are being
285 # The code only gets here if inactive branches are being
286 # displayed or the branch is active.
286 # displayed or the branch is active.
287 isinactive = ((not ishead) and " (inactive)") or ''
287 isinactive = ((not ishead) and " (inactive)") or ''
288 ui.write("%s%s %s:%s%s\n" % (t, spaces, r, hexfunc(n), isinactive))
288 ui.write("%s%s %s:%s%s\n" % (t, spaces, r, hexfunc(n), isinactive))
289
289
290 def bundle(ui, repo, fname, dest=None, **opts):
290 def bundle(ui, repo, fname, dest=None, **opts):
291 """create a changegroup file
291 """create a changegroup file
292
292
293 Generate a compressed changegroup file collecting changesets not
293 Generate a compressed changegroup file collecting changesets not
294 found in the other repository.
294 found in the other repository.
295
295
296 If no destination repository is specified the destination is assumed
296 If no destination repository is specified the destination is assumed
297 to have all the nodes specified by one or more --base parameters.
297 to have all the nodes specified by one or more --base parameters.
298
298
299 The bundle file can then be transferred using conventional means and
299 The bundle file can then be transferred using conventional means and
300 applied to another repository with the unbundle or pull command.
300 applied to another repository with the unbundle or pull command.
301 This is useful when direct push and pull are not available or when
301 This is useful when direct push and pull are not available or when
302 exporting an entire repository is undesirable.
302 exporting an entire repository is undesirable.
303
303
304 Applying bundles preserves all changeset contents including
304 Applying bundles preserves all changeset contents including
305 permissions, copy/rename information, and revision history.
305 permissions, copy/rename information, and revision history.
306 """
306 """
307 revs = opts.get('rev') or None
307 revs = opts.get('rev') or None
308 if revs:
308 if revs:
309 revs = [repo.lookup(rev) for rev in revs]
309 revs = [repo.lookup(rev) for rev in revs]
310 base = opts.get('base')
310 base = opts.get('base')
311 if base:
311 if base:
312 if dest:
312 if dest:
313 raise util.Abort(_("--base is incompatible with specifiying "
313 raise util.Abort(_("--base is incompatible with specifiying "
314 "a destination"))
314 "a destination"))
315 base = [repo.lookup(rev) for rev in base]
315 base = [repo.lookup(rev) for rev in base]
316 # create the right base
316 # create the right base
317 # XXX: nodesbetween / changegroup* should be "fixed" instead
317 # XXX: nodesbetween / changegroup* should be "fixed" instead
318 o = []
318 o = []
319 has = {nullid: None}
319 has = {nullid: None}
320 for n in base:
320 for n in base:
321 has.update(repo.changelog.reachable(n))
321 has.update(repo.changelog.reachable(n))
322 if revs:
322 if revs:
323 visit = list(revs)
323 visit = list(revs)
324 else:
324 else:
325 visit = repo.changelog.heads()
325 visit = repo.changelog.heads()
326 seen = {}
326 seen = {}
327 while visit:
327 while visit:
328 n = visit.pop(0)
328 n = visit.pop(0)
329 parents = [p for p in repo.changelog.parents(n) if p not in has]
329 parents = [p for p in repo.changelog.parents(n) if p not in has]
330 if len(parents) == 0:
330 if len(parents) == 0:
331 o.insert(0, n)
331 o.insert(0, n)
332 else:
332 else:
333 for p in parents:
333 for p in parents:
334 if p not in seen:
334 if p not in seen:
335 seen[p] = 1
335 seen[p] = 1
336 visit.append(p)
336 visit.append(p)
337 else:
337 else:
338 cmdutil.setremoteconfig(ui, opts)
338 cmdutil.setremoteconfig(ui, opts)
339 dest, revs = cmdutil.parseurl(
339 dest, revs = cmdutil.parseurl(
340 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
340 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
341 other = hg.repository(ui, dest)
341 other = hg.repository(ui, dest)
342 o = repo.findoutgoing(other, force=opts['force'])
342 o = repo.findoutgoing(other, force=opts['force'])
343
343
344 if revs:
344 if revs:
345 cg = repo.changegroupsubset(o, revs, 'bundle')
345 cg = repo.changegroupsubset(o, revs, 'bundle')
346 else:
346 else:
347 cg = repo.changegroup(o, 'bundle')
347 cg = repo.changegroup(o, 'bundle')
348 changegroup.writebundle(cg, fname, "HG10BZ")
348 changegroup.writebundle(cg, fname, "HG10BZ")
349
349
350 def cat(ui, repo, file1, *pats, **opts):
350 def cat(ui, repo, file1, *pats, **opts):
351 """output the current or given revision of files
351 """output the current or given revision of files
352
352
353 Print the specified files as they were at the given revision.
353 Print the specified files as they were at the given revision.
354 If no revision is given, the parent of the working directory is used,
354 If no revision is given, the parent of the working directory is used,
355 or tip if no revision is checked out.
355 or tip if no revision is checked out.
356
356
357 Output may be to a file, in which case the name of the file is
357 Output may be to a file, in which case the name of the file is
358 given using a format string. The formatting rules are the same as
358 given using a format string. The formatting rules are the same as
359 for the export command, with the following additions:
359 for the export command, with the following additions:
360
360
361 %s basename of file being printed
361 %s basename of file being printed
362 %d dirname of file being printed, or '.' if in repo root
362 %d dirname of file being printed, or '.' if in repo root
363 %p root-relative path name of file being printed
363 %p root-relative path name of file being printed
364 """
364 """
365 ctx = repo.changectx(opts['rev'])
365 ctx = repo.changectx(opts['rev'])
366 err = 1
366 err = 1
367 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
367 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
368 ctx.node()):
368 ctx.node()):
369 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
369 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
370 fp.write(ctx.filectx(abs).data())
370 fp.write(ctx.filectx(abs).data())
371 err = 0
371 err = 0
372 return err
372 return err
373
373
374 def clone(ui, source, dest=None, **opts):
374 def clone(ui, source, dest=None, **opts):
375 """make a copy of an existing repository
375 """make a copy of an existing repository
376
376
377 Create a copy of an existing repository in a new directory.
377 Create a copy of an existing repository in a new directory.
378
378
379 If no destination directory name is specified, it defaults to the
379 If no destination directory name is specified, it defaults to the
380 basename of the source.
380 basename of the source.
381
381
382 The location of the source is added to the new repository's
382 The location of the source is added to the new repository's
383 .hg/hgrc file, as the default to be used for future pulls.
383 .hg/hgrc file, as the default to be used for future pulls.
384
384
385 For efficiency, hardlinks are used for cloning whenever the source
385 For efficiency, hardlinks are used for cloning whenever the source
386 and destination are on the same filesystem (note this applies only
386 and destination are on the same filesystem (note this applies only
387 to the repository data, not to the checked out files). Some
387 to the repository data, not to the checked out files). Some
388 filesystems, such as AFS, implement hardlinking incorrectly, but
388 filesystems, such as AFS, implement hardlinking incorrectly, but
389 do not report errors. In these cases, use the --pull option to
389 do not report errors. In these cases, use the --pull option to
390 avoid hardlinking.
390 avoid hardlinking.
391
391
392 You can safely clone repositories and checked out files using full
392 You can safely clone repositories and checked out files using full
393 hardlinks with
393 hardlinks with
394
394
395 $ cp -al REPO REPOCLONE
395 $ cp -al REPO REPOCLONE
396
396
397 which is the fastest way to clone. However, the operation is not
397 which is the fastest way to clone. However, the operation is not
398 atomic (making sure REPO is not modified during the operation is
398 atomic (making sure REPO is not modified during the operation is
399 up to you) and you have to make sure your editor breaks hardlinks
399 up to you) and you have to make sure your editor breaks hardlinks
400 (Emacs and most Linux Kernel tools do so).
400 (Emacs and most Linux Kernel tools do so).
401
401
402 If you use the -r option to clone up to a specific revision, no
402 If you use the -r option to clone up to a specific revision, no
403 subsequent revisions will be present in the cloned repository.
403 subsequent revisions will be present in the cloned repository.
404 This option implies --pull, even on local repositories.
404 This option implies --pull, even on local repositories.
405
405
406 See pull for valid source format details.
406 See pull for valid source format details.
407
407
408 It is possible to specify an ssh:// URL as the destination, but no
408 It is possible to specify an ssh:// URL as the destination, but no
409 .hg/hgrc and working directory will be created on the remote side.
409 .hg/hgrc and working directory will be created on the remote side.
410 Look at the help text for the pull command for important details
410 Look at the help text for the pull command for important details
411 about ssh:// URLs.
411 about ssh:// URLs.
412 """
412 """
413 cmdutil.setremoteconfig(ui, opts)
413 cmdutil.setremoteconfig(ui, opts)
414 hg.clone(ui, source, dest,
414 hg.clone(ui, source, dest,
415 pull=opts['pull'],
415 pull=opts['pull'],
416 stream=opts['uncompressed'],
416 stream=opts['uncompressed'],
417 rev=opts['rev'],
417 rev=opts['rev'],
418 update=not opts['noupdate'])
418 update=not opts['noupdate'])
419
419
420 def commit(ui, repo, *pats, **opts):
420 def commit(ui, repo, *pats, **opts):
421 """commit the specified files or all outstanding changes
421 """commit the specified files or all outstanding changes
422
422
423 Commit changes to the given files into the repository.
423 Commit changes to the given files into the repository.
424
424
425 If a list of files is omitted, all changes reported by "hg status"
425 If a list of files is omitted, all changes reported by "hg status"
426 will be committed.
426 will be committed.
427
427
428 If no commit message is specified, the editor configured in your hgrc
428 If no commit message is specified, the editor configured in your hgrc
429 or in the EDITOR environment variable is started to enter a message.
429 or in the EDITOR environment variable is started to enter a message.
430 """
430 """
431 def commitfunc(ui, repo, files, message, match, opts):
431 def commitfunc(ui, repo, files, message, match, opts):
432 return repo.commit(files, message, opts['user'], opts['date'], match,
432 return repo.commit(files, message, opts['user'], opts['date'], match,
433 force_editor=opts.get('force_editor'))
433 force_editor=opts.get('force_editor'))
434 cmdutil.commit(ui, repo, commitfunc, pats, opts)
434 cmdutil.commit(ui, repo, commitfunc, pats, opts)
435
435
436 def docopy(ui, repo, pats, opts):
436 def docopy(ui, repo, pats, opts):
437 # called with the repo lock held
437 # called with the repo lock held
438 #
438 #
439 # hgsep => pathname that uses "/" to separate directories
439 # hgsep => pathname that uses "/" to separate directories
440 # ossep => pathname that uses os.sep to separate directories
440 # ossep => pathname that uses os.sep to separate directories
441 cwd = repo.getcwd()
441 cwd = repo.getcwd()
442 errors = 0
442 errors = 0
443 copied = []
443 copied = []
444 targets = {}
444 targets = {}
445
445
446 # abs: hgsep
446 # abs: hgsep
447 # rel: ossep
447 # rel: ossep
448 # return: hgsep
448 # return: hgsep
449 def okaytocopy(abs, rel, exact):
449 def okaytocopy(abs, rel, exact):
450 reasons = {'?': _('is not managed'),
450 reasons = {'?': _('is not managed'),
451 'r': _('has been marked for remove')}
451 'r': _('has been marked for remove')}
452 state = repo.dirstate[abs]
452 state = repo.dirstate[abs]
453 reason = reasons.get(state)
453 reason = reasons.get(state)
454 if reason:
454 if reason:
455 if exact:
455 if exact:
456 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
456 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
457 else:
457 else:
458 if state == 'a':
458 if state == 'a':
459 origsrc = repo.dirstate.copied(abs)
459 origsrc = repo.dirstate.copied(abs)
460 if origsrc is not None:
460 if origsrc is not None:
461 return origsrc
461 return origsrc
462 return abs
462 return abs
463
463
464 # origsrc: hgsep
464 # origsrc: hgsep
465 # abssrc: hgsep
465 # abssrc: hgsep
466 # relsrc: ossep
466 # relsrc: ossep
467 # otarget: ossep
467 # otarget: ossep
468 def copy(origsrc, abssrc, relsrc, otarget, exact):
468 def copy(origsrc, abssrc, relsrc, otarget, exact):
469 abstarget = util.canonpath(repo.root, cwd, otarget)
469 abstarget = util.canonpath(repo.root, cwd, otarget)
470 reltarget = repo.pathto(abstarget, cwd)
470 reltarget = repo.pathto(abstarget, cwd)
471 prevsrc = targets.get(abstarget)
471 prevsrc = targets.get(abstarget)
472 src = repo.wjoin(abssrc)
472 src = repo.wjoin(abssrc)
473 target = repo.wjoin(abstarget)
473 target = repo.wjoin(abstarget)
474 if prevsrc is not None:
474 if prevsrc is not None:
475 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
475 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
476 (reltarget, repo.pathto(abssrc, cwd),
476 (reltarget, repo.pathto(abssrc, cwd),
477 repo.pathto(prevsrc, cwd)))
477 repo.pathto(prevsrc, cwd)))
478 return
478 return
479 if (not opts['after'] and os.path.exists(target) or
479 if (not opts['after'] and os.path.exists(target) or
480 opts['after'] and repo.dirstate[abstarget] in 'mn'):
480 opts['after'] and repo.dirstate[abstarget] in 'mn'):
481 if not opts['force']:
481 if not opts['force']:
482 ui.warn(_('%s: not overwriting - file exists\n') %
482 ui.warn(_('%s: not overwriting - file exists\n') %
483 reltarget)
483 reltarget)
484 return
484 return
485 if not opts['after'] and not opts.get('dry_run'):
485 if not opts['after'] and not opts.get('dry_run'):
486 os.unlink(target)
486 os.unlink(target)
487 if opts['after']:
487 if opts['after']:
488 if not os.path.exists(target):
488 if not os.path.exists(target):
489 return
489 return
490 else:
490 else:
491 targetdir = os.path.dirname(target) or '.'
491 targetdir = os.path.dirname(target) or '.'
492 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
492 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
493 os.makedirs(targetdir)
493 os.makedirs(targetdir)
494 try:
494 try:
495 restore = repo.dirstate[abstarget] == 'r'
495 restore = repo.dirstate[abstarget] == 'r'
496 if restore and not opts.get('dry_run'):
496 if restore and not opts.get('dry_run'):
497 repo.undelete([abstarget])
497 repo.undelete([abstarget])
498 try:
498 try:
499 if not opts.get('dry_run'):
499 if not opts.get('dry_run'):
500 util.copyfile(src, target)
500 util.copyfile(src, target)
501 restore = False
501 restore = False
502 finally:
502 finally:
503 if restore:
503 if restore:
504 repo.remove([abstarget])
504 repo.remove([abstarget])
505 except IOError, inst:
505 except IOError, inst:
506 if inst.errno == errno.ENOENT:
506 if inst.errno == errno.ENOENT:
507 ui.warn(_('%s: deleted in working copy\n') % relsrc)
507 ui.warn(_('%s: deleted in working copy\n') % relsrc)
508 else:
508 else:
509 ui.warn(_('%s: cannot copy - %s\n') %
509 ui.warn(_('%s: cannot copy - %s\n') %
510 (relsrc, inst.strerror))
510 (relsrc, inst.strerror))
511 errors += 1
511 errors += 1
512 return
512 return
513 if ui.verbose or not exact:
513 if ui.verbose or not exact:
514 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
514 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
515 targets[abstarget] = abssrc
515 targets[abstarget] = abssrc
516 if abstarget != origsrc:
516 if abstarget != origsrc:
517 if repo.dirstate[origsrc] == 'a':
517 if repo.dirstate[origsrc] == 'a':
518 if not ui.quiet:
518 if not ui.quiet:
519 ui.warn(_("%s has not been committed yet, so no copy "
519 ui.warn(_("%s has not been committed yet, so no copy "
520 "data will be stored for %s.\n")
520 "data will be stored for %s.\n")
521 % (repo.pathto(origsrc, cwd), reltarget))
521 % (repo.pathto(origsrc, cwd), reltarget))
522 if abstarget not in repo.dirstate and not opts.get('dry_run'):
522 if abstarget not in repo.dirstate and not opts.get('dry_run'):
523 repo.add([abstarget])
523 repo.add([abstarget])
524 elif not opts.get('dry_run'):
524 elif not opts.get('dry_run'):
525 repo.copy(origsrc, abstarget)
525 repo.copy(origsrc, abstarget)
526 copied.append((abssrc, relsrc, exact))
526 copied.append((abssrc, relsrc, exact))
527
527
528 # pat: ossep
528 # pat: ossep
529 # dest ossep
529 # dest ossep
530 # srcs: list of (hgsep, hgsep, ossep, bool)
530 # srcs: list of (hgsep, hgsep, ossep, bool)
531 # return: function that takes hgsep and returns ossep
531 # return: function that takes hgsep and returns ossep
532 def targetpathfn(pat, dest, srcs):
532 def targetpathfn(pat, dest, srcs):
533 if os.path.isdir(pat):
533 if os.path.isdir(pat):
534 abspfx = util.canonpath(repo.root, cwd, pat)
534 abspfx = util.canonpath(repo.root, cwd, pat)
535 abspfx = util.localpath(abspfx)
535 abspfx = util.localpath(abspfx)
536 if destdirexists:
536 if destdirexists:
537 striplen = len(os.path.split(abspfx)[0])
537 striplen = len(os.path.split(abspfx)[0])
538 else:
538 else:
539 striplen = len(abspfx)
539 striplen = len(abspfx)
540 if striplen:
540 if striplen:
541 striplen += len(os.sep)
541 striplen += len(os.sep)
542 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
542 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
543 elif destdirexists:
543 elif destdirexists:
544 res = lambda p: os.path.join(dest,
544 res = lambda p: os.path.join(dest,
545 os.path.basename(util.localpath(p)))
545 os.path.basename(util.localpath(p)))
546 else:
546 else:
547 res = lambda p: dest
547 res = lambda p: dest
548 return res
548 return res
549
549
550 # pat: ossep
550 # pat: ossep
551 # dest ossep
551 # dest ossep
552 # srcs: list of (hgsep, hgsep, ossep, bool)
552 # srcs: list of (hgsep, hgsep, ossep, bool)
553 # return: function that takes hgsep and returns ossep
553 # return: function that takes hgsep and returns ossep
554 def targetpathafterfn(pat, dest, srcs):
554 def targetpathafterfn(pat, dest, srcs):
555 if util.patkind(pat, None)[0]:
555 if util.patkind(pat, None)[0]:
556 # a mercurial pattern
556 # a mercurial pattern
557 res = lambda p: os.path.join(dest,
557 res = lambda p: os.path.join(dest,
558 os.path.basename(util.localpath(p)))
558 os.path.basename(util.localpath(p)))
559 else:
559 else:
560 abspfx = util.canonpath(repo.root, cwd, pat)
560 abspfx = util.canonpath(repo.root, cwd, pat)
561 if len(abspfx) < len(srcs[0][0]):
561 if len(abspfx) < len(srcs[0][0]):
562 # A directory. Either the target path contains the last
562 # A directory. Either the target path contains the last
563 # component of the source path or it does not.
563 # component of the source path or it does not.
564 def evalpath(striplen):
564 def evalpath(striplen):
565 score = 0
565 score = 0
566 for s in srcs:
566 for s in srcs:
567 t = os.path.join(dest, util.localpath(s[0])[striplen:])
567 t = os.path.join(dest, util.localpath(s[0])[striplen:])
568 if os.path.exists(t):
568 if os.path.exists(t):
569 score += 1
569 score += 1
570 return score
570 return score
571
571
572 abspfx = util.localpath(abspfx)
572 abspfx = util.localpath(abspfx)
573 striplen = len(abspfx)
573 striplen = len(abspfx)
574 if striplen:
574 if striplen:
575 striplen += len(os.sep)
575 striplen += len(os.sep)
576 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
576 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
577 score = evalpath(striplen)
577 score = evalpath(striplen)
578 striplen1 = len(os.path.split(abspfx)[0])
578 striplen1 = len(os.path.split(abspfx)[0])
579 if striplen1:
579 if striplen1:
580 striplen1 += len(os.sep)
580 striplen1 += len(os.sep)
581 if evalpath(striplen1) > score:
581 if evalpath(striplen1) > score:
582 striplen = striplen1
582 striplen = striplen1
583 res = lambda p: os.path.join(dest,
583 res = lambda p: os.path.join(dest,
584 util.localpath(p)[striplen:])
584 util.localpath(p)[striplen:])
585 else:
585 else:
586 # a file
586 # a file
587 if destdirexists:
587 if destdirexists:
588 res = lambda p: os.path.join(dest,
588 res = lambda p: os.path.join(dest,
589 os.path.basename(util.localpath(p)))
589 os.path.basename(util.localpath(p)))
590 else:
590 else:
591 res = lambda p: dest
591 res = lambda p: dest
592 return res
592 return res
593
593
594
594
595 pats = util.expand_glob(pats)
595 pats = util.expand_glob(pats)
596 if not pats:
596 if not pats:
597 raise util.Abort(_('no source or destination specified'))
597 raise util.Abort(_('no source or destination specified'))
598 if len(pats) == 1:
598 if len(pats) == 1:
599 raise util.Abort(_('no destination specified'))
599 raise util.Abort(_('no destination specified'))
600 dest = pats.pop()
600 dest = pats.pop()
601 destdirexists = os.path.isdir(dest)
601 destdirexists = os.path.isdir(dest)
602 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
602 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
603 raise util.Abort(_('with multiple sources, destination must be an '
603 raise util.Abort(_('with multiple sources, destination must be an '
604 'existing directory'))
604 'existing directory'))
605 if opts['after']:
605 if opts['after']:
606 tfn = targetpathafterfn
606 tfn = targetpathafterfn
607 else:
607 else:
608 tfn = targetpathfn
608 tfn = targetpathfn
609 copylist = []
609 copylist = []
610 for pat in pats:
610 for pat in pats:
611 srcs = []
611 srcs = []
612 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts,
612 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts,
613 globbed=True):
613 globbed=True):
614 origsrc = okaytocopy(abssrc, relsrc, exact)
614 origsrc = okaytocopy(abssrc, relsrc, exact)
615 if origsrc:
615 if origsrc:
616 srcs.append((origsrc, abssrc, relsrc, exact))
616 srcs.append((origsrc, abssrc, relsrc, exact))
617 if not srcs:
617 if not srcs:
618 continue
618 continue
619 copylist.append((tfn(pat, dest, srcs), srcs))
619 copylist.append((tfn(pat, dest, srcs), srcs))
620 if not copylist:
620 if not copylist:
621 raise util.Abort(_('no files to copy'))
621 raise util.Abort(_('no files to copy'))
622
622
623 for targetpath, srcs in copylist:
623 for targetpath, srcs in copylist:
624 for origsrc, abssrc, relsrc, exact in srcs:
624 for origsrc, abssrc, relsrc, exact in srcs:
625 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
625 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
626
626
627 if errors:
627 if errors:
628 ui.warn(_('(consider using --after)\n'))
628 ui.warn(_('(consider using --after)\n'))
629 return errors, copied
629 return errors, copied
630
630
631 def copy(ui, repo, *pats, **opts):
631 def copy(ui, repo, *pats, **opts):
632 """mark files as copied for the next commit
632 """mark files as copied for the next commit
633
633
634 Mark dest as having copies of source files. If dest is a
634 Mark dest as having copies of source files. If dest is a
635 directory, copies are put in that directory. If dest is a file,
635 directory, copies are put in that directory. If dest is a file,
636 there can only be one source.
636 there can only be one source.
637
637
638 By default, this command copies the contents of files as they
638 By default, this command copies the contents of files as they
639 stand in the working directory. If invoked with --after, the
639 stand in the working directory. If invoked with --after, the
640 operation is recorded, but no copying is performed.
640 operation is recorded, but no copying is performed.
641
641
642 This command takes effect in the next commit. To undo a copy
642 This command takes effect in the next commit. To undo a copy
643 before that, see hg revert.
643 before that, see hg revert.
644 """
644 """
645 wlock = repo.wlock(False)
645 wlock = repo.wlock(False)
646 try:
646 try:
647 errs, copied = docopy(ui, repo, pats, opts)
647 errs, copied = docopy(ui, repo, pats, opts)
648 finally:
648 finally:
649 del wlock
649 del wlock
650 return errs
650 return errs
651
651
652 def debugancestor(ui, index, rev1, rev2):
652 def debugancestor(ui, index, rev1, rev2):
653 """find the ancestor revision of two revisions in a given index"""
653 """find the ancestor revision of two revisions in a given index"""
654 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
654 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
655 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
655 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
656 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
656 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
657
657
658 def debugcomplete(ui, cmd='', **opts):
658 def debugcomplete(ui, cmd='', **opts):
659 """returns the completion list associated with the given command"""
659 """returns the completion list associated with the given command"""
660
660
661 if opts['options']:
661 if opts['options']:
662 options = []
662 options = []
663 otables = [globalopts]
663 otables = [globalopts]
664 if cmd:
664 if cmd:
665 aliases, entry = cmdutil.findcmd(ui, cmd)
665 aliases, entry = cmdutil.findcmd(ui, cmd)
666 otables.append(entry[1])
666 otables.append(entry[1])
667 for t in otables:
667 for t in otables:
668 for o in t:
668 for o in t:
669 if o[0]:
669 if o[0]:
670 options.append('-%s' % o[0])
670 options.append('-%s' % o[0])
671 options.append('--%s' % o[1])
671 options.append('--%s' % o[1])
672 ui.write("%s\n" % "\n".join(options))
672 ui.write("%s\n" % "\n".join(options))
673 return
673 return
674
674
675 clist = cmdutil.findpossible(ui, cmd).keys()
675 clist = cmdutil.findpossible(ui, cmd).keys()
676 clist.sort()
676 clist.sort()
677 ui.write("%s\n" % "\n".join(clist))
677 ui.write("%s\n" % "\n".join(clist))
678
678
679 def debugrebuildstate(ui, repo, rev=""):
679 def debugrebuildstate(ui, repo, rev=""):
680 """rebuild the dirstate as it would look like for the given revision"""
680 """rebuild the dirstate as it would look like for the given revision"""
681 if rev == "":
681 if rev == "":
682 rev = repo.changelog.tip()
682 rev = repo.changelog.tip()
683 ctx = repo.changectx(rev)
683 ctx = repo.changectx(rev)
684 files = ctx.manifest()
684 files = ctx.manifest()
685 wlock = repo.wlock()
685 wlock = repo.wlock()
686 try:
686 try:
687 repo.dirstate.rebuild(rev, files)
687 repo.dirstate.rebuild(rev, files)
688 finally:
688 finally:
689 del wlock
689 del wlock
690
690
691 def debugcheckstate(ui, repo):
691 def debugcheckstate(ui, repo):
692 """validate the correctness of the current dirstate"""
692 """validate the correctness of the current dirstate"""
693 parent1, parent2 = repo.dirstate.parents()
693 parent1, parent2 = repo.dirstate.parents()
694 m1 = repo.changectx(parent1).manifest()
694 m1 = repo.changectx(parent1).manifest()
695 m2 = repo.changectx(parent2).manifest()
695 m2 = repo.changectx(parent2).manifest()
696 errors = 0
696 errors = 0
697 for f in repo.dirstate:
697 for f in repo.dirstate:
698 state = repo.dirstate[f]
698 state = repo.dirstate[f]
699 if state in "nr" and f not in m1:
699 if state in "nr" and f not in m1:
700 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
700 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
701 errors += 1
701 errors += 1
702 if state in "a" and f in m1:
702 if state in "a" and f in m1:
703 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
703 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
704 errors += 1
704 errors += 1
705 if state in "m" and f not in m1 and f not in m2:
705 if state in "m" and f not in m1 and f not in m2:
706 ui.warn(_("%s in state %s, but not in either manifest\n") %
706 ui.warn(_("%s in state %s, but not in either manifest\n") %
707 (f, state))
707 (f, state))
708 errors += 1
708 errors += 1
709 for f in m1:
709 for f in m1:
710 state = repo.dirstate[f]
710 state = repo.dirstate[f]
711 if state not in "nrm":
711 if state not in "nrm":
712 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
712 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
713 errors += 1
713 errors += 1
714 if errors:
714 if errors:
715 error = _(".hg/dirstate inconsistent with current parent's manifest")
715 error = _(".hg/dirstate inconsistent with current parent's manifest")
716 raise util.Abort(error)
716 raise util.Abort(error)
717
717
718 def showconfig(ui, repo, *values, **opts):
718 def showconfig(ui, repo, *values, **opts):
719 """show combined config settings from all hgrc files
719 """show combined config settings from all hgrc files
720
720
721 With no args, print names and values of all config items.
721 With no args, print names and values of all config items.
722
722
723 With one arg of the form section.name, print just the value of
723 With one arg of the form section.name, print just the value of
724 that config item.
724 that config item.
725
725
726 With multiple args, print names and values of all config items
726 With multiple args, print names and values of all config items
727 with matching section names."""
727 with matching section names."""
728
728
729 untrusted = bool(opts.get('untrusted'))
729 untrusted = bool(opts.get('untrusted'))
730 if values:
730 if values:
731 if len([v for v in values if '.' in v]) > 1:
731 if len([v for v in values if '.' in v]) > 1:
732 raise util.Abort(_('only one config item permitted'))
732 raise util.Abort(_('only one config item permitted'))
733 for section, name, value in ui.walkconfig(untrusted=untrusted):
733 for section, name, value in ui.walkconfig(untrusted=untrusted):
734 sectname = section + '.' + name
734 sectname = section + '.' + name
735 if values:
735 if values:
736 for v in values:
736 for v in values:
737 if v == section:
737 if v == section:
738 ui.write('%s=%s\n' % (sectname, value))
738 ui.write('%s=%s\n' % (sectname, value))
739 elif v == sectname:
739 elif v == sectname:
740 ui.write(value, '\n')
740 ui.write(value, '\n')
741 else:
741 else:
742 ui.write('%s=%s\n' % (sectname, value))
742 ui.write('%s=%s\n' % (sectname, value))
743
743
744 def debugsetparents(ui, repo, rev1, rev2=None):
744 def debugsetparents(ui, repo, rev1, rev2=None):
745 """manually set the parents of the current working directory
745 """manually set the parents of the current working directory
746
746
747 This is useful for writing repository conversion tools, but should
747 This is useful for writing repository conversion tools, but should
748 be used with care.
748 be used with care.
749 """
749 """
750
750
751 if not rev2:
751 if not rev2:
752 rev2 = hex(nullid)
752 rev2 = hex(nullid)
753
753
754 wlock = repo.wlock()
754 wlock = repo.wlock()
755 try:
755 try:
756 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
756 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
757 finally:
757 finally:
758 del wlock
758 del wlock
759
759
760 def debugstate(ui, repo):
760 def debugstate(ui, repo):
761 """show the contents of the current dirstate"""
761 """show the contents of the current dirstate"""
762 dc = repo.dirstate._map
762 dc = repo.dirstate._map
763 k = dc.keys()
763 k = dc.keys()
764 k.sort()
764 k.sort()
765 for file_ in k:
765 for file_ in k:
766 if dc[file_][3] == -1:
766 if dc[file_][3] == -1:
767 # Pad or slice to locale representation
767 # Pad or slice to locale representation
768 locale_len = len(time.strftime("%x %X", time.localtime(0)))
768 locale_len = len(time.strftime("%x %X", time.localtime(0)))
769 timestr = 'unset'
769 timestr = 'unset'
770 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
770 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
771 else:
771 else:
772 timestr = time.strftime("%x %X", time.localtime(dc[file_][3]))
772 timestr = time.strftime("%x %X", time.localtime(dc[file_][3]))
773 ui.write("%c %3o %10d %s %s\n"
773 ui.write("%c %3o %10d %s %s\n"
774 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
774 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
775 timestr, file_))
775 timestr, file_))
776 for f in repo.dirstate.copies():
776 for f in repo.dirstate.copies():
777 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
777 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
778
778
779 def debugdata(ui, file_, rev):
779 def debugdata(ui, file_, rev):
780 """dump the contents of a data file revision"""
780 """dump the contents of a data file revision"""
781 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
781 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
782 try:
782 try:
783 ui.write(r.revision(r.lookup(rev)))
783 ui.write(r.revision(r.lookup(rev)))
784 except KeyError:
784 except KeyError:
785 raise util.Abort(_('invalid revision identifier %s') % rev)
785 raise util.Abort(_('invalid revision identifier %s') % rev)
786
786
787 def debugdate(ui, date, range=None, **opts):
787 def debugdate(ui, date, range=None, **opts):
788 """parse and display a date"""
788 """parse and display a date"""
789 if opts["extended"]:
789 if opts["extended"]:
790 d = util.parsedate(date, util.extendeddateformats)
790 d = util.parsedate(date, util.extendeddateformats)
791 else:
791 else:
792 d = util.parsedate(date)
792 d = util.parsedate(date)
793 ui.write("internal: %s %s\n" % d)
793 ui.write("internal: %s %s\n" % d)
794 ui.write("standard: %s\n" % util.datestr(d))
794 ui.write("standard: %s\n" % util.datestr(d))
795 if range:
795 if range:
796 m = util.matchdate(range)
796 m = util.matchdate(range)
797 ui.write("match: %s\n" % m(d[0]))
797 ui.write("match: %s\n" % m(d[0]))
798
798
799 def debugindex(ui, file_):
799 def debugindex(ui, file_):
800 """dump the contents of an index file"""
800 """dump the contents of an index file"""
801 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
801 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
802 ui.write(" rev offset length base linkrev" +
802 ui.write(" rev offset length base linkrev" +
803 " nodeid p1 p2\n")
803 " nodeid p1 p2\n")
804 for i in xrange(r.count()):
804 for i in xrange(r.count()):
805 node = r.node(i)
805 node = r.node(i)
806 pp = r.parents(node)
806 pp = r.parents(node)
807 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
807 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
808 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
808 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
809 short(node), short(pp[0]), short(pp[1])))
809 short(node), short(pp[0]), short(pp[1])))
810
810
811 def debugindexdot(ui, file_):
811 def debugindexdot(ui, file_):
812 """dump an index DAG as a .dot file"""
812 """dump an index DAG as a .dot file"""
813 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
813 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
814 ui.write("digraph G {\n")
814 ui.write("digraph G {\n")
815 for i in xrange(r.count()):
815 for i in xrange(r.count()):
816 node = r.node(i)
816 node = r.node(i)
817 pp = r.parents(node)
817 pp = r.parents(node)
818 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
818 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
819 if pp[1] != nullid:
819 if pp[1] != nullid:
820 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
820 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
821 ui.write("}\n")
821 ui.write("}\n")
822
822
823 def debuginstall(ui):
823 def debuginstall(ui):
824 '''test Mercurial installation'''
824 '''test Mercurial installation'''
825
825
826 def writetemp(contents):
826 def writetemp(contents):
827 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
827 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
828 f = os.fdopen(fd, "wb")
828 f = os.fdopen(fd, "wb")
829 f.write(contents)
829 f.write(contents)
830 f.close()
830 f.close()
831 return name
831 return name
832
832
833 problems = 0
833 problems = 0
834
834
835 # encoding
835 # encoding
836 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
836 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
837 try:
837 try:
838 util.fromlocal("test")
838 util.fromlocal("test")
839 except util.Abort, inst:
839 except util.Abort, inst:
840 ui.write(" %s\n" % inst)
840 ui.write(" %s\n" % inst)
841 ui.write(_(" (check that your locale is properly set)\n"))
841 ui.write(_(" (check that your locale is properly set)\n"))
842 problems += 1
842 problems += 1
843
843
844 # compiled modules
844 # compiled modules
845 ui.status(_("Checking extensions...\n"))
845 ui.status(_("Checking extensions...\n"))
846 try:
846 try:
847 import bdiff, mpatch, base85
847 import bdiff, mpatch, base85
848 except Exception, inst:
848 except Exception, inst:
849 ui.write(" %s\n" % inst)
849 ui.write(" %s\n" % inst)
850 ui.write(_(" One or more extensions could not be found"))
850 ui.write(_(" One or more extensions could not be found"))
851 ui.write(_(" (check that you compiled the extensions)\n"))
851 ui.write(_(" (check that you compiled the extensions)\n"))
852 problems += 1
852 problems += 1
853
853
854 # templates
854 # templates
855 ui.status(_("Checking templates...\n"))
855 ui.status(_("Checking templates...\n"))
856 try:
856 try:
857 import templater
857 import templater
858 t = templater.templater(templater.templatepath("map-cmdline.default"))
858 t = templater.templater(templater.templatepath("map-cmdline.default"))
859 except Exception, inst:
859 except Exception, inst:
860 ui.write(" %s\n" % inst)
860 ui.write(" %s\n" % inst)
861 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
861 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
862 problems += 1
862 problems += 1
863
863
864 # patch
864 # patch
865 ui.status(_("Checking patch...\n"))
865 ui.status(_("Checking patch...\n"))
866 patcher = ui.config('ui', 'patch')
866 patcher = ui.config('ui', 'patch')
867 patcher = ((patcher and util.find_exe(patcher)) or
867 patcher = ((patcher and util.find_exe(patcher)) or
868 util.find_exe('gpatch') or
868 util.find_exe('gpatch') or
869 util.find_exe('patch'))
869 util.find_exe('patch'))
870 if not patcher:
870 if not patcher:
871 ui.write(_(" Can't find patch or gpatch in PATH\n"))
871 ui.write(_(" Can't find patch or gpatch in PATH\n"))
872 ui.write(_(" (specify a patch utility in your .hgrc file)\n"))
872 ui.write(_(" (specify a patch utility in your .hgrc file)\n"))
873 problems += 1
873 problems += 1
874 else:
874 else:
875 # actually attempt a patch here
875 # actually attempt a patch here
876 a = "1\n2\n3\n4\n"
876 a = "1\n2\n3\n4\n"
877 b = "1\n2\n3\ninsert\n4\n"
877 b = "1\n2\n3\ninsert\n4\n"
878 fa = writetemp(a)
878 fa = writetemp(a)
879 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa))
879 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa))
880 fd = writetemp(d)
880 fd = writetemp(d)
881
881
882 files = {}
882 files = {}
883 try:
883 try:
884 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
884 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
885 except util.Abort, e:
885 except util.Abort, e:
886 ui.write(_(" patch call failed:\n"))
886 ui.write(_(" patch call failed:\n"))
887 ui.write(" " + str(e) + "\n")
887 ui.write(" " + str(e) + "\n")
888 problems += 1
888 problems += 1
889 else:
889 else:
890 if list(files) != [os.path.basename(fa)]:
890 if list(files) != [os.path.basename(fa)]:
891 ui.write(_(" unexpected patch output!"))
891 ui.write(_(" unexpected patch output!"))
892 ui.write(_(" (you may have an incompatible version of patch)\n"))
892 ui.write(_(" (you may have an incompatible version of patch)\n"))
893 problems += 1
893 problems += 1
894 a = file(fa).read()
894 a = file(fa).read()
895 if a != b:
895 if a != b:
896 ui.write(_(" patch test failed!"))
896 ui.write(_(" patch test failed!"))
897 ui.write(_(" (you may have an incompatible version of patch)\n"))
897 ui.write(_(" (you may have an incompatible version of patch)\n"))
898 problems += 1
898 problems += 1
899
899
900 os.unlink(fa)
900 os.unlink(fa)
901 os.unlink(fd)
901 os.unlink(fd)
902
902
903 # merge helper
903 # merge helper
904 ui.status(_("Checking merge helper...\n"))
904 ui.status(_("Checking merge helper...\n"))
905 cmd = (os.environ.get("HGMERGE") or ui.config("ui", "merge")
905 cmd = (os.environ.get("HGMERGE") or ui.config("ui", "merge")
906 or "hgmerge")
906 or "hgmerge")
907 cmdpath = util.find_exe(cmd) or util.find_exe(cmd.split()[0])
907 cmdpath = util.find_exe(cmd) or util.find_exe(cmd.split()[0])
908 if not cmdpath:
908 if not cmdpath:
909 if cmd == 'hgmerge':
909 if cmd == 'hgmerge':
910 ui.write(_(" No merge helper set and can't find default"
910 ui.write(_(" No merge helper set and can't find default"
911 " hgmerge script in PATH\n"))
911 " hgmerge script in PATH\n"))
912 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
912 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
913 else:
913 else:
914 ui.write(_(" Can't find merge helper '%s' in PATH\n") % cmd)
914 ui.write(_(" Can't find merge helper '%s' in PATH\n") % cmd)
915 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
915 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
916 problems += 1
916 problems += 1
917 else:
917 else:
918 # actually attempt a patch here
918 # actually attempt a patch here
919 fa = writetemp("1\n2\n3\n4\n")
919 fa = writetemp("1\n2\n3\n4\n")
920 fl = writetemp("1\n2\n3\ninsert\n4\n")
920 fl = writetemp("1\n2\n3\ninsert\n4\n")
921 fr = writetemp("begin\n1\n2\n3\n4\n")
921 fr = writetemp("begin\n1\n2\n3\n4\n")
922 r = util.system('%s "%s" "%s" "%s"' % (cmd, fl, fa, fr))
922 r = util.system('%s "%s" "%s" "%s"' % (cmd, fl, fa, fr))
923 if r:
923 if r:
924 ui.write(_(" Got unexpected merge error %d!\n") % r)
924 ui.write(_(" Got unexpected merge error %d!\n") % r)
925 problems += 1
925 problems += 1
926 m = file(fl).read()
926 m = file(fl).read()
927 if m != "begin\n1\n2\n3\ninsert\n4\n":
927 if m != "begin\n1\n2\n3\ninsert\n4\n":
928 ui.write(_(" Got unexpected merge results!\n"))
928 ui.write(_(" Got unexpected merge results!\n"))
929 ui.write(_(" (your merge helper may have the"
929 ui.write(_(" (your merge helper may have the"
930 " wrong argument order)\n"))
930 " wrong argument order)\n"))
931 ui.write(_(" Result: %r\n") % m)
931 ui.write(_(" Result: %r\n") % m)
932 problems += 1
932 problems += 1
933 os.unlink(fa)
933 os.unlink(fa)
934 os.unlink(fl)
934 os.unlink(fl)
935 os.unlink(fr)
935 os.unlink(fr)
936
936
937 # editor
937 # editor
938 ui.status(_("Checking commit editor...\n"))
938 ui.status(_("Checking commit editor...\n"))
939 editor = (os.environ.get("HGEDITOR") or
939 editor = (os.environ.get("HGEDITOR") or
940 ui.config("ui", "editor") or
940 ui.config("ui", "editor") or
941 os.environ.get("EDITOR", "vi"))
941 os.environ.get("EDITOR", "vi"))
942 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
942 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
943 if not cmdpath:
943 if not cmdpath:
944 if editor == 'vi':
944 if editor == 'vi':
945 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
945 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
946 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
946 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
947 else:
947 else:
948 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
948 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
949 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
949 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
950 problems += 1
950 problems += 1
951
951
952 # check username
952 # check username
953 ui.status(_("Checking username...\n"))
953 ui.status(_("Checking username...\n"))
954 user = os.environ.get("HGUSER")
954 user = os.environ.get("HGUSER")
955 if user is None:
955 if user is None:
956 user = ui.config("ui", "username")
956 user = ui.config("ui", "username")
957 if user is None:
957 if user is None:
958 user = os.environ.get("EMAIL")
958 user = os.environ.get("EMAIL")
959 if not user:
959 if not user:
960 ui.warn(" ")
960 ui.warn(" ")
961 ui.username()
961 ui.username()
962 ui.write(_(" (specify a username in your .hgrc file)\n"))
962 ui.write(_(" (specify a username in your .hgrc file)\n"))
963
963
964 if not problems:
964 if not problems:
965 ui.status(_("No problems detected\n"))
965 ui.status(_("No problems detected\n"))
966 else:
966 else:
967 ui.write(_("%s problems detected,"
967 ui.write(_("%s problems detected,"
968 " please check your install!\n") % problems)
968 " please check your install!\n") % problems)
969
969
970 return problems
970 return problems
971
971
972 def debugrename(ui, repo, file1, *pats, **opts):
972 def debugrename(ui, repo, file1, *pats, **opts):
973 """dump rename information"""
973 """dump rename information"""
974
974
975 ctx = repo.changectx(opts.get('rev', 'tip'))
975 ctx = repo.changectx(opts.get('rev', 'tip'))
976 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
976 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
977 ctx.node()):
977 ctx.node()):
978 m = ctx.filectx(abs).renamed()
978 m = ctx.filectx(abs).renamed()
979 if m:
979 if m:
980 ui.write(_("%s renamed from %s:%s\n") % (rel, m[0], hex(m[1])))
980 ui.write(_("%s renamed from %s:%s\n") % (rel, m[0], hex(m[1])))
981 else:
981 else:
982 ui.write(_("%s not renamed\n") % rel)
982 ui.write(_("%s not renamed\n") % rel)
983
983
984 def debugwalk(ui, repo, *pats, **opts):
984 def debugwalk(ui, repo, *pats, **opts):
985 """show how files match on given patterns"""
985 """show how files match on given patterns"""
986 items = list(cmdutil.walk(repo, pats, opts))
986 items = list(cmdutil.walk(repo, pats, opts))
987 if not items:
987 if not items:
988 return
988 return
989 fmt = '%%s %%-%ds %%-%ds %%s' % (
989 fmt = '%%s %%-%ds %%-%ds %%s' % (
990 max([len(abs) for (src, abs, rel, exact) in items]),
990 max([len(abs) for (src, abs, rel, exact) in items]),
991 max([len(rel) for (src, abs, rel, exact) in items]))
991 max([len(rel) for (src, abs, rel, exact) in items]))
992 for src, abs, rel, exact in items:
992 for src, abs, rel, exact in items:
993 line = fmt % (src, abs, rel, exact and 'exact' or '')
993 line = fmt % (src, abs, rel, exact and 'exact' or '')
994 ui.write("%s\n" % line.rstrip())
994 ui.write("%s\n" % line.rstrip())
995
995
996 def diff(ui, repo, *pats, **opts):
996 def diff(ui, repo, *pats, **opts):
997 """diff repository (or selected files)
997 """diff repository (or selected files)
998
998
999 Show differences between revisions for the specified files.
999 Show differences between revisions for the specified files.
1000
1000
1001 Differences between files are shown using the unified diff format.
1001 Differences between files are shown using the unified diff format.
1002
1002
1003 NOTE: diff may generate unexpected results for merges, as it will
1003 NOTE: diff may generate unexpected results for merges, as it will
1004 default to comparing against the working directory's first parent
1004 default to comparing against the working directory's first parent
1005 changeset if no revisions are specified.
1005 changeset if no revisions are specified.
1006
1006
1007 When two revision arguments are given, then changes are shown
1007 When two revision arguments are given, then changes are shown
1008 between those revisions. If only one revision is specified then
1008 between those revisions. If only one revision is specified then
1009 that revision is compared to the working directory, and, when no
1009 that revision is compared to the working directory, and, when no
1010 revisions are specified, the working directory files are compared
1010 revisions are specified, the working directory files are compared
1011 to its parent.
1011 to its parent.
1012
1012
1013 Without the -a option, diff will avoid generating diffs of files
1013 Without the -a option, diff will avoid generating diffs of files
1014 it detects as binary. With -a, diff will generate a diff anyway,
1014 it detects as binary. With -a, diff will generate a diff anyway,
1015 probably with undesirable results.
1015 probably with undesirable results.
1016 """
1016 """
1017 node1, node2 = cmdutil.revpair(repo, opts['rev'])
1017 node1, node2 = cmdutil.revpair(repo, opts['rev'])
1018
1018
1019 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1019 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1020
1020
1021 patch.diff(repo, node1, node2, fns, match=matchfn,
1021 patch.diff(repo, node1, node2, fns, match=matchfn,
1022 opts=patch.diffopts(ui, opts))
1022 opts=patch.diffopts(ui, opts))
1023
1023
1024 def export(ui, repo, *changesets, **opts):
1024 def export(ui, repo, *changesets, **opts):
1025 """dump the header and diffs for one or more changesets
1025 """dump the header and diffs for one or more changesets
1026
1026
1027 Print the changeset header and diffs for one or more revisions.
1027 Print the changeset header and diffs for one or more revisions.
1028
1028
1029 The information shown in the changeset header is: author,
1029 The information shown in the changeset header is: author,
1030 changeset hash, parent(s) and commit comment.
1030 changeset hash, parent(s) and commit comment.
1031
1031
1032 NOTE: export may generate unexpected diff output for merge changesets,
1032 NOTE: export may generate unexpected diff output for merge changesets,
1033 as it will compare the merge changeset against its first parent only.
1033 as it will compare the merge changeset against its first parent only.
1034
1034
1035 Output may be to a file, in which case the name of the file is
1035 Output may be to a file, in which case the name of the file is
1036 given using a format string. The formatting rules are as follows:
1036 given using a format string. The formatting rules are as follows:
1037
1037
1038 %% literal "%" character
1038 %% literal "%" character
1039 %H changeset hash (40 bytes of hexadecimal)
1039 %H changeset hash (40 bytes of hexadecimal)
1040 %N number of patches being generated
1040 %N number of patches being generated
1041 %R changeset revision number
1041 %R changeset revision number
1042 %b basename of the exporting repository
1042 %b basename of the exporting repository
1043 %h short-form changeset hash (12 bytes of hexadecimal)
1043 %h short-form changeset hash (12 bytes of hexadecimal)
1044 %n zero-padded sequence number, starting at 1
1044 %n zero-padded sequence number, starting at 1
1045 %r zero-padded changeset revision number
1045 %r zero-padded changeset revision number
1046
1046
1047 Without the -a option, export will avoid generating diffs of files
1047 Without the -a option, export will avoid generating diffs of files
1048 it detects as binary. With -a, export will generate a diff anyway,
1048 it detects as binary. With -a, export will generate a diff anyway,
1049 probably with undesirable results.
1049 probably with undesirable results.
1050
1050
1051 With the --switch-parent option, the diff will be against the second
1051 With the --switch-parent option, the diff will be against the second
1052 parent. It can be useful to review a merge.
1052 parent. It can be useful to review a merge.
1053 """
1053 """
1054 if not changesets:
1054 if not changesets:
1055 raise util.Abort(_("export requires at least one changeset"))
1055 raise util.Abort(_("export requires at least one changeset"))
1056 revs = cmdutil.revrange(repo, changesets)
1056 revs = cmdutil.revrange(repo, changesets)
1057 if len(revs) > 1:
1057 if len(revs) > 1:
1058 ui.note(_('exporting patches:\n'))
1058 ui.note(_('exporting patches:\n'))
1059 else:
1059 else:
1060 ui.note(_('exporting patch:\n'))
1060 ui.note(_('exporting patch:\n'))
1061 patch.export(repo, revs, template=opts['output'],
1061 patch.export(repo, revs, template=opts['output'],
1062 switch_parent=opts['switch_parent'],
1062 switch_parent=opts['switch_parent'],
1063 opts=patch.diffopts(ui, opts))
1063 opts=patch.diffopts(ui, opts))
1064
1064
1065 def grep(ui, repo, pattern, *pats, **opts):
1065 def grep(ui, repo, pattern, *pats, **opts):
1066 """search for a pattern in specified files and revisions
1066 """search for a pattern in specified files and revisions
1067
1067
1068 Search revisions of files for a regular expression.
1068 Search revisions of files for a regular expression.
1069
1069
1070 This command behaves differently than Unix grep. It only accepts
1070 This command behaves differently than Unix grep. It only accepts
1071 Python/Perl regexps. It searches repository history, not the
1071 Python/Perl regexps. It searches repository history, not the
1072 working directory. It always prints the revision number in which
1072 working directory. It always prints the revision number in which
1073 a match appears.
1073 a match appears.
1074
1074
1075 By default, grep only prints output for the first revision of a
1075 By default, grep only prints output for the first revision of a
1076 file in which it finds a match. To get it to print every revision
1076 file in which it finds a match. To get it to print every revision
1077 that contains a change in match status ("-" for a match that
1077 that contains a change in match status ("-" for a match that
1078 becomes a non-match, or "+" for a non-match that becomes a match),
1078 becomes a non-match, or "+" for a non-match that becomes a match),
1079 use the --all flag.
1079 use the --all flag.
1080 """
1080 """
1081 reflags = 0
1081 reflags = 0
1082 if opts['ignore_case']:
1082 if opts['ignore_case']:
1083 reflags |= re.I
1083 reflags |= re.I
1084 try:
1084 try:
1085 regexp = re.compile(pattern, reflags)
1085 regexp = re.compile(pattern, reflags)
1086 except Exception, inst:
1086 except Exception, inst:
1087 ui.warn(_("grep: invalid match pattern: %s!\n") % inst)
1087 ui.warn(_("grep: invalid match pattern: %s!\n") % inst)
1088 return None
1088 return None
1089 sep, eol = ':', '\n'
1089 sep, eol = ':', '\n'
1090 if opts['print0']:
1090 if opts['print0']:
1091 sep = eol = '\0'
1091 sep = eol = '\0'
1092
1092
1093 fcache = {}
1093 fcache = {}
1094 def getfile(fn):
1094 def getfile(fn):
1095 if fn not in fcache:
1095 if fn not in fcache:
1096 fcache[fn] = repo.file(fn)
1096 fcache[fn] = repo.file(fn)
1097 return fcache[fn]
1097 return fcache[fn]
1098
1098
1099 def matchlines(body):
1099 def matchlines(body):
1100 begin = 0
1100 begin = 0
1101 linenum = 0
1101 linenum = 0
1102 while True:
1102 while True:
1103 match = regexp.search(body, begin)
1103 match = regexp.search(body, begin)
1104 if not match:
1104 if not match:
1105 break
1105 break
1106 mstart, mend = match.span()
1106 mstart, mend = match.span()
1107 linenum += body.count('\n', begin, mstart) + 1
1107 linenum += body.count('\n', begin, mstart) + 1
1108 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1108 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1109 lend = body.find('\n', mend)
1109 lend = body.find('\n', mend)
1110 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1110 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1111 begin = lend + 1
1111 begin = lend + 1
1112
1112
1113 class linestate(object):
1113 class linestate(object):
1114 def __init__(self, line, linenum, colstart, colend):
1114 def __init__(self, line, linenum, colstart, colend):
1115 self.line = line
1115 self.line = line
1116 self.linenum = linenum
1116 self.linenum = linenum
1117 self.colstart = colstart
1117 self.colstart = colstart
1118 self.colend = colend
1118 self.colend = colend
1119
1119
1120 def __eq__(self, other):
1120 def __eq__(self, other):
1121 return self.line == other.line
1121 return self.line == other.line
1122
1122
1123 matches = {}
1123 matches = {}
1124 copies = {}
1124 copies = {}
1125 def grepbody(fn, rev, body):
1125 def grepbody(fn, rev, body):
1126 matches[rev].setdefault(fn, [])
1126 matches[rev].setdefault(fn, [])
1127 m = matches[rev][fn]
1127 m = matches[rev][fn]
1128 for lnum, cstart, cend, line in matchlines(body):
1128 for lnum, cstart, cend, line in matchlines(body):
1129 s = linestate(line, lnum, cstart, cend)
1129 s = linestate(line, lnum, cstart, cend)
1130 m.append(s)
1130 m.append(s)
1131
1131
1132 def difflinestates(a, b):
1132 def difflinestates(a, b):
1133 sm = difflib.SequenceMatcher(None, a, b)
1133 sm = difflib.SequenceMatcher(None, a, b)
1134 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1134 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1135 if tag == 'insert':
1135 if tag == 'insert':
1136 for i in xrange(blo, bhi):
1136 for i in xrange(blo, bhi):
1137 yield ('+', b[i])
1137 yield ('+', b[i])
1138 elif tag == 'delete':
1138 elif tag == 'delete':
1139 for i in xrange(alo, ahi):
1139 for i in xrange(alo, ahi):
1140 yield ('-', a[i])
1140 yield ('-', a[i])
1141 elif tag == 'replace':
1141 elif tag == 'replace':
1142 for i in xrange(alo, ahi):
1142 for i in xrange(alo, ahi):
1143 yield ('-', a[i])
1143 yield ('-', a[i])
1144 for i in xrange(blo, bhi):
1144 for i in xrange(blo, bhi):
1145 yield ('+', b[i])
1145 yield ('+', b[i])
1146
1146
1147 prev = {}
1147 prev = {}
1148 def display(fn, rev, states, prevstates):
1148 def display(fn, rev, states, prevstates):
1149 found = False
1149 found = False
1150 filerevmatches = {}
1150 filerevmatches = {}
1151 r = prev.get(fn, -1)
1151 r = prev.get(fn, -1)
1152 if opts['all']:
1152 if opts['all']:
1153 iter = difflinestates(states, prevstates)
1153 iter = difflinestates(states, prevstates)
1154 else:
1154 else:
1155 iter = [('', l) for l in prevstates]
1155 iter = [('', l) for l in prevstates]
1156 for change, l in iter:
1156 for change, l in iter:
1157 cols = [fn, str(r)]
1157 cols = [fn, str(r)]
1158 if opts['line_number']:
1158 if opts['line_number']:
1159 cols.append(str(l.linenum))
1159 cols.append(str(l.linenum))
1160 if opts['all']:
1160 if opts['all']:
1161 cols.append(change)
1161 cols.append(change)
1162 if opts['user']:
1162 if opts['user']:
1163 cols.append(ui.shortuser(get(r)[1]))
1163 cols.append(ui.shortuser(get(r)[1]))
1164 if opts['files_with_matches']:
1164 if opts['files_with_matches']:
1165 c = (fn, r)
1165 c = (fn, r)
1166 if c in filerevmatches:
1166 if c in filerevmatches:
1167 continue
1167 continue
1168 filerevmatches[c] = 1
1168 filerevmatches[c] = 1
1169 else:
1169 else:
1170 cols.append(l.line)
1170 cols.append(l.line)
1171 ui.write(sep.join(cols), eol)
1171 ui.write(sep.join(cols), eol)
1172 found = True
1172 found = True
1173 return found
1173 return found
1174
1174
1175 fstate = {}
1175 fstate = {}
1176 skip = {}
1176 skip = {}
1177 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1177 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1178 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1178 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1179 found = False
1179 found = False
1180 follow = opts.get('follow')
1180 follow = opts.get('follow')
1181 for st, rev, fns in changeiter:
1181 for st, rev, fns in changeiter:
1182 if st == 'window':
1182 if st == 'window':
1183 matches.clear()
1183 matches.clear()
1184 elif st == 'add':
1184 elif st == 'add':
1185 mf = repo.changectx(rev).manifest()
1185 mf = repo.changectx(rev).manifest()
1186 matches[rev] = {}
1186 matches[rev] = {}
1187 for fn in fns:
1187 for fn in fns:
1188 if fn in skip:
1188 if fn in skip:
1189 continue
1189 continue
1190 fstate.setdefault(fn, {})
1190 fstate.setdefault(fn, {})
1191 try:
1191 try:
1192 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1192 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1193 if follow:
1193 if follow:
1194 copied = getfile(fn).renamed(mf[fn])
1194 copied = getfile(fn).renamed(mf[fn])
1195 if copied:
1195 if copied:
1196 copies.setdefault(rev, {})[fn] = copied[0]
1196 copies.setdefault(rev, {})[fn] = copied[0]
1197 except KeyError:
1197 except KeyError:
1198 pass
1198 pass
1199 elif st == 'iter':
1199 elif st == 'iter':
1200 states = matches[rev].items()
1200 states = matches[rev].items()
1201 states.sort()
1201 states.sort()
1202 for fn, m in states:
1202 for fn, m in states:
1203 copy = copies.get(rev, {}).get(fn)
1203 copy = copies.get(rev, {}).get(fn)
1204 if fn in skip:
1204 if fn in skip:
1205 if copy:
1205 if copy:
1206 skip[copy] = True
1206 skip[copy] = True
1207 continue
1207 continue
1208 if fn in prev or fstate[fn]:
1208 if fn in prev or fstate[fn]:
1209 r = display(fn, rev, m, fstate[fn])
1209 r = display(fn, rev, m, fstate[fn])
1210 found = found or r
1210 found = found or r
1211 if r and not opts['all']:
1211 if r and not opts['all']:
1212 skip[fn] = True
1212 skip[fn] = True
1213 if copy:
1213 if copy:
1214 skip[copy] = True
1214 skip[copy] = True
1215 fstate[fn] = m
1215 fstate[fn] = m
1216 if copy:
1216 if copy:
1217 fstate[copy] = m
1217 fstate[copy] = m
1218 prev[fn] = rev
1218 prev[fn] = rev
1219
1219
1220 fstate = fstate.items()
1220 fstate = fstate.items()
1221 fstate.sort()
1221 fstate.sort()
1222 for fn, state in fstate:
1222 for fn, state in fstate:
1223 if fn in skip:
1223 if fn in skip:
1224 continue
1224 continue
1225 if fn not in copies.get(prev[fn], {}):
1225 if fn not in copies.get(prev[fn], {}):
1226 found = display(fn, rev, {}, state) or found
1226 found = display(fn, rev, {}, state) or found
1227 return (not found and 1) or 0
1227 return (not found and 1) or 0
1228
1228
1229 def heads(ui, repo, *branchrevs, **opts):
1229 def heads(ui, repo, *branchrevs, **opts):
1230 """show current repository heads or show branch heads
1230 """show current repository heads or show branch heads
1231
1231
1232 With no arguments, show all repository head changesets.
1232 With no arguments, show all repository head changesets.
1233
1233
1234 If branch or revisions names are given this will show the heads of
1234 If branch or revisions names are given this will show the heads of
1235 the specified branches or the branches those revisions are tagged
1235 the specified branches or the branches those revisions are tagged
1236 with.
1236 with.
1237
1237
1238 Repository "heads" are changesets that don't have child
1238 Repository "heads" are changesets that don't have child
1239 changesets. They are where development generally takes place and
1239 changesets. They are where development generally takes place and
1240 are the usual targets for update and merge operations.
1240 are the usual targets for update and merge operations.
1241
1241
1242 Branch heads are changesets that have a given branch tag, but have
1242 Branch heads are changesets that have a given branch tag, but have
1243 no child changesets with that tag. They are usually where
1243 no child changesets with that tag. They are usually where
1244 development on the given branch takes place.
1244 development on the given branch takes place.
1245 """
1245 """
1246 if opts['rev']:
1246 if opts['rev']:
1247 start = repo.lookup(opts['rev'])
1247 start = repo.lookup(opts['rev'])
1248 else:
1248 else:
1249 start = None
1249 start = None
1250 if not branchrevs:
1250 if not branchrevs:
1251 # Assume we're looking repo-wide heads if no revs were specified.
1251 # Assume we're looking repo-wide heads if no revs were specified.
1252 heads = repo.heads(start)
1252 heads = repo.heads(start)
1253 else:
1253 else:
1254 heads = []
1254 heads = []
1255 visitedset = util.set()
1255 visitedset = util.set()
1256 for branchrev in branchrevs:
1256 for branchrev in branchrevs:
1257 branch = repo.changectx(branchrev).branch()
1257 branch = repo.changectx(branchrev).branch()
1258 if branch in visitedset:
1258 if branch in visitedset:
1259 continue
1259 continue
1260 visitedset.add(branch)
1260 visitedset.add(branch)
1261 bheads = repo.branchheads(branch, start)
1261 bheads = repo.branchheads(branch, start)
1262 if not bheads:
1262 if not bheads:
1263 if branch != branchrev:
1263 if branch != branchrev:
1264 ui.warn(_("no changes on branch %s containing %s are "
1264 ui.warn(_("no changes on branch %s containing %s are "
1265 "reachable from %s\n")
1265 "reachable from %s\n")
1266 % (branch, branchrev, opts['rev']))
1266 % (branch, branchrev, opts['rev']))
1267 else:
1267 else:
1268 ui.warn(_("no changes on branch %s are reachable from %s\n")
1268 ui.warn(_("no changes on branch %s are reachable from %s\n")
1269 % (branch, opts['rev']))
1269 % (branch, opts['rev']))
1270 heads.extend(bheads)
1270 heads.extend(bheads)
1271 if not heads:
1271 if not heads:
1272 return 1
1272 return 1
1273 displayer = cmdutil.show_changeset(ui, repo, opts)
1273 displayer = cmdutil.show_changeset(ui, repo, opts)
1274 for n in heads:
1274 for n in heads:
1275 displayer.show(changenode=n)
1275 displayer.show(changenode=n)
1276
1276
1277 def help_(ui, name=None, with_version=False):
1277 def help_(ui, name=None, with_version=False):
1278 """show help for a command, extension, or list of commands
1278 """show help for a command, extension, or list of commands
1279
1279
1280 With no arguments, print a list of commands and short help.
1280 With no arguments, print a list of commands and short help.
1281
1281
1282 Given a command name, print help for that command.
1282 Given a command name, print help for that command.
1283
1283
1284 Given an extension name, print help for that extension, and the
1284 Given an extension name, print help for that extension, and the
1285 commands it provides."""
1285 commands it provides."""
1286 option_lists = []
1286 option_lists = []
1287
1287
1288 def addglobalopts(aliases):
1288 def addglobalopts(aliases):
1289 if ui.verbose:
1289 if ui.verbose:
1290 option_lists.append((_("global options:"), globalopts))
1290 option_lists.append((_("global options:"), globalopts))
1291 if name == 'shortlist':
1291 if name == 'shortlist':
1292 option_lists.append((_('use "hg help" for the full list '
1292 option_lists.append((_('use "hg help" for the full list '
1293 'of commands'), ()))
1293 'of commands'), ()))
1294 else:
1294 else:
1295 if name == 'shortlist':
1295 if name == 'shortlist':
1296 msg = _('use "hg help" for the full list of commands '
1296 msg = _('use "hg help" for the full list of commands '
1297 'or "hg -v" for details')
1297 'or "hg -v" for details')
1298 elif aliases:
1298 elif aliases:
1299 msg = _('use "hg -v help%s" to show aliases and '
1299 msg = _('use "hg -v help%s" to show aliases and '
1300 'global options') % (name and " " + name or "")
1300 'global options') % (name and " " + name or "")
1301 else:
1301 else:
1302 msg = _('use "hg -v help %s" to show global options') % name
1302 msg = _('use "hg -v help %s" to show global options') % name
1303 option_lists.append((msg, ()))
1303 option_lists.append((msg, ()))
1304
1304
1305 def helpcmd(name):
1305 def helpcmd(name):
1306 if with_version:
1306 if with_version:
1307 version_(ui)
1307 version_(ui)
1308 ui.write('\n')
1308 ui.write('\n')
1309 aliases, i = cmdutil.findcmd(ui, name)
1309 aliases, i = cmdutil.findcmd(ui, name)
1310 # synopsis
1310 # synopsis
1311 ui.write("%s\n\n" % i[2])
1311 ui.write("%s\n\n" % i[2])
1312
1312
1313 # description
1313 # description
1314 doc = i[0].__doc__
1314 doc = i[0].__doc__
1315 if not doc:
1315 if not doc:
1316 doc = _("(No help text available)")
1316 doc = _("(No help text available)")
1317 if ui.quiet:
1317 if ui.quiet:
1318 doc = doc.splitlines(0)[0]
1318 doc = doc.splitlines(0)[0]
1319 ui.write("%s\n" % doc.rstrip())
1319 ui.write("%s\n" % doc.rstrip())
1320
1320
1321 if not ui.quiet:
1321 if not ui.quiet:
1322 # aliases
1322 # aliases
1323 if len(aliases) > 1:
1323 if len(aliases) > 1:
1324 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1324 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1325
1325
1326 # options
1326 # options
1327 if i[1]:
1327 if i[1]:
1328 option_lists.append((_("options:\n"), i[1]))
1328 option_lists.append((_("options:\n"), i[1]))
1329
1329
1330 addglobalopts(False)
1330 addglobalopts(False)
1331
1331
1332 def helplist(header, select=None):
1332 def helplist(header, select=None):
1333 h = {}
1333 h = {}
1334 cmds = {}
1334 cmds = {}
1335 for c, e in table.items():
1335 for c, e in table.items():
1336 f = c.split("|", 1)[0]
1336 f = c.split("|", 1)[0]
1337 if select and not select(f):
1337 if select and not select(f):
1338 continue
1338 continue
1339 if name == "shortlist" and not f.startswith("^"):
1339 if name == "shortlist" and not f.startswith("^"):
1340 continue
1340 continue
1341 f = f.lstrip("^")
1341 f = f.lstrip("^")
1342 if not ui.debugflag and f.startswith("debug"):
1342 if not ui.debugflag and f.startswith("debug"):
1343 continue
1343 continue
1344 doc = e[0].__doc__
1344 doc = e[0].__doc__
1345 if not doc:
1345 if not doc:
1346 doc = _("(No help text available)")
1346 doc = _("(No help text available)")
1347 h[f] = doc.splitlines(0)[0].rstrip()
1347 h[f] = doc.splitlines(0)[0].rstrip()
1348 cmds[f] = c.lstrip("^")
1348 cmds[f] = c.lstrip("^")
1349
1349
1350 if not h:
1350 if not h:
1351 ui.status(_('no commands defined\n'))
1351 ui.status(_('no commands defined\n'))
1352 return
1352 return
1353
1353
1354 ui.status(header)
1354 ui.status(header)
1355 fns = h.keys()
1355 fns = h.keys()
1356 fns.sort()
1356 fns.sort()
1357 m = max(map(len, fns))
1357 m = max(map(len, fns))
1358 for f in fns:
1358 for f in fns:
1359 if ui.verbose:
1359 if ui.verbose:
1360 commands = cmds[f].replace("|",", ")
1360 commands = cmds[f].replace("|",", ")
1361 ui.write(" %s:\n %s\n"%(commands, h[f]))
1361 ui.write(" %s:\n %s\n"%(commands, h[f]))
1362 else:
1362 else:
1363 ui.write(' %-*s %s\n' % (m, f, h[f]))
1363 ui.write(' %-*s %s\n' % (m, f, h[f]))
1364
1364
1365 if not ui.quiet:
1365 if not ui.quiet:
1366 addglobalopts(True)
1366 addglobalopts(True)
1367
1367
1368 def helptopic(name):
1368 def helptopic(name):
1369 v = None
1369 v = None
1370 for i in help.helptable:
1370 for i in help.helptable:
1371 l = i.split('|')
1371 l = i.split('|')
1372 if name in l:
1372 if name in l:
1373 v = i
1373 v = i
1374 header = l[-1]
1374 header = l[-1]
1375 if not v:
1375 if not v:
1376 raise cmdutil.UnknownCommand(name)
1376 raise cmdutil.UnknownCommand(name)
1377
1377
1378 # description
1378 # description
1379 doc = help.helptable[v]
1379 doc = help.helptable[v]
1380 if not doc:
1380 if not doc:
1381 doc = _("(No help text available)")
1381 doc = _("(No help text available)")
1382 if callable(doc):
1382 if callable(doc):
1383 doc = doc()
1383 doc = doc()
1384
1384
1385 ui.write("%s\n" % header)
1385 ui.write("%s\n" % header)
1386 ui.write("%s\n" % doc.rstrip())
1386 ui.write("%s\n" % doc.rstrip())
1387
1387
1388 def helpext(name):
1388 def helpext(name):
1389 try:
1389 try:
1390 mod = extensions.find(name)
1390 mod = extensions.find(name)
1391 except KeyError:
1391 except KeyError:
1392 raise cmdutil.UnknownCommand(name)
1392 raise cmdutil.UnknownCommand(name)
1393
1393
1394 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1394 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1395 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1395 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1396 for d in doc[1:]:
1396 for d in doc[1:]:
1397 ui.write(d, '\n')
1397 ui.write(d, '\n')
1398
1398
1399 ui.status('\n')
1399 ui.status('\n')
1400
1400
1401 try:
1401 try:
1402 ct = mod.cmdtable
1402 ct = mod.cmdtable
1403 except AttributeError:
1403 except AttributeError:
1404 ct = {}
1404 ct = {}
1405
1405
1406 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1406 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1407 helplist(_('list of commands:\n\n'), modcmds.has_key)
1407 helplist(_('list of commands:\n\n'), modcmds.has_key)
1408
1408
1409 if name and name != 'shortlist':
1409 if name and name != 'shortlist':
1410 i = None
1410 i = None
1411 for f in (helpcmd, helptopic, helpext):
1411 for f in (helpcmd, helptopic, helpext):
1412 try:
1412 try:
1413 f(name)
1413 f(name)
1414 i = None
1414 i = None
1415 break
1415 break
1416 except cmdutil.UnknownCommand, inst:
1416 except cmdutil.UnknownCommand, inst:
1417 i = inst
1417 i = inst
1418 if i:
1418 if i:
1419 raise i
1419 raise i
1420
1420
1421 else:
1421 else:
1422 # program name
1422 # program name
1423 if ui.verbose or with_version:
1423 if ui.verbose or with_version:
1424 version_(ui)
1424 version_(ui)
1425 else:
1425 else:
1426 ui.status(_("Mercurial Distributed SCM\n"))
1426 ui.status(_("Mercurial Distributed SCM\n"))
1427 ui.status('\n')
1427 ui.status('\n')
1428
1428
1429 # list of commands
1429 # list of commands
1430 if name == "shortlist":
1430 if name == "shortlist":
1431 header = _('basic commands:\n\n')
1431 header = _('basic commands:\n\n')
1432 else:
1432 else:
1433 header = _('list of commands:\n\n')
1433 header = _('list of commands:\n\n')
1434
1434
1435 helplist(header)
1435 helplist(header)
1436
1436
1437 # list all option lists
1437 # list all option lists
1438 opt_output = []
1438 opt_output = []
1439 for title, options in option_lists:
1439 for title, options in option_lists:
1440 opt_output.append(("\n%s" % title, None))
1440 opt_output.append(("\n%s" % title, None))
1441 for shortopt, longopt, default, desc in options:
1441 for shortopt, longopt, default, desc in options:
1442 if "DEPRECATED" in desc and not ui.verbose: continue
1442 if "DEPRECATED" in desc and not ui.verbose: continue
1443 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1443 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1444 longopt and " --%s" % longopt),
1444 longopt and " --%s" % longopt),
1445 "%s%s" % (desc,
1445 "%s%s" % (desc,
1446 default
1446 default
1447 and _(" (default: %s)") % default
1447 and _(" (default: %s)") % default
1448 or "")))
1448 or "")))
1449
1449
1450 if opt_output:
1450 if opt_output:
1451 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1451 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1452 for first, second in opt_output:
1452 for first, second in opt_output:
1453 if second:
1453 if second:
1454 ui.write(" %-*s %s\n" % (opts_len, first, second))
1454 ui.write(" %-*s %s\n" % (opts_len, first, second))
1455 else:
1455 else:
1456 ui.write("%s\n" % first)
1456 ui.write("%s\n" % first)
1457
1457
1458 def identify(ui, repo, source=None,
1458 def identify(ui, repo, source=None,
1459 rev=None, num=None, id=None, branch=None, tags=None):
1459 rev=None, num=None, id=None, branch=None, tags=None):
1460 """identify the working copy or specified revision
1460 """identify the working copy or specified revision
1461
1461
1462 With no revision, print a summary of the current state of the repo.
1462 With no revision, print a summary of the current state of the repo.
1463
1463
1464 With a path, do a lookup in another repository.
1464 With a path, do a lookup in another repository.
1465
1465
1466 This summary identifies the repository state using one or two parent
1466 This summary identifies the repository state using one or two parent
1467 hash identifiers, followed by a "+" if there are uncommitted changes
1467 hash identifiers, followed by a "+" if there are uncommitted changes
1468 in the working directory, a list of tags for this revision and a branch
1468 in the working directory, a list of tags for this revision and a branch
1469 name for non-default branches.
1469 name for non-default branches.
1470 """
1470 """
1471
1471
1472 hexfunc = ui.debugflag and hex or short
1472 hexfunc = ui.debugflag and hex or short
1473 default = not (num or id or branch or tags)
1473 default = not (num or id or branch or tags)
1474 output = []
1474 output = []
1475
1475
1476 if source:
1476 if source:
1477 source, revs = cmdutil.parseurl(ui.expandpath(source), [])
1477 source, revs = cmdutil.parseurl(ui.expandpath(source), [])
1478 srepo = hg.repository(ui, source)
1478 srepo = hg.repository(ui, source)
1479 if not rev and revs:
1479 if not rev and revs:
1480 rev = revs[0]
1480 rev = revs[0]
1481 if not rev:
1481 if not rev:
1482 rev = "tip"
1482 rev = "tip"
1483 if num or branch or tags:
1483 if num or branch or tags:
1484 raise util.Abort(
1484 raise util.Abort(
1485 "can't query remote revision number, branch, or tags")
1485 "can't query remote revision number, branch, or tags")
1486 output = [hexfunc(srepo.lookup(rev))]
1486 output = [hexfunc(srepo.lookup(rev))]
1487 elif not rev:
1487 elif not rev:
1488 ctx = repo.workingctx()
1488 ctx = repo.workingctx()
1489 parents = ctx.parents()
1489 parents = ctx.parents()
1490 changed = False
1490 changed = False
1491 if default or id or num:
1491 if default or id or num:
1492 changed = ctx.files() + ctx.deleted()
1492 changed = ctx.files() + ctx.deleted()
1493 if default or id:
1493 if default or id:
1494 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1494 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1495 (changed) and "+" or "")]
1495 (changed) and "+" or "")]
1496 if num:
1496 if num:
1497 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1497 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1498 (changed) and "+" or ""))
1498 (changed) and "+" or ""))
1499 else:
1499 else:
1500 ctx = repo.changectx(rev)
1500 ctx = repo.changectx(rev)
1501 if default or id:
1501 if default or id:
1502 output = [hexfunc(ctx.node())]
1502 output = [hexfunc(ctx.node())]
1503 if num:
1503 if num:
1504 output.append(str(ctx.rev()))
1504 output.append(str(ctx.rev()))
1505
1505
1506 if not source and default and not ui.quiet:
1506 if not source and default and not ui.quiet:
1507 b = util.tolocal(ctx.branch())
1507 b = util.tolocal(ctx.branch())
1508 if b != 'default':
1508 if b != 'default':
1509 output.append("(%s)" % b)
1509 output.append("(%s)" % b)
1510
1510
1511 # multiple tags for a single parent separated by '/'
1511 # multiple tags for a single parent separated by '/'
1512 t = "/".join(ctx.tags())
1512 t = "/".join(ctx.tags())
1513 if t:
1513 if t:
1514 output.append(t)
1514 output.append(t)
1515
1515
1516 if branch:
1516 if branch:
1517 output.append(util.tolocal(ctx.branch()))
1517 output.append(util.tolocal(ctx.branch()))
1518
1518
1519 if tags:
1519 if tags:
1520 output.extend(ctx.tags())
1520 output.extend(ctx.tags())
1521
1521
1522 ui.write("%s\n" % ' '.join(output))
1522 ui.write("%s\n" % ' '.join(output))
1523
1523
1524 def import_(ui, repo, patch1, *patches, **opts):
1524 def import_(ui, repo, patch1, *patches, **opts):
1525 """import an ordered set of patches
1525 """import an ordered set of patches
1526
1526
1527 Import a list of patches and commit them individually.
1527 Import a list of patches and commit them individually.
1528
1528
1529 If there are outstanding changes in the working directory, import
1529 If there are outstanding changes in the working directory, import
1530 will abort unless given the -f flag.
1530 will abort unless given the -f flag.
1531
1531
1532 You can import a patch straight from a mail message. Even patches
1532 You can import a patch straight from a mail message. Even patches
1533 as attachments work (body part must be type text/plain or
1533 as attachments work (body part must be type text/plain or
1534 text/x-patch to be used). From and Subject headers of email
1534 text/x-patch to be used). From and Subject headers of email
1535 message are used as default committer and commit message. All
1535 message are used as default committer and commit message. All
1536 text/plain body parts before first diff are added to commit
1536 text/plain body parts before first diff are added to commit
1537 message.
1537 message.
1538
1538
1539 If the imported patch was generated by hg export, user and description
1539 If the imported patch was generated by hg export, user and description
1540 from patch override values from message headers and body. Values
1540 from patch override values from message headers and body. Values
1541 given on command line with -m and -u override these.
1541 given on command line with -m and -u override these.
1542
1542
1543 If --exact is specified, import will set the working directory
1543 If --exact is specified, import will set the working directory
1544 to the parent of each patch before applying it, and will abort
1544 to the parent of each patch before applying it, and will abort
1545 if the resulting changeset has a different ID than the one
1545 if the resulting changeset has a different ID than the one
1546 recorded in the patch. This may happen due to character set
1546 recorded in the patch. This may happen due to character set
1547 problems or other deficiencies in the text patch format.
1547 problems or other deficiencies in the text patch format.
1548
1548
1549 To read a patch from standard input, use patch name "-".
1549 To read a patch from standard input, use patch name "-".
1550 """
1550 """
1551 patches = (patch1,) + patches
1551 patches = (patch1,) + patches
1552
1552
1553 if opts.get('exact') or not opts['force']:
1553 if opts.get('exact') or not opts['force']:
1554 cmdutil.bail_if_changed(repo)
1554 cmdutil.bail_if_changed(repo)
1555
1555
1556 d = opts["base"]
1556 d = opts["base"]
1557 strip = opts["strip"]
1557 strip = opts["strip"]
1558 wlock = lock = None
1558 wlock = lock = None
1559 try:
1559 try:
1560 wlock = repo.wlock()
1560 wlock = repo.wlock()
1561 lock = repo.lock()
1561 lock = repo.lock()
1562 for p in patches:
1562 for p in patches:
1563 pf = os.path.join(d, p)
1563 pf = os.path.join(d, p)
1564
1564
1565 if pf == '-':
1565 if pf == '-':
1566 ui.status(_("applying patch from stdin\n"))
1566 ui.status(_("applying patch from stdin\n"))
1567 data = patch.extract(ui, sys.stdin)
1567 data = patch.extract(ui, sys.stdin)
1568 else:
1568 else:
1569 ui.status(_("applying %s\n") % p)
1569 ui.status(_("applying %s\n") % p)
1570 data = patch.extract(ui, file(pf, 'rb'))
1570 data = patch.extract(ui, file(pf, 'rb'))
1571
1571
1572 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1572 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1573
1573
1574 if tmpname is None:
1574 if tmpname is None:
1575 raise util.Abort(_('no diffs found'))
1575 raise util.Abort(_('no diffs found'))
1576
1576
1577 try:
1577 try:
1578 cmdline_message = cmdutil.logmessage(opts)
1578 cmdline_message = cmdutil.logmessage(opts)
1579 if cmdline_message:
1579 if cmdline_message:
1580 # pickup the cmdline msg
1580 # pickup the cmdline msg
1581 message = cmdline_message
1581 message = cmdline_message
1582 elif message:
1582 elif message:
1583 # pickup the patch msg
1583 # pickup the patch msg
1584 message = message.strip()
1584 message = message.strip()
1585 else:
1585 else:
1586 # launch the editor
1586 # launch the editor
1587 message = None
1587 message = None
1588 ui.debug(_('message:\n%s\n') % message)
1588 ui.debug(_('message:\n%s\n') % message)
1589
1589
1590 wp = repo.workingctx().parents()
1590 wp = repo.workingctx().parents()
1591 if opts.get('exact'):
1591 if opts.get('exact'):
1592 if not nodeid or not p1:
1592 if not nodeid or not p1:
1593 raise util.Abort(_('not a mercurial patch'))
1593 raise util.Abort(_('not a mercurial patch'))
1594 p1 = repo.lookup(p1)
1594 p1 = repo.lookup(p1)
1595 p2 = repo.lookup(p2 or hex(nullid))
1595 p2 = repo.lookup(p2 or hex(nullid))
1596
1596
1597 if p1 != wp[0].node():
1597 if p1 != wp[0].node():
1598 hg.clean(repo, p1)
1598 hg.clean(repo, p1)
1599 repo.dirstate.setparents(p1, p2)
1599 repo.dirstate.setparents(p1, p2)
1600 elif p2:
1600 elif p2:
1601 try:
1601 try:
1602 p1 = repo.lookup(p1)
1602 p1 = repo.lookup(p1)
1603 p2 = repo.lookup(p2)
1603 p2 = repo.lookup(p2)
1604 if p1 == wp[0].node():
1604 if p1 == wp[0].node():
1605 repo.dirstate.setparents(p1, p2)
1605 repo.dirstate.setparents(p1, p2)
1606 except hg.RepoError:
1606 except hg.RepoError:
1607 pass
1607 pass
1608 if opts.get('exact') or opts.get('import_branch'):
1608 if opts.get('exact') or opts.get('import_branch'):
1609 repo.dirstate.setbranch(branch or 'default')
1609 repo.dirstate.setbranch(branch or 'default')
1610
1610
1611 files = {}
1611 files = {}
1612 try:
1612 try:
1613 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1613 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1614 files=files)
1614 files=files)
1615 finally:
1615 finally:
1616 files = patch.updatedir(ui, repo, files)
1616 files = patch.updatedir(ui, repo, files)
1617 n = repo.commit(files, message, user, date)
1617 n = repo.commit(files, message, user, date)
1618 if opts.get('exact'):
1618 if opts.get('exact'):
1619 if hex(n) != nodeid:
1619 if hex(n) != nodeid:
1620 repo.rollback()
1620 repo.rollback()
1621 raise util.Abort(_('patch is damaged' +
1621 raise util.Abort(_('patch is damaged' +
1622 ' or loses information'))
1622 ' or loses information'))
1623 finally:
1623 finally:
1624 os.unlink(tmpname)
1624 os.unlink(tmpname)
1625 finally:
1625 finally:
1626 del lock, wlock
1626 del lock, wlock
1627
1627
1628 def incoming(ui, repo, source="default", **opts):
1628 def incoming(ui, repo, source="default", **opts):
1629 """show new changesets found in source
1629 """show new changesets found in source
1630
1630
1631 Show new changesets found in the specified path/URL or the default
1631 Show new changesets found in the specified path/URL or the default
1632 pull location. These are the changesets that would be pulled if a pull
1632 pull location. These are the changesets that would be pulled if a pull
1633 was requested.
1633 was requested.
1634
1634
1635 For remote repository, using --bundle avoids downloading the changesets
1635 For remote repository, using --bundle avoids downloading the changesets
1636 twice if the incoming is followed by a pull.
1636 twice if the incoming is followed by a pull.
1637
1637
1638 See pull for valid source format details.
1638 See pull for valid source format details.
1639 """
1639 """
1640 source, revs = cmdutil.parseurl(ui.expandpath(source), opts['rev'])
1640 source, revs = cmdutil.parseurl(ui.expandpath(source), opts['rev'])
1641 cmdutil.setremoteconfig(ui, opts)
1641 cmdutil.setremoteconfig(ui, opts)
1642
1642
1643 other = hg.repository(ui, source)
1643 other = hg.repository(ui, source)
1644 ui.status(_('comparing with %s\n') % source)
1644 ui.status(_('comparing with %s\n') % source)
1645 if revs:
1645 if revs:
1646 if 'lookup' in other.capabilities:
1646 if 'lookup' in other.capabilities:
1647 revs = [other.lookup(rev) for rev in revs]
1647 revs = [other.lookup(rev) for rev in revs]
1648 else:
1648 else:
1649 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
1649 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
1650 raise util.Abort(error)
1650 raise util.Abort(error)
1651 incoming = repo.findincoming(other, heads=revs, force=opts["force"])
1651 incoming = repo.findincoming(other, heads=revs, force=opts["force"])
1652 if not incoming:
1652 if not incoming:
1653 try:
1653 try:
1654 os.unlink(opts["bundle"])
1654 os.unlink(opts["bundle"])
1655 except:
1655 except:
1656 pass
1656 pass
1657 ui.status(_("no changes found\n"))
1657 ui.status(_("no changes found\n"))
1658 return 1
1658 return 1
1659
1659
1660 cleanup = None
1660 cleanup = None
1661 try:
1661 try:
1662 fname = opts["bundle"]
1662 fname = opts["bundle"]
1663 if fname or not other.local():
1663 if fname or not other.local():
1664 # create a bundle (uncompressed if other repo is not local)
1664 # create a bundle (uncompressed if other repo is not local)
1665 if revs is None:
1665 if revs is None:
1666 cg = other.changegroup(incoming, "incoming")
1666 cg = other.changegroup(incoming, "incoming")
1667 else:
1667 else:
1668 if 'changegroupsubset' not in other.capabilities:
1668 if 'changegroupsubset' not in other.capabilities:
1669 raise util.Abort(_("Partial incoming cannot be done because other repository doesn't support changegroupsubset."))
1669 raise util.Abort(_("Partial incoming cannot be done because other repository doesn't support changegroupsubset."))
1670 cg = other.changegroupsubset(incoming, revs, 'incoming')
1670 cg = other.changegroupsubset(incoming, revs, 'incoming')
1671 bundletype = other.local() and "HG10BZ" or "HG10UN"
1671 bundletype = other.local() and "HG10BZ" or "HG10UN"
1672 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1672 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1673 # keep written bundle?
1673 # keep written bundle?
1674 if opts["bundle"]:
1674 if opts["bundle"]:
1675 cleanup = None
1675 cleanup = None
1676 if not other.local():
1676 if not other.local():
1677 # use the created uncompressed bundlerepo
1677 # use the created uncompressed bundlerepo
1678 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1678 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1679
1679
1680 o = other.changelog.nodesbetween(incoming, revs)[0]
1680 o = other.changelog.nodesbetween(incoming, revs)[0]
1681 if opts['newest_first']:
1681 if opts['newest_first']:
1682 o.reverse()
1682 o.reverse()
1683 displayer = cmdutil.show_changeset(ui, other, opts)
1683 displayer = cmdutil.show_changeset(ui, other, opts)
1684 for n in o:
1684 for n in o:
1685 parents = [p for p in other.changelog.parents(n) if p != nullid]
1685 parents = [p for p in other.changelog.parents(n) if p != nullid]
1686 if opts['no_merges'] and len(parents) == 2:
1686 if opts['no_merges'] and len(parents) == 2:
1687 continue
1687 continue
1688 displayer.show(changenode=n)
1688 displayer.show(changenode=n)
1689 finally:
1689 finally:
1690 if hasattr(other, 'close'):
1690 if hasattr(other, 'close'):
1691 other.close()
1691 other.close()
1692 if cleanup:
1692 if cleanup:
1693 os.unlink(cleanup)
1693 os.unlink(cleanup)
1694
1694
1695 def init(ui, dest=".", **opts):
1695 def init(ui, dest=".", **opts):
1696 """create a new repository in the given directory
1696 """create a new repository in the given directory
1697
1697
1698 Initialize a new repository in the given directory. If the given
1698 Initialize a new repository in the given directory. If the given
1699 directory does not exist, it is created.
1699 directory does not exist, it is created.
1700
1700
1701 If no directory is given, the current directory is used.
1701 If no directory is given, the current directory is used.
1702
1702
1703 It is possible to specify an ssh:// URL as the destination.
1703 It is possible to specify an ssh:// URL as the destination.
1704 Look at the help text for the pull command for important details
1704 Look at the help text for the pull command for important details
1705 about ssh:// URLs.
1705 about ssh:// URLs.
1706 """
1706 """
1707 cmdutil.setremoteconfig(ui, opts)
1707 cmdutil.setremoteconfig(ui, opts)
1708 hg.repository(ui, dest, create=1)
1708 hg.repository(ui, dest, create=1)
1709
1709
1710 def locate(ui, repo, *pats, **opts):
1710 def locate(ui, repo, *pats, **opts):
1711 """locate files matching specific patterns
1711 """locate files matching specific patterns
1712
1712
1713 Print all files under Mercurial control whose names match the
1713 Print all files under Mercurial control whose names match the
1714 given patterns.
1714 given patterns.
1715
1715
1716 This command searches the entire repository by default. To search
1716 This command searches the entire repository by default. To search
1717 just the current directory and its subdirectories, use
1717 just the current directory and its subdirectories, use
1718 "--include .".
1718 "--include .".
1719
1719
1720 If no patterns are given to match, this command prints all file
1720 If no patterns are given to match, this command prints all file
1721 names.
1721 names.
1722
1722
1723 If you want to feed the output of this command into the "xargs"
1723 If you want to feed the output of this command into the "xargs"
1724 command, use the "-0" option to both this command and "xargs".
1724 command, use the "-0" option to both this command and "xargs".
1725 This will avoid the problem of "xargs" treating single filenames
1725 This will avoid the problem of "xargs" treating single filenames
1726 that contain white space as multiple filenames.
1726 that contain white space as multiple filenames.
1727 """
1727 """
1728 end = opts['print0'] and '\0' or '\n'
1728 end = opts['print0'] and '\0' or '\n'
1729 rev = opts['rev']
1729 rev = opts['rev']
1730 if rev:
1730 if rev:
1731 node = repo.lookup(rev)
1731 node = repo.lookup(rev)
1732 else:
1732 else:
1733 node = None
1733 node = None
1734
1734
1735 ret = 1
1735 ret = 1
1736 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1736 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1737 badmatch=util.always,
1737 badmatch=util.always,
1738 default='relglob'):
1738 default='relglob'):
1739 if src == 'b':
1739 if src == 'b':
1740 continue
1740 continue
1741 if not node and abs not in repo.dirstate:
1741 if not node and abs not in repo.dirstate:
1742 continue
1742 continue
1743 if opts['fullpath']:
1743 if opts['fullpath']:
1744 ui.write(os.path.join(repo.root, abs), end)
1744 ui.write(os.path.join(repo.root, abs), end)
1745 else:
1745 else:
1746 ui.write(((pats and rel) or abs), end)
1746 ui.write(((pats and rel) or abs), end)
1747 ret = 0
1747 ret = 0
1748
1748
1749 return ret
1749 return ret
1750
1750
1751 def log(ui, repo, *pats, **opts):
1751 def log(ui, repo, *pats, **opts):
1752 """show revision history of entire repository or files
1752 """show revision history of entire repository or files
1753
1753
1754 Print the revision history of the specified files or the entire
1754 Print the revision history of the specified files or the entire
1755 project.
1755 project.
1756
1756
1757 File history is shown without following rename or copy history of
1757 File history is shown without following rename or copy history of
1758 files. Use -f/--follow with a file name to follow history across
1758 files. Use -f/--follow with a file name to follow history across
1759 renames and copies. --follow without a file name will only show
1759 renames and copies. --follow without a file name will only show
1760 ancestors or descendants of the starting revision. --follow-first
1760 ancestors or descendants of the starting revision. --follow-first
1761 only follows the first parent of merge revisions.
1761 only follows the first parent of merge revisions.
1762
1762
1763 If no revision range is specified, the default is tip:0 unless
1763 If no revision range is specified, the default is tip:0 unless
1764 --follow is set, in which case the working directory parent is
1764 --follow is set, in which case the working directory parent is
1765 used as the starting revision.
1765 used as the starting revision.
1766
1766
1767 By default this command outputs: changeset id and hash, tags,
1767 By default this command outputs: changeset id and hash, tags,
1768 non-trivial parents, user, date and time, and a summary for each
1768 non-trivial parents, user, date and time, and a summary for each
1769 commit. When the -v/--verbose switch is used, the list of changed
1769 commit. When the -v/--verbose switch is used, the list of changed
1770 files and full commit message is shown.
1770 files and full commit message is shown.
1771
1771
1772 NOTE: log -p may generate unexpected diff output for merge
1772 NOTE: log -p may generate unexpected diff output for merge
1773 changesets, as it will compare the merge changeset against its
1773 changesets, as it will compare the merge changeset against its
1774 first parent only. Also, the files: list will only reflect files
1774 first parent only. Also, the files: list will only reflect files
1775 that are different from BOTH parents.
1775 that are different from BOTH parents.
1776
1776
1777 """
1777 """
1778
1778
1779 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1779 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1780 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1780 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1781
1781
1782 if opts['limit']:
1782 if opts['limit']:
1783 try:
1783 try:
1784 limit = int(opts['limit'])
1784 limit = int(opts['limit'])
1785 except ValueError:
1785 except ValueError:
1786 raise util.Abort(_('limit must be a positive integer'))
1786 raise util.Abort(_('limit must be a positive integer'))
1787 if limit <= 0: raise util.Abort(_('limit must be positive'))
1787 if limit <= 0: raise util.Abort(_('limit must be positive'))
1788 else:
1788 else:
1789 limit = sys.maxint
1789 limit = sys.maxint
1790 count = 0
1790 count = 0
1791
1791
1792 if opts['copies'] and opts['rev']:
1792 if opts['copies'] and opts['rev']:
1793 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1793 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1794 else:
1794 else:
1795 endrev = repo.changelog.count()
1795 endrev = repo.changelog.count()
1796 rcache = {}
1796 rcache = {}
1797 ncache = {}
1797 ncache = {}
1798 dcache = []
1798 dcache = []
1799 def getrenamed(fn, rev, man):
1799 def getrenamed(fn, rev, man):
1800 '''looks up all renames for a file (up to endrev) the first
1800 '''looks up all renames for a file (up to endrev) the first
1801 time the file is given. It indexes on the changerev and only
1801 time the file is given. It indexes on the changerev and only
1802 parses the manifest if linkrev != changerev.
1802 parses the manifest if linkrev != changerev.
1803 Returns rename info for fn at changerev rev.'''
1803 Returns rename info for fn at changerev rev.'''
1804 if fn not in rcache:
1804 if fn not in rcache:
1805 rcache[fn] = {}
1805 rcache[fn] = {}
1806 ncache[fn] = {}
1806 ncache[fn] = {}
1807 fl = repo.file(fn)
1807 fl = repo.file(fn)
1808 for i in xrange(fl.count()):
1808 for i in xrange(fl.count()):
1809 node = fl.node(i)
1809 node = fl.node(i)
1810 lr = fl.linkrev(node)
1810 lr = fl.linkrev(node)
1811 renamed = fl.renamed(node)
1811 renamed = fl.renamed(node)
1812 rcache[fn][lr] = renamed
1812 rcache[fn][lr] = renamed
1813 if renamed:
1813 if renamed:
1814 ncache[fn][node] = renamed
1814 ncache[fn][node] = renamed
1815 if lr >= endrev:
1815 if lr >= endrev:
1816 break
1816 break
1817 if rev in rcache[fn]:
1817 if rev in rcache[fn]:
1818 return rcache[fn][rev]
1818 return rcache[fn][rev]
1819 mr = repo.manifest.rev(man)
1819 mr = repo.manifest.rev(man)
1820 if repo.manifest.parentrevs(mr) != (mr - 1, nullrev):
1820 if repo.manifest.parentrevs(mr) != (mr - 1, nullrev):
1821 return ncache[fn].get(repo.manifest.find(man, fn)[0])
1821 return ncache[fn].get(repo.manifest.find(man, fn)[0])
1822 if not dcache or dcache[0] != man:
1822 if not dcache or dcache[0] != man:
1823 dcache[:] = [man, repo.manifest.readdelta(man)]
1823 dcache[:] = [man, repo.manifest.readdelta(man)]
1824 if fn in dcache[1]:
1824 if fn in dcache[1]:
1825 return ncache[fn].get(dcache[1][fn])
1825 return ncache[fn].get(dcache[1][fn])
1826 return None
1826 return None
1827
1827
1828 df = False
1828 df = False
1829 if opts["date"]:
1829 if opts["date"]:
1830 df = util.matchdate(opts["date"])
1830 df = util.matchdate(opts["date"])
1831
1831
1832 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1832 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1833 for st, rev, fns in changeiter:
1833 for st, rev, fns in changeiter:
1834 if st == 'add':
1834 if st == 'add':
1835 changenode = repo.changelog.node(rev)
1835 changenode = repo.changelog.node(rev)
1836 parents = [p for p in repo.changelog.parentrevs(rev)
1836 parents = [p for p in repo.changelog.parentrevs(rev)
1837 if p != nullrev]
1837 if p != nullrev]
1838 if opts['no_merges'] and len(parents) == 2:
1838 if opts['no_merges'] and len(parents) == 2:
1839 continue
1839 continue
1840 if opts['only_merges'] and len(parents) != 2:
1840 if opts['only_merges'] and len(parents) != 2:
1841 continue
1841 continue
1842
1842
1843 if df:
1843 if df:
1844 changes = get(rev)
1844 changes = get(rev)
1845 if not df(changes[2][0]):
1845 if not df(changes[2][0]):
1846 continue
1846 continue
1847
1847
1848 if opts['keyword']:
1848 if opts['keyword']:
1849 changes = get(rev)
1849 changes = get(rev)
1850 miss = 0
1850 miss = 0
1851 for k in [kw.lower() for kw in opts['keyword']]:
1851 for k in [kw.lower() for kw in opts['keyword']]:
1852 if not (k in changes[1].lower() or
1852 if not (k in changes[1].lower() or
1853 k in changes[4].lower() or
1853 k in changes[4].lower() or
1854 k in " ".join(changes[3]).lower()):
1854 k in " ".join(changes[3]).lower()):
1855 miss = 1
1855 miss = 1
1856 break
1856 break
1857 if miss:
1857 if miss:
1858 continue
1858 continue
1859
1859
1860 copies = []
1860 copies = []
1861 if opts.get('copies') and rev:
1861 if opts.get('copies') and rev:
1862 mf = get(rev)[0]
1862 mf = get(rev)[0]
1863 for fn in get(rev)[3]:
1863 for fn in get(rev)[3]:
1864 rename = getrenamed(fn, rev, mf)
1864 rename = getrenamed(fn, rev, mf)
1865 if rename:
1865 if rename:
1866 copies.append((fn, rename[0]))
1866 copies.append((fn, rename[0]))
1867 displayer.show(rev, changenode, copies=copies)
1867 displayer.show(rev, changenode, copies=copies)
1868 elif st == 'iter':
1868 elif st == 'iter':
1869 if count == limit: break
1869 if count == limit: break
1870 if displayer.flush(rev):
1870 if displayer.flush(rev):
1871 count += 1
1871 count += 1
1872
1872
1873 def manifest(ui, repo, rev=None):
1873 def manifest(ui, repo, rev=None):
1874 """output the current or given revision of the project manifest
1874 """output the current or given revision of the project manifest
1875
1875
1876 Print a list of version controlled files for the given revision.
1876 Print a list of version controlled files for the given revision.
1877 If no revision is given, the parent of the working directory is used,
1877 If no revision is given, the parent of the working directory is used,
1878 or tip if no revision is checked out.
1878 or tip if no revision is checked out.
1879
1879
1880 The manifest is the list of files being version controlled. If no revision
1880 The manifest is the list of files being version controlled. If no revision
1881 is given then the first parent of the working directory is used.
1881 is given then the first parent of the working directory is used.
1882
1882
1883 With -v flag, print file permissions. With --debug flag, print
1883 With -v flag, print file permissions. With --debug flag, print
1884 file revision hashes.
1884 file revision hashes.
1885 """
1885 """
1886
1886
1887 m = repo.changectx(rev).manifest()
1887 m = repo.changectx(rev).manifest()
1888 files = m.keys()
1888 files = m.keys()
1889 files.sort()
1889 files.sort()
1890
1890
1891 for f in files:
1891 for f in files:
1892 if ui.debugflag:
1892 if ui.debugflag:
1893 ui.write("%40s " % hex(m[f]))
1893 ui.write("%40s " % hex(m[f]))
1894 if ui.verbose:
1894 if ui.verbose:
1895 ui.write("%3s " % (m.execf(f) and "755" or "644"))
1895 ui.write("%3s " % (m.execf(f) and "755" or "644"))
1896 ui.write("%s\n" % f)
1896 ui.write("%s\n" % f)
1897
1897
1898 def merge(ui, repo, node=None, force=None, rev=None):
1898 def merge(ui, repo, node=None, force=None, rev=None):
1899 """merge working directory with another revision
1899 """merge working directory with another revision
1900
1900
1901 Merge the contents of the current working directory and the
1901 Merge the contents of the current working directory and the
1902 requested revision. Files that changed between either parent are
1902 requested revision. Files that changed between either parent are
1903 marked as changed for the next commit and a commit must be
1903 marked as changed for the next commit and a commit must be
1904 performed before any further updates are allowed.
1904 performed before any further updates are allowed.
1905
1905
1906 If no revision is specified, the working directory's parent is a
1906 If no revision is specified, the working directory's parent is a
1907 head revision, and the repository contains exactly one other head,
1907 head revision, and the repository contains exactly one other head,
1908 the other head is merged with by default. Otherwise, an explicit
1908 the other head is merged with by default. Otherwise, an explicit
1909 revision to merge with must be provided.
1909 revision to merge with must be provided.
1910 """
1910 """
1911
1911
1912 if rev and node:
1912 if rev and node:
1913 raise util.Abort(_("please specify just one revision"))
1913 raise util.Abort(_("please specify just one revision"))
1914
1914
1915 if not node:
1915 if not node:
1916 node = rev
1916 node = rev
1917
1917
1918 if not node:
1918 if not node:
1919 heads = repo.heads()
1919 heads = repo.heads()
1920 if len(heads) > 2:
1920 if len(heads) > 2:
1921 raise util.Abort(_('repo has %d heads - '
1921 raise util.Abort(_('repo has %d heads - '
1922 'please merge with an explicit rev') %
1922 'please merge with an explicit rev') %
1923 len(heads))
1923 len(heads))
1924 if len(heads) == 1:
1924 if len(heads) == 1:
1925 raise util.Abort(_('there is nothing to merge - '
1925 raise util.Abort(_('there is nothing to merge - '
1926 'use "hg update" instead'))
1926 'use "hg update" instead'))
1927 parent = repo.dirstate.parents()[0]
1927 parent = repo.dirstate.parents()[0]
1928 if parent not in heads:
1928 if parent not in heads:
1929 raise util.Abort(_('working dir not at a head rev - '
1929 raise util.Abort(_('working dir not at a head rev - '
1930 'use "hg update" or merge with an explicit rev'))
1930 'use "hg update" or merge with an explicit rev'))
1931 node = parent == heads[0] and heads[-1] or heads[0]
1931 node = parent == heads[0] and heads[-1] or heads[0]
1932 return hg.merge(repo, node, force=force)
1932 return hg.merge(repo, node, force=force)
1933
1933
1934 def outgoing(ui, repo, dest=None, **opts):
1934 def outgoing(ui, repo, dest=None, **opts):
1935 """show changesets not found in destination
1935 """show changesets not found in destination
1936
1936
1937 Show changesets not found in the specified destination repository or
1937 Show changesets not found in the specified destination repository or
1938 the default push location. These are the changesets that would be pushed
1938 the default push location. These are the changesets that would be pushed
1939 if a push was requested.
1939 if a push was requested.
1940
1940
1941 See pull for valid destination format details.
1941 See pull for valid destination format details.
1942 """
1942 """
1943 dest, revs = cmdutil.parseurl(
1943 dest, revs = cmdutil.parseurl(
1944 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
1944 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
1945 cmdutil.setremoteconfig(ui, opts)
1945 cmdutil.setremoteconfig(ui, opts)
1946 if revs:
1946 if revs:
1947 revs = [repo.lookup(rev) for rev in revs]
1947 revs = [repo.lookup(rev) for rev in revs]
1948
1948
1949 other = hg.repository(ui, dest)
1949 other = hg.repository(ui, dest)
1950 ui.status(_('comparing with %s\n') % dest)
1950 ui.status(_('comparing with %s\n') % dest)
1951 o = repo.findoutgoing(other, force=opts['force'])
1951 o = repo.findoutgoing(other, force=opts['force'])
1952 if not o:
1952 if not o:
1953 ui.status(_("no changes found\n"))
1953 ui.status(_("no changes found\n"))
1954 return 1
1954 return 1
1955 o = repo.changelog.nodesbetween(o, revs)[0]
1955 o = repo.changelog.nodesbetween(o, revs)[0]
1956 if opts['newest_first']:
1956 if opts['newest_first']:
1957 o.reverse()
1957 o.reverse()
1958 displayer = cmdutil.show_changeset(ui, repo, opts)
1958 displayer = cmdutil.show_changeset(ui, repo, opts)
1959 for n in o:
1959 for n in o:
1960 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1960 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1961 if opts['no_merges'] and len(parents) == 2:
1961 if opts['no_merges'] and len(parents) == 2:
1962 continue
1962 continue
1963 displayer.show(changenode=n)
1963 displayer.show(changenode=n)
1964
1964
1965 def parents(ui, repo, file_=None, **opts):
1965 def parents(ui, repo, file_=None, **opts):
1966 """show the parents of the working dir or revision
1966 """show the parents of the working dir or revision
1967
1967
1968 Print the working directory's parent revisions. If a
1968 Print the working directory's parent revisions. If a
1969 revision is given via --rev, the parent of that revision
1969 revision is given via --rev, the parent of that revision
1970 will be printed. If a file argument is given, revision in
1970 will be printed. If a file argument is given, revision in
1971 which the file was last changed (before the working directory
1971 which the file was last changed (before the working directory
1972 revision or the argument to --rev if given) is printed.
1972 revision or the argument to --rev if given) is printed.
1973 """
1973 """
1974 rev = opts.get('rev')
1974 rev = opts.get('rev')
1975 if file_:
1975 if file_:
1976 files, match, anypats = cmdutil.matchpats(repo, (file_,), opts)
1976 files, match, anypats = cmdutil.matchpats(repo, (file_,), opts)
1977 if anypats or len(files) != 1:
1977 if anypats or len(files) != 1:
1978 raise util.Abort(_('can only specify an explicit file name'))
1978 raise util.Abort(_('can only specify an explicit file name'))
1979 ctx = repo.filectx(files[0], changeid=rev)
1979 ctx = repo.filectx(files[0], changeid=rev)
1980 elif rev:
1980 elif rev:
1981 ctx = repo.changectx(rev)
1981 ctx = repo.changectx(rev)
1982 else:
1982 else:
1983 ctx = repo.workingctx()
1983 ctx = repo.workingctx()
1984 p = [cp.node() for cp in ctx.parents()]
1984 p = [cp.node() for cp in ctx.parents()]
1985
1985
1986 displayer = cmdutil.show_changeset(ui, repo, opts)
1986 displayer = cmdutil.show_changeset(ui, repo, opts)
1987 for n in p:
1987 for n in p:
1988 if n != nullid:
1988 if n != nullid:
1989 displayer.show(changenode=n)
1989 displayer.show(changenode=n)
1990
1990
1991 def paths(ui, repo, search=None):
1991 def paths(ui, repo, search=None):
1992 """show definition of symbolic path names
1992 """show definition of symbolic path names
1993
1993
1994 Show definition of symbolic path name NAME. If no name is given, show
1994 Show definition of symbolic path name NAME. If no name is given, show
1995 definition of available names.
1995 definition of available names.
1996
1996
1997 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1997 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1998 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1998 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1999 """
1999 """
2000 if search:
2000 if search:
2001 for name, path in ui.configitems("paths"):
2001 for name, path in ui.configitems("paths"):
2002 if name == search:
2002 if name == search:
2003 ui.write("%s\n" % path)
2003 ui.write("%s\n" % path)
2004 return
2004 return
2005 ui.warn(_("not found!\n"))
2005 ui.warn(_("not found!\n"))
2006 return 1
2006 return 1
2007 else:
2007 else:
2008 for name, path in ui.configitems("paths"):
2008 for name, path in ui.configitems("paths"):
2009 ui.write("%s = %s\n" % (name, path))
2009 ui.write("%s = %s\n" % (name, path))
2010
2010
2011 def postincoming(ui, repo, modheads, optupdate):
2011 def postincoming(ui, repo, modheads, optupdate):
2012 if modheads == 0:
2012 if modheads == 0:
2013 return
2013 return
2014 if optupdate:
2014 if optupdate:
2015 if modheads <= 1:
2015 if modheads <= 1:
2016 return hg.update(repo, None)
2016 return hg.update(repo, None)
2017 else:
2017 else:
2018 ui.status(_("not updating, since new heads added\n"))
2018 ui.status(_("not updating, since new heads added\n"))
2019 if modheads > 1:
2019 if modheads > 1:
2020 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2020 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2021 else:
2021 else:
2022 ui.status(_("(run 'hg update' to get a working copy)\n"))
2022 ui.status(_("(run 'hg update' to get a working copy)\n"))
2023
2023
2024 def pull(ui, repo, source="default", **opts):
2024 def pull(ui, repo, source="default", **opts):
2025 """pull changes from the specified source
2025 """pull changes from the specified source
2026
2026
2027 Pull changes from a remote repository to a local one.
2027 Pull changes from a remote repository to a local one.
2028
2028
2029 This finds all changes from the repository at the specified path
2029 This finds all changes from the repository at the specified path
2030 or URL and adds them to the local repository. By default, this
2030 or URL and adds them to the local repository. By default, this
2031 does not update the copy of the project in the working directory.
2031 does not update the copy of the project in the working directory.
2032
2032
2033 Valid URLs are of the form:
2033 Valid URLs are of the form:
2034
2034
2035 local/filesystem/path (or file://local/filesystem/path)
2035 local/filesystem/path (or file://local/filesystem/path)
2036 http://[user@]host[:port]/[path]
2036 http://[user@]host[:port]/[path]
2037 https://[user@]host[:port]/[path]
2037 https://[user@]host[:port]/[path]
2038 ssh://[user@]host[:port]/[path]
2038 ssh://[user@]host[:port]/[path]
2039 static-http://host[:port]/[path]
2039 static-http://host[:port]/[path]
2040
2040
2041 Paths in the local filesystem can either point to Mercurial
2041 Paths in the local filesystem can either point to Mercurial
2042 repositories or to bundle files (as created by 'hg bundle' or
2042 repositories or to bundle files (as created by 'hg bundle' or
2043 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
2043 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
2044 allows access to a Mercurial repository where you simply use a web
2044 allows access to a Mercurial repository where you simply use a web
2045 server to publish the .hg directory as static content.
2045 server to publish the .hg directory as static content.
2046
2046
2047 An optional identifier after # indicates a particular branch, tag,
2047 An optional identifier after # indicates a particular branch, tag,
2048 or changeset to pull.
2048 or changeset to pull.
2049
2049
2050 Some notes about using SSH with Mercurial:
2050 Some notes about using SSH with Mercurial:
2051 - SSH requires an accessible shell account on the destination machine
2051 - SSH requires an accessible shell account on the destination machine
2052 and a copy of hg in the remote path or specified with as remotecmd.
2052 and a copy of hg in the remote path or specified with as remotecmd.
2053 - path is relative to the remote user's home directory by default.
2053 - path is relative to the remote user's home directory by default.
2054 Use an extra slash at the start of a path to specify an absolute path:
2054 Use an extra slash at the start of a path to specify an absolute path:
2055 ssh://example.com//tmp/repository
2055 ssh://example.com//tmp/repository
2056 - Mercurial doesn't use its own compression via SSH; the right thing
2056 - Mercurial doesn't use its own compression via SSH; the right thing
2057 to do is to configure it in your ~/.ssh/config, e.g.:
2057 to do is to configure it in your ~/.ssh/config, e.g.:
2058 Host *.mylocalnetwork.example.com
2058 Host *.mylocalnetwork.example.com
2059 Compression no
2059 Compression no
2060 Host *
2060 Host *
2061 Compression yes
2061 Compression yes
2062 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2062 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2063 with the --ssh command line option.
2063 with the --ssh command line option.
2064 """
2064 """
2065 source, revs = cmdutil.parseurl(ui.expandpath(source), opts['rev'])
2065 source, revs = cmdutil.parseurl(ui.expandpath(source), opts['rev'])
2066 cmdutil.setremoteconfig(ui, opts)
2066 cmdutil.setremoteconfig(ui, opts)
2067
2067
2068 other = hg.repository(ui, source)
2068 other = hg.repository(ui, source)
2069 ui.status(_('pulling from %s\n') % (source))
2069 ui.status(_('pulling from %s\n') % (source))
2070 if revs:
2070 if revs:
2071 if 'lookup' in other.capabilities:
2071 if 'lookup' in other.capabilities:
2072 revs = [other.lookup(rev) for rev in revs]
2072 revs = [other.lookup(rev) for rev in revs]
2073 else:
2073 else:
2074 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
2074 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
2075 raise util.Abort(error)
2075 raise util.Abort(error)
2076
2076
2077 modheads = repo.pull(other, heads=revs, force=opts['force'])
2077 modheads = repo.pull(other, heads=revs, force=opts['force'])
2078 return postincoming(ui, repo, modheads, opts['update'])
2078 return postincoming(ui, repo, modheads, opts['update'])
2079
2079
2080 def push(ui, repo, dest=None, **opts):
2080 def push(ui, repo, dest=None, **opts):
2081 """push changes to the specified destination
2081 """push changes to the specified destination
2082
2082
2083 Push changes from the local repository to the given destination.
2083 Push changes from the local repository to the given destination.
2084
2084
2085 This is the symmetrical operation for pull. It helps to move
2085 This is the symmetrical operation for pull. It helps to move
2086 changes from the current repository to a different one. If the
2086 changes from the current repository to a different one. If the
2087 destination is local this is identical to a pull in that directory
2087 destination is local this is identical to a pull in that directory
2088 from the current one.
2088 from the current one.
2089
2089
2090 By default, push will refuse to run if it detects the result would
2090 By default, push will refuse to run if it detects the result would
2091 increase the number of remote heads. This generally indicates the
2091 increase the number of remote heads. This generally indicates the
2092 the client has forgotten to sync and merge before pushing.
2092 the client has forgotten to sync and merge before pushing.
2093
2093
2094 Valid URLs are of the form:
2094 Valid URLs are of the form:
2095
2095
2096 local/filesystem/path (or file://local/filesystem/path)
2096 local/filesystem/path (or file://local/filesystem/path)
2097 ssh://[user@]host[:port]/[path]
2097 ssh://[user@]host[:port]/[path]
2098 http://[user@]host[:port]/[path]
2098 http://[user@]host[:port]/[path]
2099 https://[user@]host[:port]/[path]
2099 https://[user@]host[:port]/[path]
2100
2100
2101 An optional identifier after # indicates a particular branch, tag,
2101 An optional identifier after # indicates a particular branch, tag,
2102 or changeset to push.
2102 or changeset to push.
2103
2103
2104 Look at the help text for the pull command for important details
2104 Look at the help text for the pull command for important details
2105 about ssh:// URLs.
2105 about ssh:// URLs.
2106
2106
2107 Pushing to http:// and https:// URLs is only possible, if this
2107 Pushing to http:// and https:// URLs is only possible, if this
2108 feature is explicitly enabled on the remote Mercurial server.
2108 feature is explicitly enabled on the remote Mercurial server.
2109 """
2109 """
2110 dest, revs = cmdutil.parseurl(
2110 dest, revs = cmdutil.parseurl(
2111 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
2111 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
2112 cmdutil.setremoteconfig(ui, opts)
2112 cmdutil.setremoteconfig(ui, opts)
2113
2113
2114 other = hg.repository(ui, dest)
2114 other = hg.repository(ui, dest)
2115 ui.status('pushing to %s\n' % (dest))
2115 ui.status('pushing to %s\n' % (dest))
2116 if revs:
2116 if revs:
2117 revs = [repo.lookup(rev) for rev in revs]
2117 revs = [repo.lookup(rev) for rev in revs]
2118 r = repo.push(other, opts['force'], revs=revs)
2118 r = repo.push(other, opts['force'], revs=revs)
2119 return r == 0
2119 return r == 0
2120
2120
2121 def rawcommit(ui, repo, *pats, **opts):
2121 def rawcommit(ui, repo, *pats, **opts):
2122 """raw commit interface (DEPRECATED)
2122 """raw commit interface (DEPRECATED)
2123
2123
2124 (DEPRECATED)
2124 (DEPRECATED)
2125 Lowlevel commit, for use in helper scripts.
2125 Lowlevel commit, for use in helper scripts.
2126
2126
2127 This command is not intended to be used by normal users, as it is
2127 This command is not intended to be used by normal users, as it is
2128 primarily useful for importing from other SCMs.
2128 primarily useful for importing from other SCMs.
2129
2129
2130 This command is now deprecated and will be removed in a future
2130 This command is now deprecated and will be removed in a future
2131 release, please use debugsetparents and commit instead.
2131 release, please use debugsetparents and commit instead.
2132 """
2132 """
2133
2133
2134 ui.warn(_("(the rawcommit command is deprecated)\n"))
2134 ui.warn(_("(the rawcommit command is deprecated)\n"))
2135
2135
2136 message = cmdutil.logmessage(opts)
2136 message = cmdutil.logmessage(opts)
2137
2137
2138 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
2138 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
2139 if opts['files']:
2139 if opts['files']:
2140 files += open(opts['files']).read().splitlines()
2140 files += open(opts['files']).read().splitlines()
2141
2141
2142 parents = [repo.lookup(p) for p in opts['parent']]
2142 parents = [repo.lookup(p) for p in opts['parent']]
2143
2143
2144 try:
2144 try:
2145 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2145 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2146 except ValueError, inst:
2146 except ValueError, inst:
2147 raise util.Abort(str(inst))
2147 raise util.Abort(str(inst))
2148
2148
2149 def recover(ui, repo):
2149 def recover(ui, repo):
2150 """roll back an interrupted transaction
2150 """roll back an interrupted transaction
2151
2151
2152 Recover from an interrupted commit or pull.
2152 Recover from an interrupted commit or pull.
2153
2153
2154 This command tries to fix the repository status after an interrupted
2154 This command tries to fix the repository status after an interrupted
2155 operation. It should only be necessary when Mercurial suggests it.
2155 operation. It should only be necessary when Mercurial suggests it.
2156 """
2156 """
2157 if repo.recover():
2157 if repo.recover():
2158 return hg.verify(repo)
2158 return hg.verify(repo)
2159 return 1
2159 return 1
2160
2160
2161 def remove(ui, repo, *pats, **opts):
2161 def remove(ui, repo, *pats, **opts):
2162 """remove the specified files on the next commit
2162 """remove the specified files on the next commit
2163
2163
2164 Schedule the indicated files for removal from the repository.
2164 Schedule the indicated files for removal from the repository.
2165
2165
2166 This only removes files from the current branch, not from the
2166 This only removes files from the current branch, not from the
2167 entire project history. If the files still exist in the working
2167 entire project history. If the files still exist in the working
2168 directory, they will be deleted from it. If invoked with --after,
2168 directory, they will be deleted from it. If invoked with --after,
2169 files are marked as removed, but not actually unlinked unless --force
2169 files are marked as removed, but not actually unlinked unless --force
2170 is also given. Without exact file names, --after will only mark
2170 is also given. Without exact file names, --after will only mark
2171 files as removed if they are no longer in the working directory.
2171 files as removed if they are no longer in the working directory.
2172
2172
2173 This command schedules the files to be removed at the next commit.
2173 This command schedules the files to be removed at the next commit.
2174 To undo a remove before that, see hg revert.
2174 To undo a remove before that, see hg revert.
2175
2175
2176 Modified files and added files are not removed by default. To
2176 Modified files and added files are not removed by default. To
2177 remove them, use the -f/--force option.
2177 remove them, use the -f/--force option.
2178 """
2178 """
2179 if not opts['after'] and not pats:
2179 if not opts['after'] and not pats:
2180 raise util.Abort(_('no files specified'))
2180 raise util.Abort(_('no files specified'))
2181 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2181 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2182 exact = dict.fromkeys(files)
2182 exact = dict.fromkeys(files)
2183 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2183 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2184 modified, added, removed, deleted, unknown = mardu
2184 modified, added, removed, deleted, unknown = mardu
2185 remove, forget = [], []
2185 remove, forget = [], []
2186 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2186 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2187 reason = None
2187 reason = None
2188 if abs in modified and not opts['force']:
2188 if abs in modified and not opts['force']:
2189 reason = _('is modified (use -f to force removal)')
2189 reason = _('is modified (use -f to force removal)')
2190 elif abs in added:
2190 elif abs in added:
2191 if opts['force']:
2191 if opts['force']:
2192 forget.append(abs)
2192 forget.append(abs)
2193 continue
2193 continue
2194 reason = _('has been marked for add (use -f to force removal)')
2194 reason = _('has been marked for add (use -f to force removal)')
2195 elif abs not in repo.dirstate:
2195 elif abs not in repo.dirstate:
2196 reason = _('is not managed')
2196 reason = _('is not managed')
2197 elif opts['after'] and not exact and abs not in deleted:
2197 elif opts['after'] and not exact and abs not in deleted:
2198 continue
2198 continue
2199 elif abs in removed:
2199 elif abs in removed:
2200 continue
2200 continue
2201 if reason:
2201 if reason:
2202 if exact:
2202 if exact:
2203 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2203 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2204 else:
2204 else:
2205 if ui.verbose or not exact:
2205 if ui.verbose or not exact:
2206 ui.status(_('removing %s\n') % rel)
2206 ui.status(_('removing %s\n') % rel)
2207 remove.append(abs)
2207 remove.append(abs)
2208 repo.forget(forget)
2208 repo.forget(forget)
2209 repo.remove(remove, unlink=opts['force'] or not opts['after'])
2209 repo.remove(remove, unlink=opts['force'] or not opts['after'])
2210
2210
2211 def rename(ui, repo, *pats, **opts):
2211 def rename(ui, repo, *pats, **opts):
2212 """rename files; equivalent of copy + remove
2212 """rename files; equivalent of copy + remove
2213
2213
2214 Mark dest as copies of sources; mark sources for deletion. If
2214 Mark dest as copies of sources; mark sources for deletion. If
2215 dest is a directory, copies are put in that directory. If dest is
2215 dest is a directory, copies are put in that directory. If dest is
2216 a file, there can only be one source.
2216 a file, there can only be one source.
2217
2217
2218 By default, this command copies the contents of files as they
2218 By default, this command copies the contents of files as they
2219 stand in the working directory. If invoked with --after, the
2219 stand in the working directory. If invoked with --after, the
2220 operation is recorded, but no copying is performed.
2220 operation is recorded, but no copying is performed.
2221
2221
2222 This command takes effect in the next commit. To undo a rename
2222 This command takes effect in the next commit. To undo a rename
2223 before that, see hg revert.
2223 before that, see hg revert.
2224 """
2224 """
2225 wlock = repo.wlock(False)
2225 wlock = repo.wlock(False)
2226 try:
2226 try:
2227 errs, copied = docopy(ui, repo, pats, opts)
2227 errs, copied = docopy(ui, repo, pats, opts)
2228 names = []
2228 names = []
2229 for abs, rel, exact in copied:
2229 for abs, rel, exact in copied:
2230 if ui.verbose or not exact:
2230 if ui.verbose or not exact:
2231 ui.status(_('removing %s\n') % rel)
2231 ui.status(_('removing %s\n') % rel)
2232 names.append(abs)
2232 names.append(abs)
2233 if not opts.get('dry_run'):
2233 if not opts.get('dry_run'):
2234 repo.remove(names, True)
2234 repo.remove(names, True)
2235 return errs
2235 return errs
2236 finally:
2236 finally:
2237 del wlock
2237 del wlock
2238
2238
2239 def revert(ui, repo, *pats, **opts):
2239 def revert(ui, repo, *pats, **opts):
2240 """revert files or dirs to their states as of some revision
2240 """revert files or dirs to their states as of some revision
2241
2241
2242 With no revision specified, revert the named files or directories
2242 With no revision specified, revert the named files or directories
2243 to the contents they had in the parent of the working directory.
2243 to the contents they had in the parent of the working directory.
2244 This restores the contents of the affected files to an unmodified
2244 This restores the contents of the affected files to an unmodified
2245 state and unschedules adds, removes, copies, and renames. If the
2245 state and unschedules adds, removes, copies, and renames. If the
2246 working directory has two parents, you must explicitly specify the
2246 working directory has two parents, you must explicitly specify the
2247 revision to revert to.
2247 revision to revert to.
2248
2248
2249 Modified files are saved with a .orig suffix before reverting.
2249 Modified files are saved with a .orig suffix before reverting.
2250 To disable these backups, use --no-backup.
2250 To disable these backups, use --no-backup.
2251
2251
2252 Using the -r option, revert the given files or directories to their
2252 Using the -r option, revert the given files or directories to their
2253 contents as of a specific revision. This can be helpful to "roll
2253 contents as of a specific revision. This can be helpful to "roll
2254 back" some or all of a change that should not have been committed.
2254 back" some or all of a change that should not have been committed.
2255
2255
2256 Revert modifies the working directory. It does not commit any
2256 Revert modifies the working directory. It does not commit any
2257 changes, or change the parent of the working directory. If you
2257 changes, or change the parent of the working directory. If you
2258 revert to a revision other than the parent of the working
2258 revert to a revision other than the parent of the working
2259 directory, the reverted files will thus appear modified
2259 directory, the reverted files will thus appear modified
2260 afterwards.
2260 afterwards.
2261
2261
2262 If a file has been deleted, it is restored. If the executable
2262 If a file has been deleted, it is restored. If the executable
2263 mode of a file was changed, it is reset.
2263 mode of a file was changed, it is reset.
2264
2264
2265 If names are given, all files matching the names are reverted.
2265 If names are given, all files matching the names are reverted.
2266
2266
2267 If no arguments are given, no files are reverted.
2267 If no arguments are given, no files are reverted.
2268 """
2268 """
2269
2269
2270 if opts["date"]:
2270 if opts["date"]:
2271 if opts["rev"]:
2271 if opts["rev"]:
2272 raise util.Abort(_("you can't specify a revision and a date"))
2272 raise util.Abort(_("you can't specify a revision and a date"))
2273 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2273 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2274
2274
2275 if not pats and not opts['all']:
2275 if not pats and not opts['all']:
2276 raise util.Abort(_('no files or directories specified; '
2276 raise util.Abort(_('no files or directories specified; '
2277 'use --all to revert the whole repo'))
2277 'use --all to revert the whole repo'))
2278
2278
2279 parent, p2 = repo.dirstate.parents()
2279 parent, p2 = repo.dirstate.parents()
2280 if not opts['rev'] and p2 != nullid:
2280 if not opts['rev'] and p2 != nullid:
2281 raise util.Abort(_('uncommitted merge - please provide a '
2281 raise util.Abort(_('uncommitted merge - please provide a '
2282 'specific revision'))
2282 'specific revision'))
2283 ctx = repo.changectx(opts['rev'])
2283 ctx = repo.changectx(opts['rev'])
2284 node = ctx.node()
2284 node = ctx.node()
2285 mf = ctx.manifest()
2285 mf = ctx.manifest()
2286 if node == parent:
2286 if node == parent:
2287 pmf = mf
2287 pmf = mf
2288 else:
2288 else:
2289 pmf = None
2289 pmf = None
2290
2290
2291 # need all matching names in dirstate and manifest of target rev,
2291 # need all matching names in dirstate and manifest of target rev,
2292 # so have to walk both. do not print errors if files exist in one
2292 # so have to walk both. do not print errors if files exist in one
2293 # but not other.
2293 # but not other.
2294
2294
2295 names = {}
2295 names = {}
2296 target_only = {}
2296 target_only = {}
2297
2297
2298 wlock = repo.wlock()
2298 wlock = repo.wlock()
2299 try:
2299 try:
2300 # walk dirstate.
2300 # walk dirstate.
2301 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2301 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2302 badmatch=mf.has_key):
2302 badmatch=mf.has_key):
2303 names[abs] = (rel, exact)
2303 names[abs] = (rel, exact)
2304 if src == 'b':
2304 if src == 'b':
2305 target_only[abs] = True
2305 target_only[abs] = True
2306
2306
2307 # walk target manifest.
2307 # walk target manifest.
2308
2308
2309 def badmatch(path):
2309 def badmatch(path):
2310 if path in names:
2310 if path in names:
2311 return True
2311 return True
2312 path_ = path + '/'
2312 path_ = path + '/'
2313 for f in names:
2313 for f in names:
2314 if f.startswith(path_):
2314 if f.startswith(path_):
2315 return True
2315 return True
2316 return False
2316 return False
2317
2317
2318 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2318 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2319 badmatch=badmatch):
2319 badmatch=badmatch):
2320 if abs in names or src == 'b':
2320 if abs in names or src == 'b':
2321 continue
2321 continue
2322 names[abs] = (rel, exact)
2322 names[abs] = (rel, exact)
2323 target_only[abs] = True
2323 target_only[abs] = True
2324
2324
2325 changes = repo.status(match=names.has_key)[:5]
2325 changes = repo.status(match=names.has_key)[:5]
2326 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2326 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2327
2327
2328 revert = ([], _('reverting %s\n'))
2328 revert = ([], _('reverting %s\n'))
2329 add = ([], _('adding %s\n'))
2329 add = ([], _('adding %s\n'))
2330 remove = ([], _('removing %s\n'))
2330 remove = ([], _('removing %s\n'))
2331 forget = ([], _('forgetting %s\n'))
2331 forget = ([], _('forgetting %s\n'))
2332 undelete = ([], _('undeleting %s\n'))
2332 undelete = ([], _('undeleting %s\n'))
2333 update = {}
2333 update = {}
2334
2334
2335 disptable = (
2335 disptable = (
2336 # dispatch table:
2336 # dispatch table:
2337 # file state
2337 # file state
2338 # action if in target manifest
2338 # action if in target manifest
2339 # action if not in target manifest
2339 # action if not in target manifest
2340 # make backup if in target manifest
2340 # make backup if in target manifest
2341 # make backup if not in target manifest
2341 # make backup if not in target manifest
2342 (modified, revert, remove, True, True),
2342 (modified, revert, remove, True, True),
2343 (added, revert, forget, True, False),
2343 (added, revert, forget, True, False),
2344 (removed, undelete, None, False, False),
2344 (removed, undelete, None, False, False),
2345 (deleted, revert, remove, False, False),
2345 (deleted, revert, remove, False, False),
2346 (unknown, add, None, True, False),
2346 (unknown, add, None, True, False),
2347 (target_only, add, None, False, False),
2347 (target_only, add, None, False, False),
2348 )
2348 )
2349
2349
2350 entries = names.items()
2350 entries = names.items()
2351 entries.sort()
2351 entries.sort()
2352
2352
2353 for abs, (rel, exact) in entries:
2353 for abs, (rel, exact) in entries:
2354 mfentry = mf.get(abs)
2354 mfentry = mf.get(abs)
2355 target = repo.wjoin(abs)
2355 target = repo.wjoin(abs)
2356 def handle(xlist, dobackup):
2356 def handle(xlist, dobackup):
2357 xlist[0].append(abs)
2357 xlist[0].append(abs)
2358 update[abs] = 1
2358 update[abs] = 1
2359 if dobackup and not opts['no_backup'] and util.lexists(target):
2359 if dobackup and not opts['no_backup'] and util.lexists(target):
2360 bakname = "%s.orig" % rel
2360 bakname = "%s.orig" % rel
2361 ui.note(_('saving current version of %s as %s\n') %
2361 ui.note(_('saving current version of %s as %s\n') %
2362 (rel, bakname))
2362 (rel, bakname))
2363 if not opts.get('dry_run'):
2363 if not opts.get('dry_run'):
2364 util.copyfile(target, bakname)
2364 util.copyfile(target, bakname)
2365 if ui.verbose or not exact:
2365 if ui.verbose or not exact:
2366 ui.status(xlist[1] % rel)
2366 ui.status(xlist[1] % rel)
2367 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2367 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2368 if abs not in table: continue
2368 if abs not in table: continue
2369 # file has changed in dirstate
2369 # file has changed in dirstate
2370 if mfentry:
2370 if mfentry:
2371 handle(hitlist, backuphit)
2371 handle(hitlist, backuphit)
2372 elif misslist is not None:
2372 elif misslist is not None:
2373 handle(misslist, backupmiss)
2373 handle(misslist, backupmiss)
2374 else:
2374 else:
2375 if exact: ui.warn(_('file not managed: %s\n') % rel)
2375 if exact: ui.warn(_('file not managed: %s\n') % rel)
2376 break
2376 break
2377 else:
2377 else:
2378 # file has not changed in dirstate
2378 # file has not changed in dirstate
2379 if node == parent:
2379 if node == parent:
2380 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2380 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2381 continue
2381 continue
2382 if pmf is None:
2382 if pmf is None:
2383 # only need parent manifest in this unlikely case,
2383 # only need parent manifest in this unlikely case,
2384 # so do not read by default
2384 # so do not read by default
2385 pmf = repo.changectx(parent).manifest()
2385 pmf = repo.changectx(parent).manifest()
2386 if abs in pmf:
2386 if abs in pmf:
2387 if mfentry:
2387 if mfentry:
2388 # if version of file is same in parent and target
2388 # if version of file is same in parent and target
2389 # manifests, do nothing
2389 # manifests, do nothing
2390 if pmf[abs] != mfentry:
2390 if pmf[abs] != mfentry:
2391 handle(revert, False)
2391 handle(revert, False)
2392 else:
2392 else:
2393 handle(remove, False)
2393 handle(remove, False)
2394
2394
2395 if not opts.get('dry_run'):
2395 if not opts.get('dry_run'):
2396 for f in forget[0]:
2396 for f in forget[0]:
2397 repo.dirstate.forget(f)
2397 repo.dirstate.forget(f)
2398 r = hg.revert(repo, node, update.has_key)
2398 r = hg.revert(repo, node, update.has_key)
2399 for f in add[0]:
2399 for f in add[0]:
2400 repo.dirstate.add(f)
2400 repo.dirstate.add(f)
2401 for f in undelete[0]:
2401 for f in undelete[0]:
2402 repo.dirstate.normal(f)
2402 repo.dirstate.normal(f)
2403 for f in remove[0]:
2403 for f in remove[0]:
2404 repo.dirstate.remove(f)
2404 repo.dirstate.remove(f)
2405 return r
2405 return r
2406 finally:
2406 finally:
2407 del wlock
2407 del wlock
2408
2408
2409 def rollback(ui, repo):
2409 def rollback(ui, repo):
2410 """roll back the last transaction in this repository
2410 """roll back the last transaction in this repository
2411
2411
2412 Roll back the last transaction in this repository, restoring the
2412 Roll back the last transaction in this repository, restoring the
2413 project to its state prior to the transaction.
2413 project to its state prior to the transaction.
2414
2414
2415 Transactions are used to encapsulate the effects of all commands
2415 Transactions are used to encapsulate the effects of all commands
2416 that create new changesets or propagate existing changesets into a
2416 that create new changesets or propagate existing changesets into a
2417 repository. For example, the following commands are transactional,
2417 repository. For example, the following commands are transactional,
2418 and their effects can be rolled back:
2418 and their effects can be rolled back:
2419
2419
2420 commit
2420 commit
2421 import
2421 import
2422 pull
2422 pull
2423 push (with this repository as destination)
2423 push (with this repository as destination)
2424 unbundle
2424 unbundle
2425
2425
2426 This command should be used with care. There is only one level of
2426 This command should be used with care. There is only one level of
2427 rollback, and there is no way to undo a rollback. It will also
2427 rollback, and there is no way to undo a rollback. It will also
2428 restore the dirstate at the time of the last transaction, which
2428 restore the dirstate at the time of the last transaction, which
2429 may lose subsequent dirstate changes.
2429 may lose subsequent dirstate changes.
2430
2430
2431 This command is not intended for use on public repositories. Once
2431 This command is not intended for use on public repositories. Once
2432 changes are visible for pull by other users, rolling a transaction
2432 changes are visible for pull by other users, rolling a transaction
2433 back locally is ineffective (someone else may already have pulled
2433 back locally is ineffective (someone else may already have pulled
2434 the changes). Furthermore, a race is possible with readers of the
2434 the changes). Furthermore, a race is possible with readers of the
2435 repository; for example an in-progress pull from the repository
2435 repository; for example an in-progress pull from the repository
2436 may fail if a rollback is performed.
2436 may fail if a rollback is performed.
2437 """
2437 """
2438 repo.rollback()
2438 repo.rollback()
2439
2439
2440 def root(ui, repo):
2440 def root(ui, repo):
2441 """print the root (top) of the current working dir
2441 """print the root (top) of the current working dir
2442
2442
2443 Print the root directory of the current repository.
2443 Print the root directory of the current repository.
2444 """
2444 """
2445 ui.write(repo.root + "\n")
2445 ui.write(repo.root + "\n")
2446
2446
2447 def serve(ui, repo, **opts):
2447 def serve(ui, repo, **opts):
2448 """export the repository via HTTP
2448 """export the repository via HTTP
2449
2449
2450 Start a local HTTP repository browser and pull server.
2450 Start a local HTTP repository browser and pull server.
2451
2451
2452 By default, the server logs accesses to stdout and errors to
2452 By default, the server logs accesses to stdout and errors to
2453 stderr. Use the "-A" and "-E" options to log to files.
2453 stderr. Use the "-A" and "-E" options to log to files.
2454 """
2454 """
2455
2455
2456 if opts["stdio"]:
2456 if opts["stdio"]:
2457 if repo is None:
2457 if repo is None:
2458 raise hg.RepoError(_("There is no Mercurial repository here"
2458 raise hg.RepoError(_("There is no Mercurial repository here"
2459 " (.hg not found)"))
2459 " (.hg not found)"))
2460 s = sshserver.sshserver(ui, repo)
2460 s = sshserver.sshserver(ui, repo)
2461 s.serve_forever()
2461 s.serve_forever()
2462
2462
2463 parentui = ui.parentui or ui
2463 parentui = ui.parentui or ui
2464 optlist = ("name templates style address port ipv6"
2464 optlist = ("name templates style address port ipv6"
2465 " accesslog errorlog webdir_conf certificate")
2465 " accesslog errorlog webdir_conf certificate")
2466 for o in optlist.split():
2466 for o in optlist.split():
2467 if opts[o]:
2467 if opts[o]:
2468 parentui.setconfig("web", o, str(opts[o]))
2468 parentui.setconfig("web", o, str(opts[o]))
2469 if repo.ui != parentui:
2469 if repo.ui != parentui:
2470 repo.ui.setconfig("web", o, str(opts[o]))
2470 repo.ui.setconfig("web", o, str(opts[o]))
2471
2471
2472 if repo is None and not ui.config("web", "webdir_conf"):
2472 if repo is None and not ui.config("web", "webdir_conf"):
2473 raise hg.RepoError(_("There is no Mercurial repository here"
2473 raise hg.RepoError(_("There is no Mercurial repository here"
2474 " (.hg not found)"))
2474 " (.hg not found)"))
2475
2475
2476 class service:
2476 class service:
2477 def init(self):
2477 def init(self):
2478 util.set_signal_handler()
2478 util.set_signal_handler()
2479 try:
2479 try:
2480 self.httpd = hgweb.server.create_server(parentui, repo)
2480 self.httpd = hgweb.server.create_server(parentui, repo)
2481 except socket.error, inst:
2481 except socket.error, inst:
2482 raise util.Abort(_('cannot start server: ') + inst.args[1])
2482 raise util.Abort(_('cannot start server: ') + inst.args[1])
2483
2483
2484 if not ui.verbose: return
2484 if not ui.verbose: return
2485
2485
2486 if self.httpd.port != 80:
2486 if self.httpd.port != 80:
2487 ui.status(_('listening at http://%s:%d/\n') %
2487 ui.status(_('listening at http://%s:%d/\n') %
2488 (self.httpd.addr, self.httpd.port))
2488 (self.httpd.addr, self.httpd.port))
2489 else:
2489 else:
2490 ui.status(_('listening at http://%s/\n') % self.httpd.addr)
2490 ui.status(_('listening at http://%s/\n') % self.httpd.addr)
2491
2491
2492 def run(self):
2492 def run(self):
2493 self.httpd.serve_forever()
2493 self.httpd.serve_forever()
2494
2494
2495 service = service()
2495 service = service()
2496
2496
2497 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2497 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2498
2498
2499 def status(ui, repo, *pats, **opts):
2499 def status(ui, repo, *pats, **opts):
2500 """show changed files in the working directory
2500 """show changed files in the working directory
2501
2501
2502 Show status of files in the repository. If names are given, only
2502 Show status of files in the repository. If names are given, only
2503 files that match are shown. Files that are clean or ignored, are
2503 files that match are shown. Files that are clean or ignored, are
2504 not listed unless -c (clean), -i (ignored) or -A is given.
2504 not listed unless -c (clean), -i (ignored) or -A is given.
2505
2505
2506 NOTE: status may appear to disagree with diff if permissions have
2506 NOTE: status may appear to disagree with diff if permissions have
2507 changed or a merge has occurred. The standard diff format does not
2507 changed or a merge has occurred. The standard diff format does not
2508 report permission changes and diff only reports changes relative
2508 report permission changes and diff only reports changes relative
2509 to one merge parent.
2509 to one merge parent.
2510
2510
2511 If one revision is given, it is used as the base revision.
2511 If one revision is given, it is used as the base revision.
2512 If two revisions are given, the difference between them is shown.
2512 If two revisions are given, the difference between them is shown.
2513
2513
2514 The codes used to show the status of files are:
2514 The codes used to show the status of files are:
2515 M = modified
2515 M = modified
2516 A = added
2516 A = added
2517 R = removed
2517 R = removed
2518 C = clean
2518 C = clean
2519 ! = deleted, but still tracked
2519 ! = deleted, but still tracked
2520 ? = not tracked
2520 ? = not tracked
2521 I = ignored (not shown by default)
2521 I = ignored (not shown by default)
2522 = the previous added file was copied from here
2522 = the previous added file was copied from here
2523 """
2523 """
2524
2524
2525 all = opts['all']
2525 all = opts['all']
2526 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2526 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2527
2527
2528 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2528 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2529 cwd = (pats and repo.getcwd()) or ''
2529 cwd = (pats and repo.getcwd()) or ''
2530 modified, added, removed, deleted, unknown, ignored, clean = [
2530 modified, added, removed, deleted, unknown, ignored, clean = [
2531 n for n in repo.status(node1=node1, node2=node2, files=files,
2531 n for n in repo.status(node1=node1, node2=node2, files=files,
2532 match=matchfn,
2532 match=matchfn,
2533 list_ignored=all or opts['ignored'],
2533 list_ignored=all or opts['ignored'],
2534 list_clean=all or opts['clean'])]
2534 list_clean=all or opts['clean'])]
2535
2535
2536 changetypes = (('modified', 'M', modified),
2536 changetypes = (('modified', 'M', modified),
2537 ('added', 'A', added),
2537 ('added', 'A', added),
2538 ('removed', 'R', removed),
2538 ('removed', 'R', removed),
2539 ('deleted', '!', deleted),
2539 ('deleted', '!', deleted),
2540 ('unknown', '?', unknown),
2540 ('unknown', '?', unknown),
2541 ('ignored', 'I', ignored))
2541 ('ignored', 'I', ignored))
2542
2542
2543 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2543 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2544
2544
2545 end = opts['print0'] and '\0' or '\n'
2545 end = opts['print0'] and '\0' or '\n'
2546
2546
2547 for opt, char, changes in ([ct for ct in explicit_changetypes
2547 for opt, char, changes in ([ct for ct in explicit_changetypes
2548 if all or opts[ct[0]]]
2548 if all or opts[ct[0]]]
2549 or changetypes):
2549 or changetypes):
2550 if opts['no_status']:
2550 if opts['no_status']:
2551 format = "%%s%s" % end
2551 format = "%%s%s" % end
2552 else:
2552 else:
2553 format = "%s %%s%s" % (char, end)
2553 format = "%s %%s%s" % (char, end)
2554
2554
2555 for f in changes:
2555 for f in changes:
2556 ui.write(format % repo.pathto(f, cwd))
2556 ui.write(format % repo.pathto(f, cwd))
2557 if ((all or opts.get('copies')) and not opts.get('no_status')):
2557 if ((all or opts.get('copies')) and not opts.get('no_status')):
2558 copied = repo.dirstate.copied(f)
2558 copied = repo.dirstate.copied(f)
2559 if copied:
2559 if copied:
2560 ui.write(' %s%s' % (repo.pathto(copied, cwd), end))
2560 ui.write(' %s%s' % (repo.pathto(copied, cwd), end))
2561
2561
2562 def tag(ui, repo, name, rev_=None, **opts):
2562 def tag(ui, repo, name, rev_=None, **opts):
2563 """add a tag for the current or given revision
2563 """add a tag for the current or given revision
2564
2564
2565 Name a particular revision using <name>.
2565 Name a particular revision using <name>.
2566
2566
2567 Tags are used to name particular revisions of the repository and are
2567 Tags are used to name particular revisions of the repository and are
2568 very useful to compare different revision, to go back to significant
2568 very useful to compare different revision, to go back to significant
2569 earlier versions or to mark branch points as releases, etc.
2569 earlier versions or to mark branch points as releases, etc.
2570
2570
2571 If no revision is given, the parent of the working directory is used,
2571 If no revision is given, the parent of the working directory is used,
2572 or tip if no revision is checked out.
2572 or tip if no revision is checked out.
2573
2573
2574 To facilitate version control, distribution, and merging of tags,
2574 To facilitate version control, distribution, and merging of tags,
2575 they are stored as a file named ".hgtags" which is managed
2575 they are stored as a file named ".hgtags" which is managed
2576 similarly to other project files and can be hand-edited if
2576 similarly to other project files and can be hand-edited if
2577 necessary. The file '.hg/localtags' is used for local tags (not
2577 necessary. The file '.hg/localtags' is used for local tags (not
2578 shared among repositories).
2578 shared among repositories).
2579 """
2579 """
2580 if name in ['tip', '.', 'null']:
2580 if name in ['tip', '.', 'null']:
2581 raise util.Abort(_("the name '%s' is reserved") % name)
2581 raise util.Abort(_("the name '%s' is reserved") % name)
2582 if rev_ is not None:
2582 if rev_ is not None:
2583 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2583 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2584 "please use 'hg tag [-r REV] NAME' instead\n"))
2584 "please use 'hg tag [-r REV] NAME' instead\n"))
2585 if opts['rev']:
2585 if opts['rev']:
2586 raise util.Abort(_("use only one form to specify the revision"))
2586 raise util.Abort(_("use only one form to specify the revision"))
2587 if opts['rev'] and opts['remove']:
2587 if opts['rev'] and opts['remove']:
2588 raise util.Abort(_("--rev and --remove are incompatible"))
2588 raise util.Abort(_("--rev and --remove are incompatible"))
2589 if opts['rev']:
2589 if opts['rev']:
2590 rev_ = opts['rev']
2590 rev_ = opts['rev']
2591 message = opts['message']
2591 message = opts['message']
2592 if opts['remove']:
2592 if opts['remove']:
2593 if not name in repo.tags():
2593 if not name in repo.tags():
2594 raise util.Abort(_('tag %s does not exist') % name)
2594 raise util.Abort(_('tag %s does not exist') % name)
2595 rev_ = nullid
2595 rev_ = nullid
2596 if not message:
2596 if not message:
2597 message = _('Removed tag %s') % name
2597 message = _('Removed tag %s') % name
2598 elif name in repo.tags() and not opts['force']:
2598 elif name in repo.tags() and not opts['force']:
2599 raise util.Abort(_('a tag named %s already exists (use -f to force)')
2599 raise util.Abort(_('a tag named %s already exists (use -f to force)')
2600 % name)
2600 % name)
2601 if not rev_ and repo.dirstate.parents()[1] != nullid:
2601 if not rev_ and repo.dirstate.parents()[1] != nullid:
2602 raise util.Abort(_('uncommitted merge - please provide a '
2602 raise util.Abort(_('uncommitted merge - please provide a '
2603 'specific revision'))
2603 'specific revision'))
2604 r = repo.changectx(rev_).node()
2604 r = repo.changectx(rev_).node()
2605
2605
2606 if not message:
2606 if not message:
2607 message = _('Added tag %s for changeset %s') % (name, short(r))
2607 message = _('Added tag %s for changeset %s') % (name, short(r))
2608
2608
2609 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2609 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2610
2610
2611 def tags(ui, repo):
2611 def tags(ui, repo):
2612 """list repository tags
2612 """list repository tags
2613
2613
2614 List the repository tags.
2614 List the repository tags.
2615
2615
2616 This lists both regular and local tags.
2616 This lists both regular and local tags.
2617 """
2617 """
2618
2618
2619 l = repo.tagslist()
2619 l = repo.tagslist()
2620 l.reverse()
2620 l.reverse()
2621 hexfunc = ui.debugflag and hex or short
2621 hexfunc = ui.debugflag and hex or short
2622 for t, n in l:
2622 for t, n in l:
2623 try:
2623 try:
2624 hn = hexfunc(n)
2624 hn = hexfunc(n)
2625 r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n))
2625 r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n))
2626 except revlog.LookupError:
2626 except revlog.LookupError:
2627 r = " ?:%s" % hn
2627 r = " ?:%s" % hn
2628 if ui.quiet:
2628 if ui.quiet:
2629 ui.write("%s\n" % t)
2629 ui.write("%s\n" % t)
2630 else:
2630 else:
2631 spaces = " " * (30 - util.locallen(t))
2631 spaces = " " * (30 - util.locallen(t))
2632 ui.write("%s%s %s\n" % (t, spaces, r))
2632 ui.write("%s%s %s\n" % (t, spaces, r))
2633
2633
2634 def tip(ui, repo, **opts):
2634 def tip(ui, repo, **opts):
2635 """show the tip revision
2635 """show the tip revision
2636
2636
2637 Show the tip revision.
2637 Show the tip revision.
2638 """
2638 """
2639 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2639 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2640
2640
2641 def unbundle(ui, repo, fname1, *fnames, **opts):
2641 def unbundle(ui, repo, fname1, *fnames, **opts):
2642 """apply one or more changegroup files
2642 """apply one or more changegroup files
2643
2643
2644 Apply one or more compressed changegroup files generated by the
2644 Apply one or more compressed changegroup files generated by the
2645 bundle command.
2645 bundle command.
2646 """
2646 """
2647 fnames = (fname1,) + fnames
2647 fnames = (fname1,) + fnames
2648 for fname in fnames:
2648 for fname in fnames:
2649 if os.path.exists(fname):
2649 if os.path.exists(fname):
2650 f = open(fname, "rb")
2650 f = open(fname, "rb")
2651 else:
2651 else:
2652 f = urllib.urlopen(fname)
2652 f = urllib.urlopen(fname)
2653 gen = changegroup.readbundle(f, fname)
2653 gen = changegroup.readbundle(f, fname)
2654 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2654 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2655
2655
2656 return postincoming(ui, repo, modheads, opts['update'])
2656 return postincoming(ui, repo, modheads, opts['update'])
2657
2657
2658 def update(ui, repo, node=None, rev=None, clean=False, date=None):
2658 def update(ui, repo, node=None, rev=None, clean=False, date=None):
2659 """update working directory
2659 """update working directory
2660
2660
2661 Update the working directory to the specified revision, or the
2661 Update the working directory to the specified revision, or the
2662 tip of the current branch if none is specified.
2662 tip of the current branch if none is specified.
2663
2663
2664 If there are no outstanding changes in the working directory and
2664 If there are no outstanding changes in the working directory and
2665 there is a linear relationship between the current version and the
2665 there is a linear relationship between the current version and the
2666 requested version, the result is the requested version.
2666 requested version, the result is the requested version.
2667
2667
2668 To merge the working directory with another revision, use the
2668 To merge the working directory with another revision, use the
2669 merge command.
2669 merge command.
2670
2670
2671 By default, update will refuse to run if doing so would require
2671 By default, update will refuse to run if doing so would require
2672 discarding local changes.
2672 discarding local changes.
2673 """
2673 """
2674 if rev and node:
2674 if rev and node:
2675 raise util.Abort(_("please specify just one revision"))
2675 raise util.Abort(_("please specify just one revision"))
2676
2676
2677 if not rev:
2677 if not rev:
2678 rev = node
2678 rev = node
2679
2679
2680 if date:
2680 if date:
2681 if rev:
2681 if rev:
2682 raise util.Abort(_("you can't specify a revision and a date"))
2682 raise util.Abort(_("you can't specify a revision and a date"))
2683 rev = cmdutil.finddate(ui, repo, date)
2683 rev = cmdutil.finddate(ui, repo, date)
2684
2684
2685 if clean:
2685 if clean:
2686 return hg.clean(repo, rev)
2686 return hg.clean(repo, rev)
2687 else:
2687 else:
2688 return hg.update(repo, rev)
2688 return hg.update(repo, rev)
2689
2689
2690 def verify(ui, repo):
2690 def verify(ui, repo):
2691 """verify the integrity of the repository
2691 """verify the integrity of the repository
2692
2692
2693 Verify the integrity of the current repository.
2693 Verify the integrity of the current repository.
2694
2694
2695 This will perform an extensive check of the repository's
2695 This will perform an extensive check of the repository's
2696 integrity, validating the hashes and checksums of each entry in
2696 integrity, validating the hashes and checksums of each entry in
2697 the changelog, manifest, and tracked files, as well as the
2697 the changelog, manifest, and tracked files, as well as the
2698 integrity of their crosslinks and indices.
2698 integrity of their crosslinks and indices.
2699 """
2699 """
2700 return hg.verify(repo)
2700 return hg.verify(repo)
2701
2701
2702 def version_(ui):
2702 def version_(ui):
2703 """output version and copyright information"""
2703 """output version and copyright information"""
2704 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2704 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2705 % version.get_version())
2705 % version.get_version())
2706 ui.status(_(
2706 ui.status(_(
2707 "\nCopyright (C) 2005-2007 Matt Mackall <mpm@selenic.com> and others\n"
2707 "\nCopyright (C) 2005-2007 Matt Mackall <mpm@selenic.com> and others\n"
2708 "This is free software; see the source for copying conditions. "
2708 "This is free software; see the source for copying conditions. "
2709 "There is NO\nwarranty; "
2709 "There is NO\nwarranty; "
2710 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2710 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2711 ))
2711 ))
2712
2712
2713 # Command options and aliases are listed here, alphabetically
2713 # Command options and aliases are listed here, alphabetically
2714
2714
2715 globalopts = [
2715 globalopts = [
2716 ('R', 'repository', '',
2716 ('R', 'repository', '',
2717 _('repository root directory or symbolic path name')),
2717 _('repository root directory or symbolic path name')),
2718 ('', 'cwd', '', _('change working directory')),
2718 ('', 'cwd', '', _('change working directory')),
2719 ('y', 'noninteractive', None,
2719 ('y', 'noninteractive', None,
2720 _('do not prompt, assume \'yes\' for any required answers')),
2720 _('do not prompt, assume \'yes\' for any required answers')),
2721 ('q', 'quiet', None, _('suppress output')),
2721 ('q', 'quiet', None, _('suppress output')),
2722 ('v', 'verbose', None, _('enable additional output')),
2722 ('v', 'verbose', None, _('enable additional output')),
2723 ('', 'config', [], _('set/override config option')),
2723 ('', 'config', [], _('set/override config option')),
2724 ('', 'debug', None, _('enable debugging output')),
2724 ('', 'debug', None, _('enable debugging output')),
2725 ('', 'debugger', None, _('start debugger')),
2725 ('', 'debugger', None, _('start debugger')),
2726 ('', 'encoding', util._encoding, _('set the charset encoding')),
2726 ('', 'encoding', util._encoding, _('set the charset encoding')),
2727 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2727 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2728 ('', 'lsprof', None, _('print improved command execution profile')),
2728 ('', 'lsprof', None, _('print improved command execution profile')),
2729 ('', 'traceback', None, _('print traceback on exception')),
2729 ('', 'traceback', None, _('print traceback on exception')),
2730 ('', 'time', None, _('time how long the command takes')),
2730 ('', 'time', None, _('time how long the command takes')),
2731 ('', 'profile', None, _('print command execution profile')),
2731 ('', 'profile', None, _('print command execution profile')),
2732 ('', 'version', None, _('output version information and exit')),
2732 ('', 'version', None, _('output version information and exit')),
2733 ('h', 'help', None, _('display help and exit')),
2733 ('h', 'help', None, _('display help and exit')),
2734 ]
2734 ]
2735
2735
2736 dryrunopts = [('n', 'dry-run', None,
2736 dryrunopts = [('n', 'dry-run', None,
2737 _('do not perform actions, just print output'))]
2737 _('do not perform actions, just print output'))]
2738
2738
2739 remoteopts = [
2739 remoteopts = [
2740 ('e', 'ssh', '', _('specify ssh command to use')),
2740 ('e', 'ssh', '', _('specify ssh command to use')),
2741 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2741 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2742 ]
2742 ]
2743
2743
2744 walkopts = [
2744 walkopts = [
2745 ('I', 'include', [], _('include names matching the given patterns')),
2745 ('I', 'include', [], _('include names matching the given patterns')),
2746 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2746 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2747 ]
2747 ]
2748
2748
2749 commitopts = [
2749 commitopts = [
2750 ('m', 'message', '', _('use <text> as commit message')),
2750 ('m', 'message', '', _('use <text> as commit message')),
2751 ('l', 'logfile', '', _('read commit message from <file>')),
2751 ('l', 'logfile', '', _('read commit message from <file>')),
2752 ]
2752 ]
2753
2753
2754 commitopts2 = [
2755 ('d', 'date', '', _('record datecode as commit date')),
2756 ('u', 'user', '', _('record user as committer')),
2757 ]
2758
2754 table = {
2759 table = {
2755 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2760 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2756 "addremove":
2761 "addremove":
2757 (addremove,
2762 (addremove,
2758 [('s', 'similarity', '',
2763 [('s', 'similarity', '',
2759 _('guess renamed files by similarity (0<=s<=100)')),
2764 _('guess renamed files by similarity (0<=s<=100)')),
2760 ] + walkopts + dryrunopts,
2765 ] + walkopts + dryrunopts,
2761 _('hg addremove [OPTION]... [FILE]...')),
2766 _('hg addremove [OPTION]... [FILE]...')),
2762 "^annotate":
2767 "^annotate":
2763 (annotate,
2768 (annotate,
2764 [('r', 'rev', '', _('annotate the specified revision')),
2769 [('r', 'rev', '', _('annotate the specified revision')),
2765 ('f', 'follow', None, _('follow file copies and renames')),
2770 ('f', 'follow', None, _('follow file copies and renames')),
2766 ('a', 'text', None, _('treat all files as text')),
2771 ('a', 'text', None, _('treat all files as text')),
2767 ('u', 'user', None, _('list the author')),
2772 ('u', 'user', None, _('list the author')),
2768 ('d', 'date', None, _('list the date')),
2773 ('d', 'date', None, _('list the date')),
2769 ('n', 'number', None, _('list the revision number (default)')),
2774 ('n', 'number', None, _('list the revision number (default)')),
2770 ('c', 'changeset', None, _('list the changeset')),
2775 ('c', 'changeset', None, _('list the changeset')),
2771 ('l', 'line-number', None,
2776 ('l', 'line-number', None,
2772 _('show line number at the first appearance'))
2777 _('show line number at the first appearance'))
2773 ] + walkopts,
2778 ] + walkopts,
2774 _('hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
2779 _('hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
2775 "archive":
2780 "archive":
2776 (archive,
2781 (archive,
2777 [('', 'no-decode', None, _('do not pass files through decoders')),
2782 [('', 'no-decode', None, _('do not pass files through decoders')),
2778 ('p', 'prefix', '', _('directory prefix for files in archive')),
2783 ('p', 'prefix', '', _('directory prefix for files in archive')),
2779 ('r', 'rev', '', _('revision to distribute')),
2784 ('r', 'rev', '', _('revision to distribute')),
2780 ('t', 'type', '', _('type of distribution to create')),
2785 ('t', 'type', '', _('type of distribution to create')),
2781 ] + walkopts,
2786 ] + walkopts,
2782 _('hg archive [OPTION]... DEST')),
2787 _('hg archive [OPTION]... DEST')),
2783 "backout":
2788 "backout":
2784 (backout,
2789 (backout,
2785 [('', 'merge', None,
2790 [('', 'merge', None,
2786 _('merge with old dirstate parent after backout')),
2791 _('merge with old dirstate parent after backout')),
2787 ('d', 'date', '', _('record datecode as commit date')),
2788 ('', 'parent', '', _('parent to choose when backing out merge')),
2792 ('', 'parent', '', _('parent to choose when backing out merge')),
2789 ('u', 'user', '', _('record user as committer')),
2790 ('r', 'rev', '', _('revision to backout')),
2793 ('r', 'rev', '', _('revision to backout')),
2791 ] + walkopts + commitopts,
2794 ] + walkopts + commitopts + commitopts2,
2792 _('hg backout [OPTION]... [-r] REV')),
2795 _('hg backout [OPTION]... [-r] REV')),
2793 "branch":
2796 "branch":
2794 (branch,
2797 (branch,
2795 [('f', 'force', None,
2798 [('f', 'force', None,
2796 _('set branch name even if it shadows an existing branch'))],
2799 _('set branch name even if it shadows an existing branch'))],
2797 _('hg branch [NAME]')),
2800 _('hg branch [NAME]')),
2798 "branches":
2801 "branches":
2799 (branches,
2802 (branches,
2800 [('a', 'active', False,
2803 [('a', 'active', False,
2801 _('show only branches that have unmerged heads'))],
2804 _('show only branches that have unmerged heads'))],
2802 _('hg branches [-a]')),
2805 _('hg branches [-a]')),
2803 "bundle":
2806 "bundle":
2804 (bundle,
2807 (bundle,
2805 [('f', 'force', None,
2808 [('f', 'force', None,
2806 _('run even when remote repository is unrelated')),
2809 _('run even when remote repository is unrelated')),
2807 ('r', 'rev', [],
2810 ('r', 'rev', [],
2808 _('a changeset you would like to bundle')),
2811 _('a changeset you would like to bundle')),
2809 ('', 'base', [],
2812 ('', 'base', [],
2810 _('a base changeset to specify instead of a destination')),
2813 _('a base changeset to specify instead of a destination')),
2811 ] + remoteopts,
2814 ] + remoteopts,
2812 _('hg bundle [-f] [-r REV]... [--base REV]... FILE [DEST]')),
2815 _('hg bundle [-f] [-r REV]... [--base REV]... FILE [DEST]')),
2813 "cat":
2816 "cat":
2814 (cat,
2817 (cat,
2815 [('o', 'output', '', _('print output to file with formatted name')),
2818 [('o', 'output', '', _('print output to file with formatted name')),
2816 ('r', 'rev', '', _('print the given revision')),
2819 ('r', 'rev', '', _('print the given revision')),
2817 ] + walkopts,
2820 ] + walkopts,
2818 _('hg cat [OPTION]... FILE...')),
2821 _('hg cat [OPTION]... FILE...')),
2819 "^clone":
2822 "^clone":
2820 (clone,
2823 (clone,
2821 [('U', 'noupdate', None, _('do not update the new working directory')),
2824 [('U', 'noupdate', None, _('do not update the new working directory')),
2822 ('r', 'rev', [],
2825 ('r', 'rev', [],
2823 _('a changeset you would like to have after cloning')),
2826 _('a changeset you would like to have after cloning')),
2824 ('', 'pull', None, _('use pull protocol to copy metadata')),
2827 ('', 'pull', None, _('use pull protocol to copy metadata')),
2825 ('', 'uncompressed', None,
2828 ('', 'uncompressed', None,
2826 _('use uncompressed transfer (fast over LAN)')),
2829 _('use uncompressed transfer (fast over LAN)')),
2827 ] + remoteopts,
2830 ] + remoteopts,
2828 _('hg clone [OPTION]... SOURCE [DEST]')),
2831 _('hg clone [OPTION]... SOURCE [DEST]')),
2829 "^commit|ci":
2832 "^commit|ci":
2830 (commit,
2833 (commit,
2831 [('A', 'addremove', None,
2834 [('A', 'addremove', None,
2832 _('mark new/missing files as added/removed before committing')),
2835 _('mark new/missing files as added/removed before committing')),
2833 ('d', 'date', '', _('record datecode as commit date')),
2836 ] + walkopts + commitopts + commitopts2,
2834 ('u', 'user', '', _('record user as commiter')),
2835 ] + walkopts + commitopts,
2836 _('hg commit [OPTION]... [FILE]...')),
2837 _('hg commit [OPTION]... [FILE]...')),
2837 "copy|cp":
2838 "copy|cp":
2838 (copy,
2839 (copy,
2839 [('A', 'after', None, _('record a copy that has already occurred')),
2840 [('A', 'after', None, _('record a copy that has already occurred')),
2840 ('f', 'force', None,
2841 ('f', 'force', None,
2841 _('forcibly copy over an existing managed file')),
2842 _('forcibly copy over an existing managed file')),
2842 ] + walkopts + dryrunopts,
2843 ] + walkopts + dryrunopts,
2843 _('hg copy [OPTION]... [SOURCE]... DEST')),
2844 _('hg copy [OPTION]... [SOURCE]... DEST')),
2844 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2845 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2845 "debugcomplete":
2846 "debugcomplete":
2846 (debugcomplete,
2847 (debugcomplete,
2847 [('o', 'options', None, _('show the command options'))],
2848 [('o', 'options', None, _('show the command options'))],
2848 _('debugcomplete [-o] CMD')),
2849 _('debugcomplete [-o] CMD')),
2849 "debuginstall": (debuginstall, [], _('debuginstall')),
2850 "debuginstall": (debuginstall, [], _('debuginstall')),
2850 "debugrebuildstate":
2851 "debugrebuildstate":
2851 (debugrebuildstate,
2852 (debugrebuildstate,
2852 [('r', 'rev', '', _('revision to rebuild to'))],
2853 [('r', 'rev', '', _('revision to rebuild to'))],
2853 _('debugrebuildstate [-r REV] [REV]')),
2854 _('debugrebuildstate [-r REV] [REV]')),
2854 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2855 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2855 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2856 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2856 "debugstate": (debugstate, [], _('debugstate')),
2857 "debugstate": (debugstate, [], _('debugstate')),
2857 "debugdate":
2858 "debugdate":
2858 (debugdate,
2859 (debugdate,
2859 [('e', 'extended', None, _('try extended date formats'))],
2860 [('e', 'extended', None, _('try extended date formats'))],
2860 _('debugdate [-e] DATE [RANGE]')),
2861 _('debugdate [-e] DATE [RANGE]')),
2861 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2862 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2862 "debugindex": (debugindex, [], _('debugindex FILE')),
2863 "debugindex": (debugindex, [], _('debugindex FILE')),
2863 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2864 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2864 "debugrename":
2865 "debugrename":
2865 (debugrename,
2866 (debugrename,
2866 [('r', 'rev', '', _('revision to debug'))],
2867 [('r', 'rev', '', _('revision to debug'))],
2867 _('debugrename [-r REV] FILE')),
2868 _('debugrename [-r REV] FILE')),
2868 "debugwalk": (debugwalk, walkopts, _('debugwalk [OPTION]... [FILE]...')),
2869 "debugwalk": (debugwalk, walkopts, _('debugwalk [OPTION]... [FILE]...')),
2869 "^diff":
2870 "^diff":
2870 (diff,
2871 (diff,
2871 [('r', 'rev', [], _('revision')),
2872 [('r', 'rev', [], _('revision')),
2872 ('a', 'text', None, _('treat all files as text')),
2873 ('a', 'text', None, _('treat all files as text')),
2873 ('p', 'show-function', None,
2874 ('p', 'show-function', None,
2874 _('show which function each change is in')),
2875 _('show which function each change is in')),
2875 ('g', 'git', None, _('use git extended diff format')),
2876 ('g', 'git', None, _('use git extended diff format')),
2876 ('', 'nodates', None, _("don't include dates in diff headers")),
2877 ('', 'nodates', None, _("don't include dates in diff headers")),
2877 ('w', 'ignore-all-space', None,
2878 ('w', 'ignore-all-space', None,
2878 _('ignore white space when comparing lines')),
2879 _('ignore white space when comparing lines')),
2879 ('b', 'ignore-space-change', None,
2880 ('b', 'ignore-space-change', None,
2880 _('ignore changes in the amount of white space')),
2881 _('ignore changes in the amount of white space')),
2881 ('B', 'ignore-blank-lines', None,
2882 ('B', 'ignore-blank-lines', None,
2882 _('ignore changes whose lines are all blank')),
2883 _('ignore changes whose lines are all blank')),
2883 ] + walkopts,
2884 ] + walkopts,
2884 _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
2885 _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
2885 "^export":
2886 "^export":
2886 (export,
2887 (export,
2887 [('o', 'output', '', _('print output to file with formatted name')),
2888 [('o', 'output', '', _('print output to file with formatted name')),
2888 ('a', 'text', None, _('treat all files as text')),
2889 ('a', 'text', None, _('treat all files as text')),
2889 ('g', 'git', None, _('use git extended diff format')),
2890 ('g', 'git', None, _('use git extended diff format')),
2890 ('', 'nodates', None, _("don't include dates in diff headers")),
2891 ('', 'nodates', None, _("don't include dates in diff headers")),
2891 ('', 'switch-parent', None, _('diff against the second parent'))],
2892 ('', 'switch-parent', None, _('diff against the second parent'))],
2892 _('hg export [OPTION]... [-o OUTFILESPEC] REV...')),
2893 _('hg export [OPTION]... [-o OUTFILESPEC] REV...')),
2893 "grep":
2894 "grep":
2894 (grep,
2895 (grep,
2895 [('0', 'print0', None, _('end fields with NUL')),
2896 [('0', 'print0', None, _('end fields with NUL')),
2896 ('', 'all', None, _('print all revisions that match')),
2897 ('', 'all', None, _('print all revisions that match')),
2897 ('f', 'follow', None,
2898 ('f', 'follow', None,
2898 _('follow changeset history, or file history across copies and renames')),
2899 _('follow changeset history, or file history across copies and renames')),
2899 ('i', 'ignore-case', None, _('ignore case when matching')),
2900 ('i', 'ignore-case', None, _('ignore case when matching')),
2900 ('l', 'files-with-matches', None,
2901 ('l', 'files-with-matches', None,
2901 _('print only filenames and revs that match')),
2902 _('print only filenames and revs that match')),
2902 ('n', 'line-number', None, _('print matching line numbers')),
2903 ('n', 'line-number', None, _('print matching line numbers')),
2903 ('r', 'rev', [], _('search in given revision range')),
2904 ('r', 'rev', [], _('search in given revision range')),
2904 ('u', 'user', None, _('print user who committed change')),
2905 ('u', 'user', None, _('print user who committed change')),
2905 ] + walkopts,
2906 ] + walkopts,
2906 _('hg grep [OPTION]... PATTERN [FILE]...')),
2907 _('hg grep [OPTION]... PATTERN [FILE]...')),
2907 "heads":
2908 "heads":
2908 (heads,
2909 (heads,
2909 [('', 'style', '', _('display using template map file')),
2910 [('', 'style', '', _('display using template map file')),
2910 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2911 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2911 ('', 'template', '', _('display with template'))],
2912 ('', 'template', '', _('display with template'))],
2912 _('hg heads [-r REV] [REV]...')),
2913 _('hg heads [-r REV] [REV]...')),
2913 "help": (help_, [], _('hg help [COMMAND]')),
2914 "help": (help_, [], _('hg help [COMMAND]')),
2914 "identify|id":
2915 "identify|id":
2915 (identify,
2916 (identify,
2916 [('r', 'rev', '', _('identify the specified rev')),
2917 [('r', 'rev', '', _('identify the specified rev')),
2917 ('n', 'num', None, _('show local revision number')),
2918 ('n', 'num', None, _('show local revision number')),
2918 ('i', 'id', None, _('show global revision id')),
2919 ('i', 'id', None, _('show global revision id')),
2919 ('b', 'branch', None, _('show branch')),
2920 ('b', 'branch', None, _('show branch')),
2920 ('t', 'tags', None, _('show tags'))],
2921 ('t', 'tags', None, _('show tags'))],
2921 _('hg identify [-nibt] [-r REV] [SOURCE]')),
2922 _('hg identify [-nibt] [-r REV] [SOURCE]')),
2922 "import|patch":
2923 "import|patch":
2923 (import_,
2924 (import_,
2924 [('p', 'strip', 1,
2925 [('p', 'strip', 1,
2925 _('directory strip option for patch. This has the same\n'
2926 _('directory strip option for patch. This has the same\n'
2926 'meaning as the corresponding patch option')),
2927 'meaning as the corresponding patch option')),
2927 ('b', 'base', '', _('base path')),
2928 ('b', 'base', '', _('base path')),
2928 ('f', 'force', None,
2929 ('f', 'force', None,
2929 _('skip check for outstanding uncommitted changes')),
2930 _('skip check for outstanding uncommitted changes')),
2930 ('', 'exact', None,
2931 ('', 'exact', None,
2931 _('apply patch to the nodes from which it was generated')),
2932 _('apply patch to the nodes from which it was generated')),
2932 ('', 'import-branch', None,
2933 ('', 'import-branch', None,
2933 _('Use any branch information in patch (implied by --exact)'))] + commitopts,
2934 _('Use any branch information in patch (implied by --exact)'))] + commitopts,
2934 _('hg import [-p NUM] [-m MESSAGE] [-f] PATCH...')),
2935 _('hg import [-p NUM] [-m MESSAGE] [-f] PATCH...')),
2935 "incoming|in": (incoming,
2936 "incoming|in": (incoming,
2936 [('M', 'no-merges', None, _('do not show merges')),
2937 [('M', 'no-merges', None, _('do not show merges')),
2937 ('f', 'force', None,
2938 ('f', 'force', None,
2938 _('run even when remote repository is unrelated')),
2939 _('run even when remote repository is unrelated')),
2939 ('', 'style', '', _('display using template map file')),
2940 ('', 'style', '', _('display using template map file')),
2940 ('n', 'newest-first', None, _('show newest record first')),
2941 ('n', 'newest-first', None, _('show newest record first')),
2941 ('', 'bundle', '', _('file to store the bundles into')),
2942 ('', 'bundle', '', _('file to store the bundles into')),
2942 ('p', 'patch', None, _('show patch')),
2943 ('p', 'patch', None, _('show patch')),
2943 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2944 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2944 ('', 'template', '', _('display with template')),
2945 ('', 'template', '', _('display with template')),
2945 ] + remoteopts,
2946 ] + remoteopts,
2946 _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
2947 _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
2947 ' [--bundle FILENAME] [SOURCE]')),
2948 ' [--bundle FILENAME] [SOURCE]')),
2948 "^init":
2949 "^init":
2949 (init,
2950 (init,
2950 remoteopts,
2951 remoteopts,
2951 _('hg init [-e CMD] [--remotecmd CMD] [DEST]')),
2952 _('hg init [-e CMD] [--remotecmd CMD] [DEST]')),
2952 "locate":
2953 "locate":
2953 (locate,
2954 (locate,
2954 [('r', 'rev', '', _('search the repository as it stood at rev')),
2955 [('r', 'rev', '', _('search the repository as it stood at rev')),
2955 ('0', 'print0', None,
2956 ('0', 'print0', None,
2956 _('end filenames with NUL, for use with xargs')),
2957 _('end filenames with NUL, for use with xargs')),
2957 ('f', 'fullpath', None,
2958 ('f', 'fullpath', None,
2958 _('print complete paths from the filesystem root')),
2959 _('print complete paths from the filesystem root')),
2959 ] + walkopts,
2960 ] + walkopts,
2960 _('hg locate [OPTION]... [PATTERN]...')),
2961 _('hg locate [OPTION]... [PATTERN]...')),
2961 "^log|history":
2962 "^log|history":
2962 (log,
2963 (log,
2963 [('f', 'follow', None,
2964 [('f', 'follow', None,
2964 _('follow changeset history, or file history across copies and renames')),
2965 _('follow changeset history, or file history across copies and renames')),
2965 ('', 'follow-first', None,
2966 ('', 'follow-first', None,
2966 _('only follow the first parent of merge changesets')),
2967 _('only follow the first parent of merge changesets')),
2967 ('d', 'date', '', _('show revs matching date spec')),
2968 ('d', 'date', '', _('show revs matching date spec')),
2968 ('C', 'copies', None, _('show copied files')),
2969 ('C', 'copies', None, _('show copied files')),
2969 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
2970 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
2970 ('l', 'limit', '', _('limit number of changes displayed')),
2971 ('l', 'limit', '', _('limit number of changes displayed')),
2971 ('r', 'rev', [], _('show the specified revision or range')),
2972 ('r', 'rev', [], _('show the specified revision or range')),
2972 ('', 'removed', None, _('include revs where files were removed')),
2973 ('', 'removed', None, _('include revs where files were removed')),
2973 ('M', 'no-merges', None, _('do not show merges')),
2974 ('M', 'no-merges', None, _('do not show merges')),
2974 ('', 'style', '', _('display using template map file')),
2975 ('', 'style', '', _('display using template map file')),
2975 ('m', 'only-merges', None, _('show only merges')),
2976 ('m', 'only-merges', None, _('show only merges')),
2976 ('p', 'patch', None, _('show patch')),
2977 ('p', 'patch', None, _('show patch')),
2977 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
2978 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
2978 ('', 'template', '', _('display with template')),
2979 ('', 'template', '', _('display with template')),
2979 ] + walkopts,
2980 ] + walkopts,
2980 _('hg log [OPTION]... [FILE]')),
2981 _('hg log [OPTION]... [FILE]')),
2981 "manifest": (manifest, [], _('hg manifest [REV]')),
2982 "manifest": (manifest, [], _('hg manifest [REV]')),
2982 "^merge":
2983 "^merge":
2983 (merge,
2984 (merge,
2984 [('f', 'force', None, _('force a merge with outstanding changes')),
2985 [('f', 'force', None, _('force a merge with outstanding changes')),
2985 ('r', 'rev', '', _('revision to merge')),
2986 ('r', 'rev', '', _('revision to merge')),
2986 ],
2987 ],
2987 _('hg merge [-f] [[-r] REV]')),
2988 _('hg merge [-f] [[-r] REV]')),
2988 "outgoing|out": (outgoing,
2989 "outgoing|out": (outgoing,
2989 [('M', 'no-merges', None, _('do not show merges')),
2990 [('M', 'no-merges', None, _('do not show merges')),
2990 ('f', 'force', None,
2991 ('f', 'force', None,
2991 _('run even when remote repository is unrelated')),
2992 _('run even when remote repository is unrelated')),
2992 ('p', 'patch', None, _('show patch')),
2993 ('p', 'patch', None, _('show patch')),
2993 ('', 'style', '', _('display using template map file')),
2994 ('', 'style', '', _('display using template map file')),
2994 ('r', 'rev', [], _('a specific revision you would like to push')),
2995 ('r', 'rev', [], _('a specific revision you would like to push')),
2995 ('n', 'newest-first', None, _('show newest record first')),
2996 ('n', 'newest-first', None, _('show newest record first')),
2996 ('', 'template', '', _('display with template')),
2997 ('', 'template', '', _('display with template')),
2997 ] + remoteopts,
2998 ] + remoteopts,
2998 _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
2999 _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
2999 "^parents":
3000 "^parents":
3000 (parents,
3001 (parents,
3001 [('r', 'rev', '', _('show parents from the specified rev')),
3002 [('r', 'rev', '', _('show parents from the specified rev')),
3002 ('', 'style', '', _('display using template map file')),
3003 ('', 'style', '', _('display using template map file')),
3003 ('', 'template', '', _('display with template'))],
3004 ('', 'template', '', _('display with template'))],
3004 _('hg parents [-r REV] [FILE]')),
3005 _('hg parents [-r REV] [FILE]')),
3005 "paths": (paths, [], _('hg paths [NAME]')),
3006 "paths": (paths, [], _('hg paths [NAME]')),
3006 "^pull":
3007 "^pull":
3007 (pull,
3008 (pull,
3008 [('u', 'update', None,
3009 [('u', 'update', None,
3009 _('update to new tip if changesets were pulled')),
3010 _('update to new tip if changesets were pulled')),
3010 ('f', 'force', None,
3011 ('f', 'force', None,
3011 _('run even when remote repository is unrelated')),
3012 _('run even when remote repository is unrelated')),
3012 ('r', 'rev', [],
3013 ('r', 'rev', [],
3013 _('a specific revision up to which you would like to pull')),
3014 _('a specific revision up to which you would like to pull')),
3014 ] + remoteopts,
3015 ] + remoteopts,
3015 _('hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3016 _('hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3016 "^push":
3017 "^push":
3017 (push,
3018 (push,
3018 [('f', 'force', None, _('force push')),
3019 [('f', 'force', None, _('force push')),
3019 ('r', 'rev', [], _('a specific revision you would like to push')),
3020 ('r', 'rev', [], _('a specific revision you would like to push')),
3020 ] + remoteopts,
3021 ] + remoteopts,
3021 _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3022 _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3022 "debugrawcommit|rawcommit":
3023 "debugrawcommit|rawcommit":
3023 (rawcommit,
3024 (rawcommit,
3024 [('p', 'parent', [], _('parent')),
3025 [('p', 'parent', [], _('parent')),
3025 ('d', 'date', '', _('date code')),
3026 ('u', 'user', '', _('user')),
3027 ('F', 'files', '', _('file list'))
3026 ('F', 'files', '', _('file list'))
3028 ] + commitopts,
3027 ] + commitopts + commitopts2,
3029 _('hg debugrawcommit [OPTION]... [FILE]...')),
3028 _('hg debugrawcommit [OPTION]... [FILE]...')),
3030 "recover": (recover, [], _('hg recover')),
3029 "recover": (recover, [], _('hg recover')),
3031 "^remove|rm":
3030 "^remove|rm":
3032 (remove,
3031 (remove,
3033 [('A', 'after', None, _('record remove that has already occurred')),
3032 [('A', 'after', None, _('record remove that has already occurred')),
3034 ('f', 'force', None, _('remove file even if modified')),
3033 ('f', 'force', None, _('remove file even if modified')),
3035 ] + walkopts,
3034 ] + walkopts,
3036 _('hg remove [OPTION]... FILE...')),
3035 _('hg remove [OPTION]... FILE...')),
3037 "rename|mv":
3036 "rename|mv":
3038 (rename,
3037 (rename,
3039 [('A', 'after', None, _('record a rename that has already occurred')),
3038 [('A', 'after', None, _('record a rename that has already occurred')),
3040 ('f', 'force', None,
3039 ('f', 'force', None,
3041 _('forcibly copy over an existing managed file')),
3040 _('forcibly copy over an existing managed file')),
3042 ] + walkopts + dryrunopts,
3041 ] + walkopts + dryrunopts,
3043 _('hg rename [OPTION]... SOURCE... DEST')),
3042 _('hg rename [OPTION]... SOURCE... DEST')),
3044 "^revert":
3043 "^revert":
3045 (revert,
3044 (revert,
3046 [('a', 'all', None, _('revert all changes when no arguments given')),
3045 [('a', 'all', None, _('revert all changes when no arguments given')),
3047 ('d', 'date', '', _('tipmost revision matching date')),
3046 ('d', 'date', '', _('tipmost revision matching date')),
3048 ('r', 'rev', '', _('revision to revert to')),
3047 ('r', 'rev', '', _('revision to revert to')),
3049 ('', 'no-backup', None, _('do not save backup copies of files')),
3048 ('', 'no-backup', None, _('do not save backup copies of files')),
3050 ] + walkopts + dryrunopts,
3049 ] + walkopts + dryrunopts,
3051 _('hg revert [OPTION]... [-r REV] [NAME]...')),
3050 _('hg revert [OPTION]... [-r REV] [NAME]...')),
3052 "rollback": (rollback, [], _('hg rollback')),
3051 "rollback": (rollback, [], _('hg rollback')),
3053 "root": (root, [], _('hg root')),
3052 "root": (root, [], _('hg root')),
3054 "showconfig|debugconfig":
3053 "showconfig|debugconfig":
3055 (showconfig,
3054 (showconfig,
3056 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3055 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3057 _('showconfig [-u] [NAME]...')),
3056 _('showconfig [-u] [NAME]...')),
3058 "^serve":
3057 "^serve":
3059 (serve,
3058 (serve,
3060 [('A', 'accesslog', '', _('name of access log file to write to')),
3059 [('A', 'accesslog', '', _('name of access log file to write to')),
3061 ('d', 'daemon', None, _('run server in background')),
3060 ('d', 'daemon', None, _('run server in background')),
3062 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3061 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3063 ('E', 'errorlog', '', _('name of error log file to write to')),
3062 ('E', 'errorlog', '', _('name of error log file to write to')),
3064 ('p', 'port', 0, _('port to use (default: 8000)')),
3063 ('p', 'port', 0, _('port to use (default: 8000)')),
3065 ('a', 'address', '', _('address to use')),
3064 ('a', 'address', '', _('address to use')),
3066 ('n', 'name', '',
3065 ('n', 'name', '',
3067 _('name to show in web pages (default: working dir)')),
3066 _('name to show in web pages (default: working dir)')),
3068 ('', 'webdir-conf', '', _('name of the webdir config file'
3067 ('', 'webdir-conf', '', _('name of the webdir config file'
3069 ' (serve more than one repo)')),
3068 ' (serve more than one repo)')),
3070 ('', 'pid-file', '', _('name of file to write process ID to')),
3069 ('', 'pid-file', '', _('name of file to write process ID to')),
3071 ('', 'stdio', None, _('for remote clients')),
3070 ('', 'stdio', None, _('for remote clients')),
3072 ('t', 'templates', '', _('web templates to use')),
3071 ('t', 'templates', '', _('web templates to use')),
3073 ('', 'style', '', _('template style to use')),
3072 ('', 'style', '', _('template style to use')),
3074 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3073 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3075 ('', 'certificate', '', _('SSL certificate file'))],
3074 ('', 'certificate', '', _('SSL certificate file'))],
3076 _('hg serve [OPTION]...')),
3075 _('hg serve [OPTION]...')),
3077 "^status|st":
3076 "^status|st":
3078 (status,
3077 (status,
3079 [('A', 'all', None, _('show status of all files')),
3078 [('A', 'all', None, _('show status of all files')),
3080 ('m', 'modified', None, _('show only modified files')),
3079 ('m', 'modified', None, _('show only modified files')),
3081 ('a', 'added', None, _('show only added files')),
3080 ('a', 'added', None, _('show only added files')),
3082 ('r', 'removed', None, _('show only removed files')),
3081 ('r', 'removed', None, _('show only removed files')),
3083 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3082 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3084 ('c', 'clean', None, _('show only files without changes')),
3083 ('c', 'clean', None, _('show only files without changes')),
3085 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3084 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3086 ('i', 'ignored', None, _('show only ignored files')),
3085 ('i', 'ignored', None, _('show only ignored files')),
3087 ('n', 'no-status', None, _('hide status prefix')),
3086 ('n', 'no-status', None, _('hide status prefix')),
3088 ('C', 'copies', None, _('show source of copied files')),
3087 ('C', 'copies', None, _('show source of copied files')),
3089 ('0', 'print0', None,
3088 ('0', 'print0', None,
3090 _('end filenames with NUL, for use with xargs')),
3089 _('end filenames with NUL, for use with xargs')),
3091 ('', 'rev', [], _('show difference from revision')),
3090 ('', 'rev', [], _('show difference from revision')),
3092 ] + walkopts,
3091 ] + walkopts,
3093 _('hg status [OPTION]... [FILE]...')),
3092 _('hg status [OPTION]... [FILE]...')),
3094 "tag":
3093 "tag":
3095 (tag,
3094 (tag,
3096 [('f', 'force', None, _('replace existing tag')),
3095 [('f', 'force', None, _('replace existing tag')),
3097 ('l', 'local', None, _('make the tag local')),
3096 ('l', 'local', None, _('make the tag local')),
3098 ('m', 'message', '', _('message for tag commit log entry')),
3099 ('d', 'date', '', _('record datecode as commit date')),
3100 ('u', 'user', '', _('record user as commiter')),
3101 ('r', 'rev', '', _('revision to tag')),
3097 ('r', 'rev', '', _('revision to tag')),
3102 ('', 'remove', None, _('remove a tag'))],
3098 ('', 'remove', None, _('remove a tag')),
3099 # -l/--local is already there, commitopts cannot be used
3100 ('m', 'message', '', _('use <text> as commit message')),
3101 ] + commitopts2,
3103 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
3102 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
3104 "tags": (tags, [], _('hg tags')),
3103 "tags": (tags, [], _('hg tags')),
3105 "tip":
3104 "tip":
3106 (tip,
3105 (tip,
3107 [('', 'style', '', _('display using template map file')),
3106 [('', 'style', '', _('display using template map file')),
3108 ('p', 'patch', None, _('show patch')),
3107 ('p', 'patch', None, _('show patch')),
3109 ('', 'template', '', _('display with template'))],
3108 ('', 'template', '', _('display with template'))],
3110 _('hg tip [-p]')),
3109 _('hg tip [-p]')),
3111 "unbundle":
3110 "unbundle":
3112 (unbundle,
3111 (unbundle,
3113 [('u', 'update', None,
3112 [('u', 'update', None,
3114 _('update to new tip if changesets were unbundled'))],
3113 _('update to new tip if changesets were unbundled'))],
3115 _('hg unbundle [-u] FILE...')),
3114 _('hg unbundle [-u] FILE...')),
3116 "^update|up|checkout|co":
3115 "^update|up|checkout|co":
3117 (update,
3116 (update,
3118 [('C', 'clean', None, _('overwrite locally modified files')),
3117 [('C', 'clean', None, _('overwrite locally modified files')),
3119 ('d', 'date', '', _('tipmost revision matching date')),
3118 ('d', 'date', '', _('tipmost revision matching date')),
3120 ('r', 'rev', '', _('revision'))],
3119 ('r', 'rev', '', _('revision'))],
3121 _('hg update [-C] [-d DATE] [[-r] REV]')),
3120 _('hg update [-C] [-d DATE] [[-r] REV]')),
3122 "verify": (verify, [], _('hg verify')),
3121 "verify": (verify, [], _('hg verify')),
3123 "version": (version_, [], _('hg version')),
3122 "version": (version_, [], _('hg version')),
3124 }
3123 }
3125
3124
3126 extensions.commandtable = table
3125 extensions.commandtable = table
3127
3126
3128 norepo = ("clone init version help debugancestor debugcomplete debugdata"
3127 norepo = ("clone init version help debugancestor debugcomplete debugdata"
3129 " debugindex debugindexdot debugdate debuginstall")
3128 " debugindex debugindexdot debugdate debuginstall")
3130 optionalrepo = ("paths serve showconfig")
3129 optionalrepo = ("paths serve showconfig")
3131
3130
3132 def dispatch(args):
3131 def dispatch(args):
3133 try:
3132 try:
3134 u = ui.ui(traceback='--traceback' in args)
3133 u = ui.ui(traceback='--traceback' in args)
3135 except util.Abort, inst:
3134 except util.Abort, inst:
3136 sys.stderr.write(_("abort: %s\n") % inst)
3135 sys.stderr.write(_("abort: %s\n") % inst)
3137 return -1
3136 return -1
3138 return cmdutil.runcatch(u, args)
3137 return cmdutil.runcatch(u, args)
3139
3138
3140 def run():
3139 def run():
3141 sys.exit(dispatch(sys.argv[1:]))
3140 sys.exit(dispatch(sys.argv[1:]))
@@ -1,403 +1,403 b''
1 % help
1 % help
2 hg record [OPTION]... [FILE]...
2 hg record [OPTION]... [FILE]...
3
3
4 interactively select changes to commit
4 interactively select changes to commit
5
5
6 options:
6 options:
7
7
8 -A --addremove mark new/missing files as added/removed before committing
8 -A --addremove mark new/missing files as added/removed before committing
9 -d --date record datecode as commit date
10 -u --user record user as commiter
11 -I --include include names matching the given patterns
9 -I --include include names matching the given patterns
12 -X --exclude exclude names matching the given patterns
10 -X --exclude exclude names matching the given patterns
13 -m --message use <text> as commit message
11 -m --message use <text> as commit message
14 -l --logfile read commit message from <file>
12 -l --logfile read commit message from <file>
13 -d --date record datecode as commit date
14 -u --user record user as committer
15
15
16 use "hg -v help record" to show global options
16 use "hg -v help record" to show global options
17 % select no files
17 % select no files
18 diff --git a/empty-rw b/empty-rw
18 diff --git a/empty-rw b/empty-rw
19 new file mode 100644
19 new file mode 100644
20 record changes to 'empty-rw'? [y]es [n]o no changes to record
20 record changes to 'empty-rw'? [y]es [n]o no changes to record
21
21
22 changeset: -1:000000000000
22 changeset: -1:000000000000
23 tag: tip
23 tag: tip
24 user:
24 user:
25 date: Thu Jan 01 00:00:00 1970 +0000
25 date: Thu Jan 01 00:00:00 1970 +0000
26
26
27
27
28 % select files but no hunks
28 % select files but no hunks
29 diff --git a/empty-rw b/empty-rw
29 diff --git a/empty-rw b/empty-rw
30 new file mode 100644
30 new file mode 100644
31 record changes to 'empty-rw'? [y]es [n]o transaction abort!
31 record changes to 'empty-rw'? [y]es [n]o transaction abort!
32 rollback completed
32 rollback completed
33
33
34 changeset: -1:000000000000
34 changeset: -1:000000000000
35 tag: tip
35 tag: tip
36 user:
36 user:
37 date: Thu Jan 01 00:00:00 1970 +0000
37 date: Thu Jan 01 00:00:00 1970 +0000
38
38
39
39
40 % record empty file
40 % record empty file
41 diff --git a/empty-rw b/empty-rw
41 diff --git a/empty-rw b/empty-rw
42 new file mode 100644
42 new file mode 100644
43 record changes to 'empty-rw'? [y]es [n]o
43 record changes to 'empty-rw'? [y]es [n]o
44 changeset: 0:c0708cf4e46e
44 changeset: 0:c0708cf4e46e
45 tag: tip
45 tag: tip
46 user: test
46 user: test
47 date: Thu Jan 01 00:00:00 1970 +0000
47 date: Thu Jan 01 00:00:00 1970 +0000
48 summary: empty
48 summary: empty
49
49
50
50
51 % rename empty file
51 % rename empty file
52 diff --git a/empty-rw b/empty-rename
52 diff --git a/empty-rw b/empty-rename
53 rename from empty-rw
53 rename from empty-rw
54 rename to empty-rename
54 rename to empty-rename
55 record changes to 'empty-rw' and 'empty-rename'? [y]es [n]o
55 record changes to 'empty-rw' and 'empty-rename'? [y]es [n]o
56 changeset: 1:df251d174da3
56 changeset: 1:df251d174da3
57 tag: tip
57 tag: tip
58 user: test
58 user: test
59 date: Thu Jan 01 00:00:01 1970 +0000
59 date: Thu Jan 01 00:00:01 1970 +0000
60 summary: rename
60 summary: rename
61
61
62
62
63 % copy empty file
63 % copy empty file
64 diff --git a/empty-rename b/empty-copy
64 diff --git a/empty-rename b/empty-copy
65 copy from empty-rename
65 copy from empty-rename
66 copy to empty-copy
66 copy to empty-copy
67 record changes to 'empty-rename' and 'empty-copy'? [y]es [n]o
67 record changes to 'empty-rename' and 'empty-copy'? [y]es [n]o
68 changeset: 2:b63ea3939f8d
68 changeset: 2:b63ea3939f8d
69 tag: tip
69 tag: tip
70 user: test
70 user: test
71 date: Thu Jan 01 00:00:02 1970 +0000
71 date: Thu Jan 01 00:00:02 1970 +0000
72 summary: copy
72 summary: copy
73
73
74
74
75 % delete empty file
75 % delete empty file
76 diff --git a/empty-copy b/empty-copy
76 diff --git a/empty-copy b/empty-copy
77 deleted file mode 100644
77 deleted file mode 100644
78 record changes to 'empty-copy'? [y]es [n]o
78 record changes to 'empty-copy'? [y]es [n]o
79 changeset: 3:a2546574bce9
79 changeset: 3:a2546574bce9
80 tag: tip
80 tag: tip
81 user: test
81 user: test
82 date: Thu Jan 01 00:00:03 1970 +0000
82 date: Thu Jan 01 00:00:03 1970 +0000
83 summary: delete
83 summary: delete
84
84
85
85
86 % add binary file
86 % add binary file
87 diff --git a/tip.bundle b/tip.bundle
87 diff --git a/tip.bundle b/tip.bundle
88 new file mode 100644
88 new file mode 100644
89 this is a binary file
89 this is a binary file
90 record changes to 'tip.bundle'? [y]es [n]o
90 record changes to 'tip.bundle'? [y]es [n]o
91 changeset: 4:9e998a545a8b
91 changeset: 4:9e998a545a8b
92 tag: tip
92 tag: tip
93 user: test
93 user: test
94 date: Thu Jan 01 00:00:04 1970 +0000
94 date: Thu Jan 01 00:00:04 1970 +0000
95 summary: binary
95 summary: binary
96
96
97 diff -r a2546574bce9 -r 9e998a545a8b tip.bundle
97 diff -r a2546574bce9 -r 9e998a545a8b tip.bundle
98 Binary file tip.bundle has changed
98 Binary file tip.bundle has changed
99
99
100 % change binary file
100 % change binary file
101 diff --git a/tip.bundle b/tip.bundle
101 diff --git a/tip.bundle b/tip.bundle
102 this modifies a binary file (all or nothing)
102 this modifies a binary file (all or nothing)
103 record changes to 'tip.bundle'? [y]es [n]o
103 record changes to 'tip.bundle'? [y]es [n]o
104 changeset: 5:93d05561507d
104 changeset: 5:93d05561507d
105 tag: tip
105 tag: tip
106 user: test
106 user: test
107 date: Thu Jan 01 00:00:05 1970 +0000
107 date: Thu Jan 01 00:00:05 1970 +0000
108 summary: binary-change
108 summary: binary-change
109
109
110 diff -r 9e998a545a8b -r 93d05561507d tip.bundle
110 diff -r 9e998a545a8b -r 93d05561507d tip.bundle
111 Binary file tip.bundle has changed
111 Binary file tip.bundle has changed
112
112
113 % rename and change binary file
113 % rename and change binary file
114 diff --git a/tip.bundle b/top.bundle
114 diff --git a/tip.bundle b/top.bundle
115 rename from tip.bundle
115 rename from tip.bundle
116 rename to top.bundle
116 rename to top.bundle
117 this modifies a binary file (all or nothing)
117 this modifies a binary file (all or nothing)
118 record changes to 'tip.bundle' and 'top.bundle'? [y]es [n]o
118 record changes to 'tip.bundle' and 'top.bundle'? [y]es [n]o
119 changeset: 6:699cc1bea9aa
119 changeset: 6:699cc1bea9aa
120 tag: tip
120 tag: tip
121 user: test
121 user: test
122 date: Thu Jan 01 00:00:06 1970 +0000
122 date: Thu Jan 01 00:00:06 1970 +0000
123 summary: binary-change-rename
123 summary: binary-change-rename
124
124
125 diff -r 93d05561507d -r 699cc1bea9aa tip.bundle
125 diff -r 93d05561507d -r 699cc1bea9aa tip.bundle
126 Binary file tip.bundle has changed
126 Binary file tip.bundle has changed
127 diff -r 93d05561507d -r 699cc1bea9aa top.bundle
127 diff -r 93d05561507d -r 699cc1bea9aa top.bundle
128 Binary file top.bundle has changed
128 Binary file top.bundle has changed
129
129
130 % add plain file
130 % add plain file
131 diff --git a/plain b/plain
131 diff --git a/plain b/plain
132 new file mode 100644
132 new file mode 100644
133 record changes to 'plain'? [y]es [n]o
133 record changes to 'plain'? [y]es [n]o
134 changeset: 7:118ed744216b
134 changeset: 7:118ed744216b
135 tag: tip
135 tag: tip
136 user: test
136 user: test
137 date: Thu Jan 01 00:00:07 1970 +0000
137 date: Thu Jan 01 00:00:07 1970 +0000
138 summary: plain
138 summary: plain
139
139
140 diff -r 699cc1bea9aa -r 118ed744216b plain
140 diff -r 699cc1bea9aa -r 118ed744216b plain
141 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
141 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
142 +++ b/plain Thu Jan 01 00:00:07 1970 +0000
142 +++ b/plain Thu Jan 01 00:00:07 1970 +0000
143 @@ -0,0 +1,10 @@
143 @@ -0,0 +1,10 @@
144 +1
144 +1
145 +2
145 +2
146 +3
146 +3
147 +4
147 +4
148 +5
148 +5
149 +6
149 +6
150 +7
150 +7
151 +8
151 +8
152 +9
152 +9
153 +10
153 +10
154
154
155 % modify end of plain file
155 % modify end of plain file
156 diff --git a/plain b/plain
156 diff --git a/plain b/plain
157 1 hunks, 1 lines changed
157 1 hunks, 1 lines changed
158 record changes to 'plain'? [y]es [n]o @@ -8,3 +8,4 @@ 8
158 record changes to 'plain'? [y]es [n]o @@ -8,3 +8,4 @@ 8
159 8
159 8
160 9
160 9
161 10
161 10
162 +11
162 +11
163 record this change to 'plain'? [y]es [n]o % modify end of plain file, no EOL
163 record this change to 'plain'? [y]es [n]o % modify end of plain file, no EOL
164 diff --git a/plain b/plain
164 diff --git a/plain b/plain
165 1 hunks, 1 lines changed
165 1 hunks, 1 lines changed
166 record changes to 'plain'? [y]es [n]o @@ -9,3 +9,4 @@ 9
166 record changes to 'plain'? [y]es [n]o @@ -9,3 +9,4 @@ 9
167 9
167 9
168 10
168 10
169 11
169 11
170 +cf81a2760718a74d44c0c2eecb72f659e63a69c5
170 +cf81a2760718a74d44c0c2eecb72f659e63a69c5
171 \ No newline at end of file
171 \ No newline at end of file
172 record this change to 'plain'? [y]es [n]o % modify end of plain file, add EOL
172 record this change to 'plain'? [y]es [n]o % modify end of plain file, add EOL
173 diff --git a/plain b/plain
173 diff --git a/plain b/plain
174 1 hunks, 2 lines changed
174 1 hunks, 2 lines changed
175 record changes to 'plain'? [y]es [n]o @@ -9,4 +9,4 @@ 9
175 record changes to 'plain'? [y]es [n]o @@ -9,4 +9,4 @@ 9
176 9
176 9
177 10
177 10
178 11
178 11
179 -cf81a2760718a74d44c0c2eecb72f659e63a69c5
179 -cf81a2760718a74d44c0c2eecb72f659e63a69c5
180 \ No newline at end of file
180 \ No newline at end of file
181 +cf81a2760718a74d44c0c2eecb72f659e63a69c5
181 +cf81a2760718a74d44c0c2eecb72f659e63a69c5
182 record this change to 'plain'? [y]es [n]o % modify beginning, trim end, record both
182 record this change to 'plain'? [y]es [n]o % modify beginning, trim end, record both
183 diff --git a/plain b/plain
183 diff --git a/plain b/plain
184 2 hunks, 4 lines changed
184 2 hunks, 4 lines changed
185 record changes to 'plain'? [y]es [n]o @@ -1,4 +1,4 @@ 1
185 record changes to 'plain'? [y]es [n]o @@ -1,4 +1,4 @@ 1
186 -1
186 -1
187 +2
187 +2
188 2
188 2
189 3
189 3
190 4
190 4
191 record this change to 'plain'? [y]es [n]o @@ -8,5 +8,3 @@ 8
191 record this change to 'plain'? [y]es [n]o @@ -8,5 +8,3 @@ 8
192 8
192 8
193 9
193 9
194 10
194 10
195 -11
195 -11
196 -cf81a2760718a74d44c0c2eecb72f659e63a69c5
196 -cf81a2760718a74d44c0c2eecb72f659e63a69c5
197 record this change to 'plain'? [y]es [n]o
197 record this change to 'plain'? [y]es [n]o
198 changeset: 11:d09ab1967dab
198 changeset: 11:d09ab1967dab
199 tag: tip
199 tag: tip
200 user: test
200 user: test
201 date: Thu Jan 01 00:00:10 1970 +0000
201 date: Thu Jan 01 00:00:10 1970 +0000
202 summary: begin-and-end
202 summary: begin-and-end
203
203
204 diff -r e2ecd9b0b78d -r d09ab1967dab plain
204 diff -r e2ecd9b0b78d -r d09ab1967dab plain
205 --- a/plain Thu Jan 01 00:00:10 1970 +0000
205 --- a/plain Thu Jan 01 00:00:10 1970 +0000
206 +++ b/plain Thu Jan 01 00:00:10 1970 +0000
206 +++ b/plain Thu Jan 01 00:00:10 1970 +0000
207 @@ -1,4 +1,4 @@ 1
207 @@ -1,4 +1,4 @@ 1
208 -1
208 -1
209 +2
209 +2
210 2
210 2
211 3
211 3
212 4
212 4
213 @@ -8,5 +8,3 @@ 8
213 @@ -8,5 +8,3 @@ 8
214 8
214 8
215 9
215 9
216 10
216 10
217 -11
217 -11
218 -cf81a2760718a74d44c0c2eecb72f659e63a69c5
218 -cf81a2760718a74d44c0c2eecb72f659e63a69c5
219
219
220 % trim beginning, modify end
220 % trim beginning, modify end
221 % record end
221 % record end
222 diff --git a/plain b/plain
222 diff --git a/plain b/plain
223 2 hunks, 5 lines changed
223 2 hunks, 5 lines changed
224 record changes to 'plain'? [y]es [n]o @@ -1,9 +1,6 @@ 2
224 record changes to 'plain'? [y]es [n]o @@ -1,9 +1,6 @@ 2
225 -2
225 -2
226 -2
226 -2
227 -3
227 -3
228 4
228 4
229 5
229 5
230 6
230 6
231 7
231 7
232 8
232 8
233 9
233 9
234 record this change to 'plain'? [y]es [n]o @@ -4,7 +1,7 @@
234 record this change to 'plain'? [y]es [n]o @@ -4,7 +1,7 @@
235 4
235 4
236 5
236 5
237 6
237 6
238 7
238 7
239 8
239 8
240 9
240 9
241 -10
241 -10
242 +10.new
242 +10.new
243 record this change to 'plain'? [y]es [n]o
243 record this change to 'plain'? [y]es [n]o
244 changeset: 12:44516c9708ae
244 changeset: 12:44516c9708ae
245 tag: tip
245 tag: tip
246 user: test
246 user: test
247 date: Thu Jan 01 00:00:11 1970 +0000
247 date: Thu Jan 01 00:00:11 1970 +0000
248 summary: end-only
248 summary: end-only
249
249
250 diff -r d09ab1967dab -r 44516c9708ae plain
250 diff -r d09ab1967dab -r 44516c9708ae plain
251 --- a/plain Thu Jan 01 00:00:10 1970 +0000
251 --- a/plain Thu Jan 01 00:00:10 1970 +0000
252 +++ b/plain Thu Jan 01 00:00:11 1970 +0000
252 +++ b/plain Thu Jan 01 00:00:11 1970 +0000
253 @@ -7,4 +7,4 @@ 7
253 @@ -7,4 +7,4 @@ 7
254 7
254 7
255 8
255 8
256 9
256 9
257 -10
257 -10
258 +10.new
258 +10.new
259
259
260 % record beginning
260 % record beginning
261 diff --git a/plain b/plain
261 diff --git a/plain b/plain
262 1 hunks, 3 lines changed
262 1 hunks, 3 lines changed
263 record changes to 'plain'? [y]es [n]o @@ -1,6 +1,3 @@ 2
263 record changes to 'plain'? [y]es [n]o @@ -1,6 +1,3 @@ 2
264 -2
264 -2
265 -2
265 -2
266 -3
266 -3
267 4
267 4
268 5
268 5
269 6
269 6
270 record this change to 'plain'? [y]es [n]o
270 record this change to 'plain'? [y]es [n]o
271 changeset: 13:3ebbace64a8d
271 changeset: 13:3ebbace64a8d
272 tag: tip
272 tag: tip
273 user: test
273 user: test
274 date: Thu Jan 01 00:00:12 1970 +0000
274 date: Thu Jan 01 00:00:12 1970 +0000
275 summary: begin-only
275 summary: begin-only
276
276
277 diff -r 44516c9708ae -r 3ebbace64a8d plain
277 diff -r 44516c9708ae -r 3ebbace64a8d plain
278 --- a/plain Thu Jan 01 00:00:11 1970 +0000
278 --- a/plain Thu Jan 01 00:00:11 1970 +0000
279 +++ b/plain Thu Jan 01 00:00:12 1970 +0000
279 +++ b/plain Thu Jan 01 00:00:12 1970 +0000
280 @@ -1,6 +1,3 @@ 2
280 @@ -1,6 +1,3 @@ 2
281 -2
281 -2
282 -2
282 -2
283 -3
283 -3
284 4
284 4
285 5
285 5
286 6
286 6
287
287
288 % add to beginning, trim from end
288 % add to beginning, trim from end
289 % record end
289 % record end
290 diff --git a/plain b/plain
290 diff --git a/plain b/plain
291 2 hunks, 4 lines changed
291 2 hunks, 4 lines changed
292 record changes to 'plain'? [y]es [n]o @@ -1,6 +1,9 @@ 4
292 record changes to 'plain'? [y]es [n]o @@ -1,6 +1,9 @@ 4
293 +1
293 +1
294 +2
294 +2
295 +3
295 +3
296 4
296 4
297 5
297 5
298 6
298 6
299 7
299 7
300 8
300 8
301 9
301 9
302 record this change to 'plain'? [y]es [n]o @@ -1,7 +4,6 @@
302 record this change to 'plain'? [y]es [n]o @@ -1,7 +4,6 @@
303 4
303 4
304 5
304 5
305 6
305 6
306 7
306 7
307 8
307 8
308 9
308 9
309 -10.new
309 -10.new
310 record this change to 'plain'? [y]es [n]o % add to beginning, middle, end
310 record this change to 'plain'? [y]es [n]o % add to beginning, middle, end
311 % record beginning, middle
311 % record beginning, middle
312 diff --git a/plain b/plain
312 diff --git a/plain b/plain
313 3 hunks, 7 lines changed
313 3 hunks, 7 lines changed
314 record changes to 'plain'? [y]es [n]o @@ -1,2 +1,5 @@ 4
314 record changes to 'plain'? [y]es [n]o @@ -1,2 +1,5 @@ 4
315 +1
315 +1
316 +2
316 +2
317 +3
317 +3
318 4
318 4
319 5
319 5
320 record this change to 'plain'? [y]es [n]o @@ -1,6 +4,8 @@
320 record this change to 'plain'? [y]es [n]o @@ -1,6 +4,8 @@
321 4
321 4
322 5
322 5
323 +5.new
323 +5.new
324 +5.reallynew
324 +5.reallynew
325 6
325 6
326 7
326 7
327 8
327 8
328 9
328 9
329 record this change to 'plain'? [y]es [n]o @@ -3,4 +8,6 @@
329 record this change to 'plain'? [y]es [n]o @@ -3,4 +8,6 @@
330 6
330 6
331 7
331 7
332 8
332 8
333 9
333 9
334 +10
334 +10
335 +11
335 +11
336 record this change to 'plain'? [y]es [n]o
336 record this change to 'plain'? [y]es [n]o
337 changeset: 15:c1c639d8b268
337 changeset: 15:c1c639d8b268
338 tag: tip
338 tag: tip
339 user: test
339 user: test
340 date: Thu Jan 01 00:00:14 1970 +0000
340 date: Thu Jan 01 00:00:14 1970 +0000
341 summary: middle-only
341 summary: middle-only
342
342
343 diff -r efc0dad7bd9f -r c1c639d8b268 plain
343 diff -r efc0dad7bd9f -r c1c639d8b268 plain
344 --- a/plain Thu Jan 01 00:00:13 1970 +0000
344 --- a/plain Thu Jan 01 00:00:13 1970 +0000
345 +++ b/plain Thu Jan 01 00:00:14 1970 +0000
345 +++ b/plain Thu Jan 01 00:00:14 1970 +0000
346 @@ -1,5 +1,10 @@ 4
346 @@ -1,5 +1,10 @@ 4
347 +1
347 +1
348 +2
348 +2
349 +3
349 +3
350 4
350 4
351 5
351 5
352 +5.new
352 +5.new
353 +5.reallynew
353 +5.reallynew
354 6
354 6
355 7
355 7
356 8
356 8
357
357
358 % record end
358 % record end
359 diff --git a/plain b/plain
359 diff --git a/plain b/plain
360 1 hunks, 2 lines changed
360 1 hunks, 2 lines changed
361 record changes to 'plain'? [y]es [n]o @@ -9,3 +9,5 @@ 7
361 record changes to 'plain'? [y]es [n]o @@ -9,3 +9,5 @@ 7
362 7
362 7
363 8
363 8
364 9
364 9
365 +10
365 +10
366 +11
366 +11
367 record this change to 'plain'? [y]es [n]o
367 record this change to 'plain'? [y]es [n]o
368 changeset: 16:80b74bbc7808
368 changeset: 16:80b74bbc7808
369 tag: tip
369 tag: tip
370 user: test
370 user: test
371 date: Thu Jan 01 00:00:15 1970 +0000
371 date: Thu Jan 01 00:00:15 1970 +0000
372 summary: end-only
372 summary: end-only
373
373
374 diff -r c1c639d8b268 -r 80b74bbc7808 plain
374 diff -r c1c639d8b268 -r 80b74bbc7808 plain
375 --- a/plain Thu Jan 01 00:00:14 1970 +0000
375 --- a/plain Thu Jan 01 00:00:14 1970 +0000
376 +++ b/plain Thu Jan 01 00:00:15 1970 +0000
376 +++ b/plain Thu Jan 01 00:00:15 1970 +0000
377 @@ -9,3 +9,5 @@ 7
377 @@ -9,3 +9,5 @@ 7
378 7
378 7
379 8
379 8
380 9
380 9
381 +10
381 +10
382 +11
382 +11
383
383
384 adding subdir/a
384 adding subdir/a
385 diff --git a/subdir/a b/subdir/a
385 diff --git a/subdir/a b/subdir/a
386 1 hunks, 1 lines changed
386 1 hunks, 1 lines changed
387 record changes to 'subdir/a'? [y]es [n]o @@ -1,1 +1,2 @@ a
387 record changes to 'subdir/a'? [y]es [n]o @@ -1,1 +1,2 @@ a
388 a
388 a
389 +a
389 +a
390 record this change to 'subdir/a'? [y]es [n]o
390 record this change to 'subdir/a'? [y]es [n]o
391 changeset: 18:33ff5c4fb017
391 changeset: 18:33ff5c4fb017
392 tag: tip
392 tag: tip
393 user: test
393 user: test
394 date: Thu Jan 01 00:00:16 1970 +0000
394 date: Thu Jan 01 00:00:16 1970 +0000
395 summary: subdir-change
395 summary: subdir-change
396
396
397 diff -r aecf2b2ea83c -r 33ff5c4fb017 subdir/a
397 diff -r aecf2b2ea83c -r 33ff5c4fb017 subdir/a
398 --- a/subdir/a Thu Jan 01 00:00:16 1970 +0000
398 --- a/subdir/a Thu Jan 01 00:00:16 1970 +0000
399 +++ b/subdir/a Thu Jan 01 00:00:16 1970 +0000
399 +++ b/subdir/a Thu Jan 01 00:00:16 1970 +0000
400 @@ -1,1 +1,2 @@ a
400 @@ -1,1 +1,2 @@ a
401 a
401 a
402 +a
402 +a
403
403
General Comments 0
You need to be logged in to leave comments. Login now