|
@@
-1,1159
+1,1160
b''
|
|
1
|
# cmdutil.py - help for command processing in mercurial
|
|
1
|
# cmdutil.py - help for command processing in 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
|
from node import *
|
|
8
|
from node import *
|
|
9
|
from i18n import _
|
|
9
|
from i18n import _
|
|
10
|
import os, sys, bisect, stat
|
|
10
|
import os, sys, bisect, stat
|
|
11
|
import mdiff, bdiff, util, templater, templatefilters, patch, errno
|
|
11
|
import mdiff, bdiff, util, templater, templatefilters, patch, errno
|
|
12
|
|
|
12
|
|
|
13
|
revrangesep = ':'
|
|
13
|
revrangesep = ':'
|
|
14
|
|
|
14
|
|
|
15
|
class UnknownCommand(Exception):
|
|
15
|
class UnknownCommand(Exception):
|
|
16
|
"""Exception raised if command is not in the command table."""
|
|
16
|
"""Exception raised if command is not in the command table."""
|
|
17
|
class AmbiguousCommand(Exception):
|
|
17
|
class AmbiguousCommand(Exception):
|
|
18
|
"""Exception raised if command shortcut matches more than one command."""
|
|
18
|
"""Exception raised if command shortcut matches more than one command."""
|
|
19
|
|
|
19
|
|
|
20
|
def findpossible(ui, cmd, table):
|
|
20
|
def findpossible(ui, cmd, table):
|
|
21
|
"""
|
|
21
|
"""
|
|
22
|
Return cmd -> (aliases, command table entry)
|
|
22
|
Return cmd -> (aliases, command table entry)
|
|
23
|
for each matching command.
|
|
23
|
for each matching command.
|
|
24
|
Return debug commands (or their aliases) only if no normal command matches.
|
|
24
|
Return debug commands (or their aliases) only if no normal command matches.
|
|
25
|
"""
|
|
25
|
"""
|
|
26
|
choice = {}
|
|
26
|
choice = {}
|
|
27
|
debugchoice = {}
|
|
27
|
debugchoice = {}
|
|
28
|
for e in table.keys():
|
|
28
|
for e in table.keys():
|
|
29
|
aliases = e.lstrip("^").split("|")
|
|
29
|
aliases = e.lstrip("^").split("|")
|
|
30
|
found = None
|
|
30
|
found = None
|
|
31
|
if cmd in aliases:
|
|
31
|
if cmd in aliases:
|
|
32
|
found = cmd
|
|
32
|
found = cmd
|
|
33
|
elif not ui.config("ui", "strict"):
|
|
33
|
elif not ui.config("ui", "strict"):
|
|
34
|
for a in aliases:
|
|
34
|
for a in aliases:
|
|
35
|
if a.startswith(cmd):
|
|
35
|
if a.startswith(cmd):
|
|
36
|
found = a
|
|
36
|
found = a
|
|
37
|
break
|
|
37
|
break
|
|
38
|
if found is not None:
|
|
38
|
if found is not None:
|
|
39
|
if aliases[0].startswith("debug") or found.startswith("debug"):
|
|
39
|
if aliases[0].startswith("debug") or found.startswith("debug"):
|
|
40
|
debugchoice[found] = (aliases, table[e])
|
|
40
|
debugchoice[found] = (aliases, table[e])
|
|
41
|
else:
|
|
41
|
else:
|
|
42
|
choice[found] = (aliases, table[e])
|
|
42
|
choice[found] = (aliases, table[e])
|
|
43
|
|
|
43
|
|
|
44
|
if not choice and debugchoice:
|
|
44
|
if not choice and debugchoice:
|
|
45
|
choice = debugchoice
|
|
45
|
choice = debugchoice
|
|
46
|
|
|
46
|
|
|
47
|
return choice
|
|
47
|
return choice
|
|
48
|
|
|
48
|
|
|
49
|
def findcmd(ui, cmd, table):
|
|
49
|
def findcmd(ui, cmd, table):
|
|
50
|
"""Return (aliases, command table entry) for command string."""
|
|
50
|
"""Return (aliases, command table entry) for command string."""
|
|
51
|
choice = findpossible(ui, cmd, table)
|
|
51
|
choice = findpossible(ui, cmd, table)
|
|
52
|
|
|
52
|
|
|
53
|
if cmd in choice:
|
|
53
|
if cmd in choice:
|
|
54
|
return choice[cmd]
|
|
54
|
return choice[cmd]
|
|
55
|
|
|
55
|
|
|
56
|
if len(choice) > 1:
|
|
56
|
if len(choice) > 1:
|
|
57
|
clist = choice.keys()
|
|
57
|
clist = choice.keys()
|
|
58
|
clist.sort()
|
|
58
|
clist.sort()
|
|
59
|
raise AmbiguousCommand(cmd, clist)
|
|
59
|
raise AmbiguousCommand(cmd, clist)
|
|
60
|
|
|
60
|
|
|
61
|
if choice:
|
|
61
|
if choice:
|
|
62
|
return choice.values()[0]
|
|
62
|
return choice.values()[0]
|
|
63
|
|
|
63
|
|
|
64
|
raise UnknownCommand(cmd)
|
|
64
|
raise UnknownCommand(cmd)
|
|
65
|
|
|
65
|
|
|
66
|
def bail_if_changed(repo):
|
|
66
|
def bail_if_changed(repo):
|
|
67
|
if repo.dirstate.parents()[1] != nullid:
|
|
67
|
if repo.dirstate.parents()[1] != nullid:
|
|
68
|
raise util.Abort(_('outstanding uncommitted merge'))
|
|
68
|
raise util.Abort(_('outstanding uncommitted merge'))
|
|
69
|
modified, added, removed, deleted = repo.status()[:4]
|
|
69
|
modified, added, removed, deleted = repo.status()[:4]
|
|
70
|
if modified or added or removed or deleted:
|
|
70
|
if modified or added or removed or deleted:
|
|
71
|
raise util.Abort(_("outstanding uncommitted changes"))
|
|
71
|
raise util.Abort(_("outstanding uncommitted changes"))
|
|
72
|
|
|
72
|
|
|
73
|
def logmessage(opts):
|
|
73
|
def logmessage(opts):
|
|
74
|
""" get the log message according to -m and -l option """
|
|
74
|
""" get the log message according to -m and -l option """
|
|
75
|
message = opts['message']
|
|
75
|
message = opts['message']
|
|
76
|
logfile = opts['logfile']
|
|
76
|
logfile = opts['logfile']
|
|
77
|
|
|
77
|
|
|
78
|
if message and logfile:
|
|
78
|
if message and logfile:
|
|
79
|
raise util.Abort(_('options --message and --logfile are mutually '
|
|
79
|
raise util.Abort(_('options --message and --logfile are mutually '
|
|
80
|
'exclusive'))
|
|
80
|
'exclusive'))
|
|
81
|
if not message and logfile:
|
|
81
|
if not message and logfile:
|
|
82
|
try:
|
|
82
|
try:
|
|
83
|
if logfile == '-':
|
|
83
|
if logfile == '-':
|
|
84
|
message = sys.stdin.read()
|
|
84
|
message = sys.stdin.read()
|
|
85
|
else:
|
|
85
|
else:
|
|
86
|
message = open(logfile).read()
|
|
86
|
message = open(logfile).read()
|
|
87
|
except IOError, inst:
|
|
87
|
except IOError, inst:
|
|
88
|
raise util.Abort(_("can't read commit message '%s': %s") %
|
|
88
|
raise util.Abort(_("can't read commit message '%s': %s") %
|
|
89
|
(logfile, inst.strerror))
|
|
89
|
(logfile, inst.strerror))
|
|
90
|
return message
|
|
90
|
return message
|
|
91
|
|
|
91
|
|
|
92
|
def setremoteconfig(ui, opts):
|
|
92
|
def setremoteconfig(ui, opts):
|
|
93
|
"copy remote options to ui tree"
|
|
93
|
"copy remote options to ui tree"
|
|
94
|
if opts.get('ssh'):
|
|
94
|
if opts.get('ssh'):
|
|
95
|
ui.setconfig("ui", "ssh", opts['ssh'])
|
|
95
|
ui.setconfig("ui", "ssh", opts['ssh'])
|
|
96
|
if opts.get('remotecmd'):
|
|
96
|
if opts.get('remotecmd'):
|
|
97
|
ui.setconfig("ui", "remotecmd", opts['remotecmd'])
|
|
97
|
ui.setconfig("ui", "remotecmd", opts['remotecmd'])
|
|
98
|
|
|
98
|
|
|
99
|
def revpair(repo, revs):
|
|
99
|
def revpair(repo, revs):
|
|
100
|
'''return pair of nodes, given list of revisions. second item can
|
|
100
|
'''return pair of nodes, given list of revisions. second item can
|
|
101
|
be None, meaning use working dir.'''
|
|
101
|
be None, meaning use working dir.'''
|
|
102
|
|
|
102
|
|
|
103
|
def revfix(repo, val, defval):
|
|
103
|
def revfix(repo, val, defval):
|
|
104
|
if not val and val != 0 and defval is not None:
|
|
104
|
if not val and val != 0 and defval is not None:
|
|
105
|
val = defval
|
|
105
|
val = defval
|
|
106
|
return repo.lookup(val)
|
|
106
|
return repo.lookup(val)
|
|
107
|
|
|
107
|
|
|
108
|
if not revs:
|
|
108
|
if not revs:
|
|
109
|
return repo.dirstate.parents()[0], None
|
|
109
|
return repo.dirstate.parents()[0], None
|
|
110
|
end = None
|
|
110
|
end = None
|
|
111
|
if len(revs) == 1:
|
|
111
|
if len(revs) == 1:
|
|
112
|
if revrangesep in revs[0]:
|
|
112
|
if revrangesep in revs[0]:
|
|
113
|
start, end = revs[0].split(revrangesep, 1)
|
|
113
|
start, end = revs[0].split(revrangesep, 1)
|
|
114
|
start = revfix(repo, start, 0)
|
|
114
|
start = revfix(repo, start, 0)
|
|
115
|
end = revfix(repo, end, repo.changelog.count() - 1)
|
|
115
|
end = revfix(repo, end, repo.changelog.count() - 1)
|
|
116
|
else:
|
|
116
|
else:
|
|
117
|
start = revfix(repo, revs[0], None)
|
|
117
|
start = revfix(repo, revs[0], None)
|
|
118
|
elif len(revs) == 2:
|
|
118
|
elif len(revs) == 2:
|
|
119
|
if revrangesep in revs[0] or revrangesep in revs[1]:
|
|
119
|
if revrangesep in revs[0] or revrangesep in revs[1]:
|
|
120
|
raise util.Abort(_('too many revisions specified'))
|
|
120
|
raise util.Abort(_('too many revisions specified'))
|
|
121
|
start = revfix(repo, revs[0], None)
|
|
121
|
start = revfix(repo, revs[0], None)
|
|
122
|
end = revfix(repo, revs[1], None)
|
|
122
|
end = revfix(repo, revs[1], None)
|
|
123
|
else:
|
|
123
|
else:
|
|
124
|
raise util.Abort(_('too many revisions specified'))
|
|
124
|
raise util.Abort(_('too many revisions specified'))
|
|
125
|
return start, end
|
|
125
|
return start, end
|
|
126
|
|
|
126
|
|
|
127
|
def revrange(repo, revs):
|
|
127
|
def revrange(repo, revs):
|
|
128
|
"""Yield revision as strings from a list of revision specifications."""
|
|
128
|
"""Yield revision as strings from a list of revision specifications."""
|
|
129
|
|
|
129
|
|
|
130
|
def revfix(repo, val, defval):
|
|
130
|
def revfix(repo, val, defval):
|
|
131
|
if not val and val != 0 and defval is not None:
|
|
131
|
if not val and val != 0 and defval is not None:
|
|
132
|
return defval
|
|
132
|
return defval
|
|
133
|
return repo.changelog.rev(repo.lookup(val))
|
|
133
|
return repo.changelog.rev(repo.lookup(val))
|
|
134
|
|
|
134
|
|
|
135
|
seen, l = {}, []
|
|
135
|
seen, l = {}, []
|
|
136
|
for spec in revs:
|
|
136
|
for spec in revs:
|
|
137
|
if revrangesep in spec:
|
|
137
|
if revrangesep in spec:
|
|
138
|
start, end = spec.split(revrangesep, 1)
|
|
138
|
start, end = spec.split(revrangesep, 1)
|
|
139
|
start = revfix(repo, start, 0)
|
|
139
|
start = revfix(repo, start, 0)
|
|
140
|
end = revfix(repo, end, repo.changelog.count() - 1)
|
|
140
|
end = revfix(repo, end, repo.changelog.count() - 1)
|
|
141
|
step = start > end and -1 or 1
|
|
141
|
step = start > end and -1 or 1
|
|
142
|
for rev in xrange(start, end+step, step):
|
|
142
|
for rev in xrange(start, end+step, step):
|
|
143
|
if rev in seen:
|
|
143
|
if rev in seen:
|
|
144
|
continue
|
|
144
|
continue
|
|
145
|
seen[rev] = 1
|
|
145
|
seen[rev] = 1
|
|
146
|
l.append(rev)
|
|
146
|
l.append(rev)
|
|
147
|
else:
|
|
147
|
else:
|
|
148
|
rev = revfix(repo, spec, None)
|
|
148
|
rev = revfix(repo, spec, None)
|
|
149
|
if rev in seen:
|
|
149
|
if rev in seen:
|
|
150
|
continue
|
|
150
|
continue
|
|
151
|
seen[rev] = 1
|
|
151
|
seen[rev] = 1
|
|
152
|
l.append(rev)
|
|
152
|
l.append(rev)
|
|
153
|
|
|
153
|
|
|
154
|
return l
|
|
154
|
return l
|
|
155
|
|
|
155
|
|
|
156
|
def make_filename(repo, pat, node,
|
|
156
|
def make_filename(repo, pat, node,
|
|
157
|
total=None, seqno=None, revwidth=None, pathname=None):
|
|
157
|
total=None, seqno=None, revwidth=None, pathname=None):
|
|
158
|
node_expander = {
|
|
158
|
node_expander = {
|
|
159
|
'H': lambda: hex(node),
|
|
159
|
'H': lambda: hex(node),
|
|
160
|
'R': lambda: str(repo.changelog.rev(node)),
|
|
160
|
'R': lambda: str(repo.changelog.rev(node)),
|
|
161
|
'h': lambda: short(node),
|
|
161
|
'h': lambda: short(node),
|
|
162
|
}
|
|
162
|
}
|
|
163
|
expander = {
|
|
163
|
expander = {
|
|
164
|
'%': lambda: '%',
|
|
164
|
'%': lambda: '%',
|
|
165
|
'b': lambda: os.path.basename(repo.root),
|
|
165
|
'b': lambda: os.path.basename(repo.root),
|
|
166
|
}
|
|
166
|
}
|
|
167
|
|
|
167
|
|
|
168
|
try:
|
|
168
|
try:
|
|
169
|
if node:
|
|
169
|
if node:
|
|
170
|
expander.update(node_expander)
|
|
170
|
expander.update(node_expander)
|
|
171
|
if node:
|
|
171
|
if node:
|
|
172
|
expander['r'] = (lambda:
|
|
172
|
expander['r'] = (lambda:
|
|
173
|
str(repo.changelog.rev(node)).zfill(revwidth or 0))
|
|
173
|
str(repo.changelog.rev(node)).zfill(revwidth or 0))
|
|
174
|
if total is not None:
|
|
174
|
if total is not None:
|
|
175
|
expander['N'] = lambda: str(total)
|
|
175
|
expander['N'] = lambda: str(total)
|
|
176
|
if seqno is not None:
|
|
176
|
if seqno is not None:
|
|
177
|
expander['n'] = lambda: str(seqno)
|
|
177
|
expander['n'] = lambda: str(seqno)
|
|
178
|
if total is not None and seqno is not None:
|
|
178
|
if total is not None and seqno is not None:
|
|
179
|
expander['n'] = lambda: str(seqno).zfill(len(str(total)))
|
|
179
|
expander['n'] = lambda: str(seqno).zfill(len(str(total)))
|
|
180
|
if pathname is not None:
|
|
180
|
if pathname is not None:
|
|
181
|
expander['s'] = lambda: os.path.basename(pathname)
|
|
181
|
expander['s'] = lambda: os.path.basename(pathname)
|
|
182
|
expander['d'] = lambda: os.path.dirname(pathname) or '.'
|
|
182
|
expander['d'] = lambda: os.path.dirname(pathname) or '.'
|
|
183
|
expander['p'] = lambda: pathname
|
|
183
|
expander['p'] = lambda: pathname
|
|
184
|
|
|
184
|
|
|
185
|
newname = []
|
|
185
|
newname = []
|
|
186
|
patlen = len(pat)
|
|
186
|
patlen = len(pat)
|
|
187
|
i = 0
|
|
187
|
i = 0
|
|
188
|
while i < patlen:
|
|
188
|
while i < patlen:
|
|
189
|
c = pat[i]
|
|
189
|
c = pat[i]
|
|
190
|
if c == '%':
|
|
190
|
if c == '%':
|
|
191
|
i += 1
|
|
191
|
i += 1
|
|
192
|
c = pat[i]
|
|
192
|
c = pat[i]
|
|
193
|
c = expander[c]()
|
|
193
|
c = expander[c]()
|
|
194
|
newname.append(c)
|
|
194
|
newname.append(c)
|
|
195
|
i += 1
|
|
195
|
i += 1
|
|
196
|
return ''.join(newname)
|
|
196
|
return ''.join(newname)
|
|
197
|
except KeyError, inst:
|
|
197
|
except KeyError, inst:
|
|
198
|
raise util.Abort(_("invalid format spec '%%%s' in output file name") %
|
|
198
|
raise util.Abort(_("invalid format spec '%%%s' in output file name") %
|
|
199
|
inst.args[0])
|
|
199
|
inst.args[0])
|
|
200
|
|
|
200
|
|
|
201
|
def make_file(repo, pat, node=None,
|
|
201
|
def make_file(repo, pat, node=None,
|
|
202
|
total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
|
|
202
|
total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
|
|
203
|
if not pat or pat == '-':
|
|
203
|
if not pat or pat == '-':
|
|
204
|
return 'w' in mode and sys.stdout or sys.stdin
|
|
204
|
return 'w' in mode and sys.stdout or sys.stdin
|
|
205
|
if hasattr(pat, 'write') and 'w' in mode:
|
|
205
|
if hasattr(pat, 'write') and 'w' in mode:
|
|
206
|
return pat
|
|
206
|
return pat
|
|
207
|
if hasattr(pat, 'read') and 'r' in mode:
|
|
207
|
if hasattr(pat, 'read') and 'r' in mode:
|
|
208
|
return pat
|
|
208
|
return pat
|
|
209
|
return open(make_filename(repo, pat, node, total, seqno, revwidth,
|
|
209
|
return open(make_filename(repo, pat, node, total, seqno, revwidth,
|
|
210
|
pathname),
|
|
210
|
pathname),
|
|
211
|
mode)
|
|
211
|
mode)
|
|
212
|
|
|
212
|
|
|
213
|
def matchpats(repo, pats=[], opts={}, globbed=False, default=None):
|
|
213
|
def matchpats(repo, pats=[], opts={}, globbed=False, default=None):
|
|
214
|
cwd = repo.getcwd()
|
|
214
|
cwd = repo.getcwd()
|
|
215
|
return util.cmdmatcher(repo.root, cwd, pats or [], opts.get('include'),
|
|
215
|
return util.cmdmatcher(repo.root, cwd, pats or [], opts.get('include'),
|
|
216
|
opts.get('exclude'), globbed=globbed,
|
|
216
|
opts.get('exclude'), globbed=globbed,
|
|
217
|
default=default)
|
|
217
|
default=default)
|
|
218
|
|
|
218
|
|
|
219
|
def walk(repo, pats=[], opts={}, node=None, badmatch=None, globbed=False,
|
|
219
|
def walk(repo, pats=[], opts={}, node=None, badmatch=None, globbed=False,
|
|
220
|
default=None):
|
|
220
|
default=None):
|
|
221
|
files, matchfn, anypats = matchpats(repo, pats, opts, globbed=globbed,
|
|
221
|
files, matchfn, anypats = matchpats(repo, pats, opts, globbed=globbed,
|
|
222
|
default=default)
|
|
222
|
default=default)
|
|
223
|
exact = dict.fromkeys(files)
|
|
223
|
exact = dict.fromkeys(files)
|
|
224
|
cwd = repo.getcwd()
|
|
224
|
cwd = repo.getcwd()
|
|
225
|
for src, fn in repo.walk(node=node, files=files, match=matchfn,
|
|
225
|
for src, fn in repo.walk(node=node, files=files, match=matchfn,
|
|
226
|
badmatch=badmatch):
|
|
226
|
badmatch=badmatch):
|
|
227
|
yield src, fn, repo.pathto(fn, cwd), fn in exact
|
|
227
|
yield src, fn, repo.pathto(fn, cwd), fn in exact
|
|
228
|
|
|
228
|
|
|
229
|
def findrenames(repo, added=None, removed=None, threshold=0.5):
|
|
229
|
def findrenames(repo, added=None, removed=None, threshold=0.5):
|
|
230
|
'''find renamed files -- yields (before, after, score) tuples'''
|
|
230
|
'''find renamed files -- yields (before, after, score) tuples'''
|
|
231
|
if added is None or removed is None:
|
|
231
|
if added is None or removed is None:
|
|
232
|
added, removed = repo.status()[1:3]
|
|
232
|
added, removed = repo.status()[1:3]
|
|
233
|
ctx = repo.changectx()
|
|
233
|
ctx = repo.changectx()
|
|
234
|
for a in added:
|
|
234
|
for a in added:
|
|
235
|
aa = repo.wread(a)
|
|
235
|
aa = repo.wread(a)
|
|
236
|
bestname, bestscore = None, threshold
|
|
236
|
bestname, bestscore = None, threshold
|
|
237
|
for r in removed:
|
|
237
|
for r in removed:
|
|
238
|
rr = ctx.filectx(r).data()
|
|
238
|
rr = ctx.filectx(r).data()
|
|
239
|
|
|
239
|
|
|
240
|
# bdiff.blocks() returns blocks of matching lines
|
|
240
|
# bdiff.blocks() returns blocks of matching lines
|
|
241
|
# count the number of bytes in each
|
|
241
|
# count the number of bytes in each
|
|
242
|
equal = 0
|
|
242
|
equal = 0
|
|
243
|
alines = mdiff.splitnewlines(aa)
|
|
243
|
alines = mdiff.splitnewlines(aa)
|
|
244
|
matches = bdiff.blocks(aa, rr)
|
|
244
|
matches = bdiff.blocks(aa, rr)
|
|
245
|
for x1,x2,y1,y2 in matches:
|
|
245
|
for x1,x2,y1,y2 in matches:
|
|
246
|
for line in alines[x1:x2]:
|
|
246
|
for line in alines[x1:x2]:
|
|
247
|
equal += len(line)
|
|
247
|
equal += len(line)
|
|
248
|
|
|
248
|
|
|
249
|
lengths = len(aa) + len(rr)
|
|
249
|
lengths = len(aa) + len(rr)
|
|
250
|
if lengths:
|
|
250
|
if lengths:
|
|
251
|
myscore = equal*2.0 / lengths
|
|
251
|
myscore = equal*2.0 / lengths
|
|
252
|
if myscore >= bestscore:
|
|
252
|
if myscore >= bestscore:
|
|
253
|
bestname, bestscore = r, myscore
|
|
253
|
bestname, bestscore = r, myscore
|
|
254
|
if bestname:
|
|
254
|
if bestname:
|
|
255
|
yield bestname, a, bestscore
|
|
255
|
yield bestname, a, bestscore
|
|
256
|
|
|
256
|
|
|
257
|
def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
|
|
257
|
def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
|
|
258
|
if dry_run is None:
|
|
258
|
if dry_run is None:
|
|
259
|
dry_run = opts.get('dry_run')
|
|
259
|
dry_run = opts.get('dry_run')
|
|
260
|
if similarity is None:
|
|
260
|
if similarity is None:
|
|
261
|
similarity = float(opts.get('similarity') or 0)
|
|
261
|
similarity = float(opts.get('similarity') or 0)
|
|
262
|
add, remove = [], []
|
|
262
|
add, remove = [], []
|
|
263
|
mapping = {}
|
|
263
|
mapping = {}
|
|
264
|
for src, abs, rel, exact in walk(repo, pats, opts):
|
|
264
|
for src, abs, rel, exact in walk(repo, pats, opts):
|
|
265
|
target = repo.wjoin(abs)
|
|
265
|
target = repo.wjoin(abs)
|
|
266
|
if src == 'f' and abs not in repo.dirstate:
|
|
266
|
if src == 'f' and abs not in repo.dirstate:
|
|
267
|
add.append(abs)
|
|
267
|
add.append(abs)
|
|
268
|
mapping[abs] = rel, exact
|
|
268
|
mapping[abs] = rel, exact
|
|
269
|
if repo.ui.verbose or not exact:
|
|
269
|
if repo.ui.verbose or not exact:
|
|
270
|
repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
|
|
270
|
repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
|
|
271
|
if repo.dirstate[abs] != 'r' and (not util.lexists(target)
|
|
271
|
if repo.dirstate[abs] != 'r' and (not util.lexists(target)
|
|
272
|
or (os.path.isdir(target) and not os.path.islink(target))):
|
|
272
|
or (os.path.isdir(target) and not os.path.islink(target))):
|
|
273
|
remove.append(abs)
|
|
273
|
remove.append(abs)
|
|
274
|
mapping[abs] = rel, exact
|
|
274
|
mapping[abs] = rel, exact
|
|
275
|
if repo.ui.verbose or not exact:
|
|
275
|
if repo.ui.verbose or not exact:
|
|
276
|
repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
|
|
276
|
repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
|
|
277
|
if not dry_run:
|
|
277
|
if not dry_run:
|
|
278
|
repo.remove(remove)
|
|
278
|
repo.remove(remove)
|
|
279
|
repo.add(add)
|
|
279
|
repo.add(add)
|
|
280
|
if similarity > 0:
|
|
280
|
if similarity > 0:
|
|
281
|
for old, new, score in findrenames(repo, add, remove, similarity):
|
|
281
|
for old, new, score in findrenames(repo, add, remove, similarity):
|
|
282
|
oldrel, oldexact = mapping[old]
|
|
282
|
oldrel, oldexact = mapping[old]
|
|
283
|
newrel, newexact = mapping[new]
|
|
283
|
newrel, newexact = mapping[new]
|
|
284
|
if repo.ui.verbose or not oldexact or not newexact:
|
|
284
|
if repo.ui.verbose or not oldexact or not newexact:
|
|
285
|
repo.ui.status(_('recording removal of %s as rename to %s '
|
|
285
|
repo.ui.status(_('recording removal of %s as rename to %s '
|
|
286
|
'(%d%% similar)\n') %
|
|
286
|
'(%d%% similar)\n') %
|
|
287
|
(oldrel, newrel, score * 100))
|
|
287
|
(oldrel, newrel, score * 100))
|
|
288
|
if not dry_run:
|
|
288
|
if not dry_run:
|
|
289
|
repo.copy(old, new)
|
|
289
|
repo.copy(old, new)
|
|
290
|
|
|
290
|
|
|
291
|
def copy(ui, repo, pats, opts, rename=False):
|
|
291
|
def copy(ui, repo, pats, opts, rename=False):
|
|
292
|
# called with the repo lock held
|
|
292
|
# called with the repo lock held
|
|
293
|
#
|
|
293
|
#
|
|
294
|
# hgsep => pathname that uses "/" to separate directories
|
|
294
|
# hgsep => pathname that uses "/" to separate directories
|
|
295
|
# ossep => pathname that uses os.sep to separate directories
|
|
295
|
# ossep => pathname that uses os.sep to separate directories
|
|
296
|
cwd = repo.getcwd()
|
|
296
|
cwd = repo.getcwd()
|
|
297
|
targets = {}
|
|
297
|
targets = {}
|
|
298
|
after = opts.get("after")
|
|
298
|
after = opts.get("after")
|
|
299
|
dryrun = opts.get("dry_run")
|
|
299
|
dryrun = opts.get("dry_run")
|
|
300
|
|
|
300
|
|
|
301
|
def walkpat(pat):
|
|
301
|
def walkpat(pat):
|
|
302
|
srcs = []
|
|
302
|
srcs = []
|
|
303
|
for tag, abs, rel, exact in walk(repo, [pat], opts, globbed=True):
|
|
303
|
for tag, abs, rel, exact in walk(repo, [pat], opts, globbed=True):
|
|
304
|
state = repo.dirstate[abs]
|
|
304
|
state = repo.dirstate[abs]
|
|
305
|
if state in '?r':
|
|
305
|
if state in '?r':
|
|
306
|
if exact and state == '?':
|
|
306
|
if exact and state == '?':
|
|
307
|
ui.warn(_('%s: not copying - file is not managed\n') % rel)
|
|
307
|
ui.warn(_('%s: not copying - file is not managed\n') % rel)
|
|
308
|
if exact and state == 'r':
|
|
308
|
if exact and state == 'r':
|
|
309
|
ui.warn(_('%s: not copying - file has been marked for'
|
|
309
|
ui.warn(_('%s: not copying - file has been marked for'
|
|
310
|
' remove\n') % rel)
|
|
310
|
' remove\n') % rel)
|
|
311
|
continue
|
|
311
|
continue
|
|
312
|
# abs: hgsep
|
|
312
|
# abs: hgsep
|
|
313
|
# rel: ossep
|
|
313
|
# rel: ossep
|
|
314
|
srcs.append((abs, rel, exact))
|
|
314
|
srcs.append((abs, rel, exact))
|
|
315
|
return srcs
|
|
315
|
return srcs
|
|
316
|
|
|
316
|
|
|
317
|
# abssrc: hgsep
|
|
317
|
# abssrc: hgsep
|
|
318
|
# relsrc: ossep
|
|
318
|
# relsrc: ossep
|
|
319
|
# otarget: ossep
|
|
319
|
# otarget: ossep
|
|
320
|
def copyfile(abssrc, relsrc, otarget, exact):
|
|
320
|
def copyfile(abssrc, relsrc, otarget, exact):
|
|
321
|
abstarget = util.canonpath(repo.root, cwd, otarget)
|
|
321
|
abstarget = util.canonpath(repo.root, cwd, otarget)
|
|
322
|
reltarget = repo.pathto(abstarget, cwd)
|
|
322
|
reltarget = repo.pathto(abstarget, cwd)
|
|
323
|
target = repo.wjoin(abstarget)
|
|
323
|
target = repo.wjoin(abstarget)
|
|
324
|
src = repo.wjoin(abssrc)
|
|
324
|
src = repo.wjoin(abssrc)
|
|
325
|
state = repo.dirstate[abstarget]
|
|
325
|
state = repo.dirstate[abstarget]
|
|
326
|
|
|
326
|
|
|
327
|
# check for collisions
|
|
327
|
# check for collisions
|
|
328
|
prevsrc = targets.get(abstarget)
|
|
328
|
prevsrc = targets.get(abstarget)
|
|
329
|
if prevsrc is not None:
|
|
329
|
if prevsrc is not None:
|
|
330
|
ui.warn(_('%s: not overwriting - %s collides with %s\n') %
|
|
330
|
ui.warn(_('%s: not overwriting - %s collides with %s\n') %
|
|
331
|
(reltarget, repo.pathto(abssrc, cwd),
|
|
331
|
(reltarget, repo.pathto(abssrc, cwd),
|
|
332
|
repo.pathto(prevsrc, cwd)))
|
|
332
|
repo.pathto(prevsrc, cwd)))
|
|
333
|
return
|
|
333
|
return
|
|
334
|
|
|
334
|
|
|
335
|
# check for overwrites
|
|
335
|
# check for overwrites
|
|
336
|
exists = os.path.exists(target)
|
|
336
|
exists = os.path.exists(target)
|
|
337
|
if (not after and exists or after and state in 'mn'):
|
|
337
|
if (not after and exists or after and state in 'mn'):
|
|
338
|
if not opts['force']:
|
|
338
|
if not opts['force']:
|
|
339
|
ui.warn(_('%s: not overwriting - file exists\n') %
|
|
339
|
ui.warn(_('%s: not overwriting - file exists\n') %
|
|
340
|
reltarget)
|
|
340
|
reltarget)
|
|
341
|
return
|
|
341
|
return
|
|
342
|
|
|
342
|
|
|
343
|
if after:
|
|
343
|
if after:
|
|
344
|
if not exists:
|
|
344
|
if not exists:
|
|
345
|
return
|
|
345
|
return
|
|
346
|
elif not dryrun:
|
|
346
|
elif not dryrun:
|
|
347
|
try:
|
|
347
|
try:
|
|
348
|
if exists:
|
|
348
|
if exists:
|
|
349
|
os.unlink(target)
|
|
349
|
os.unlink(target)
|
|
350
|
targetdir = os.path.dirname(target) or '.'
|
|
350
|
targetdir = os.path.dirname(target) or '.'
|
|
351
|
if not os.path.isdir(targetdir):
|
|
351
|
if not os.path.isdir(targetdir):
|
|
352
|
os.makedirs(targetdir)
|
|
352
|
os.makedirs(targetdir)
|
|
353
|
util.copyfile(src, target)
|
|
353
|
util.copyfile(src, target)
|
|
354
|
except IOError, inst:
|
|
354
|
except IOError, inst:
|
|
355
|
if inst.errno == errno.ENOENT:
|
|
355
|
if inst.errno == errno.ENOENT:
|
|
356
|
ui.warn(_('%s: deleted in working copy\n') % relsrc)
|
|
356
|
ui.warn(_('%s: deleted in working copy\n') % relsrc)
|
|
357
|
else:
|
|
357
|
else:
|
|
358
|
ui.warn(_('%s: cannot copy - %s\n') %
|
|
358
|
ui.warn(_('%s: cannot copy - %s\n') %
|
|
359
|
(relsrc, inst.strerror))
|
|
359
|
(relsrc, inst.strerror))
|
|
360
|
return True # report a failure
|
|
360
|
return True # report a failure
|
|
361
|
|
|
361
|
|
|
362
|
if ui.verbose or not exact:
|
|
362
|
if ui.verbose or not exact:
|
|
363
|
action = rename and "moving" or "copying"
|
|
363
|
action = rename and "moving" or "copying"
|
|
364
|
ui.status(_('%s %s to %s\n') % (action, relsrc, reltarget))
|
|
364
|
ui.status(_('%s %s to %s\n') % (action, relsrc, reltarget))
|
|
365
|
|
|
365
|
|
|
366
|
targets[abstarget] = abssrc
|
|
366
|
targets[abstarget] = abssrc
|
|
367
|
|
|
367
|
|
|
368
|
# fix up dirstate
|
|
368
|
# fix up dirstate
|
|
369
|
origsrc = repo.dirstate.copied(abssrc) or abssrc
|
|
369
|
origsrc = repo.dirstate.copied(abssrc) or abssrc
|
|
370
|
if abstarget == origsrc: # copying back a copy?
|
|
370
|
if abstarget == origsrc: # copying back a copy?
|
|
371
|
if state not in 'mn' and not dryrun:
|
|
371
|
if state not in 'mn' and not dryrun:
|
|
372
|
repo.dirstate.normallookup(abstarget)
|
|
372
|
repo.dirstate.normallookup(abstarget)
|
|
373
|
else:
|
|
373
|
else:
|
|
374
|
if repo.dirstate[origsrc] == 'a':
|
|
374
|
if repo.dirstate[origsrc] == 'a':
|
|
375
|
if not ui.quiet:
|
|
375
|
if not ui.quiet:
|
|
376
|
ui.warn(_("%s has not been committed yet, so no copy "
|
|
376
|
ui.warn(_("%s has not been committed yet, so no copy "
|
|
377
|
"data will be stored for %s.\n")
|
|
377
|
"data will be stored for %s.\n")
|
|
378
|
% (repo.pathto(origsrc, cwd), reltarget))
|
|
378
|
% (repo.pathto(origsrc, cwd), reltarget))
|
|
379
|
if abstarget not in repo.dirstate and not dryrun:
|
|
379
|
if abstarget not in repo.dirstate and not dryrun:
|
|
380
|
repo.add([abstarget])
|
|
380
|
repo.add([abstarget])
|
|
381
|
elif not dryrun:
|
|
381
|
elif not dryrun:
|
|
382
|
repo.copy(origsrc, abstarget)
|
|
382
|
repo.copy(origsrc, abstarget)
|
|
383
|
|
|
383
|
|
|
384
|
if rename and not dryrun:
|
|
384
|
if rename and not dryrun:
|
|
385
|
repo.remove([abssrc], True)
|
|
385
|
repo.remove([abssrc], True)
|
|
386
|
|
|
386
|
|
|
387
|
# pat: ossep
|
|
387
|
# pat: ossep
|
|
388
|
# dest ossep
|
|
388
|
# dest ossep
|
|
389
|
# srcs: list of (hgsep, hgsep, ossep, bool)
|
|
389
|
# srcs: list of (hgsep, hgsep, ossep, bool)
|
|
390
|
# return: function that takes hgsep and returns ossep
|
|
390
|
# return: function that takes hgsep and returns ossep
|
|
391
|
def targetpathfn(pat, dest, srcs):
|
|
391
|
def targetpathfn(pat, dest, srcs):
|
|
392
|
if os.path.isdir(pat):
|
|
392
|
if os.path.isdir(pat):
|
|
393
|
abspfx = util.canonpath(repo.root, cwd, pat)
|
|
393
|
abspfx = util.canonpath(repo.root, cwd, pat)
|
|
394
|
abspfx = util.localpath(abspfx)
|
|
394
|
abspfx = util.localpath(abspfx)
|
|
395
|
if destdirexists:
|
|
395
|
if destdirexists:
|
|
396
|
striplen = len(os.path.split(abspfx)[0])
|
|
396
|
striplen = len(os.path.split(abspfx)[0])
|
|
397
|
else:
|
|
397
|
else:
|
|
398
|
striplen = len(abspfx)
|
|
398
|
striplen = len(abspfx)
|
|
399
|
if striplen:
|
|
399
|
if striplen:
|
|
400
|
striplen += len(os.sep)
|
|
400
|
striplen += len(os.sep)
|
|
401
|
res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
|
|
401
|
res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
|
|
402
|
elif destdirexists:
|
|
402
|
elif destdirexists:
|
|
403
|
res = lambda p: os.path.join(dest,
|
|
403
|
res = lambda p: os.path.join(dest,
|
|
404
|
os.path.basename(util.localpath(p)))
|
|
404
|
os.path.basename(util.localpath(p)))
|
|
405
|
else:
|
|
405
|
else:
|
|
406
|
res = lambda p: dest
|
|
406
|
res = lambda p: dest
|
|
407
|
return res
|
|
407
|
return res
|
|
408
|
|
|
408
|
|
|
409
|
# pat: ossep
|
|
409
|
# pat: ossep
|
|
410
|
# dest ossep
|
|
410
|
# dest ossep
|
|
411
|
# srcs: list of (hgsep, hgsep, ossep, bool)
|
|
411
|
# srcs: list of (hgsep, hgsep, ossep, bool)
|
|
412
|
# return: function that takes hgsep and returns ossep
|
|
412
|
# return: function that takes hgsep and returns ossep
|
|
413
|
def targetpathafterfn(pat, dest, srcs):
|
|
413
|
def targetpathafterfn(pat, dest, srcs):
|
|
414
|
if util.patkind(pat, None)[0]:
|
|
414
|
if util.patkind(pat, None)[0]:
|
|
415
|
# a mercurial pattern
|
|
415
|
# a mercurial pattern
|
|
416
|
res = lambda p: os.path.join(dest,
|
|
416
|
res = lambda p: os.path.join(dest,
|
|
417
|
os.path.basename(util.localpath(p)))
|
|
417
|
os.path.basename(util.localpath(p)))
|
|
418
|
else:
|
|
418
|
else:
|
|
419
|
abspfx = util.canonpath(repo.root, cwd, pat)
|
|
419
|
abspfx = util.canonpath(repo.root, cwd, pat)
|
|
420
|
if len(abspfx) < len(srcs[0][0]):
|
|
420
|
if len(abspfx) < len(srcs[0][0]):
|
|
421
|
# A directory. Either the target path contains the last
|
|
421
|
# A directory. Either the target path contains the last
|
|
422
|
# component of the source path or it does not.
|
|
422
|
# component of the source path or it does not.
|
|
423
|
def evalpath(striplen):
|
|
423
|
def evalpath(striplen):
|
|
424
|
score = 0
|
|
424
|
score = 0
|
|
425
|
for s in srcs:
|
|
425
|
for s in srcs:
|
|
426
|
t = os.path.join(dest, util.localpath(s[0])[striplen:])
|
|
426
|
t = os.path.join(dest, util.localpath(s[0])[striplen:])
|
|
427
|
if os.path.exists(t):
|
|
427
|
if os.path.exists(t):
|
|
428
|
score += 1
|
|
428
|
score += 1
|
|
429
|
return score
|
|
429
|
return score
|
|
430
|
|
|
430
|
|
|
431
|
abspfx = util.localpath(abspfx)
|
|
431
|
abspfx = util.localpath(abspfx)
|
|
432
|
striplen = len(abspfx)
|
|
432
|
striplen = len(abspfx)
|
|
433
|
if striplen:
|
|
433
|
if striplen:
|
|
434
|
striplen += len(os.sep)
|
|
434
|
striplen += len(os.sep)
|
|
435
|
if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
|
|
435
|
if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
|
|
436
|
score = evalpath(striplen)
|
|
436
|
score = evalpath(striplen)
|
|
437
|
striplen1 = len(os.path.split(abspfx)[0])
|
|
437
|
striplen1 = len(os.path.split(abspfx)[0])
|
|
438
|
if striplen1:
|
|
438
|
if striplen1:
|
|
439
|
striplen1 += len(os.sep)
|
|
439
|
striplen1 += len(os.sep)
|
|
440
|
if evalpath(striplen1) > score:
|
|
440
|
if evalpath(striplen1) > score:
|
|
441
|
striplen = striplen1
|
|
441
|
striplen = striplen1
|
|
442
|
res = lambda p: os.path.join(dest,
|
|
442
|
res = lambda p: os.path.join(dest,
|
|
443
|
util.localpath(p)[striplen:])
|
|
443
|
util.localpath(p)[striplen:])
|
|
444
|
else:
|
|
444
|
else:
|
|
445
|
# a file
|
|
445
|
# a file
|
|
446
|
if destdirexists:
|
|
446
|
if destdirexists:
|
|
447
|
res = lambda p: os.path.join(dest,
|
|
447
|
res = lambda p: os.path.join(dest,
|
|
448
|
os.path.basename(util.localpath(p)))
|
|
448
|
os.path.basename(util.localpath(p)))
|
|
449
|
else:
|
|
449
|
else:
|
|
450
|
res = lambda p: dest
|
|
450
|
res = lambda p: dest
|
|
451
|
return res
|
|
451
|
return res
|
|
452
|
|
|
452
|
|
|
453
|
|
|
453
|
|
|
454
|
pats = util.expand_glob(pats)
|
|
454
|
pats = util.expand_glob(pats)
|
|
455
|
if not pats:
|
|
455
|
if not pats:
|
|
456
|
raise util.Abort(_('no source or destination specified'))
|
|
456
|
raise util.Abort(_('no source or destination specified'))
|
|
457
|
if len(pats) == 1:
|
|
457
|
if len(pats) == 1:
|
|
458
|
raise util.Abort(_('no destination specified'))
|
|
458
|
raise util.Abort(_('no destination specified'))
|
|
459
|
dest = pats.pop()
|
|
459
|
dest = pats.pop()
|
|
460
|
destdirexists = os.path.isdir(dest)
|
|
460
|
destdirexists = os.path.isdir(dest)
|
|
461
|
if not destdirexists:
|
|
461
|
if not destdirexists:
|
|
462
|
if len(pats) > 1 or util.patkind(pats[0], None)[0]:
|
|
462
|
if len(pats) > 1 or util.patkind(pats[0], None)[0]:
|
|
463
|
raise util.Abort(_('with multiple sources, destination must be an '
|
|
463
|
raise util.Abort(_('with multiple sources, destination must be an '
|
|
464
|
'existing directory'))
|
|
464
|
'existing directory'))
|
|
465
|
if util.endswithsep(dest):
|
|
465
|
if util.endswithsep(dest):
|
|
466
|
raise util.Abort(_('destination %s is not a directory') % dest)
|
|
466
|
raise util.Abort(_('destination %s is not a directory') % dest)
|
|
467
|
|
|
467
|
|
|
468
|
tfn = targetpathfn
|
|
468
|
tfn = targetpathfn
|
|
469
|
if after:
|
|
469
|
if after:
|
|
470
|
tfn = targetpathafterfn
|
|
470
|
tfn = targetpathafterfn
|
|
471
|
copylist = []
|
|
471
|
copylist = []
|
|
472
|
for pat in pats:
|
|
472
|
for pat in pats:
|
|
473
|
srcs = walkpat(pat)
|
|
473
|
srcs = walkpat(pat)
|
|
474
|
if not srcs:
|
|
474
|
if not srcs:
|
|
475
|
continue
|
|
475
|
continue
|
|
476
|
copylist.append((tfn(pat, dest, srcs), srcs))
|
|
476
|
copylist.append((tfn(pat, dest, srcs), srcs))
|
|
477
|
if not copylist:
|
|
477
|
if not copylist:
|
|
478
|
raise util.Abort(_('no files to copy'))
|
|
478
|
raise util.Abort(_('no files to copy'))
|
|
479
|
|
|
479
|
|
|
480
|
errors = 0
|
|
480
|
errors = 0
|
|
481
|
for targetpath, srcs in copylist:
|
|
481
|
for targetpath, srcs in copylist:
|
|
482
|
for abssrc, relsrc, exact in srcs:
|
|
482
|
for abssrc, relsrc, exact in srcs:
|
|
483
|
if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
|
|
483
|
if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
|
|
484
|
errors += 1
|
|
484
|
errors += 1
|
|
485
|
|
|
485
|
|
|
486
|
if errors:
|
|
486
|
if errors:
|
|
487
|
ui.warn(_('(consider using --after)\n'))
|
|
487
|
ui.warn(_('(consider using --after)\n'))
|
|
488
|
|
|
488
|
|
|
489
|
return errors
|
|
489
|
return errors
|
|
490
|
|
|
490
|
|
|
491
|
def service(opts, parentfn=None, initfn=None, runfn=None):
|
|
491
|
def service(opts, parentfn=None, initfn=None, runfn=None):
|
|
492
|
'''Run a command as a service.'''
|
|
492
|
'''Run a command as a service.'''
|
|
493
|
|
|
493
|
|
|
494
|
if opts['daemon'] and not opts['daemon_pipefds']:
|
|
494
|
if opts['daemon'] and not opts['daemon_pipefds']:
|
|
495
|
rfd, wfd = os.pipe()
|
|
495
|
rfd, wfd = os.pipe()
|
|
496
|
args = sys.argv[:]
|
|
496
|
args = sys.argv[:]
|
|
497
|
args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
|
|
497
|
args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
|
|
498
|
# Don't pass --cwd to the child process, because we've already
|
|
498
|
# Don't pass --cwd to the child process, because we've already
|
|
499
|
# changed directory.
|
|
499
|
# changed directory.
|
|
500
|
for i in xrange(1,len(args)):
|
|
500
|
for i in xrange(1,len(args)):
|
|
501
|
if args[i].startswith('--cwd='):
|
|
501
|
if args[i].startswith('--cwd='):
|
|
502
|
del args[i]
|
|
502
|
del args[i]
|
|
503
|
break
|
|
503
|
break
|
|
504
|
elif args[i].startswith('--cwd'):
|
|
504
|
elif args[i].startswith('--cwd'):
|
|
505
|
del args[i:i+2]
|
|
505
|
del args[i:i+2]
|
|
506
|
break
|
|
506
|
break
|
|
507
|
pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
|
|
507
|
pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
|
|
508
|
args[0], args)
|
|
508
|
args[0], args)
|
|
509
|
os.close(wfd)
|
|
509
|
os.close(wfd)
|
|
510
|
os.read(rfd, 1)
|
|
510
|
os.read(rfd, 1)
|
|
511
|
if parentfn:
|
|
511
|
if parentfn:
|
|
512
|
return parentfn(pid)
|
|
512
|
return parentfn(pid)
|
|
513
|
else:
|
|
513
|
else:
|
|
514
|
os._exit(0)
|
|
514
|
os._exit(0)
|
|
515
|
|
|
515
|
|
|
516
|
if initfn:
|
|
516
|
if initfn:
|
|
517
|
initfn()
|
|
517
|
initfn()
|
|
518
|
|
|
518
|
|
|
519
|
if opts['pid_file']:
|
|
519
|
if opts['pid_file']:
|
|
520
|
fp = open(opts['pid_file'], 'w')
|
|
520
|
fp = open(opts['pid_file'], 'w')
|
|
521
|
fp.write(str(os.getpid()) + '\n')
|
|
521
|
fp.write(str(os.getpid()) + '\n')
|
|
522
|
fp.close()
|
|
522
|
fp.close()
|
|
523
|
|
|
523
|
|
|
524
|
if opts['daemon_pipefds']:
|
|
524
|
if opts['daemon_pipefds']:
|
|
525
|
rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
|
|
525
|
rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
|
|
526
|
os.close(rfd)
|
|
526
|
os.close(rfd)
|
|
527
|
try:
|
|
527
|
try:
|
|
528
|
os.setsid()
|
|
528
|
os.setsid()
|
|
529
|
except AttributeError:
|
|
529
|
except AttributeError:
|
|
530
|
pass
|
|
530
|
pass
|
|
531
|
os.write(wfd, 'y')
|
|
531
|
os.write(wfd, 'y')
|
|
532
|
os.close(wfd)
|
|
532
|
os.close(wfd)
|
|
533
|
sys.stdout.flush()
|
|
533
|
sys.stdout.flush()
|
|
534
|
sys.stderr.flush()
|
|
534
|
sys.stderr.flush()
|
|
535
|
fd = os.open(util.nulldev, os.O_RDWR)
|
|
535
|
fd = os.open(util.nulldev, os.O_RDWR)
|
|
536
|
if fd != 0: os.dup2(fd, 0)
|
|
536
|
if fd != 0: os.dup2(fd, 0)
|
|
537
|
if fd != 1: os.dup2(fd, 1)
|
|
537
|
if fd != 1: os.dup2(fd, 1)
|
|
538
|
if fd != 2: os.dup2(fd, 2)
|
|
538
|
if fd != 2: os.dup2(fd, 2)
|
|
539
|
if fd not in (0, 1, 2): os.close(fd)
|
|
539
|
if fd not in (0, 1, 2): os.close(fd)
|
|
540
|
|
|
540
|
|
|
541
|
if runfn:
|
|
541
|
if runfn:
|
|
542
|
return runfn()
|
|
542
|
return runfn()
|
|
543
|
|
|
543
|
|
|
544
|
class changeset_printer(object):
|
|
544
|
class changeset_printer(object):
|
|
545
|
'''show changeset information when templating not requested.'''
|
|
545
|
'''show changeset information when templating not requested.'''
|
|
546
|
|
|
546
|
|
|
547
|
def __init__(self, ui, repo, patch, buffered):
|
|
547
|
def __init__(self, ui, repo, patch, buffered):
|
|
548
|
self.ui = ui
|
|
548
|
self.ui = ui
|
|
549
|
self.repo = repo
|
|
549
|
self.repo = repo
|
|
550
|
self.buffered = buffered
|
|
550
|
self.buffered = buffered
|
|
551
|
self.patch = patch
|
|
551
|
self.patch = patch
|
|
552
|
self.header = {}
|
|
552
|
self.header = {}
|
|
553
|
self.hunk = {}
|
|
553
|
self.hunk = {}
|
|
554
|
self.lastheader = None
|
|
554
|
self.lastheader = None
|
|
555
|
|
|
555
|
|
|
556
|
def flush(self, rev):
|
|
556
|
def flush(self, rev):
|
|
557
|
if rev in self.header:
|
|
557
|
if rev in self.header:
|
|
558
|
h = self.header[rev]
|
|
558
|
h = self.header[rev]
|
|
559
|
if h != self.lastheader:
|
|
559
|
if h != self.lastheader:
|
|
560
|
self.lastheader = h
|
|
560
|
self.lastheader = h
|
|
561
|
self.ui.write(h)
|
|
561
|
self.ui.write(h)
|
|
562
|
del self.header[rev]
|
|
562
|
del self.header[rev]
|
|
563
|
if rev in self.hunk:
|
|
563
|
if rev in self.hunk:
|
|
564
|
self.ui.write(self.hunk[rev])
|
|
564
|
self.ui.write(self.hunk[rev])
|
|
565
|
del self.hunk[rev]
|
|
565
|
del self.hunk[rev]
|
|
566
|
return 1
|
|
566
|
return 1
|
|
567
|
return 0
|
|
567
|
return 0
|
|
568
|
|
|
568
|
|
|
569
|
def show(self, rev=0, changenode=None, copies=(), **props):
|
|
569
|
def show(self, rev=0, changenode=None, copies=(), **props):
|
|
570
|
if self.buffered:
|
|
570
|
if self.buffered:
|
|
571
|
self.ui.pushbuffer()
|
|
571
|
self.ui.pushbuffer()
|
|
572
|
self._show(rev, changenode, copies, props)
|
|
572
|
self._show(rev, changenode, copies, props)
|
|
573
|
self.hunk[rev] = self.ui.popbuffer()
|
|
573
|
self.hunk[rev] = self.ui.popbuffer()
|
|
574
|
else:
|
|
574
|
else:
|
|
575
|
self._show(rev, changenode, copies, props)
|
|
575
|
self._show(rev, changenode, copies, props)
|
|
576
|
|
|
576
|
|
|
577
|
def _show(self, rev, changenode, copies, props):
|
|
577
|
def _show(self, rev, changenode, copies, props):
|
|
578
|
'''show a single changeset or file revision'''
|
|
578
|
'''show a single changeset or file revision'''
|
|
579
|
log = self.repo.changelog
|
|
579
|
log = self.repo.changelog
|
|
580
|
if changenode is None:
|
|
580
|
if changenode is None:
|
|
581
|
changenode = log.node(rev)
|
|
581
|
changenode = log.node(rev)
|
|
582
|
elif not rev:
|
|
582
|
elif not rev:
|
|
583
|
rev = log.rev(changenode)
|
|
583
|
rev = log.rev(changenode)
|
|
584
|
|
|
584
|
|
|
585
|
if self.ui.quiet:
|
|
585
|
if self.ui.quiet:
|
|
586
|
self.ui.write("%d:%s\n" % (rev, short(changenode)))
|
|
586
|
self.ui.write("%d:%s\n" % (rev, short(changenode)))
|
|
587
|
return
|
|
587
|
return
|
|
588
|
|
|
588
|
|
|
589
|
changes = log.read(changenode)
|
|
589
|
changes = log.read(changenode)
|
|
590
|
date = util.datestr(changes[2])
|
|
590
|
date = util.datestr(changes[2])
|
|
591
|
extra = changes[5]
|
|
591
|
extra = changes[5]
|
|
592
|
branch = extra.get("branch")
|
|
592
|
branch = extra.get("branch")
|
|
593
|
|
|
593
|
|
|
594
|
hexfunc = self.ui.debugflag and hex or short
|
|
594
|
hexfunc = self.ui.debugflag and hex or short
|
|
595
|
|
|
595
|
|
|
596
|
parents = [(p, hexfunc(log.node(p)))
|
|
596
|
parents = [(p, hexfunc(log.node(p)))
|
|
597
|
for p in self._meaningful_parentrevs(log, rev)]
|
|
597
|
for p in self._meaningful_parentrevs(log, rev)]
|
|
598
|
|
|
598
|
|
|
599
|
self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
|
|
599
|
self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
|
|
600
|
|
|
600
|
|
|
601
|
# don't show the default branch name
|
|
601
|
# don't show the default branch name
|
|
602
|
if branch != 'default':
|
|
602
|
if branch != 'default':
|
|
603
|
branch = util.tolocal(branch)
|
|
603
|
branch = util.tolocal(branch)
|
|
604
|
self.ui.write(_("branch: %s\n") % branch)
|
|
604
|
self.ui.write(_("branch: %s\n") % branch)
|
|
605
|
for tag in self.repo.nodetags(changenode):
|
|
605
|
for tag in self.repo.nodetags(changenode):
|
|
606
|
self.ui.write(_("tag: %s\n") % tag)
|
|
606
|
self.ui.write(_("tag: %s\n") % tag)
|
|
607
|
for parent in parents:
|
|
607
|
for parent in parents:
|
|
608
|
self.ui.write(_("parent: %d:%s\n") % parent)
|
|
608
|
self.ui.write(_("parent: %d:%s\n") % parent)
|
|
609
|
|
|
609
|
|
|
610
|
if self.ui.debugflag:
|
|
610
|
if self.ui.debugflag:
|
|
611
|
self.ui.write(_("manifest: %d:%s\n") %
|
|
611
|
self.ui.write(_("manifest: %d:%s\n") %
|
|
612
|
(self.repo.manifest.rev(changes[0]), hex(changes[0])))
|
|
612
|
(self.repo.manifest.rev(changes[0]), hex(changes[0])))
|
|
613
|
self.ui.write(_("user: %s\n") % changes[1])
|
|
613
|
self.ui.write(_("user: %s\n") % changes[1])
|
|
614
|
self.ui.write(_("date: %s\n") % date)
|
|
614
|
self.ui.write(_("date: %s\n") % date)
|
|
615
|
|
|
615
|
|
|
616
|
if self.ui.debugflag:
|
|
616
|
if self.ui.debugflag:
|
|
617
|
files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
|
|
617
|
files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
|
|
618
|
for key, value in zip([_("files:"), _("files+:"), _("files-:")],
|
|
618
|
for key, value in zip([_("files:"), _("files+:"), _("files-:")],
|
|
619
|
files):
|
|
619
|
files):
|
|
620
|
if value:
|
|
620
|
if value:
|
|
621
|
self.ui.write("%-12s %s\n" % (key, " ".join(value)))
|
|
621
|
self.ui.write("%-12s %s\n" % (key, " ".join(value)))
|
|
622
|
elif changes[3] and self.ui.verbose:
|
|
622
|
elif changes[3] and self.ui.verbose:
|
|
623
|
self.ui.write(_("files: %s\n") % " ".join(changes[3]))
|
|
623
|
self.ui.write(_("files: %s\n") % " ".join(changes[3]))
|
|
624
|
if copies and self.ui.verbose:
|
|
624
|
if copies and self.ui.verbose:
|
|
625
|
copies = ['%s (%s)' % c for c in copies]
|
|
625
|
copies = ['%s (%s)' % c for c in copies]
|
|
626
|
self.ui.write(_("copies: %s\n") % ' '.join(copies))
|
|
626
|
self.ui.write(_("copies: %s\n") % ' '.join(copies))
|
|
627
|
|
|
627
|
|
|
628
|
if extra and self.ui.debugflag:
|
|
628
|
if extra and self.ui.debugflag:
|
|
629
|
extraitems = extra.items()
|
|
629
|
extraitems = extra.items()
|
|
630
|
extraitems.sort()
|
|
630
|
extraitems.sort()
|
|
631
|
for key, value in extraitems:
|
|
631
|
for key, value in extraitems:
|
|
632
|
self.ui.write(_("extra: %s=%s\n")
|
|
632
|
self.ui.write(_("extra: %s=%s\n")
|
|
633
|
% (key, value.encode('string_escape')))
|
|
633
|
% (key, value.encode('string_escape')))
|
|
634
|
|
|
634
|
|
|
635
|
description = changes[4].strip()
|
|
635
|
description = changes[4].strip()
|
|
636
|
if description:
|
|
636
|
if description:
|
|
637
|
if self.ui.verbose:
|
|
637
|
if self.ui.verbose:
|
|
638
|
self.ui.write(_("description:\n"))
|
|
638
|
self.ui.write(_("description:\n"))
|
|
639
|
self.ui.write(description)
|
|
639
|
self.ui.write(description)
|
|
640
|
self.ui.write("\n\n")
|
|
640
|
self.ui.write("\n\n")
|
|
641
|
else:
|
|
641
|
else:
|
|
642
|
self.ui.write(_("summary: %s\n") %
|
|
642
|
self.ui.write(_("summary: %s\n") %
|
|
643
|
description.splitlines()[0])
|
|
643
|
description.splitlines()[0])
|
|
644
|
self.ui.write("\n")
|
|
644
|
self.ui.write("\n")
|
|
645
|
|
|
645
|
|
|
646
|
self.showpatch(changenode)
|
|
646
|
self.showpatch(changenode)
|
|
647
|
|
|
647
|
|
|
648
|
def showpatch(self, node):
|
|
648
|
def showpatch(self, node):
|
|
649
|
if self.patch:
|
|
649
|
if self.patch:
|
|
650
|
prev = self.repo.changelog.parents(node)[0]
|
|
650
|
prev = self.repo.changelog.parents(node)[0]
|
|
651
|
patch.diff(self.repo, prev, node, match=self.patch, fp=self.ui,
|
|
651
|
patch.diff(self.repo, prev, node, match=self.patch, fp=self.ui,
|
|
652
|
opts=patch.diffopts(self.ui))
|
|
652
|
opts=patch.diffopts(self.ui))
|
|
653
|
self.ui.write("\n")
|
|
653
|
self.ui.write("\n")
|
|
654
|
|
|
654
|
|
|
655
|
def _meaningful_parentrevs(self, log, rev):
|
|
655
|
def _meaningful_parentrevs(self, log, rev):
|
|
656
|
"""Return list of meaningful (or all if debug) parentrevs for rev.
|
|
656
|
"""Return list of meaningful (or all if debug) parentrevs for rev.
|
|
657
|
|
|
657
|
|
|
658
|
For merges (two non-nullrev revisions) both parents are meaningful.
|
|
658
|
For merges (two non-nullrev revisions) both parents are meaningful.
|
|
659
|
Otherwise the first parent revision is considered meaningful if it
|
|
659
|
Otherwise the first parent revision is considered meaningful if it
|
|
660
|
is not the preceding revision.
|
|
660
|
is not the preceding revision.
|
|
661
|
"""
|
|
661
|
"""
|
|
662
|
parents = log.parentrevs(rev)
|
|
662
|
parents = log.parentrevs(rev)
|
|
663
|
if not self.ui.debugflag and parents[1] == nullrev:
|
|
663
|
if not self.ui.debugflag and parents[1] == nullrev:
|
|
664
|
if parents[0] >= rev - 1:
|
|
664
|
if parents[0] >= rev - 1:
|
|
665
|
parents = []
|
|
665
|
parents = []
|
|
666
|
else:
|
|
666
|
else:
|
|
667
|
parents = [parents[0]]
|
|
667
|
parents = [parents[0]]
|
|
668
|
return parents
|
|
668
|
return parents
|
|
669
|
|
|
669
|
|
|
670
|
|
|
670
|
|
|
671
|
class changeset_templater(changeset_printer):
|
|
671
|
class changeset_templater(changeset_printer):
|
|
672
|
'''format changeset information.'''
|
|
672
|
'''format changeset information.'''
|
|
673
|
|
|
673
|
|
|
674
|
def __init__(self, ui, repo, patch, mapfile, buffered):
|
|
674
|
def __init__(self, ui, repo, patch, mapfile, buffered):
|
|
675
|
changeset_printer.__init__(self, ui, repo, patch, buffered)
|
|
675
|
changeset_printer.__init__(self, ui, repo, patch, buffered)
|
|
676
|
filters = templatefilters.filters.copy()
|
|
676
|
filters = templatefilters.filters.copy()
|
|
677
|
filters['formatnode'] = (ui.debugflag and (lambda x: x)
|
|
677
|
filters['formatnode'] = (ui.debugflag and (lambda x: x)
|
|
678
|
or (lambda x: x[:12]))
|
|
678
|
or (lambda x: x[:12]))
|
|
679
|
self.t = templater.templater(mapfile, filters,
|
|
679
|
self.t = templater.templater(mapfile, filters,
|
|
680
|
cache={
|
|
680
|
cache={
|
|
681
|
'parent': '{rev}:{node|formatnode} ',
|
|
681
|
'parent': '{rev}:{node|formatnode} ',
|
|
682
|
'manifest': '{rev}:{node|formatnode}',
|
|
682
|
'manifest': '{rev}:{node|formatnode}',
|
|
683
|
'filecopy': '{name} ({source})'})
|
|
683
|
'filecopy': '{name} ({source})'})
|
|
684
|
|
|
684
|
|
|
685
|
def use_template(self, t):
|
|
685
|
def use_template(self, t):
|
|
686
|
'''set template string to use'''
|
|
686
|
'''set template string to use'''
|
|
687
|
self.t.cache['changeset'] = t
|
|
687
|
self.t.cache['changeset'] = t
|
|
688
|
|
|
688
|
|
|
689
|
def _show(self, rev, changenode, copies, props):
|
|
689
|
def _show(self, rev, changenode, copies, props):
|
|
690
|
'''show a single changeset or file revision'''
|
|
690
|
'''show a single changeset or file revision'''
|
|
691
|
log = self.repo.changelog
|
|
691
|
log = self.repo.changelog
|
|
692
|
if changenode is None:
|
|
692
|
if changenode is None:
|
|
693
|
changenode = log.node(rev)
|
|
693
|
changenode = log.node(rev)
|
|
694
|
elif not rev:
|
|
694
|
elif not rev:
|
|
695
|
rev = log.rev(changenode)
|
|
695
|
rev = log.rev(changenode)
|
|
696
|
|
|
696
|
|
|
697
|
changes = log.read(changenode)
|
|
697
|
changes = log.read(changenode)
|
|
698
|
|
|
698
|
|
|
699
|
def showlist(name, values, plural=None, **args):
|
|
699
|
def showlist(name, values, plural=None, **args):
|
|
700
|
'''expand set of values.
|
|
700
|
'''expand set of values.
|
|
701
|
name is name of key in template map.
|
|
701
|
name is name of key in template map.
|
|
702
|
values is list of strings or dicts.
|
|
702
|
values is list of strings or dicts.
|
|
703
|
plural is plural of name, if not simply name + 's'.
|
|
703
|
plural is plural of name, if not simply name + 's'.
|
|
704
|
|
|
704
|
|
|
705
|
expansion works like this, given name 'foo'.
|
|
705
|
expansion works like this, given name 'foo'.
|
|
706
|
|
|
706
|
|
|
707
|
if values is empty, expand 'no_foos'.
|
|
707
|
if values is empty, expand 'no_foos'.
|
|
708
|
|
|
708
|
|
|
709
|
if 'foo' not in template map, return values as a string,
|
|
709
|
if 'foo' not in template map, return values as a string,
|
|
710
|
joined by space.
|
|
710
|
joined by space.
|
|
711
|
|
|
711
|
|
|
712
|
expand 'start_foos'.
|
|
712
|
expand 'start_foos'.
|
|
713
|
|
|
713
|
|
|
714
|
for each value, expand 'foo'. if 'last_foo' in template
|
|
714
|
for each value, expand 'foo'. if 'last_foo' in template
|
|
715
|
map, expand it instead of 'foo' for last key.
|
|
715
|
map, expand it instead of 'foo' for last key.
|
|
716
|
|
|
716
|
|
|
717
|
expand 'end_foos'.
|
|
717
|
expand 'end_foos'.
|
|
718
|
'''
|
|
718
|
'''
|
|
719
|
if plural: names = plural
|
|
719
|
if plural: names = plural
|
|
720
|
else: names = name + 's'
|
|
720
|
else: names = name + 's'
|
|
721
|
if not values:
|
|
721
|
if not values:
|
|
722
|
noname = 'no_' + names
|
|
722
|
noname = 'no_' + names
|
|
723
|
if noname in self.t:
|
|
723
|
if noname in self.t:
|
|
724
|
yield self.t(noname, **args)
|
|
724
|
yield self.t(noname, **args)
|
|
725
|
return
|
|
725
|
return
|
|
726
|
if name not in self.t:
|
|
726
|
if name not in self.t:
|
|
727
|
if isinstance(values[0], str):
|
|
727
|
if isinstance(values[0], str):
|
|
728
|
yield ' '.join(values)
|
|
728
|
yield ' '.join(values)
|
|
729
|
else:
|
|
729
|
else:
|
|
730
|
for v in values:
|
|
730
|
for v in values:
|
|
731
|
yield dict(v, **args)
|
|
731
|
yield dict(v, **args)
|
|
732
|
return
|
|
732
|
return
|
|
733
|
startname = 'start_' + names
|
|
733
|
startname = 'start_' + names
|
|
734
|
if startname in self.t:
|
|
734
|
if startname in self.t:
|
|
735
|
yield self.t(startname, **args)
|
|
735
|
yield self.t(startname, **args)
|
|
736
|
vargs = args.copy()
|
|
736
|
vargs = args.copy()
|
|
737
|
def one(v, tag=name):
|
|
737
|
def one(v, tag=name):
|
|
738
|
try:
|
|
738
|
try:
|
|
739
|
vargs.update(v)
|
|
739
|
vargs.update(v)
|
|
740
|
except (AttributeError, ValueError):
|
|
740
|
except (AttributeError, ValueError):
|
|
741
|
try:
|
|
741
|
try:
|
|
742
|
for a, b in v:
|
|
742
|
for a, b in v:
|
|
743
|
vargs[a] = b
|
|
743
|
vargs[a] = b
|
|
744
|
except ValueError:
|
|
744
|
except ValueError:
|
|
745
|
vargs[name] = v
|
|
745
|
vargs[name] = v
|
|
746
|
return self.t(tag, **vargs)
|
|
746
|
return self.t(tag, **vargs)
|
|
747
|
lastname = 'last_' + name
|
|
747
|
lastname = 'last_' + name
|
|
748
|
if lastname in self.t:
|
|
748
|
if lastname in self.t:
|
|
749
|
last = values.pop()
|
|
749
|
last = values.pop()
|
|
750
|
else:
|
|
750
|
else:
|
|
751
|
last = None
|
|
751
|
last = None
|
|
752
|
for v in values:
|
|
752
|
for v in values:
|
|
753
|
yield one(v)
|
|
753
|
yield one(v)
|
|
754
|
if last is not None:
|
|
754
|
if last is not None:
|
|
755
|
yield one(last, tag=lastname)
|
|
755
|
yield one(last, tag=lastname)
|
|
756
|
endname = 'end_' + names
|
|
756
|
endname = 'end_' + names
|
|
757
|
if endname in self.t:
|
|
757
|
if endname in self.t:
|
|
758
|
yield self.t(endname, **args)
|
|
758
|
yield self.t(endname, **args)
|
|
759
|
|
|
759
|
|
|
760
|
def showbranches(**args):
|
|
760
|
def showbranches(**args):
|
|
761
|
branch = changes[5].get("branch")
|
|
761
|
branch = changes[5].get("branch")
|
|
762
|
if branch != 'default':
|
|
762
|
if branch != 'default':
|
|
763
|
branch = util.tolocal(branch)
|
|
763
|
branch = util.tolocal(branch)
|
|
764
|
return showlist('branch', [branch], plural='branches', **args)
|
|
764
|
return showlist('branch', [branch], plural='branches', **args)
|
|
765
|
|
|
765
|
|
|
766
|
def showparents(**args):
|
|
766
|
def showparents(**args):
|
|
767
|
parents = [[('rev', p), ('node', hex(log.node(p)))]
|
|
767
|
parents = [[('rev', p), ('node', hex(log.node(p)))]
|
|
768
|
for p in self._meaningful_parentrevs(log, rev)]
|
|
768
|
for p in self._meaningful_parentrevs(log, rev)]
|
|
769
|
return showlist('parent', parents, **args)
|
|
769
|
return showlist('parent', parents, **args)
|
|
770
|
|
|
770
|
|
|
771
|
def showtags(**args):
|
|
771
|
def showtags(**args):
|
|
772
|
return showlist('tag', self.repo.nodetags(changenode), **args)
|
|
772
|
return showlist('tag', self.repo.nodetags(changenode), **args)
|
|
773
|
|
|
773
|
|
|
774
|
def showextras(**args):
|
|
774
|
def showextras(**args):
|
|
775
|
extras = changes[5].items()
|
|
775
|
extras = changes[5].items()
|
|
776
|
extras.sort()
|
|
776
|
extras.sort()
|
|
777
|
for key, value in extras:
|
|
777
|
for key, value in extras:
|
|
778
|
args = args.copy()
|
|
778
|
args = args.copy()
|
|
779
|
args.update(dict(key=key, value=value))
|
|
779
|
args.update(dict(key=key, value=value))
|
|
780
|
yield self.t('extra', **args)
|
|
780
|
yield self.t('extra', **args)
|
|
781
|
|
|
781
|
|
|
782
|
def showcopies(**args):
|
|
782
|
def showcopies(**args):
|
|
783
|
c = [{'name': x[0], 'source': x[1]} for x in copies]
|
|
783
|
c = [{'name': x[0], 'source': x[1]} for x in copies]
|
|
784
|
return showlist('file_copy', c, plural='file_copies', **args)
|
|
784
|
return showlist('file_copy', c, plural='file_copies', **args)
|
|
785
|
|
|
785
|
|
|
786
|
files = []
|
|
786
|
files = []
|
|
787
|
def getfiles():
|
|
787
|
def getfiles():
|
|
788
|
if not files:
|
|
788
|
if not files:
|
|
789
|
files[:] = self.repo.status(
|
|
789
|
files[:] = self.repo.status(
|
|
790
|
log.parents(changenode)[0], changenode)[:3]
|
|
790
|
log.parents(changenode)[0], changenode)[:3]
|
|
791
|
return files
|
|
791
|
return files
|
|
792
|
def showfiles(**args):
|
|
792
|
def showfiles(**args):
|
|
793
|
return showlist('file', changes[3], **args)
|
|
793
|
return showlist('file', changes[3], **args)
|
|
794
|
def showmods(**args):
|
|
794
|
def showmods(**args):
|
|
795
|
return showlist('file_mod', getfiles()[0], **args)
|
|
795
|
return showlist('file_mod', getfiles()[0], **args)
|
|
796
|
def showadds(**args):
|
|
796
|
def showadds(**args):
|
|
797
|
return showlist('file_add', getfiles()[1], **args)
|
|
797
|
return showlist('file_add', getfiles()[1], **args)
|
|
798
|
def showdels(**args):
|
|
798
|
def showdels(**args):
|
|
799
|
return showlist('file_del', getfiles()[2], **args)
|
|
799
|
return showlist('file_del', getfiles()[2], **args)
|
|
800
|
def showmanifest(**args):
|
|
800
|
def showmanifest(**args):
|
|
801
|
args = args.copy()
|
|
801
|
args = args.copy()
|
|
802
|
args.update(dict(rev=self.repo.manifest.rev(changes[0]),
|
|
802
|
args.update(dict(rev=self.repo.manifest.rev(changes[0]),
|
|
803
|
node=hex(changes[0])))
|
|
803
|
node=hex(changes[0])))
|
|
804
|
return self.t('manifest', **args)
|
|
804
|
return self.t('manifest', **args)
|
|
805
|
|
|
805
|
|
|
806
|
defprops = {
|
|
806
|
defprops = {
|
|
807
|
'author': changes[1],
|
|
807
|
'author': changes[1],
|
|
808
|
'branches': showbranches,
|
|
808
|
'branches': showbranches,
|
|
809
|
'date': changes[2],
|
|
809
|
'date': changes[2],
|
|
810
|
'desc': changes[4].strip(),
|
|
810
|
'desc': changes[4].strip(),
|
|
811
|
'file_adds': showadds,
|
|
811
|
'file_adds': showadds,
|
|
812
|
'file_dels': showdels,
|
|
812
|
'file_dels': showdels,
|
|
813
|
'file_mods': showmods,
|
|
813
|
'file_mods': showmods,
|
|
814
|
'files': showfiles,
|
|
814
|
'files': showfiles,
|
|
815
|
'file_copies': showcopies,
|
|
815
|
'file_copies': showcopies,
|
|
816
|
'manifest': showmanifest,
|
|
816
|
'manifest': showmanifest,
|
|
817
|
'node': hex(changenode),
|
|
817
|
'node': hex(changenode),
|
|
818
|
'parents': showparents,
|
|
818
|
'parents': showparents,
|
|
819
|
'rev': rev,
|
|
819
|
'rev': rev,
|
|
820
|
'tags': showtags,
|
|
820
|
'tags': showtags,
|
|
821
|
'extras': showextras,
|
|
821
|
'extras': showextras,
|
|
822
|
}
|
|
822
|
}
|
|
823
|
props = props.copy()
|
|
823
|
props = props.copy()
|
|
824
|
props.update(defprops)
|
|
824
|
props.update(defprops)
|
|
825
|
|
|
825
|
|
|
826
|
try:
|
|
826
|
try:
|
|
827
|
if self.ui.debugflag and 'header_debug' in self.t:
|
|
827
|
if self.ui.debugflag and 'header_debug' in self.t:
|
|
828
|
key = 'header_debug'
|
|
828
|
key = 'header_debug'
|
|
829
|
elif self.ui.quiet and 'header_quiet' in self.t:
|
|
829
|
elif self.ui.quiet and 'header_quiet' in self.t:
|
|
830
|
key = 'header_quiet'
|
|
830
|
key = 'header_quiet'
|
|
831
|
elif self.ui.verbose and 'header_verbose' in self.t:
|
|
831
|
elif self.ui.verbose and 'header_verbose' in self.t:
|
|
832
|
key = 'header_verbose'
|
|
832
|
key = 'header_verbose'
|
|
833
|
elif 'header' in self.t:
|
|
833
|
elif 'header' in self.t:
|
|
834
|
key = 'header'
|
|
834
|
key = 'header'
|
|
835
|
else:
|
|
835
|
else:
|
|
836
|
key = ''
|
|
836
|
key = ''
|
|
837
|
if key:
|
|
837
|
if key:
|
|
838
|
h = templater.stringify(self.t(key, **props))
|
|
838
|
h = templater.stringify(self.t(key, **props))
|
|
839
|
if self.buffered:
|
|
839
|
if self.buffered:
|
|
840
|
self.header[rev] = h
|
|
840
|
self.header[rev] = h
|
|
841
|
else:
|
|
841
|
else:
|
|
842
|
self.ui.write(h)
|
|
842
|
self.ui.write(h)
|
|
843
|
if self.ui.debugflag and 'changeset_debug' in self.t:
|
|
843
|
if self.ui.debugflag and 'changeset_debug' in self.t:
|
|
844
|
key = 'changeset_debug'
|
|
844
|
key = 'changeset_debug'
|
|
845
|
elif self.ui.quiet and 'changeset_quiet' in self.t:
|
|
845
|
elif self.ui.quiet and 'changeset_quiet' in self.t:
|
|
846
|
key = 'changeset_quiet'
|
|
846
|
key = 'changeset_quiet'
|
|
847
|
elif self.ui.verbose and 'changeset_verbose' in self.t:
|
|
847
|
elif self.ui.verbose and 'changeset_verbose' in self.t:
|
|
848
|
key = 'changeset_verbose'
|
|
848
|
key = 'changeset_verbose'
|
|
849
|
else:
|
|
849
|
else:
|
|
850
|
key = 'changeset'
|
|
850
|
key = 'changeset'
|
|
851
|
self.ui.write(templater.stringify(self.t(key, **props)))
|
|
851
|
self.ui.write(templater.stringify(self.t(key, **props)))
|
|
852
|
self.showpatch(changenode)
|
|
852
|
self.showpatch(changenode)
|
|
853
|
except KeyError, inst:
|
|
853
|
except KeyError, inst:
|
|
854
|
raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
|
|
854
|
raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
|
|
855
|
inst.args[0]))
|
|
855
|
inst.args[0]))
|
|
856
|
except SyntaxError, inst:
|
|
856
|
except SyntaxError, inst:
|
|
857
|
raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
|
|
857
|
raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
|
|
858
|
|
|
858
|
|
|
859
|
def show_changeset(ui, repo, opts, buffered=False, matchfn=False):
|
|
859
|
def show_changeset(ui, repo, opts, buffered=False, matchfn=False):
|
|
860
|
"""show one changeset using template or regular display.
|
|
860
|
"""show one changeset using template or regular display.
|
|
861
|
|
|
861
|
|
|
862
|
Display format will be the first non-empty hit of:
|
|
862
|
Display format will be the first non-empty hit of:
|
|
863
|
1. option 'template'
|
|
863
|
1. option 'template'
|
|
864
|
2. option 'style'
|
|
864
|
2. option 'style'
|
|
865
|
3. [ui] setting 'logtemplate'
|
|
865
|
3. [ui] setting 'logtemplate'
|
|
866
|
4. [ui] setting 'style'
|
|
866
|
4. [ui] setting 'style'
|
|
867
|
If all of these values are either the unset or the empty string,
|
|
867
|
If all of these values are either the unset or the empty string,
|
|
868
|
regular display via changeset_printer() is done.
|
|
868
|
regular display via changeset_printer() is done.
|
|
869
|
"""
|
|
869
|
"""
|
|
870
|
# options
|
|
870
|
# options
|
|
871
|
patch = False
|
|
871
|
patch = False
|
|
872
|
if opts.get('patch'):
|
|
872
|
if opts.get('patch'):
|
|
873
|
patch = matchfn or util.always
|
|
873
|
patch = matchfn or util.always
|
|
874
|
|
|
874
|
|
|
875
|
tmpl = opts.get('template')
|
|
875
|
tmpl = opts.get('template')
|
|
876
|
mapfile = None
|
|
876
|
mapfile = None
|
|
877
|
if tmpl:
|
|
877
|
if tmpl:
|
|
878
|
tmpl = templater.parsestring(tmpl, quoted=False)
|
|
878
|
tmpl = templater.parsestring(tmpl, quoted=False)
|
|
879
|
else:
|
|
879
|
else:
|
|
880
|
mapfile = opts.get('style')
|
|
880
|
mapfile = opts.get('style')
|
|
881
|
# ui settings
|
|
881
|
# ui settings
|
|
882
|
if not mapfile:
|
|
882
|
if not mapfile:
|
|
883
|
tmpl = ui.config('ui', 'logtemplate')
|
|
883
|
tmpl = ui.config('ui', 'logtemplate')
|
|
884
|
if tmpl:
|
|
884
|
if tmpl:
|
|
885
|
tmpl = templater.parsestring(tmpl)
|
|
885
|
tmpl = templater.parsestring(tmpl)
|
|
886
|
else:
|
|
886
|
else:
|
|
887
|
mapfile = ui.config('ui', 'style')
|
|
887
|
mapfile = ui.config('ui', 'style')
|
|
888
|
|
|
888
|
|
|
889
|
if tmpl or mapfile:
|
|
889
|
if tmpl or mapfile:
|
|
890
|
if mapfile:
|
|
890
|
if mapfile:
|
|
891
|
if not os.path.split(mapfile)[0]:
|
|
891
|
if not os.path.split(mapfile)[0]:
|
|
892
|
mapname = (templater.templatepath('map-cmdline.' + mapfile)
|
|
892
|
mapname = (templater.templatepath('map-cmdline.' + mapfile)
|
|
893
|
or templater.templatepath(mapfile))
|
|
893
|
or templater.templatepath(mapfile))
|
|
894
|
if mapname: mapfile = mapname
|
|
894
|
if mapname: mapfile = mapname
|
|
895
|
try:
|
|
895
|
try:
|
|
896
|
t = changeset_templater(ui, repo, patch, mapfile, buffered)
|
|
896
|
t = changeset_templater(ui, repo, patch, mapfile, buffered)
|
|
897
|
except SyntaxError, inst:
|
|
897
|
except SyntaxError, inst:
|
|
898
|
raise util.Abort(inst.args[0])
|
|
898
|
raise util.Abort(inst.args[0])
|
|
899
|
if tmpl: t.use_template(tmpl)
|
|
899
|
if tmpl: t.use_template(tmpl)
|
|
900
|
return t
|
|
900
|
return t
|
|
901
|
return changeset_printer(ui, repo, patch, buffered)
|
|
901
|
return changeset_printer(ui, repo, patch, buffered)
|
|
902
|
|
|
902
|
|
|
903
|
def finddate(ui, repo, date):
|
|
903
|
def finddate(ui, repo, date):
|
|
904
|
"""Find the tipmost changeset that matches the given date spec"""
|
|
904
|
"""Find the tipmost changeset that matches the given date spec"""
|
|
905
|
df = util.matchdate(date)
|
|
905
|
df = util.matchdate(date)
|
|
906
|
get = util.cachefunc(lambda r: repo.changectx(r).changeset())
|
|
906
|
get = util.cachefunc(lambda r: repo.changectx(r).changeset())
|
|
907
|
changeiter, matchfn = walkchangerevs(ui, repo, [], get, {'rev':None})
|
|
907
|
changeiter, matchfn = walkchangerevs(ui, repo, [], get, {'rev':None})
|
|
908
|
results = {}
|
|
908
|
results = {}
|
|
909
|
for st, rev, fns in changeiter:
|
|
909
|
for st, rev, fns in changeiter:
|
|
910
|
if st == 'add':
|
|
910
|
if st == 'add':
|
|
911
|
d = get(rev)[2]
|
|
911
|
d = get(rev)[2]
|
|
912
|
if df(d[0]):
|
|
912
|
if df(d[0]):
|
|
913
|
results[rev] = d
|
|
913
|
results[rev] = d
|
|
914
|
elif st == 'iter':
|
|
914
|
elif st == 'iter':
|
|
915
|
if rev in results:
|
|
915
|
if rev in results:
|
|
916
|
ui.status("Found revision %s from %s\n" %
|
|
916
|
ui.status("Found revision %s from %s\n" %
|
|
917
|
(rev, util.datestr(results[rev])))
|
|
917
|
(rev, util.datestr(results[rev])))
|
|
918
|
return str(rev)
|
|
918
|
return str(rev)
|
|
919
|
|
|
919
|
|
|
920
|
raise util.Abort(_("revision matching date not found"))
|
|
920
|
raise util.Abort(_("revision matching date not found"))
|
|
921
|
|
|
921
|
|
|
922
|
def walkchangerevs(ui, repo, pats, change, opts):
|
|
922
|
def walkchangerevs(ui, repo, pats, change, opts):
|
|
923
|
'''Iterate over files and the revs they changed in.
|
|
923
|
'''Iterate over files and the revs they changed in.
|
|
924
|
|
|
924
|
|
|
925
|
Callers most commonly need to iterate backwards over the history
|
|
925
|
Callers most commonly need to iterate backwards over the history
|
|
926
|
it is interested in. Doing so has awful (quadratic-looking)
|
|
926
|
it is interested in. Doing so has awful (quadratic-looking)
|
|
927
|
performance, so we use iterators in a "windowed" way.
|
|
927
|
performance, so we use iterators in a "windowed" way.
|
|
928
|
|
|
928
|
|
|
929
|
We walk a window of revisions in the desired order. Within the
|
|
929
|
We walk a window of revisions in the desired order. Within the
|
|
930
|
window, we first walk forwards to gather data, then in the desired
|
|
930
|
window, we first walk forwards to gather data, then in the desired
|
|
931
|
order (usually backwards) to display it.
|
|
931
|
order (usually backwards) to display it.
|
|
932
|
|
|
932
|
|
|
933
|
This function returns an (iterator, matchfn) tuple. The iterator
|
|
933
|
This function returns an (iterator, matchfn) tuple. The iterator
|
|
934
|
yields 3-tuples. They will be of one of the following forms:
|
|
934
|
yields 3-tuples. They will be of one of the following forms:
|
|
935
|
|
|
935
|
|
|
936
|
"window", incrementing, lastrev: stepping through a window,
|
|
936
|
"window", incrementing, lastrev: stepping through a window,
|
|
937
|
positive if walking forwards through revs, last rev in the
|
|
937
|
positive if walking forwards through revs, last rev in the
|
|
938
|
sequence iterated over - use to reset state for the current window
|
|
938
|
sequence iterated over - use to reset state for the current window
|
|
939
|
|
|
939
|
|
|
940
|
"add", rev, fns: out-of-order traversal of the given file names
|
|
940
|
"add", rev, fns: out-of-order traversal of the given file names
|
|
941
|
fns, which changed during revision rev - use to gather data for
|
|
941
|
fns, which changed during revision rev - use to gather data for
|
|
942
|
possible display
|
|
942
|
possible display
|
|
943
|
|
|
943
|
|
|
944
|
"iter", rev, None: in-order traversal of the revs earlier iterated
|
|
944
|
"iter", rev, None: in-order traversal of the revs earlier iterated
|
|
945
|
over with "add" - use to display data'''
|
|
945
|
over with "add" - use to display data'''
|
|
946
|
|
|
946
|
|
|
947
|
def increasing_windows(start, end, windowsize=8, sizelimit=512):
|
|
947
|
def increasing_windows(start, end, windowsize=8, sizelimit=512):
|
|
948
|
if start < end:
|
|
948
|
if start < end:
|
|
949
|
while start < end:
|
|
949
|
while start < end:
|
|
950
|
yield start, min(windowsize, end-start)
|
|
950
|
yield start, min(windowsize, end-start)
|
|
951
|
start += windowsize
|
|
951
|
start += windowsize
|
|
952
|
if windowsize < sizelimit:
|
|
952
|
if windowsize < sizelimit:
|
|
953
|
windowsize *= 2
|
|
953
|
windowsize *= 2
|
|
954
|
else:
|
|
954
|
else:
|
|
955
|
while start > end:
|
|
955
|
while start > end:
|
|
956
|
yield start, min(windowsize, start-end-1)
|
|
956
|
yield start, min(windowsize, start-end-1)
|
|
957
|
start -= windowsize
|
|
957
|
start -= windowsize
|
|
958
|
if windowsize < sizelimit:
|
|
958
|
if windowsize < sizelimit:
|
|
959
|
windowsize *= 2
|
|
959
|
windowsize *= 2
|
|
960
|
|
|
960
|
|
|
961
|
files, matchfn, anypats = matchpats(repo, pats, opts)
|
|
961
|
files, matchfn, anypats = matchpats(repo, pats, opts)
|
|
962
|
follow = opts.get('follow') or opts.get('follow_first')
|
|
962
|
follow = opts.get('follow') or opts.get('follow_first')
|
|
963
|
|
|
963
|
|
|
964
|
if repo.changelog.count() == 0:
|
|
964
|
if repo.changelog.count() == 0:
|
|
965
|
return [], matchfn
|
|
965
|
return [], matchfn
|
|
966
|
|
|
966
|
|
|
967
|
if follow:
|
|
967
|
if follow:
|
|
968
|
defrange = '%s:0' % repo.changectx().rev()
|
|
968
|
defrange = '%s:0' % repo.changectx().rev()
|
|
969
|
else:
|
|
969
|
else:
|
|
970
|
defrange = 'tip:0'
|
|
970
|
defrange = 'tip:0'
|
|
971
|
revs = revrange(repo, opts['rev'] or [defrange])
|
|
971
|
revs = revrange(repo, opts['rev'] or [defrange])
|
|
972
|
wanted = {}
|
|
972
|
wanted = {}
|
|
973
|
slowpath = anypats or opts.get('removed')
|
|
973
|
slowpath = anypats or opts.get('removed')
|
|
974
|
fncache = {}
|
|
974
|
fncache = {}
|
|
975
|
|
|
975
|
|
|
976
|
if not slowpath and not files:
|
|
976
|
if not slowpath and not files:
|
|
977
|
# No files, no patterns. Display all revs.
|
|
977
|
# No files, no patterns. Display all revs.
|
|
978
|
wanted = dict.fromkeys(revs)
|
|
978
|
wanted = dict.fromkeys(revs)
|
|
979
|
copies = []
|
|
979
|
copies = []
|
|
980
|
if not slowpath:
|
|
980
|
if not slowpath:
|
|
981
|
# Only files, no patterns. Check the history of each file.
|
|
981
|
# Only files, no patterns. Check the history of each file.
|
|
982
|
def filerevgen(filelog, node):
|
|
982
|
def filerevgen(filelog, node):
|
|
983
|
cl_count = repo.changelog.count()
|
|
983
|
cl_count = repo.changelog.count()
|
|
984
|
if node is None:
|
|
984
|
if node is None:
|
|
985
|
last = filelog.count() - 1
|
|
985
|
last = filelog.count() - 1
|
|
986
|
else:
|
|
986
|
else:
|
|
987
|
last = filelog.rev(node)
|
|
987
|
last = filelog.rev(node)
|
|
988
|
for i, window in increasing_windows(last, nullrev):
|
|
988
|
for i, window in increasing_windows(last, nullrev):
|
|
989
|
revs = []
|
|
989
|
revs = []
|
|
990
|
for j in xrange(i - window, i + 1):
|
|
990
|
for j in xrange(i - window, i + 1):
|
|
991
|
n = filelog.node(j)
|
|
991
|
n = filelog.node(j)
|
|
992
|
revs.append((filelog.linkrev(n),
|
|
992
|
revs.append((filelog.linkrev(n),
|
|
993
|
follow and filelog.renamed(n)))
|
|
993
|
follow and filelog.renamed(n)))
|
|
994
|
revs.reverse()
|
|
994
|
revs.reverse()
|
|
995
|
for rev in revs:
|
|
995
|
for rev in revs:
|
|
996
|
# only yield rev for which we have the changelog, it can
|
|
996
|
# only yield rev for which we have the changelog, it can
|
|
997
|
# happen while doing "hg log" during a pull or commit
|
|
997
|
# happen while doing "hg log" during a pull or commit
|
|
998
|
if rev[0] < cl_count:
|
|
998
|
if rev[0] < cl_count:
|
|
999
|
yield rev
|
|
999
|
yield rev
|
|
1000
|
def iterfiles():
|
|
1000
|
def iterfiles():
|
|
1001
|
for filename in files:
|
|
1001
|
for filename in files:
|
|
1002
|
yield filename, None
|
|
1002
|
yield filename, None
|
|
1003
|
for filename_node in copies:
|
|
1003
|
for filename_node in copies:
|
|
1004
|
yield filename_node
|
|
1004
|
yield filename_node
|
|
1005
|
minrev, maxrev = min(revs), max(revs)
|
|
1005
|
minrev, maxrev = min(revs), max(revs)
|
|
1006
|
for file_, node in iterfiles():
|
|
1006
|
for file_, node in iterfiles():
|
|
1007
|
filelog = repo.file(file_)
|
|
1007
|
filelog = repo.file(file_)
|
|
1008
|
# A zero count may be a directory or deleted file, so
|
|
1008
|
# A zero count may be a directory or deleted file, so
|
|
1009
|
# try to find matching entries on the slow path.
|
|
1009
|
# try to find matching entries on the slow path.
|
|
1010
|
if filelog.count() == 0:
|
|
1010
|
if filelog.count() == 0:
|
|
1011
|
slowpath = True
|
|
1011
|
slowpath = True
|
|
1012
|
break
|
|
1012
|
break
|
|
1013
|
for rev, copied in filerevgen(filelog, node):
|
|
1013
|
for rev, copied in filerevgen(filelog, node):
|
|
1014
|
if rev <= maxrev:
|
|
1014
|
if rev <= maxrev:
|
|
1015
|
if rev < minrev:
|
|
1015
|
if rev < minrev:
|
|
1016
|
break
|
|
1016
|
break
|
|
1017
|
fncache.setdefault(rev, [])
|
|
1017
|
fncache.setdefault(rev, [])
|
|
1018
|
fncache[rev].append(file_)
|
|
1018
|
fncache[rev].append(file_)
|
|
1019
|
wanted[rev] = 1
|
|
1019
|
wanted[rev] = 1
|
|
1020
|
if follow and copied:
|
|
1020
|
if follow and copied:
|
|
1021
|
copies.append(copied)
|
|
1021
|
copies.append(copied)
|
|
1022
|
if slowpath:
|
|
1022
|
if slowpath:
|
|
1023
|
if follow:
|
|
1023
|
if follow:
|
|
1024
|
raise util.Abort(_('can only follow copies/renames for explicit '
|
|
1024
|
raise util.Abort(_('can only follow copies/renames for explicit '
|
|
1025
|
'file names'))
|
|
1025
|
'file names'))
|
|
1026
|
|
|
1026
|
|
|
1027
|
# The slow path checks files modified in every changeset.
|
|
1027
|
# The slow path checks files modified in every changeset.
|
|
1028
|
def changerevgen():
|
|
1028
|
def changerevgen():
|
|
1029
|
for i, window in increasing_windows(repo.changelog.count()-1,
|
|
1029
|
for i, window in increasing_windows(repo.changelog.count()-1,
|
|
1030
|
nullrev):
|
|
1030
|
nullrev):
|
|
1031
|
for j in xrange(i - window, i + 1):
|
|
1031
|
for j in xrange(i - window, i + 1):
|
|
1032
|
yield j, change(j)[3]
|
|
1032
|
yield j, change(j)[3]
|
|
1033
|
|
|
1033
|
|
|
1034
|
for rev, changefiles in changerevgen():
|
|
1034
|
for rev, changefiles in changerevgen():
|
|
1035
|
matches = filter(matchfn, changefiles)
|
|
1035
|
matches = filter(matchfn, changefiles)
|
|
1036
|
if matches:
|
|
1036
|
if matches:
|
|
1037
|
fncache[rev] = matches
|
|
1037
|
fncache[rev] = matches
|
|
1038
|
wanted[rev] = 1
|
|
1038
|
wanted[rev] = 1
|
|
1039
|
|
|
1039
|
|
|
1040
|
class followfilter:
|
|
1040
|
class followfilter:
|
|
1041
|
def __init__(self, onlyfirst=False):
|
|
1041
|
def __init__(self, onlyfirst=False):
|
|
1042
|
self.startrev = nullrev
|
|
1042
|
self.startrev = nullrev
|
|
1043
|
self.roots = []
|
|
1043
|
self.roots = []
|
|
1044
|
self.onlyfirst = onlyfirst
|
|
1044
|
self.onlyfirst = onlyfirst
|
|
1045
|
|
|
1045
|
|
|
1046
|
def match(self, rev):
|
|
1046
|
def match(self, rev):
|
|
1047
|
def realparents(rev):
|
|
1047
|
def realparents(rev):
|
|
1048
|
if self.onlyfirst:
|
|
1048
|
if self.onlyfirst:
|
|
1049
|
return repo.changelog.parentrevs(rev)[0:1]
|
|
1049
|
return repo.changelog.parentrevs(rev)[0:1]
|
|
1050
|
else:
|
|
1050
|
else:
|
|
1051
|
return filter(lambda x: x != nullrev,
|
|
1051
|
return filter(lambda x: x != nullrev,
|
|
1052
|
repo.changelog.parentrevs(rev))
|
|
1052
|
repo.changelog.parentrevs(rev))
|
|
1053
|
|
|
1053
|
|
|
1054
|
if self.startrev == nullrev:
|
|
1054
|
if self.startrev == nullrev:
|
|
1055
|
self.startrev = rev
|
|
1055
|
self.startrev = rev
|
|
1056
|
return True
|
|
1056
|
return True
|
|
1057
|
|
|
1057
|
|
|
1058
|
if rev > self.startrev:
|
|
1058
|
if rev > self.startrev:
|
|
1059
|
# forward: all descendants
|
|
1059
|
# forward: all descendants
|
|
1060
|
if not self.roots:
|
|
1060
|
if not self.roots:
|
|
1061
|
self.roots.append(self.startrev)
|
|
1061
|
self.roots.append(self.startrev)
|
|
1062
|
for parent in realparents(rev):
|
|
1062
|
for parent in realparents(rev):
|
|
1063
|
if parent in self.roots:
|
|
1063
|
if parent in self.roots:
|
|
1064
|
self.roots.append(rev)
|
|
1064
|
self.roots.append(rev)
|
|
1065
|
return True
|
|
1065
|
return True
|
|
1066
|
else:
|
|
1066
|
else:
|
|
1067
|
# backwards: all parents
|
|
1067
|
# backwards: all parents
|
|
1068
|
if not self.roots:
|
|
1068
|
if not self.roots:
|
|
1069
|
self.roots.extend(realparents(self.startrev))
|
|
1069
|
self.roots.extend(realparents(self.startrev))
|
|
1070
|
if rev in self.roots:
|
|
1070
|
if rev in self.roots:
|
|
1071
|
self.roots.remove(rev)
|
|
1071
|
self.roots.remove(rev)
|
|
1072
|
self.roots.extend(realparents(rev))
|
|
1072
|
self.roots.extend(realparents(rev))
|
|
1073
|
return True
|
|
1073
|
return True
|
|
1074
|
|
|
1074
|
|
|
1075
|
return False
|
|
1075
|
return False
|
|
1076
|
|
|
1076
|
|
|
1077
|
# it might be worthwhile to do this in the iterator if the rev range
|
|
1077
|
# it might be worthwhile to do this in the iterator if the rev range
|
|
1078
|
# is descending and the prune args are all within that range
|
|
1078
|
# is descending and the prune args are all within that range
|
|
1079
|
for rev in opts.get('prune', ()):
|
|
1079
|
for rev in opts.get('prune', ()):
|
|
1080
|
rev = repo.changelog.rev(repo.lookup(rev))
|
|
1080
|
rev = repo.changelog.rev(repo.lookup(rev))
|
|
1081
|
ff = followfilter()
|
|
1081
|
ff = followfilter()
|
|
1082
|
stop = min(revs[0], revs[-1])
|
|
1082
|
stop = min(revs[0], revs[-1])
|
|
1083
|
for x in xrange(rev, stop-1, -1):
|
|
1083
|
for x in xrange(rev, stop-1, -1):
|
|
1084
|
if ff.match(x) and x in wanted:
|
|
1084
|
if ff.match(x) and x in wanted:
|
|
1085
|
del wanted[x]
|
|
1085
|
del wanted[x]
|
|
1086
|
|
|
1086
|
|
|
1087
|
def iterate():
|
|
1087
|
def iterate():
|
|
1088
|
if follow and not files:
|
|
1088
|
if follow and not files:
|
|
1089
|
ff = followfilter(onlyfirst=opts.get('follow_first'))
|
|
1089
|
ff = followfilter(onlyfirst=opts.get('follow_first'))
|
|
1090
|
def want(rev):
|
|
1090
|
def want(rev):
|
|
1091
|
if ff.match(rev) and rev in wanted:
|
|
1091
|
if ff.match(rev) and rev in wanted:
|
|
1092
|
return True
|
|
1092
|
return True
|
|
1093
|
return False
|
|
1093
|
return False
|
|
1094
|
else:
|
|
1094
|
else:
|
|
1095
|
def want(rev):
|
|
1095
|
def want(rev):
|
|
1096
|
return rev in wanted
|
|
1096
|
return rev in wanted
|
|
1097
|
|
|
1097
|
|
|
1098
|
for i, window in increasing_windows(0, len(revs)):
|
|
1098
|
for i, window in increasing_windows(0, len(revs)):
|
|
1099
|
yield 'window', revs[0] < revs[-1], revs[-1]
|
|
1099
|
yield 'window', revs[0] < revs[-1], revs[-1]
|
|
1100
|
nrevs = [rev for rev in revs[i:i+window] if want(rev)]
|
|
1100
|
nrevs = [rev for rev in revs[i:i+window] if want(rev)]
|
|
1101
|
srevs = list(nrevs)
|
|
1101
|
srevs = list(nrevs)
|
|
1102
|
srevs.sort()
|
|
1102
|
srevs.sort()
|
|
1103
|
for rev in srevs:
|
|
1103
|
for rev in srevs:
|
|
1104
|
fns = fncache.get(rev)
|
|
1104
|
fns = fncache.get(rev)
|
|
1105
|
if not fns:
|
|
1105
|
if not fns:
|
|
1106
|
def fns_generator():
|
|
1106
|
def fns_generator():
|
|
1107
|
for f in change(rev)[3]:
|
|
1107
|
for f in change(rev)[3]:
|
|
1108
|
if matchfn(f):
|
|
1108
|
if matchfn(f):
|
|
1109
|
yield f
|
|
1109
|
yield f
|
|
1110
|
fns = fns_generator()
|
|
1110
|
fns = fns_generator()
|
|
1111
|
yield 'add', rev, fns
|
|
1111
|
yield 'add', rev, fns
|
|
1112
|
for rev in nrevs:
|
|
1112
|
for rev in nrevs:
|
|
1113
|
yield 'iter', rev, None
|
|
1113
|
yield 'iter', rev, None
|
|
1114
|
return iterate(), matchfn
|
|
1114
|
return iterate(), matchfn
|
|
1115
|
|
|
1115
|
|
|
1116
|
def commit(ui, repo, commitfunc, pats, opts):
|
|
1116
|
def commit(ui, repo, commitfunc, pats, opts):
|
|
1117
|
'''commit the specified files or all outstanding changes'''
|
|
1117
|
'''commit the specified files or all outstanding changes'''
|
|
1118
|
message = logmessage(opts)
|
|
1118
|
message = logmessage(opts)
|
|
1119
|
|
|
1119
|
|
|
1120
|
# extract addremove carefully -- this function can be called from a command
|
|
1120
|
# extract addremove carefully -- this function can be called from a command
|
|
1121
|
# that doesn't support addremove
|
|
1121
|
# that doesn't support addremove
|
|
1122
|
if opts.get('addremove'):
|
|
1122
|
if opts.get('addremove'):
|
|
1123
|
addremove(repo, pats, opts)
|
|
1123
|
addremove(repo, pats, opts)
|
|
1124
|
|
|
1124
|
|
|
1125
|
fns, match, anypats = matchpats(repo, pats, opts)
|
|
1125
|
fns, match, anypats = matchpats(repo, pats, opts)
|
|
1126
|
if pats:
|
|
1126
|
if pats:
|
|
1127
|
status = repo.status(files=fns, match=match)
|
|
1127
|
status = repo.status(files=fns, match=match)
|
|
1128
|
modified, added, removed, deleted, unknown = status[:5]
|
|
1128
|
modified, added, removed, deleted, unknown = status[:5]
|
|
1129
|
files = modified + added + removed
|
|
1129
|
files = modified + added + removed
|
|
1130
|
slist = None
|
|
1130
|
slist = None
|
|
1131
|
for f in fns:
|
|
1131
|
for f in fns:
|
|
1132
|
if f == '.':
|
|
1132
|
if f == '.':
|
|
1133
|
continue
|
|
1133
|
continue
|
|
1134
|
if f not in files:
|
|
1134
|
if f not in files:
|
|
1135
|
rf = repo.wjoin(f)
|
|
1135
|
rf = repo.wjoin(f)
|
|
|
|
|
1136
|
rel = repo.pathto(f)
|
|
1136
|
try:
|
|
1137
|
try:
|
|
1137
|
mode = os.lstat(rf)[stat.ST_MODE]
|
|
1138
|
mode = os.lstat(rf)[stat.ST_MODE]
|
|
1138
|
except OSError:
|
|
1139
|
except OSError:
|
|
1139
|
raise util.Abort(_("file %s not found!") % rf)
|
|
1140
|
raise util.Abort(_("file %s not found!") % rel)
|
|
1140
|
if stat.S_ISDIR(mode):
|
|
1141
|
if stat.S_ISDIR(mode):
|
|
1141
|
name = f + '/'
|
|
1142
|
name = f + '/'
|
|
1142
|
if slist is None:
|
|
1143
|
if slist is None:
|
|
1143
|
slist = list(files)
|
|
1144
|
slist = list(files)
|
|
1144
|
slist.sort()
|
|
1145
|
slist.sort()
|
|
1145
|
i = bisect.bisect(slist, name)
|
|
1146
|
i = bisect.bisect(slist, name)
|
|
1146
|
if i >= len(slist) or not slist[i].startswith(name):
|
|
1147
|
if i >= len(slist) or not slist[i].startswith(name):
|
|
1147
|
raise util.Abort(_("no match under directory %s!")
|
|
1148
|
raise util.Abort(_("no match under directory %s!")
|
|
1148
|
% rf)
|
|
1149
|
% rel)
|
|
1149
|
elif not (stat.S_ISREG(mode) or stat.S_ISLNK(mode)):
|
|
1150
|
elif not (stat.S_ISREG(mode) or stat.S_ISLNK(mode)):
|
|
1150
|
raise util.Abort(_("can't commit %s: "
|
|
1151
|
raise util.Abort(_("can't commit %s: "
|
|
1151
|
"unsupported file type!") % rf)
|
|
1152
|
"unsupported file type!") % rel)
|
|
1152
|
elif f not in repo.dirstate:
|
|
1153
|
elif f not in repo.dirstate:
|
|
1153
|
raise util.Abort(_("file %s not tracked!") % rf)
|
|
1154
|
raise util.Abort(_("file %s not tracked!") % rel)
|
|
1154
|
else:
|
|
1155
|
else:
|
|
1155
|
files = []
|
|
1156
|
files = []
|
|
1156
|
try:
|
|
1157
|
try:
|
|
1157
|
return commitfunc(ui, repo, files, message, match, opts)
|
|
1158
|
return commitfunc(ui, repo, files, message, match, opts)
|
|
1158
|
except ValueError, inst:
|
|
1159
|
except ValueError, inst:
|
|
1159
|
raise util.Abort(str(inst))
|
|
1160
|
raise util.Abort(str(inst))
|