##// END OF EJS Templates
fixed typos found in translatable strings...
Martin Geisler -
r8668:aea3a231 default
parent child Browse files
Show More
@@ -1,270 +1,270 b''
1 1 # color.py color output for the status and qseries commands
2 2 #
3 3 # Copyright (C) 2007 Kevin Christen <kevin.christen@gmail.com>
4 4 #
5 5 # This program is free software; you can redistribute it and/or modify it
6 6 # under the terms of the GNU General Public License as published by the
7 7 # Free Software Foundation; either version 2 of the License, or (at your
8 8 # option) any later version.
9 9 #
10 10 # This program is distributed in the hope that it will be useful, but
11 11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
13 13 # Public License for more details.
14 14 #
15 15 # You should have received a copy of the GNU General Public License along
16 16 # with this program; if not, write to the Free Software Foundation, Inc.,
17 17 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 18
19 19 '''add color output to status, qseries, and diff-related commands
20 20
21 21 This extension modifies the status command to add color to its output
22 22 to reflect file status, the qseries command to add color to reflect
23 23 patch status (applied, unapplied, missing), and to diff-related
24 24 commands to highlight additions, removals, diff headers, and trailing
25 25 whitespace.
26 26
27 27 Other effects in addition to color, like bold and underlined text, are
28 28 also available. Effects are rendered with the ECMA-48 SGR control
29 29 function (aka ANSI escape codes). This module also provides the
30 30 render_text function, which can be used to add effects to any text.
31 31
32 32 To enable this extension, add this to your .hgrc file:
33 33 [extensions]
34 34 color =
35 35
36 Default effects my be overriden from the .hgrc file:
36 Default effects my be overridden from the .hgrc file:
37 37
38 38 [color]
39 39 status.modified = blue bold underline red_background
40 40 status.added = green bold
41 41 status.removed = red bold blue_background
42 42 status.deleted = cyan bold underline
43 43 status.unknown = magenta bold underline
44 44 status.ignored = black bold
45 45
46 46 # 'none' turns off all effects
47 47 status.clean = none
48 48 status.copied = none
49 49
50 50 qseries.applied = blue bold underline
51 51 qseries.unapplied = black bold
52 52 qseries.missing = red bold
53 53
54 54 diff.diffline = bold
55 55 diff.extended = cyan bold
56 56 diff.file_a = red bold
57 57 diff.file_b = green bold
58 58 diff.hunk = magenta
59 59 diff.deleted = red
60 60 diff.inserted = green
61 61 diff.changed = white
62 62 diff.trailingwhitespace = bold red_background
63 63 '''
64 64
65 65 import os, sys
66 66
67 67 from mercurial import cmdutil, commands, extensions
68 68 from mercurial.i18n import _
69 69
70 70 # start and stop parameters for effects
71 71 _effect_params = {'none': 0,
72 72 'black': 30,
73 73 'red': 31,
74 74 'green': 32,
75 75 'yellow': 33,
76 76 'blue': 34,
77 77 'magenta': 35,
78 78 'cyan': 36,
79 79 'white': 37,
80 80 'bold': 1,
81 81 'italic': 3,
82 82 'underline': 4,
83 83 'inverse': 7,
84 84 'black_background': 40,
85 85 'red_background': 41,
86 86 'green_background': 42,
87 87 'yellow_background': 43,
88 88 'blue_background': 44,
89 89 'purple_background': 45,
90 90 'cyan_background': 46,
91 91 'white_background': 47}
92 92
93 93 def render_effects(text, effects):
94 94 'Wrap text in commands to turn on each effect.'
95 95 start = [str(_effect_params[e]) for e in ['none'] + effects]
96 96 start = '\033[' + ';'.join(start) + 'm'
97 97 stop = '\033[' + str(_effect_params['none']) + 'm'
98 98 return ''.join([start, text, stop])
99 99
100 100 def colorstatus(orig, ui, repo, *pats, **opts):
101 101 '''run the status command with colored output'''
102 102
103 103 delimiter = opts['print0'] and '\0' or '\n'
104 104
105 105 nostatus = opts.get('no_status')
106 106 opts['no_status'] = False
107 107 # run status and capture its output
108 108 ui.pushbuffer()
109 109 retval = orig(ui, repo, *pats, **opts)
110 110 # filter out empty strings
111 111 lines_with_status = [ line for line in ui.popbuffer().split(delimiter) if line ]
112 112
113 113 if nostatus:
114 114 lines = [l[2:] for l in lines_with_status]
115 115 else:
116 116 lines = lines_with_status
117 117
118 118 # apply color to output and display it
119 119 for i in xrange(len(lines)):
120 120 status = _status_abbreviations[lines_with_status[i][0]]
121 121 effects = _status_effects[status]
122 122 if effects:
123 123 lines[i] = render_effects(lines[i], effects)
124 124 ui.write(lines[i] + delimiter)
125 125 return retval
126 126
127 127 _status_abbreviations = { 'M': 'modified',
128 128 'A': 'added',
129 129 'R': 'removed',
130 130 '!': 'deleted',
131 131 '?': 'unknown',
132 132 'I': 'ignored',
133 133 'C': 'clean',
134 134 ' ': 'copied', }
135 135
136 136 _status_effects = { 'modified': ['blue', 'bold'],
137 137 'added': ['green', 'bold'],
138 138 'removed': ['red', 'bold'],
139 139 'deleted': ['cyan', 'bold', 'underline'],
140 140 'unknown': ['magenta', 'bold', 'underline'],
141 141 'ignored': ['black', 'bold'],
142 142 'clean': ['none'],
143 143 'copied': ['none'], }
144 144
145 145 def colorqseries(orig, ui, repo, *dummy, **opts):
146 146 '''run the qseries command with colored output'''
147 147 ui.pushbuffer()
148 148 retval = orig(ui, repo, **opts)
149 149 patches = ui.popbuffer().splitlines()
150 150 for patch in patches:
151 151 patchname = patch
152 152 if opts['summary']:
153 153 patchname = patchname.split(': ')[0]
154 154 if ui.verbose:
155 155 patchname = patchname.split(' ', 2)[-1]
156 156
157 157 if opts['missing']:
158 158 effects = _patch_effects['missing']
159 159 # Determine if patch is applied.
160 160 elif [ applied for applied in repo.mq.applied
161 161 if patchname == applied.name ]:
162 162 effects = _patch_effects['applied']
163 163 else:
164 164 effects = _patch_effects['unapplied']
165 165 ui.write(render_effects(patch, effects) + '\n')
166 166 return retval
167 167
168 168 _patch_effects = { 'applied': ['blue', 'bold', 'underline'],
169 169 'missing': ['red', 'bold'],
170 170 'unapplied': ['black', 'bold'], }
171 171
172 172 def colorwrap(orig, s):
173 173 '''wrap ui.write for colored diff output'''
174 174 lines = s.split('\n')
175 175 for i, line in enumerate(lines):
176 176 stripline = line
177 177 if line and line[0] in '+-':
178 178 # highlight trailing whitespace, but only in changed lines
179 179 stripline = line.rstrip()
180 180 for prefix, style in _diff_prefixes:
181 181 if stripline.startswith(prefix):
182 182 lines[i] = render_effects(stripline, _diff_effects[style])
183 183 break
184 184 if line != stripline:
185 185 lines[i] += render_effects(
186 186 line[len(stripline):], _diff_effects['trailingwhitespace'])
187 187 orig('\n'.join(lines))
188 188
189 189 def colorshowpatch(orig, self, node):
190 190 '''wrap cmdutil.changeset_printer.showpatch with colored output'''
191 191 oldwrite = extensions.wrapfunction(self.ui, 'write', colorwrap)
192 192 try:
193 193 orig(self, node)
194 194 finally:
195 195 self.ui.write = oldwrite
196 196
197 197 def colordiff(orig, ui, repo, *pats, **opts):
198 198 '''run the diff command with colored output'''
199 199 oldwrite = extensions.wrapfunction(ui, 'write', colorwrap)
200 200 try:
201 201 orig(ui, repo, *pats, **opts)
202 202 finally:
203 203 ui.write = oldwrite
204 204
205 205 _diff_prefixes = [('diff', 'diffline'),
206 206 ('copy', 'extended'),
207 207 ('rename', 'extended'),
208 208 ('old', 'extended'),
209 209 ('new', 'extended'),
210 210 ('deleted', 'extended'),
211 211 ('---', 'file_a'),
212 212 ('+++', 'file_b'),
213 213 ('@', 'hunk'),
214 214 ('-', 'deleted'),
215 215 ('+', 'inserted')]
216 216
217 217 _diff_effects = {'diffline': ['bold'],
218 218 'extended': ['cyan', 'bold'],
219 219 'file_a': ['red', 'bold'],
220 220 'file_b': ['green', 'bold'],
221 221 'hunk': ['magenta'],
222 222 'deleted': ['red'],
223 223 'inserted': ['green'],
224 224 'changed': ['white'],
225 225 'trailingwhitespace': ['bold', 'red_background']}
226 226
227 227 def uisetup(ui):
228 228 '''Initialize the extension.'''
229 229 _setupcmd(ui, 'diff', commands.table, colordiff, _diff_effects)
230 230 _setupcmd(ui, 'incoming', commands.table, None, _diff_effects)
231 231 _setupcmd(ui, 'log', commands.table, None, _diff_effects)
232 232 _setupcmd(ui, 'outgoing', commands.table, None, _diff_effects)
233 233 _setupcmd(ui, 'tip', commands.table, None, _diff_effects)
234 234 _setupcmd(ui, 'status', commands.table, colorstatus, _status_effects)
235 235 try:
236 236 mq = extensions.find('mq')
237 237 _setupcmd(ui, 'qdiff', mq.cmdtable, colordiff, _diff_effects)
238 238 _setupcmd(ui, 'qseries', mq.cmdtable, colorqseries, _patch_effects)
239 239 except KeyError:
240 240 # The mq extension is not enabled
241 241 pass
242 242
243 243 def _setupcmd(ui, cmd, table, func, effectsmap):
244 244 '''patch in command to command table and load effect map'''
245 245 def nocolor(orig, *args, **opts):
246 246
247 247 if (opts['no_color'] or opts['color'] == 'never' or
248 248 (opts['color'] == 'auto' and (os.environ.get('TERM') == 'dumb'
249 249 or not sys.__stdout__.isatty()))):
250 250 return orig(*args, **opts)
251 251
252 252 oldshowpatch = extensions.wrapfunction(cmdutil.changeset_printer,
253 253 'showpatch', colorshowpatch)
254 254 try:
255 255 if func is not None:
256 256 return func(orig, *args, **opts)
257 257 return orig(*args, **opts)
258 258 finally:
259 259 cmdutil.changeset_printer.showpatch = oldshowpatch
260 260
261 261 entry = extensions.wrapcommand(table, cmd, nocolor)
262 262 entry[1].extend([
263 263 ('', 'color', 'auto', _("when to colorize (always, auto, or never)")),
264 264 ('', 'no-color', None, _("don't colorize output")),
265 265 ])
266 266
267 267 for status in effectsmap:
268 268 effects = ui.configlist('color', cmd + '.' + status)
269 269 if effects:
270 270 effectsmap[status] = effects
@@ -1,274 +1,274 b''
1 1 # convert.py Foreign SCM converter
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2, incorporated herein by reference.
7 7
8 8 '''converting foreign VCS repositories to Mercurial'''
9 9
10 10 import convcmd
11 11 import cvsps
12 12 import subversion
13 13 from mercurial import commands
14 14 from mercurial.i18n import _
15 15
16 16 # Commands definition was moved elsewhere to ease demandload job.
17 17
18 18 def convert(ui, src, dest=None, revmapfile=None, **opts):
19 19 """convert a foreign SCM repository to a Mercurial one.
20 20
21 21 Accepted source formats [identifiers]:
22 22 - Mercurial [hg]
23 23 - CVS [cvs]
24 24 - Darcs [darcs]
25 25 - git [git]
26 26 - Subversion [svn]
27 27 - Monotone [mtn]
28 28 - GNU Arch [gnuarch]
29 29 - Bazaar [bzr]
30 30 - Perforce [p4]
31 31
32 32 Accepted destination formats [identifiers]:
33 33 - Mercurial [hg]
34 34 - Subversion [svn] (history on branches is not preserved)
35 35
36 36 If no revision is given, all revisions will be converted.
37 37 Otherwise, convert will only import up to the named revision
38 38 (given in a format understood by the source).
39 39
40 40 If no destination directory name is specified, it defaults to the
41 41 basename of the source with '-hg' appended. If the destination
42 42 repository doesn't exist, it will be created.
43 43
44 44 If <REVMAP> isn't given, it will be put in a default location
45 45 (<dest>/.hg/shamap by default). The <REVMAP> is a simple text file
46 46 that maps each source commit ID to the destination ID for that
47 47 revision, like so:
48 48 <source ID> <destination ID>
49 49
50 50 If the file doesn't exist, it's automatically created. It's
51 51 updated on each commit copied, so convert-repo can be interrupted
52 52 and can be run repeatedly to copy new commits.
53 53
54 54 The [username mapping] file is a simple text file that maps each
55 55 source commit author to a destination commit author. It is handy
56 56 for source SCMs that use unix logins to identify authors (eg:
57 57 CVS). One line per author mapping and the line format is:
58 58 srcauthor=whatever string you want
59 59
60 60 The filemap is a file that allows filtering and remapping of files
61 61 and directories. Comment lines start with '#'. Each line can
62 62 contain one of the following directives:
63 63
64 64 include path/to/file
65 65
66 66 exclude path/to/file
67 67
68 68 rename from/file to/file
69 69
70 70 The 'include' directive causes a file, or all files under a
71 71 directory, to be included in the destination repository, and the
72 exclusion of all other files and directories not explicitely included.
72 exclusion of all other files and directories not explicitly included.
73 73 The 'exclude' directive causes files or directories to be omitted.
74 74 The 'rename' directive renames a file or directory. To rename from
75 75 a subdirectory into the root of the repository, use '.' as the
76 76 path to rename to.
77 77
78 78 The splicemap is a file that allows insertion of synthetic
79 79 history, letting you specify the parents of a revision. This is
80 80 useful if you want to e.g. give a Subversion merge two parents, or
81 81 graft two disconnected series of history together. Each entry
82 82 contains a key, followed by a space, followed by one or two
83 83 comma-separated values. The key is the revision ID in the source
84 84 revision control system whose parents should be modified (same
85 85 format as a key in .hg/shamap). The values are the revision IDs
86 86 (in either the source or destination revision control system) that
87 87 should be used as the new parents for that node.
88 88
89 89 The branchmap is a file that allows you to rename a branch when it is
90 90 being brought in from whatever external repository. When used in
91 91 conjunction with a splicemap, it allows for a powerful combination
92 92 to help fix even the most badly mismanaged repositories and turn them
93 93 into nicely structured Mercurial repositories. The branchmap contains
94 94 lines of the form "original_branch_name new_branch_name".
95 95 "original_branch_name" is the name of the branch in the source
96 96 repository, and "new_branch_name" is the name of the branch is the
97 97 destination repository. This can be used to (for instance) move code
98 98 in one repository from "default" to a named branch.
99 99
100 100 Mercurial Source
101 101 -----------------
102 102
103 103 --config convert.hg.ignoreerrors=False (boolean)
104 104 ignore integrity errors when reading. Use it to fix Mercurial
105 105 repositories with missing revlogs, by converting from and to
106 106 Mercurial.
107 107 --config convert.hg.saverev=False (boolean)
108 108 store original revision ID in changeset (forces target IDs to
109 109 change)
110 110 --config convert.hg.startrev=0 (hg revision identifier)
111 111 convert start revision and its descendants
112 112
113 113 CVS Source
114 114 ----------
115 115
116 116 CVS source will use a sandbox (i.e. a checked-out copy) from CVS
117 117 to indicate the starting point of what will be converted. Direct
118 118 access to the repository files is not needed, unless of course the
119 119 repository is :local:. The conversion uses the top level directory
120 120 in the sandbox to find the CVS repository, and then uses CVS rlog
121 121 commands to find files to convert. This means that unless a
122 122 filemap is given, all files under the starting directory will be
123 converted, and that any directory reorganisation in the CVS
123 converted, and that any directory reorganization in the CVS
124 124 sandbox is ignored.
125 125
126 126 Because CVS does not have changesets, it is necessary to collect
127 127 individual commits to CVS and merge them into changesets. CVS
128 128 source uses its internal changeset merging code by default but can
129 129 be configured to call the external 'cvsps' program by setting:
130 130 --config convert.cvsps='cvsps -A -u --cvs-direct -q'
131 131 This option is deprecated and will be removed in Mercurial 1.4.
132 132
133 133 The options shown are the defaults.
134 134
135 135 Internal cvsps is selected by setting
136 136 --config convert.cvsps=builtin
137 137 and has a few more configurable options:
138 138 --config convert.cvsps.cache=True (boolean)
139 139 Set to False to disable remote log caching, for testing and
140 140 debugging purposes.
141 141 --config convert.cvsps.fuzz=60 (integer)
142 142 Specify the maximum time (in seconds) that is allowed
143 143 between commits with identical user and log message in a
144 144 single changeset. When very large files were checked in as
145 145 part of a changeset then the default may not be long
146 146 enough.
147 147 --config convert.cvsps.mergeto='{{mergetobranch ([-\\w]+)}}'
148 148 Specify a regular expression to which commit log messages
149 149 are matched. If a match occurs, then the conversion
150 150 process will insert a dummy revision merging the branch on
151 151 which this log message occurs to the branch indicated in
152 152 the regex.
153 153 --config convert.cvsps.mergefrom='{{mergefrombranch ([-\\w]+)}}'
154 154 Specify a regular expression to which commit log messages
155 155 are matched. If a match occurs, then the conversion
156 156 process will add the most recent revision on the branch
157 157 indicated in the regex as the second parent of the
158 158 changeset.
159 159
160 160 The hgext/convert/cvsps wrapper script allows the builtin
161 161 changeset merging code to be run without doing a conversion. Its
162 162 parameters and output are similar to that of cvsps 2.1.
163 163
164 164 Subversion Source
165 165 -----------------
166 166
167 167 Subversion source detects classical trunk/branches/tags layouts.
168 168 By default, the supplied "svn://repo/path/" source URL is
169 169 converted as a single branch. If "svn://repo/path/trunk" exists it
170 170 replaces the default branch. If "svn://repo/path/branches" exists,
171 171 its subdirectories are listed as possible branches. If
172 172 "svn://repo/path/tags" exists, it is looked for tags referencing
173 173 converted branches. Default "trunk", "branches" and "tags" values
174 can be overriden with following options. Set them to paths
175 relative to the source URL, or leave them blank to disable
176 autodetection.
174 can be overridden with following options. Set them to paths
175 relative to the source URL, or leave them blank to disable auto
176 detection.
177 177
178 178 --config convert.svn.branches=branches (directory name)
179 179 specify the directory containing branches
180 180 --config convert.svn.tags=tags (directory name)
181 181 specify the directory containing tags
182 182 --config convert.svn.trunk=trunk (directory name)
183 183 specify the name of the trunk branch
184 184
185 185 Source history can be retrieved starting at a specific revision,
186 186 instead of being integrally converted. Only single branch
187 187 conversions are supported.
188 188
189 189 --config convert.svn.startrev=0 (svn revision number)
190 190 specify start Subversion revision.
191 191
192 192 Perforce Source
193 193 ---------------
194 194
195 195 The Perforce (P4) importer can be given a p4 depot path or a
196 196 client specification as source. It will convert all files in the
197 197 source to a flat Mercurial repository, ignoring labels, branches
198 198 and integrations. Note that when a depot path is given you then
199 199 usually should specify a target directory, because otherwise the
200 200 target may be named ...-hg.
201 201
202 202 It is possible to limit the amount of source history to be
203 203 converted by specifying an initial Perforce revision.
204 204
205 205 --config convert.p4.startrev=0 (perforce changelist number)
206 206 specify initial Perforce revision.
207 207
208 208
209 209 Mercurial Destination
210 210 ---------------------
211 211
212 212 --config convert.hg.clonebranches=False (boolean)
213 213 dispatch source branches in separate clones.
214 214 --config convert.hg.tagsbranch=default (branch name)
215 215 tag revisions branch name
216 216 --config convert.hg.usebranchnames=True (boolean)
217 217 preserve branch names
218 218
219 219 """
220 220 return convcmd.convert(ui, src, dest, revmapfile, **opts)
221 221
222 222 def debugsvnlog(ui, **opts):
223 223 return subversion.debugsvnlog(ui, **opts)
224 224
225 225 def debugcvsps(ui, *args, **opts):
226 226 '''create changeset information from CVS
227 227
228 228 This command is intended as a debugging tool for the CVS to
229 229 Mercurial converter, and can be used as a direct replacement for
230 230 cvsps.
231 231
232 232 Hg debugcvsps reads the CVS rlog for current directory (or any
233 233 named directory) in the CVS repository, and converts the log to a
234 234 series of changesets based on matching commit log entries and
235 235 dates.'''
236 236 return cvsps.debugcvsps(ui, *args, **opts)
237 237
238 238 commands.norepo += " convert debugsvnlog debugcvsps"
239 239
240 240 cmdtable = {
241 241 "convert":
242 242 (convert,
243 243 [('A', 'authors', '', _('username mapping filename')),
244 244 ('d', 'dest-type', '', _('destination repository type')),
245 245 ('', 'filemap', '', _('remap file names using contents of file')),
246 246 ('r', 'rev', '', _('import up to target revision REV')),
247 247 ('s', 'source-type', '', _('source repository type')),
248 248 ('', 'splicemap', '', _('splice synthesized history into place')),
249 249 ('', 'branchmap', '', _('change branch names while converting')),
250 250 ('', 'datesort', None, _('try to sort changesets by date'))],
251 251 _('hg convert [OPTION]... SOURCE [DEST [REVMAP]]')),
252 252 "debugsvnlog":
253 253 (debugsvnlog,
254 254 [],
255 255 'hg debugsvnlog'),
256 256 "debugcvsps":
257 257 (debugcvsps,
258 258 [
259 259 # Main options shared with cvsps-2.1
260 260 ('b', 'branches', [], _('only return changes on specified branches')),
261 261 ('p', 'prefix', '', _('prefix to remove from file names')),
262 262 ('r', 'revisions', [], _('only return changes after or between specified tags')),
263 263 ('u', 'update-cache', None, _("update cvs log cache")),
264 264 ('x', 'new-cache', None, _("create new cvs log cache")),
265 265 ('z', 'fuzz', 60, _('set commit time fuzz in seconds')),
266 266 ('', 'root', '', _('specify cvsroot')),
267 267 # Options specific to builtin cvsps
268 268 ('', 'parents', '', _('show parent changesets')),
269 269 ('', 'ancestors', '', _('show current changeset in ancestor branches')),
270 270 # Options that are ignored for compatibility with cvsps-2.1
271 271 ('A', 'cvs-direct', None, _('ignored for compatibility')),
272 272 ],
273 273 _('hg debugcvsps [OPTION]... [PATH]...')),
274 274 }
@@ -1,342 +1,342 b''
1 1 # gnuarch.py - GNU Arch support for the convert extension
2 2 #
3 3 # Copyright 2008, 2009 Aleix Conchillo Flaque <aleix@member.fsf.org>
4 4 # and others
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2, incorporated herein by reference.
8 8
9 9 from common import NoRepo, commandline, commit, converter_source
10 10 from mercurial.i18n import _
11 11 from mercurial import util
12 12 import os, shutil, tempfile, stat, locale
13 13 from email.Parser import Parser
14 14
15 15 class gnuarch_source(converter_source, commandline):
16 16
17 17 class gnuarch_rev:
18 18 def __init__(self, rev):
19 19 self.rev = rev
20 20 self.summary = ''
21 21 self.date = None
22 22 self.author = ''
23 23 self.continuationof = None
24 24 self.add_files = []
25 25 self.mod_files = []
26 26 self.del_files = []
27 27 self.ren_files = {}
28 28 self.ren_dirs = {}
29 29
30 30 def __init__(self, ui, path, rev=None):
31 31 super(gnuarch_source, self).__init__(ui, path, rev=rev)
32 32
33 33 if not os.path.exists(os.path.join(path, '{arch}')):
34 34 raise NoRepo(_("%s does not look like a GNU Arch repo") % path)
35 35
36 36 # Could use checktool, but we want to check for baz or tla.
37 37 self.execmd = None
38 38 if util.find_exe('baz'):
39 39 self.execmd = 'baz'
40 40 else:
41 41 if util.find_exe('tla'):
42 42 self.execmd = 'tla'
43 43 else:
44 44 raise util.Abort(_('cannot find a GNU Arch tool'))
45 45
46 46 commandline.__init__(self, ui, self.execmd)
47 47
48 48 self.path = os.path.realpath(path)
49 49 self.tmppath = None
50 50
51 51 self.treeversion = None
52 52 self.lastrev = None
53 53 self.changes = {}
54 54 self.parents = {}
55 55 self.tags = {}
56 56 self.modecache = {}
57 57 self.catlogparser = Parser()
58 58 self.locale = locale.getpreferredencoding()
59 59 self.archives = []
60 60
61 61 def before(self):
62 62 # Get registered archives
63 63 self.archives = [i.rstrip('\n')
64 64 for i in self.runlines0('archives', '-n')]
65 65
66 66 if self.execmd == 'tla':
67 67 output = self.run0('tree-version', self.path)
68 68 else:
69 69 output = self.run0('tree-version', '-d', self.path)
70 70 self.treeversion = output.strip()
71 71
72 72 # Get name of temporary directory
73 73 version = self.treeversion.split('/')
74 74 self.tmppath = os.path.join(tempfile.gettempdir(),
75 75 'hg-%s' % version[1])
76 76
77 77 # Generate parents dictionary
78 78 self.parents[None] = []
79 79 treeversion = self.treeversion
80 80 child = None
81 81 while treeversion:
82 82 self.ui.status(_('analyzing tree version %s...\n') % treeversion)
83 83
84 84 archive = treeversion.split('/')[0]
85 85 if archive not in self.archives:
86 86 self.ui.status(_('tree analysis stopped because it points to '
87 87 'an unregistered archive %s...\n') % archive)
88 88 break
89 89
90 90 # Get the complete list of revisions for that tree version
91 91 output, status = self.runlines('revisions', '-r', '-f', treeversion)
92 92 self.checkexit(status, 'failed retrieveing revisions for %s' % treeversion)
93 93
94 94 # No new iteration unless a revision has a continuation-of header
95 95 treeversion = None
96 96
97 97 for l in output:
98 98 rev = l.strip()
99 99 self.changes[rev] = self.gnuarch_rev(rev)
100 100 self.parents[rev] = []
101 101
102 102 # Read author, date and summary
103 103 catlog, status = self.run('cat-log', '-d', self.path, rev)
104 104 if status:
105 105 catlog = self.run0('cat-archive-log', rev)
106 106 self._parsecatlog(catlog, rev)
107 107
108 108 # Populate the parents map
109 109 self.parents[child].append(rev)
110 110
111 111 # Keep track of the current revision as the child of the next
112 112 # revision scanned
113 113 child = rev
114 114
115 115 # Check if we have to follow the usual incremental history
116 116 # or if we have to 'jump' to a different treeversion given
117 117 # by the continuation-of header.
118 118 if self.changes[rev].continuationof:
119 119 treeversion = '--'.join(self.changes[rev].continuationof.split('--')[:-1])
120 120 break
121 121
122 122 # If we reached a base-0 revision w/o any continuation-of
123 123 # header, it means the tree history ends here.
124 124 if rev[-6:] == 'base-0':
125 125 break
126 126
127 127 def after(self):
128 128 self.ui.debug(_('cleaning up %s\n') % self.tmppath)
129 129 shutil.rmtree(self.tmppath, ignore_errors=True)
130 130
131 131 def getheads(self):
132 132 return self.parents[None]
133 133
134 134 def getfile(self, name, rev):
135 135 if rev != self.lastrev:
136 136 raise util.Abort(_('internal calling inconsistency'))
137 137
138 138 # Raise IOError if necessary (i.e. deleted files).
139 139 if not os.path.exists(os.path.join(self.tmppath, name)):
140 140 raise IOError
141 141
142 142 data, mode = self._getfile(name, rev)
143 143 self.modecache[(name, rev)] = mode
144 144
145 145 return data
146 146
147 147 def getmode(self, name, rev):
148 148 return self.modecache[(name, rev)]
149 149
150 150 def getchanges(self, rev):
151 151 self.modecache = {}
152 152 self._update(rev)
153 153 changes = []
154 154 copies = {}
155 155
156 156 for f in self.changes[rev].add_files:
157 157 changes.append((f, rev))
158 158
159 159 for f in self.changes[rev].mod_files:
160 160 changes.append((f, rev))
161 161
162 162 for f in self.changes[rev].del_files:
163 163 changes.append((f, rev))
164 164
165 165 for src in self.changes[rev].ren_files:
166 166 to = self.changes[rev].ren_files[src]
167 167 changes.append((src, rev))
168 168 changes.append((to, rev))
169 169 copies[to] = src
170 170
171 171 for src in self.changes[rev].ren_dirs:
172 172 to = self.changes[rev].ren_dirs[src]
173 173 chgs, cps = self._rendirchanges(src, to);
174 174 changes += [(f, rev) for f in chgs]
175 175 copies.update(cps)
176 176
177 177 self.lastrev = rev
178 178 return sorted(set(changes)), copies
179 179
180 180 def getcommit(self, rev):
181 181 changes = self.changes[rev]
182 182 return commit(author=changes.author, date=changes.date,
183 183 desc=changes.summary, parents=self.parents[rev], rev=rev)
184 184
185 185 def gettags(self):
186 186 return self.tags
187 187
188 188 def _execute(self, cmd, *args, **kwargs):
189 189 cmdline = [self.execmd, cmd]
190 190 cmdline += args
191 191 cmdline = [util.shellquote(arg) for arg in cmdline]
192 192 cmdline += ['>', util.nulldev, '2>', util.nulldev]
193 193 cmdline = util.quotecommand(' '.join(cmdline))
194 194 self.ui.debug(cmdline, '\n')
195 195 return os.system(cmdline)
196 196
197 197 def _update(self, rev):
198 198 self.ui.debug(_('applying revision %s...\n') % rev)
199 199 changeset, status = self.runlines('replay', '-d', self.tmppath,
200 200 rev)
201 201 if status:
202 202 # Something went wrong while merging (baz or tla
203 203 # issue?), get latest revision and try from there
204 204 shutil.rmtree(self.tmppath, ignore_errors=True)
205 205 self._obtainrevision(rev)
206 206 else:
207 207 old_rev = self.parents[rev][0]
208 208 self.ui.debug(_('computing changeset between %s and %s...\n')
209 209 % (old_rev, rev))
210 210 self._parsechangeset(changeset, rev)
211 211
212 212 def _getfile(self, name, rev):
213 213 mode = os.lstat(os.path.join(self.tmppath, name)).st_mode
214 214 if stat.S_ISLNK(mode):
215 215 data = os.readlink(os.path.join(self.tmppath, name))
216 216 mode = mode and 'l' or ''
217 217 else:
218 218 data = open(os.path.join(self.tmppath, name), 'rb').read()
219 219 mode = (mode & 0111) and 'x' or ''
220 220 return data, mode
221 221
222 222 def _exclude(self, name):
223 223 exclude = [ '{arch}', '.arch-ids', '.arch-inventory' ]
224 224 for exc in exclude:
225 225 if name.find(exc) != -1:
226 226 return True
227 227 return False
228 228
229 229 def _readcontents(self, path):
230 230 files = []
231 231 contents = os.listdir(path)
232 232 while len(contents) > 0:
233 233 c = contents.pop()
234 234 p = os.path.join(path, c)
235 235 # os.walk could be used, but here we avoid internal GNU
236 236 # Arch files and directories, thus saving a lot time.
237 237 if not self._exclude(p):
238 238 if os.path.isdir(p):
239 239 contents += [os.path.join(c, f) for f in os.listdir(p)]
240 240 else:
241 241 files.append(c)
242 242 return files
243 243
244 244 def _rendirchanges(self, src, dest):
245 245 changes = []
246 246 copies = {}
247 247 files = self._readcontents(os.path.join(self.tmppath, dest))
248 248 for f in files:
249 249 s = os.path.join(src, f)
250 250 d = os.path.join(dest, f)
251 251 changes.append(s)
252 252 changes.append(d)
253 253 copies[d] = s
254 254 return changes, copies
255 255
256 256 def _obtainrevision(self, rev):
257 257 self.ui.debug(_('obtaining revision %s...\n') % rev)
258 258 output = self._execute('get', rev, self.tmppath)
259 259 self.checkexit(output)
260 self.ui.debug(_('analysing revision %s...\n') % rev)
260 self.ui.debug(_('analyzing revision %s...\n') % rev)
261 261 files = self._readcontents(self.tmppath)
262 262 self.changes[rev].add_files += files
263 263
264 264 def _stripbasepath(self, path):
265 265 if path.startswith('./'):
266 266 return path[2:]
267 267 return path
268 268
269 269 def _parsecatlog(self, data, rev):
270 270 try:
271 271 catlog = self.catlogparser.parsestr(data)
272 272
273 273 # Commit date
274 274 self.changes[rev].date = util.datestr(
275 275 util.strdate(catlog['Standard-date'],
276 276 '%Y-%m-%d %H:%M:%S'))
277 277
278 278 # Commit author
279 279 self.changes[rev].author = self.recode(catlog['Creator'])
280 280
281 281 # Commit description
282 282 self.changes[rev].summary = '\n\n'.join((catlog['Summary'],
283 283 catlog.get_payload()))
284 284 self.changes[rev].summary = self.recode(self.changes[rev].summary)
285 285
286 286 # Commit revision origin when dealing with a branch or tag
287 287 if catlog.has_key('Continuation-of'):
288 288 self.changes[rev].continuationof = self.recode(catlog['Continuation-of'])
289 289 except Exception:
290 290 raise util.Abort(_('could not parse cat-log of %s') % rev)
291 291
292 292 def _parsechangeset(self, data, rev):
293 293 for l in data:
294 294 l = l.strip()
295 295 # Added file (ignore added directory)
296 296 if l.startswith('A') and not l.startswith('A/'):
297 297 file = self._stripbasepath(l[1:].strip())
298 298 if not self._exclude(file):
299 299 self.changes[rev].add_files.append(file)
300 300 # Deleted file (ignore deleted directory)
301 301 elif l.startswith('D') and not l.startswith('D/'):
302 302 file = self._stripbasepath(l[1:].strip())
303 303 if not self._exclude(file):
304 304 self.changes[rev].del_files.append(file)
305 305 # Modified binary file
306 306 elif l.startswith('Mb'):
307 307 file = self._stripbasepath(l[2:].strip())
308 308 if not self._exclude(file):
309 309 self.changes[rev].mod_files.append(file)
310 310 # Modified link
311 311 elif l.startswith('M->'):
312 312 file = self._stripbasepath(l[3:].strip())
313 313 if not self._exclude(file):
314 314 self.changes[rev].mod_files.append(file)
315 315 # Modified file
316 316 elif l.startswith('M'):
317 317 file = self._stripbasepath(l[1:].strip())
318 318 if not self._exclude(file):
319 319 self.changes[rev].mod_files.append(file)
320 320 # Renamed file (or link)
321 321 elif l.startswith('=>'):
322 322 files = l[2:].strip().split(' ')
323 323 if len(files) == 1:
324 324 files = l[2:].strip().split('\t')
325 325 src = self._stripbasepath(files[0])
326 326 dst = self._stripbasepath(files[1])
327 327 if not self._exclude(src) and not self._exclude(dst):
328 328 self.changes[rev].ren_files[src] = dst
329 329 # Conversion from file to link or from link to file (modified)
330 330 elif l.startswith('ch'):
331 331 file = self._stripbasepath(l[2:].strip())
332 332 if not self._exclude(file):
333 333 self.changes[rev].mod_files.append(file)
334 334 # Renamed directory
335 335 elif l.startswith('/>'):
336 336 dirs = l[2:].strip().split(' ')
337 337 if len(dirs) == 1:
338 338 dirs = l[2:].strip().split('\t')
339 339 src = self._stripbasepath(dirs[0])
340 340 dst = self._stripbasepath(dirs[1])
341 341 if not self._exclude(src) and not self._exclude(dst):
342 342 self.changes[rev].ren_dirs[src] = dst
@@ -1,233 +1,233 b''
1 1 # extdiff.py - external diff program support for mercurial
2 2 #
3 3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2, incorporated herein by reference.
7 7
8 8 '''
9 9 The `extdiff' Mercurial extension allows you to use external programs
10 10 to compare revisions, or revision with working directory. The external diff
11 11 programs are called with a configurable set of options and two
12 12 non-option arguments: paths to directories containing snapshots of
13 13 files to compare.
14 14
15 15 To enable this extension:
16 16
17 17 [extensions]
18 18 hgext.extdiff =
19 19
20 20 The `extdiff' extension also allows to configure new diff commands, so
21 21 you do not need to type "hg extdiff -p kdiff3" always.
22 22
23 23 [extdiff]
24 24 # add new command that runs GNU diff(1) in 'context diff' mode
25 25 cdiff = gdiff -Nprc5
26 26 ## or the old way:
27 27 #cmd.cdiff = gdiff
28 28 #opts.cdiff = -Nprc5
29 29
30 30 # add new command called vdiff, runs kdiff3
31 31 vdiff = kdiff3
32 32
33 33 # add new command called meld, runs meld (no need to name twice)
34 34 meld =
35 35
36 36 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
37 37 # (see http://www.vim.org/scripts/script.php?script_id=102)
38 # Non english user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
38 # Non English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
39 39 # your .vimrc
40 40 vimdiff = gvim -f '+next' '+execute "DirDiff" argv(0) argv(1)'
41 41
42 42 You can use -I/-X and list of file or directory names like normal "hg
43 43 diff" command. The `extdiff' extension makes snapshots of only needed
44 44 files, so running the external diff program will actually be pretty
45 45 fast (at least faster than having to compare the entire tree).
46 46 '''
47 47
48 48 from mercurial.i18n import _
49 49 from mercurial.node import short
50 50 from mercurial import cmdutil, util, commands
51 51 import os, shlex, shutil, tempfile
52 52
53 53 def snapshot(ui, repo, files, node, tmproot):
54 54 '''snapshot files as of some revision
55 55 if not using snapshot, -I/-X does not work and recursive diff
56 56 in tools like kdiff3 and meld displays too many files.'''
57 57 dirname = os.path.basename(repo.root)
58 58 if dirname == "":
59 59 dirname = "root"
60 60 if node is not None:
61 61 dirname = '%s.%s' % (dirname, short(node))
62 62 base = os.path.join(tmproot, dirname)
63 63 os.mkdir(base)
64 64 if node is not None:
65 65 ui.note(_('making snapshot of %d files from rev %s\n') %
66 66 (len(files), short(node)))
67 67 else:
68 68 ui.note(_('making snapshot of %d files from working directory\n') %
69 69 (len(files)))
70 70 wopener = util.opener(base)
71 71 fns_and_mtime = []
72 72 ctx = repo[node]
73 73 for fn in files:
74 74 wfn = util.pconvert(fn)
75 75 if not wfn in ctx:
76 76 # skipping new file after a merge ?
77 77 continue
78 78 ui.note(' %s\n' % wfn)
79 79 dest = os.path.join(base, wfn)
80 80 fctx = ctx[wfn]
81 81 data = repo.wwritedata(wfn, fctx.data())
82 82 if 'l' in fctx.flags():
83 83 wopener.symlink(data, wfn)
84 84 else:
85 85 wopener(wfn, 'w').write(data)
86 86 if 'x' in fctx.flags():
87 87 util.set_flags(dest, False, True)
88 88 if node is None:
89 89 fns_and_mtime.append((dest, repo.wjoin(fn), os.path.getmtime(dest)))
90 90 return dirname, fns_and_mtime
91 91
92 92 def dodiff(ui, repo, diffcmd, diffopts, pats, opts):
93 93 '''Do the actuall diff:
94 94
95 95 - copy to a temp structure if diffing 2 internal revisions
96 96 - copy to a temp structure if diffing working revision with
97 97 another one and more than 1 file is changed
98 98 - just invoke the diff for a single file in the working dir
99 99 '''
100 100
101 101 revs = opts.get('rev')
102 102 change = opts.get('change')
103 103
104 104 if revs and change:
105 105 msg = _('cannot specify --rev and --change at the same time')
106 106 raise util.Abort(msg)
107 107 elif change:
108 108 node2 = repo.lookup(change)
109 109 node1 = repo[node2].parents()[0].node()
110 110 else:
111 111 node1, node2 = cmdutil.revpair(repo, revs)
112 112
113 113 matcher = cmdutil.match(repo, pats, opts)
114 114 modified, added, removed = repo.status(node1, node2, matcher)[:3]
115 115 if not (modified or added or removed):
116 116 return 0
117 117
118 118 tmproot = tempfile.mkdtemp(prefix='extdiff.')
119 119 dir2root = ''
120 120 try:
121 121 # Always make a copy of node1
122 122 dir1 = snapshot(ui, repo, modified + removed, node1, tmproot)[0]
123 123 changes = len(modified) + len(removed) + len(added)
124 124
125 125 # If node2 in not the wc or there is >1 change, copy it
126 126 if node2 or changes > 1:
127 127 dir2, fns_and_mtime = snapshot(ui, repo, modified + added, node2, tmproot)
128 128 else:
129 129 # This lets the diff tool open the changed file directly
130 130 dir2 = ''
131 131 dir2root = repo.root
132 132 fns_and_mtime = []
133 133
134 134 # If only one change, diff the files instead of the directories
135 135 if changes == 1 :
136 136 if len(modified):
137 137 dir1 = os.path.join(dir1, util.localpath(modified[0]))
138 138 dir2 = os.path.join(dir2root, dir2, util.localpath(modified[0]))
139 139 elif len(removed) :
140 140 dir1 = os.path.join(dir1, util.localpath(removed[0]))
141 141 dir2 = os.devnull
142 142 else:
143 143 dir1 = os.devnull
144 144 dir2 = os.path.join(dir2root, dir2, util.localpath(added[0]))
145 145
146 146 cmdline = ('%s %s %s %s' %
147 147 (util.shellquote(diffcmd), ' '.join(diffopts),
148 148 util.shellquote(dir1), util.shellquote(dir2)))
149 149 ui.debug(_('running %r in %s\n') % (cmdline, tmproot))
150 150 util.system(cmdline, cwd=tmproot)
151 151
152 152 for copy_fn, working_fn, mtime in fns_and_mtime:
153 153 if os.path.getmtime(copy_fn) != mtime:
154 154 ui.debug(_('file changed while diffing. '
155 155 'Overwriting: %s (src: %s)\n') % (working_fn, copy_fn))
156 156 util.copyfile(copy_fn, working_fn)
157 157
158 158 return 1
159 159 finally:
160 160 ui.note(_('cleaning up temp directory\n'))
161 161 shutil.rmtree(tmproot)
162 162
163 163 def extdiff(ui, repo, *pats, **opts):
164 164 '''use external program to diff repository (or selected files)
165 165
166 166 Show differences between revisions for the specified files, using
167 167 an external program. The default program used is diff, with
168 168 default options "-Npru".
169 169
170 170 To select a different program, use the -p/--program option. The
171 171 program will be passed the names of two directories to compare. To
172 172 pass additional options to the program, use -o/--option. These
173 173 will be passed before the names of the directories to compare.
174 174
175 175 When two revision arguments are given, then changes are shown
176 176 between those revisions. If only one revision is specified then
177 177 that revision is compared to the working directory, and, when no
178 178 revisions are specified, the working directory files are compared
179 179 to its parent.'''
180 180 program = opts['program'] or 'diff'
181 181 if opts['program']:
182 182 option = opts['option']
183 183 else:
184 184 option = opts['option'] or ['-Npru']
185 185 return dodiff(ui, repo, program, option, pats, opts)
186 186
187 187 cmdtable = {
188 188 "extdiff":
189 189 (extdiff,
190 190 [('p', 'program', '', _('comparison program to run')),
191 191 ('o', 'option', [], _('pass option to comparison program')),
192 192 ('r', 'rev', [], _('revision')),
193 193 ('c', 'change', '', _('change made by revision')),
194 194 ] + commands.walkopts,
195 195 _('hg extdiff [OPT]... [FILE]...')),
196 196 }
197 197
198 198 def uisetup(ui):
199 199 for cmd, path in ui.configitems('extdiff'):
200 200 if cmd.startswith('cmd.'):
201 201 cmd = cmd[4:]
202 202 if not path: path = cmd
203 203 diffopts = ui.config('extdiff', 'opts.' + cmd, '')
204 204 diffopts = diffopts and [diffopts] or []
205 205 elif cmd.startswith('opts.'):
206 206 continue
207 207 else:
208 208 # command = path opts
209 209 if path:
210 210 diffopts = shlex.split(path)
211 211 path = diffopts.pop(0)
212 212 else:
213 213 path, diffopts = cmd, []
214 214 def save(cmd, path, diffopts):
215 215 '''use closure to save diff command to use'''
216 216 def mydiff(ui, repo, *pats, **opts):
217 217 return dodiff(ui, repo, path, diffopts, pats, opts)
218 218 mydiff.__doc__ = '''use %(path)s to diff repository (or selected files)
219 219
220 220 Show differences between revisions for the specified
221 221 files, using the %(path)s program.
222 222
223 223 When two revision arguments are given, then changes are
224 224 shown between those revisions. If only one revision is
225 225 specified then that revision is compared to the working
226 226 directory, and, when no revisions are specified, the
227 227 working directory files are compared to its parent.''' % {
228 228 'path': util.uirepr(path),
229 229 }
230 230 return mydiff
231 231 cmdtable[cmd] = (save(cmd, path, diffopts),
232 232 cmdtable['extdiff'][1][1:],
233 233 _('hg %s [OPTION]... [FILE]...') % cmd)
@@ -1,534 +1,534 b''
1 1 # keyword.py - $Keyword$ expansion for Mercurial
2 2 #
3 3 # Copyright 2007, 2008 Christian Ebert <blacktrash@gmx.net>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2, incorporated herein by reference.
7 7 #
8 8 # $Id$
9 9 #
10 10 # Keyword expansion hack against the grain of a DSCM
11 11 #
12 12 # There are many good reasons why this is not needed in a distributed
13 13 # SCM, still it may be useful in very small projects based on single
14 14 # files (like LaTeX packages), that are mostly addressed to an
15 15 # audience not running a version control system.
16 16 #
17 17 # For in-depth discussion refer to
18 18 # <http://www.selenic.com/mercurial/wiki/index.cgi/KeywordPlan>.
19 19 #
20 20 # Keyword expansion is based on Mercurial's changeset template mappings.
21 21 #
22 22 # Binary files are not touched.
23 23 #
24 24 # Setup in hgrc:
25 25 #
26 26 # [extensions]
27 27 # # enable extension
28 28 # hgext.keyword =
29 29 #
30 30 # Files to act upon/ignore are specified in the [keyword] section.
31 31 # Customized keyword template mappings in the [keywordmaps] section.
32 32 #
33 33 # Run "hg help keyword" and "hg kwdemo" to get info on configuration.
34 34
35 35 '''keyword expansion in local repositories
36 36
37 37 This extension expands RCS/CVS-like or self-customized $Keywords$ in
38 38 tracked text files selected by your configuration.
39 39
40 40 Keywords are only expanded in local repositories and not stored in the
41 41 change history. The mechanism can be regarded as a convenience for the
42 42 current user or for archive distribution.
43 43
44 44 Configuration is done in the [keyword] and [keywordmaps] sections of
45 45 hgrc files.
46 46
47 47 Example:
48 48
49 49 [keyword]
50 50 # expand keywords in every python file except those matching "x*"
51 51 **.py =
52 52 x* = ignore
53 53
54 54 Note: the more specific you are in your filename patterns
55 55 the less you lose speed in huge repositories.
56 56
57 57 For [keywordmaps] template mapping and expansion demonstration and
58 58 control run "hg kwdemo".
59 59
60 60 An additional date template filter {date|utcdate} is provided.
61 61
62 62 The default template mappings (view with "hg kwdemo -d") can be
63 63 replaced with customized keywords and templates. Again, run "hg
64 64 kwdemo" to control the results of your config changes.
65 65
66 66 Before changing/disabling active keywords, run "hg kwshrink" to avoid
67 the risk of inadvertedly storing expanded keywords in the change
67 the risk of inadvertently storing expanded keywords in the change
68 68 history.
69 69
70 70 To force expansion after enabling it, or a configuration change, run
71 71 "hg kwexpand".
72 72
73 73 Also, when committing with the record extension or using mq's qrecord,
74 74 be aware that keywords cannot be updated. Again, run "hg kwexpand" on
75 75 the files in question to update keyword expansions after all changes
76 76 have been checked in.
77 77
78 78 Expansions spanning more than one line and incremental expansions,
79 79 like CVS' $Log$, are not supported. A keyword template map
80 80 "Log = {desc}" expands to the first line of the changeset description.
81 81 '''
82 82
83 83 from mercurial import commands, cmdutil, dispatch, filelog, revlog, extensions
84 84 from mercurial import patch, localrepo, templater, templatefilters, util, match
85 85 from mercurial.hgweb import webcommands
86 86 from mercurial.lock import release
87 87 from mercurial.node import nullid, hex
88 88 from mercurial.i18n import _
89 89 import re, shutil, tempfile, time
90 90
91 91 commands.optionalrepo += ' kwdemo'
92 92
93 93 # hg commands that do not act on keywords
94 94 nokwcommands = ('add addremove annotate bundle copy export grep incoming init'
95 95 ' log outgoing push rename rollback tip verify'
96 96 ' convert email glog')
97 97
98 98 # hg commands that trigger expansion only when writing to working dir,
99 99 # not when reading filelog, and unexpand when reading from working dir
100 100 restricted = 'merge record resolve qfold qimport qnew qpush qrefresh qrecord'
101 101
102 102 def utcdate(date):
103 103 '''Returns hgdate in cvs-like UTC format.'''
104 104 return time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(date[0]))
105 105
106 106 # make keyword tools accessible
107 107 kwtools = {'templater': None, 'hgcmd': '', 'inc': [], 'exc': ['.hg*']}
108 108
109 109
110 110 class kwtemplater(object):
111 111 '''
112 112 Sets up keyword templates, corresponding keyword regex, and
113 113 provides keyword substitution functions.
114 114 '''
115 115 templates = {
116 116 'Revision': '{node|short}',
117 117 'Author': '{author|user}',
118 118 'Date': '{date|utcdate}',
119 119 'RCSFile': '{file|basename},v',
120 120 'Source': '{root}/{file},v',
121 121 'Id': '{file|basename},v {node|short} {date|utcdate} {author|user}',
122 122 'Header': '{root}/{file},v {node|short} {date|utcdate} {author|user}',
123 123 }
124 124
125 125 def __init__(self, ui, repo):
126 126 self.ui = ui
127 127 self.repo = repo
128 128 self.match = match.match(repo.root, '', [],
129 129 kwtools['inc'], kwtools['exc'])
130 130 self.restrict = kwtools['hgcmd'] in restricted.split()
131 131
132 132 kwmaps = self.ui.configitems('keywordmaps')
133 133 if kwmaps: # override default templates
134 134 kwmaps = [(k, templater.parsestring(v, False))
135 135 for (k, v) in kwmaps]
136 136 self.templates = dict(kwmaps)
137 137 escaped = map(re.escape, self.templates.keys())
138 138 kwpat = r'\$(%s)(: [^$\n\r]*? )??\$' % '|'.join(escaped)
139 139 self.re_kw = re.compile(kwpat)
140 140
141 141 templatefilters.filters['utcdate'] = utcdate
142 142 self.ct = cmdutil.changeset_templater(self.ui, self.repo,
143 143 False, None, '', False)
144 144
145 145 def substitute(self, data, path, ctx, subfunc):
146 146 '''Replaces keywords in data with expanded template.'''
147 147 def kwsub(mobj):
148 148 kw = mobj.group(1)
149 149 self.ct.use_template(self.templates[kw])
150 150 self.ui.pushbuffer()
151 151 self.ct.show(ctx, root=self.repo.root, file=path)
152 152 ekw = templatefilters.firstline(self.ui.popbuffer())
153 153 return '$%s: %s $' % (kw, ekw)
154 154 return subfunc(kwsub, data)
155 155
156 156 def expand(self, path, node, data):
157 157 '''Returns data with keywords expanded.'''
158 158 if not self.restrict and self.match(path) and not util.binary(data):
159 159 ctx = self.repo.filectx(path, fileid=node).changectx()
160 160 return self.substitute(data, path, ctx, self.re_kw.sub)
161 161 return data
162 162
163 163 def iskwfile(self, path, flagfunc):
164 164 '''Returns true if path matches [keyword] pattern
165 165 and is not a symbolic link.
166 166 Caveat: localrepository._link fails on Windows.'''
167 167 return self.match(path) and not 'l' in flagfunc(path)
168 168
169 169 def overwrite(self, node, expand, files):
170 170 '''Overwrites selected files expanding/shrinking keywords.'''
171 171 ctx = self.repo[node]
172 172 mf = ctx.manifest()
173 173 if node is not None: # commit
174 174 files = [f for f in ctx.files() if f in mf]
175 175 notify = self.ui.debug
176 176 else: # kwexpand/kwshrink
177 177 notify = self.ui.note
178 178 candidates = [f for f in files if self.iskwfile(f, ctx.flags)]
179 179 if candidates:
180 180 self.restrict = True # do not expand when reading
181 181 msg = (expand and _('overwriting %s expanding keywords\n')
182 182 or _('overwriting %s shrinking keywords\n'))
183 183 for f in candidates:
184 184 fp = self.repo.file(f)
185 185 data = fp.read(mf[f])
186 186 if util.binary(data):
187 187 continue
188 188 if expand:
189 189 if node is None:
190 190 ctx = self.repo.filectx(f, fileid=mf[f]).changectx()
191 191 data, found = self.substitute(data, f, ctx,
192 192 self.re_kw.subn)
193 193 else:
194 194 found = self.re_kw.search(data)
195 195 if found:
196 196 notify(msg % f)
197 197 self.repo.wwrite(f, data, mf.flags(f))
198 198 self.repo.dirstate.normal(f)
199 199 self.restrict = False
200 200
201 201 def shrinktext(self, text):
202 202 '''Unconditionally removes all keyword substitutions from text.'''
203 203 return self.re_kw.sub(r'$\1$', text)
204 204
205 205 def shrink(self, fname, text):
206 206 '''Returns text with all keyword substitutions removed.'''
207 207 if self.match(fname) and not util.binary(text):
208 208 return self.shrinktext(text)
209 209 return text
210 210
211 211 def shrinklines(self, fname, lines):
212 212 '''Returns lines with keyword substitutions removed.'''
213 213 if self.match(fname):
214 214 text = ''.join(lines)
215 215 if not util.binary(text):
216 216 return self.shrinktext(text).splitlines(True)
217 217 return lines
218 218
219 219 def wread(self, fname, data):
220 220 '''If in restricted mode returns data read from wdir with
221 221 keyword substitutions removed.'''
222 222 return self.restrict and self.shrink(fname, data) or data
223 223
224 224 class kwfilelog(filelog.filelog):
225 225 '''
226 226 Subclass of filelog to hook into its read, add, cmp methods.
227 227 Keywords are "stored" unexpanded, and processed on reading.
228 228 '''
229 229 def __init__(self, opener, kwt, path):
230 230 super(kwfilelog, self).__init__(opener, path)
231 231 self.kwt = kwt
232 232 self.path = path
233 233
234 234 def read(self, node):
235 235 '''Expands keywords when reading filelog.'''
236 236 data = super(kwfilelog, self).read(node)
237 237 return self.kwt.expand(self.path, node, data)
238 238
239 239 def add(self, text, meta, tr, link, p1=None, p2=None):
240 240 '''Removes keyword substitutions when adding to filelog.'''
241 241 text = self.kwt.shrink(self.path, text)
242 242 return super(kwfilelog, self).add(text, meta, tr, link, p1, p2)
243 243
244 244 def cmp(self, node, text):
245 245 '''Removes keyword substitutions for comparison.'''
246 246 text = self.kwt.shrink(self.path, text)
247 247 if self.renamed(node):
248 248 t2 = super(kwfilelog, self).read(node)
249 249 return t2 != text
250 250 return revlog.revlog.cmp(self, node, text)
251 251
252 252 def _status(ui, repo, kwt, unknown, *pats, **opts):
253 253 '''Bails out if [keyword] configuration is not active.
254 254 Returns status of working directory.'''
255 255 if kwt:
256 256 match = cmdutil.match(repo, pats, opts)
257 257 return repo.status(match=match, unknown=unknown, clean=True)
258 258 if ui.configitems('keyword'):
259 259 raise util.Abort(_('[keyword] patterns cannot match'))
260 260 raise util.Abort(_('no [keyword] patterns configured'))
261 261
262 262 def _kwfwrite(ui, repo, expand, *pats, **opts):
263 263 '''Selects files and passes them to kwtemplater.overwrite.'''
264 264 if repo.dirstate.parents()[1] != nullid:
265 265 raise util.Abort(_('outstanding uncommitted merge'))
266 266 kwt = kwtools['templater']
267 267 status = _status(ui, repo, kwt, False, *pats, **opts)
268 268 modified, added, removed, deleted = status[:4]
269 269 if modified or added or removed or deleted:
270 270 raise util.Abort(_('outstanding uncommitted changes'))
271 271 wlock = lock = None
272 272 try:
273 273 wlock = repo.wlock()
274 274 lock = repo.lock()
275 275 kwt.overwrite(None, expand, status[6])
276 276 finally:
277 277 release(lock, wlock)
278 278
279 279 def demo(ui, repo, *args, **opts):
280 280 '''print [keywordmaps] configuration and an expansion example
281 281
282 282 Show current, custom, or default keyword template maps and their
283 283 expansion.
284 284
285 285 Extend current configuration by specifying maps as arguments and
286 286 optionally by reading from an additional hgrc file.
287 287
288 288 Override current keyword template maps with "default" option.
289 289 '''
290 290 def demostatus(stat):
291 291 ui.status(_('\n\t%s\n') % stat)
292 292
293 293 def demoitems(section, items):
294 294 ui.write('[%s]\n' % section)
295 295 for k, v in items:
296 296 ui.write('%s = %s\n' % (k, v))
297 297
298 298 msg = 'hg keyword config and expansion example'
299 299 kwstatus = 'current'
300 300 fn = 'demo.txt'
301 301 branchname = 'demobranch'
302 302 tmpdir = tempfile.mkdtemp('', 'kwdemo.')
303 303 ui.note(_('creating temporary repository at %s\n') % tmpdir)
304 304 repo = localrepo.localrepository(ui, tmpdir, True)
305 305 ui.setconfig('keyword', fn, '')
306 306 if args or opts.get('rcfile'):
307 307 kwstatus = 'custom'
308 308 if opts.get('rcfile'):
309 309 ui.readconfig(opts.get('rcfile'))
310 310 if opts.get('default'):
311 311 kwstatus = 'default'
312 312 kwmaps = kwtemplater.templates
313 313 if ui.configitems('keywordmaps'):
314 314 # override maps from optional rcfile
315 315 for k, v in kwmaps.iteritems():
316 316 ui.setconfig('keywordmaps', k, v)
317 317 elif args:
318 318 # simulate hgrc parsing
319 319 rcmaps = ['[keywordmaps]\n'] + [a + '\n' for a in args]
320 320 fp = repo.opener('hgrc', 'w')
321 321 fp.writelines(rcmaps)
322 322 fp.close()
323 323 ui.readconfig(repo.join('hgrc'))
324 324 if not opts.get('default'):
325 325 kwmaps = dict(ui.configitems('keywordmaps')) or kwtemplater.templates
326 326 uisetup(ui)
327 327 reposetup(ui, repo)
328 328 for k, v in ui.configitems('extensions'):
329 329 if k.endswith('keyword'):
330 330 extension = '%s = %s' % (k, v)
331 331 break
332 332 demostatus('config using %s keyword template maps' % kwstatus)
333 333 ui.write('[extensions]\n%s\n' % extension)
334 334 demoitems('keyword', ui.configitems('keyword'))
335 335 demoitems('keywordmaps', kwmaps.iteritems())
336 336 keywords = '$' + '$\n$'.join(kwmaps.keys()) + '$\n'
337 337 repo.wopener(fn, 'w').write(keywords)
338 338 repo.add([fn])
339 339 path = repo.wjoin(fn)
340 340 ui.note(_('\n%s keywords written to %s:\n') % (kwstatus, path))
341 341 ui.note(keywords)
342 342 ui.note('\nhg -R "%s" branch "%s"\n' % (tmpdir, branchname))
343 343 # silence branch command if not verbose
344 344 quiet = ui.quiet
345 345 ui.quiet = not ui.verbose
346 346 commands.branch(ui, repo, branchname)
347 347 ui.quiet = quiet
348 348 for name, cmd in ui.configitems('hooks'):
349 349 if name.split('.', 1)[0].find('commit') > -1:
350 350 repo.ui.setconfig('hooks', name, '')
351 351 ui.note(_('unhooked all commit hooks\n'))
352 352 ui.note('hg -R "%s" ci -m "%s"\n' % (tmpdir, msg))
353 353 repo.commit(text=msg)
354 354 fmt = ui.verbose and ' in %s' % path or ''
355 355 demostatus('%s keywords expanded%s' % (kwstatus, fmt))
356 356 ui.write(repo.wread(fn))
357 357 ui.debug(_('\nremoving temporary repository %s\n') % tmpdir)
358 358 shutil.rmtree(tmpdir, ignore_errors=True)
359 359
360 360 def expand(ui, repo, *pats, **opts):
361 361 '''expand keywords in working directory
362 362
363 363 Run after (re)enabling keyword expansion.
364 364
365 365 kwexpand refuses to run if given files contain local changes.
366 366 '''
367 367 # 3rd argument sets expansion to True
368 368 _kwfwrite(ui, repo, True, *pats, **opts)
369 369
370 370 def files(ui, repo, *pats, **opts):
371 371 '''print files currently configured for keyword expansion
372 372
373 373 Crosscheck which files in working directory are potential targets
374 374 for keyword expansion. That is, files matched by [keyword] config
375 375 patterns but not symlinks.
376 376 '''
377 377 kwt = kwtools['templater']
378 378 status = _status(ui, repo, kwt, opts.get('untracked'), *pats, **opts)
379 379 modified, added, removed, deleted, unknown, ignored, clean = status
380 380 files = sorted(modified + added + clean + unknown)
381 381 wctx = repo[None]
382 382 kwfiles = [f for f in files if kwt.iskwfile(f, wctx.flags)]
383 383 cwd = pats and repo.getcwd() or ''
384 384 kwfstats = not opts.get('ignore') and (('K', kwfiles),) or ()
385 385 if opts.get('all') or opts.get('ignore'):
386 386 kwfstats += (('I', [f for f in files if f not in kwfiles]),)
387 387 for char, filenames in kwfstats:
388 388 fmt = (opts.get('all') or ui.verbose) and '%s %%s\n' % char or '%s\n'
389 389 for f in filenames:
390 390 ui.write(fmt % repo.pathto(f, cwd))
391 391
392 392 def shrink(ui, repo, *pats, **opts):
393 393 '''revert expanded keywords in working directory
394 394
395 395 Run before changing/disabling active keywords or if you experience
396 396 problems with "hg import" or "hg merge".
397 397
398 398 kwshrink refuses to run if given files contain local changes.
399 399 '''
400 400 # 3rd argument sets expansion to False
401 401 _kwfwrite(ui, repo, False, *pats, **opts)
402 402
403 403
404 404 def uisetup(ui):
405 405 '''Collects [keyword] config in kwtools.
406 406 Monkeypatches dispatch._parse if needed.'''
407 407
408 408 for pat, opt in ui.configitems('keyword'):
409 409 if opt != 'ignore':
410 410 kwtools['inc'].append(pat)
411 411 else:
412 412 kwtools['exc'].append(pat)
413 413
414 414 if kwtools['inc']:
415 415 def kwdispatch_parse(orig, ui, args):
416 416 '''Monkeypatch dispatch._parse to obtain running hg command.'''
417 417 cmd, func, args, options, cmdoptions = orig(ui, args)
418 418 kwtools['hgcmd'] = cmd
419 419 return cmd, func, args, options, cmdoptions
420 420
421 421 extensions.wrapfunction(dispatch, '_parse', kwdispatch_parse)
422 422
423 423 def reposetup(ui, repo):
424 424 '''Sets up repo as kwrepo for keyword substitution.
425 425 Overrides file method to return kwfilelog instead of filelog
426 426 if file matches user configuration.
427 427 Wraps commit to overwrite configured files with updated
428 428 keyword substitutions.
429 429 Monkeypatches patch and webcommands.'''
430 430
431 431 try:
432 432 if (not repo.local() or not kwtools['inc']
433 433 or kwtools['hgcmd'] in nokwcommands.split()
434 434 or '.hg' in util.splitpath(repo.root)
435 435 or repo._url.startswith('bundle:')):
436 436 return
437 437 except AttributeError:
438 438 pass
439 439
440 440 kwtools['templater'] = kwt = kwtemplater(ui, repo)
441 441
442 442 class kwrepo(repo.__class__):
443 443 def file(self, f):
444 444 if f[0] == '/':
445 445 f = f[1:]
446 446 return kwfilelog(self.sopener, kwt, f)
447 447
448 448 def wread(self, filename):
449 449 data = super(kwrepo, self).wread(filename)
450 450 return kwt.wread(filename, data)
451 451
452 452 def commit(self, files=None, text='', user=None, date=None,
453 453 match=None, force=False, editor=None, extra={}):
454 454 wlock = lock = None
455 455 _p1 = _p2 = None
456 456 try:
457 457 wlock = self.wlock()
458 458 lock = self.lock()
459 459 # store and postpone commit hooks
460 460 commithooks = {}
461 461 for name, cmd in ui.configitems('hooks'):
462 462 if name.split('.', 1)[0] == 'commit':
463 463 commithooks[name] = cmd
464 464 ui.setconfig('hooks', name, None)
465 465 if commithooks:
466 466 # store parents for commit hook environment
467 467 _p1, _p2 = repo.dirstate.parents()
468 468 _p1 = hex(_p1)
469 469 if _p2 == nullid:
470 470 _p2 = ''
471 471 else:
472 472 _p2 = hex(_p2)
473 473
474 474 n = super(kwrepo, self).commit(files, text, user, date, match,
475 475 force, editor, extra)
476 476
477 477 # restore commit hooks
478 478 for name, cmd in commithooks.iteritems():
479 479 ui.setconfig('hooks', name, cmd)
480 480 if n is not None:
481 481 kwt.overwrite(n, True, None)
482 482 repo.hook('commit', node=n, parent1=_p1, parent2=_p2)
483 483 return n
484 484 finally:
485 485 release(lock, wlock)
486 486
487 487 # monkeypatches
488 488 def kwpatchfile_init(orig, self, ui, fname, opener, missing=False):
489 489 '''Monkeypatch/wrap patch.patchfile.__init__ to avoid
490 490 rejects or conflicts due to expanded keywords in working dir.'''
491 491 orig(self, ui, fname, opener, missing)
492 492 # shrink keywords read from working dir
493 493 self.lines = kwt.shrinklines(self.fname, self.lines)
494 494
495 495 def kw_diff(orig, repo, node1=None, node2=None, match=None, changes=None,
496 496 opts=None):
497 497 '''Monkeypatch patch.diff to avoid expansion except when
498 498 comparing against working dir.'''
499 499 if node2 is not None:
500 500 kwt.match = util.never
501 501 elif node1 is not None and node1 != repo['.'].node():
502 502 kwt.restrict = True
503 503 return orig(repo, node1, node2, match, changes, opts)
504 504
505 505 def kwweb_skip(orig, web, req, tmpl):
506 506 '''Wraps webcommands.x turning off keyword expansion.'''
507 507 kwt.match = util.never
508 508 return orig(web, req, tmpl)
509 509
510 510 repo.__class__ = kwrepo
511 511
512 512 extensions.wrapfunction(patch.patchfile, '__init__', kwpatchfile_init)
513 513 extensions.wrapfunction(patch, 'diff', kw_diff)
514 514 for c in 'annotate changeset rev filediff diff'.split():
515 515 extensions.wrapfunction(webcommands, c, kwweb_skip)
516 516
517 517 cmdtable = {
518 518 'kwdemo':
519 519 (demo,
520 520 [('d', 'default', None, _('show default keyword template maps')),
521 521 ('f', 'rcfile', [], _('read maps from rcfile'))],
522 522 _('hg kwdemo [-d] [-f RCFILE] [TEMPLATEMAP]...')),
523 523 'kwexpand': (expand, commands.walkopts,
524 524 _('hg kwexpand [OPTION]... [FILE]...')),
525 525 'kwfiles':
526 526 (files,
527 527 [('a', 'all', None, _('show keyword status flags of all files')),
528 528 ('i', 'ignore', None, _('show files excluded from expansion')),
529 529 ('u', 'untracked', None, _('additionally show untracked files')),
530 530 ] + commands.walkopts,
531 531 _('hg kwfiles [OPTION]... [FILE]...')),
532 532 'kwshrink': (shrink, commands.walkopts,
533 533 _('hg kwshrink [OPTION]... [FILE]...')),
534 534 }
@@ -1,126 +1,126 b''
1 1 # win32mbcs.py -- MBCS filename support for Mercurial
2 2 #
3 3 # Copyright (c) 2008 Shun-ichi Goto <shunichi.goto@gmail.com>
4 4 #
5 5 # Version: 0.2
6 6 # Author: Shun-ichi Goto <shunichi.goto@gmail.com>
7 7 #
8 8 # This software may be used and distributed according to the terms of the
9 9 # GNU General Public License version 2, incorporated herein by reference.
10 10 #
11 11
12 12 """allow to use MBCS path with problematic encoding.
13 13
14 14 Some MBCS encodings are not good for some path operations (i.e.
15 15 splitting path, case conversion, etc.) with its encoded bytes. We call
16 16 such a encoding (i.e. shift_jis and big5) as "problematic encoding".
17 17 This extension can be used to fix the issue with those encodings by
18 18 wrapping some functions to convert to Unicode string before path
19 19 operation.
20 20
21 This extension is usefull for:
21 This extension is useful for:
22 22 * Japanese Windows users using shift_jis encoding.
23 23 * Chinese Windows users using big5 encoding.
24 24 * All users who use a repository with one of problematic encodings on
25 25 case-insensitive file system.
26 26
27 27 This extension is not needed for:
28 28 * Any user who use only ASCII chars in path.
29 29 * Any user who do not use any of problematic encodings.
30 30
31 31 Note that there are some limitations on using this extension:
32 32 * You should use single encoding in one repository.
33 33 * You should set same encoding for the repository by locale or
34 34 HGENCODING.
35 35
36 36 To use this extension, enable the extension in .hg/hgrc or ~/.hgrc:
37 37
38 38 [extensions]
39 39 hgext.win32mbcs =
40 40
41 41 Path encoding conversion are done between Unicode and
42 42 encoding.encoding which is decided by mercurial from current locale
43 43 setting or HGENCODING.
44 44
45 45 """
46 46
47 47 import os
48 48 from mercurial.i18n import _
49 49 from mercurial import util, encoding
50 50
51 51 def decode(arg):
52 52 if isinstance(arg, str):
53 53 uarg = arg.decode(encoding.encoding)
54 54 if arg == uarg.encode(encoding.encoding):
55 55 return uarg
56 56 raise UnicodeError("Not local encoding")
57 57 elif isinstance(arg, tuple):
58 58 return tuple(map(decode, arg))
59 59 elif isinstance(arg, list):
60 60 return map(decode, arg)
61 61 return arg
62 62
63 63 def encode(arg):
64 64 if isinstance(arg, unicode):
65 65 return arg.encode(encoding.encoding)
66 66 elif isinstance(arg, tuple):
67 67 return tuple(map(encode, arg))
68 68 elif isinstance(arg, list):
69 69 return map(encode, arg)
70 70 return arg
71 71
72 72 def wrapper(func, args):
73 73 # check argument is unicode, then call original
74 74 for arg in args:
75 75 if isinstance(arg, unicode):
76 76 return func(*args)
77 77
78 78 try:
79 79 # convert arguments to unicode, call func, then convert back
80 80 return encode(func(*decode(args)))
81 81 except UnicodeError:
82 82 # If not encoded with encoding.encoding, report it then
83 83 # continue with calling original function.
84 84 raise util.Abort(_("[win32mbcs] filename conversion fail with"
85 85 " %s encoding\n") % (encoding.encoding))
86 86
87 87 def wrapname(name):
88 88 idx = name.rfind('.')
89 89 module = name[:idx]
90 90 name = name[idx+1:]
91 91 module = globals()[module]
92 92 func = getattr(module, name)
93 93 def f(*args):
94 94 return wrapper(func, args)
95 95 try:
96 96 f.__name__ = func.__name__ # fail with python23
97 97 except Exception:
98 98 pass
99 99 setattr(module, name, f)
100 100
101 101 # List of functions to be wrapped.
102 102 # NOTE: os.path.dirname() and os.path.basename() are safe because
103 103 # they use result of os.path.split()
104 104 funcs = '''os.path.join os.path.split os.path.splitext
105 105 os.path.splitunc os.path.normpath os.path.normcase os.makedirs
106 106 util.endswithsep util.splitpath util.checkcase util.fspath'''
107 107
108 108 # codec and alias names of sjis and big5 to be faked.
109 109 problematic_encodings = '''big5 big5-tw csbig5 big5hkscs big5-hkscs
110 110 hkscs cp932 932 ms932 mskanji ms-kanji shift_jis csshiftjis shiftjis
111 111 sjis s_jis shift_jis_2004 shiftjis2004 sjis_2004 sjis2004
112 112 shift_jisx0213 shiftjisx0213 sjisx0213 s_jisx0213'''
113 113
114 114 def reposetup(ui, repo):
115 115 # TODO: decide use of config section for this extension
116 116 if not os.path.supports_unicode_filenames:
117 117 ui.warn(_("[win32mbcs] cannot activate on this platform.\n"))
118 118 return
119 119
120 120 # fake is only for relevant environment.
121 121 if encoding.encoding.lower() in problematic_encodings.split():
122 122 for f in funcs.split():
123 123 wrapname(f)
124 124 ui.debug(_("[win32mbcs] activated with encoding: %s\n")
125 125 % encoding.encoding)
126 126
@@ -1,421 +1,421 b''
1 1 # help.py - help data for mercurial
2 2 #
3 3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2, incorporated herein by reference.
7 7
8 8 from i18n import _
9 9
10 10 helptable = (
11 11 (["dates"], _("Date Formats"),
12 12 _(r'''
13 13 Some commands allow the user to specify a date, e.g.:
14 14 * backout, commit, import, tag: Specify the commit date.
15 15 * log, revert, update: Select revision(s) by date.
16 16
17 17 Many date formats are valid. Here are some examples:
18 18
19 19 "Wed Dec 6 13:18:29 2006" (local timezone assumed)
20 20 "Dec 6 13:18 -0600" (year assumed, time offset provided)
21 21 "Dec 6 13:18 UTC" (UTC and GMT are aliases for +0000)
22 22 "Dec 6" (midnight)
23 23 "13:18" (today assumed)
24 24 "3:39" (3:39AM assumed)
25 25 "3:39pm" (15:39)
26 26 "2006-12-06 13:18:29" (ISO 8601 format)
27 27 "2006-12-6 13:18"
28 28 "2006-12-6"
29 29 "12-6"
30 30 "12/6"
31 31 "12/6/6" (Dec 6 2006)
32 32
33 33 Lastly, there is Mercurial's internal format:
34 34
35 35 "1165432709 0" (Wed Dec 6 13:18:29 2006 UTC)
36 36
37 37 This is the internal representation format for dates. unixtime is
38 38 the number of seconds since the epoch (1970-01-01 00:00 UTC).
39 39 offset is the offset of the local timezone, in seconds west of UTC
40 40 (negative if the timezone is east of UTC).
41 41
42 42 The log command also accepts date ranges:
43 43
44 44 "<{datetime}" - at or before a given date/time
45 45 ">{datetime}" - on or after a given date/time
46 46 "{datetime} to {datetime}" - a date range, inclusive
47 47 "-{days}" - within a given number of days of today
48 48 ''')),
49 49
50 50 (["patterns"], _("File Name Patterns"),
51 51 _(r'''
52 52 Mercurial accepts several notations for identifying one or more
53 53 files at a time.
54 54
55 55 By default, Mercurial treats filenames as shell-style extended
56 56 glob patterns.
57 57
58 58 Alternate pattern notations must be specified explicitly.
59 59
60 60 To use a plain path name without any pattern matching, start it
61 61 with "path:". These path names must completely match starting at
62 62 the current repository root.
63 63
64 64 To use an extended glob, start a name with "glob:". Globs are
65 65 rooted at the current directory; a glob such as "*.c" will only
66 66 match files in the current directory ending with ".c".
67 67
68 68 The supported glob syntax extensions are "**" to match any string
69 69 across path separators and "{a,b}" to mean "a or b".
70 70
71 71 To use a Perl/Python regular expression, start a name with "re:".
72 72 Regexp pattern matching is anchored at the root of the repository.
73 73
74 74 Plain examples:
75 75
76 76 path:foo/bar a name bar in a directory named foo in the root of
77 77 the repository
78 78 path:path:name a file or directory named "path:name"
79 79
80 80 Glob examples:
81 81
82 82 glob:*.c any name ending in ".c" in the current directory
83 83 *.c any name ending in ".c" in the current directory
84 84 **.c any name ending in ".c" in any subdirectory of the
85 85 current directory including itself.
86 86 foo/*.c any name ending in ".c" in the directory foo
87 87 foo/**.c any name ending in ".c" in any subdirectory of foo
88 88 including itself.
89 89
90 90 Regexp examples:
91 91
92 92 re:.*\.c$ any name ending in ".c", anywhere in the repository
93 93
94 94 ''')),
95 95
96 96 (['environment', 'env'], _('Environment Variables'),
97 97 _(r'''
98 98 HG::
99 99 Path to the 'hg' executable, automatically passed when running
100 100 hooks, extensions or external tools. If unset or empty, this is
101 101 the hg executable's name if it's frozen, or an executable named
102 102 'hg' (with %PATHEXT% [defaulting to COM/EXE/BAT/CMD] extensions on
103 103 Windows) is searched.
104 104
105 105 HGEDITOR::
106 106 This is the name of the editor to run when committing. See EDITOR.
107 107
108 108 (deprecated, use .hgrc)
109 109
110 110 HGENCODING::
111 111 This overrides the default locale setting detected by Mercurial.
112 112 This setting is used to convert data including usernames,
113 113 changeset descriptions, tag names, and branches. This setting can
114 114 be overridden with the --encoding command-line option.
115 115
116 116 HGENCODINGMODE::
117 117 This sets Mercurial's behavior for handling unknown characters
118 118 while transcoding user input. The default is "strict", which
119 119 causes Mercurial to abort if it can't map a character. Other
120 120 settings include "replace", which replaces unknown characters, and
121 121 "ignore", which drops them. This setting can be overridden with
122 122 the --encodingmode command-line option.
123 123
124 124 HGMERGE::
125 125 An executable to use for resolving merge conflicts. The program
126 126 will be executed with three arguments: local file, remote file,
127 127 ancestor file.
128 128
129 129 (deprecated, use .hgrc)
130 130
131 131 HGRCPATH::
132 132 A list of files or directories to search for hgrc files. Item
133 133 separator is ":" on Unix, ";" on Windows. If HGRCPATH is not set,
134 134 platform default search path is used. If empty, only the .hg/hgrc
135 135 from the current repository is read.
136 136
137 137 For each element in HGRCPATH:
138 138 * if it's a directory, all files ending with .rc are added
139 139 * otherwise, the file itself will be added
140 140
141 141 HGUSER::
142 142 This is the string used as the author of a commit. If not set,
143 143 available values will be considered in this order:
144 144
145 145 * HGUSER (deprecated)
146 146 * hgrc files from the HGRCPATH
147 147 * EMAIL
148 148 * interactive prompt
149 149 * LOGNAME (with '@hostname' appended)
150 150
151 151 (deprecated, use .hgrc)
152 152
153 153 EMAIL::
154 154 May be used as the author of a commit; see HGUSER.
155 155
156 156 LOGNAME::
157 157 May be used as the author of a commit; see HGUSER.
158 158
159 159 VISUAL::
160 160 This is the name of the editor to use when committing. See EDITOR.
161 161
162 162 EDITOR::
163 163 Sometimes Mercurial needs to open a text file in an editor for a
164 164 user to modify, for example when writing commit messages. The
165 165 editor it uses is determined by looking at the environment
166 166 variables HGEDITOR, VISUAL and EDITOR, in that order. The first
167 167 non-empty one is chosen. If all of them are empty, the editor
168 168 defaults to 'vi'.
169 169
170 170 PYTHONPATH::
171 171 This is used by Python to find imported modules and may need to be
172 172 set appropriately if this Mercurial is not installed system-wide.
173 173 ''')),
174 174
175 175 (['revs', 'revisions'], _('Specifying Single Revisions'),
176 176 _(r'''
177 177 Mercurial supports several ways to specify individual revisions.
178 178
179 179 A plain integer is treated as a revision number. Negative integers
180 180 are treated as topological offsets from the tip, with -1 denoting
181 181 the tip. As such, negative numbers are only useful if you've
182 182 memorized your local tree numbers and want to save typing a single
183 183 digit. This editor suggests copy and paste.
184 184
185 185 A 40-digit hexadecimal string is treated as a unique revision
186 186 identifier.
187 187
188 188 A hexadecimal string less than 40 characters long is treated as a
189 189 unique revision identifier, and referred to as a short-form
190 190 identifier. A short-form identifier is only valid if it is the
191 191 prefix of exactly one full-length identifier.
192 192
193 193 Any other string is treated as a tag name, which is a symbolic
194 194 name associated with a revision identifier. Tag names may not
195 195 contain the ":" character.
196 196
197 197 The reserved name "tip" is a special tag that always identifies
198 198 the most recent revision.
199 199
200 200 The reserved name "null" indicates the null revision. This is the
201 201 revision of an empty repository, and the parent of revision 0.
202 202
203 203 The reserved name "." indicates the working directory parent. If
204 204 no working directory is checked out, it is equivalent to null. If
205 205 an uncommitted merge is in progress, "." is the revision of the
206 206 first parent.
207 207 ''')),
208 208
209 209 (['mrevs', 'multirevs'], _('Specifying Multiple Revisions'),
210 210 _(r'''
211 211 When Mercurial accepts more than one revision, they may be
212 212 specified individually, or provided as a topologically continuous
213 213 range, separated by the ":" character.
214 214
215 215 The syntax of range notation is [BEGIN]:[END], where BEGIN and END
216 216 are revision identifiers. Both BEGIN and END are optional. If
217 217 BEGIN is not specified, it defaults to revision number 0. If END
218 218 is not specified, it defaults to the tip. The range ":" thus means
219 219 "all revisions".
220 220
221 221 If BEGIN is greater than END, revisions are treated in reverse
222 222 order.
223 223
224 224 A range acts as a closed interval. This means that a range of 3:5
225 225 gives 3, 4 and 5. Similarly, a range of 9:6 gives 9, 8, 7, and 6.
226 226 ''')),
227 227
228 228 (['diffs'], _('Diff Formats'),
229 229 _(r'''
230 230 Mercurial's default format for showing changes between two
231 231 versions of a file is compatible with the unified format of GNU
232 232 diff, which can be used by GNU patch and many other standard
233 233 tools.
234 234
235 235 While this standard format is often enough, it does not encode the
236 236 following information:
237 237
238 238 - executable status and other permission bits
239 239 - copy or rename information
240 240 - changes in binary files
241 241 - creation or deletion of empty files
242 242
243 243 Mercurial also supports the extended diff format from the git VCS
244 244 which addresses these limitations. The git diff format is not
245 245 produced by default because a few widespread tools still do not
246 246 understand this format.
247 247
248 248 This means that when generating diffs from a Mercurial repository
249 249 (e.g. with "hg export"), you should be careful about things like
250 250 file copies and renames or other things mentioned above, because
251 251 when applying a standard diff to a different repository, this
252 252 extra information is lost. Mercurial's internal operations (like
253 253 push and pull) are not affected by this, because they use an
254 254 internal binary format for communicating changes.
255 255
256 256 To make Mercurial produce the git extended diff format, use the
257 257 --git option available for many commands, or set 'git = True' in
258 258 the [diff] section of your hgrc. You do not need to set this
259 259 option when importing diffs in this format or using them in the mq
260 260 extension.
261 261 ''')),
262 262 (['templating'], _('Template Usage'),
263 263 _(r'''
264 264 Mercurial allows you to customize output of commands through
265 265 templates. You can either pass in a template from the command
266 266 line, via the --template option, or select an existing
267 267 template-style (--style).
268 268
269 269 You can customize output for any "log-like" command: log,
270 270 outgoing, incoming, tip, parents, heads and glog.
271 271
272 272 Three styles are packaged with Mercurial: default (the style used
273 273 when no explicit preference is passed), compact and changelog.
274 274 Usage:
275 275
276 276 $ hg log -r1 --style changelog
277 277
278 278 A template is a piece of text, with markup to invoke variable
279 279 expansion:
280 280
281 281 $ hg log -r1 --template "{node}\n"
282 282 b56ce7b07c52de7d5fd79fb89701ea538af65746
283 283
284 284 Strings in curly braces are called keywords. The availability of
285 285 keywords depends on the exact context of the templater. These
286 286 keywords are usually available for templating a log-like command:
287 287
288 288 - author: String. The unmodified author of the changeset.
289 289 - branches: String. The name of the branch on which the changeset
290 290 was committed. Will be empty if the branch name was default.
291 291 - date: Date information. The date when the changeset was committed.
292 292 - desc: String. The text of the changeset description.
293 293 - diffstat: String. Statistics of changes with the following
294 294 format: "modified files: +added/-removed lines"
295 295 - files: List of strings. All files modified, added, or removed by
296 296 this changeset.
297 297 - file_adds: List of strings. Files added by this changeset.
298 298 - file_mods: List of strings. Files modified by this changeset.
299 299 - file_dels: List of strings. Files removed by this changeset.
300 300 - node: String. The changeset identification hash, as a
301 301 40-character hexadecimal string.
302 302 - parents: List of strings. The parents of the changeset.
303 303 - rev: Integer. The repository-local changeset revision number.
304 304 - tags: List of strings. Any tags associated with the changeset.
305 305
306 306 The "date" keyword does not produce human-readable output. If you
307 307 want to use a date in your output, you can use a filter to process
308 308 it. Filters are functions which return a string based on the input
309 309 variable. You can also use a chain of filters to get the desired
310 310 output:
311 311
312 312 $ hg tip --template "{date|isodate}\n"
313 313 2008-08-21 18:22 +0000
314 314
315 315 List of filters:
316 316
317 317 - addbreaks: Any text. Add an XHTML "<br />" tag before the end of
318 318 every line except the last.
319 319 - age: Date. Returns a human-readable date/time difference between
320 320 the given date/time and the current date/time.
321 321 - basename: Any text. Treats the text as a path, and returns the
322 322 last component of the path after splitting by the path
323 separator (ignoring trailing seprators). For example,
323 separator (ignoring trailing separators). For example,
324 324 "foo/bar/baz" becomes "baz" and "foo/bar//" becomes "bar".
325 325 - stripdir: Treat the text as path and strip a directory level, if
326 326 possible. For example, "foo" and "foo/bar" becomes "foo".
327 327 - date: Date. Returns a date in a Unix date format, including
328 328 the timezone: "Mon Sep 04 15:13:13 2006 0700".
329 329 - domain: Any text. Finds the first string that looks like an
330 330 email address, and extracts just the domain component.
331 331 Example: 'User <user@example.com>' becomes 'example.com'.
332 332 - email: Any text. Extracts the first string that looks like an
333 333 email address. Example: 'User <user@example.com>' becomes
334 334 'user@example.com'.
335 335 - escape: Any text. Replaces the special XML/XHTML characters "&",
336 336 "<" and ">" with XML entities.
337 337 - fill68: Any text. Wraps the text to fit in 68 columns.
338 338 - fill76: Any text. Wraps the text to fit in 76 columns.
339 339 - firstline: Any text. Returns the first line of text.
340 340 - nonempty: Any text. Returns '(none)' if the string is empty.
341 341 - hgdate: Date. Returns the date as a pair of numbers:
342 342 "1157407993 25200" (Unix timestamp, timezone offset).
343 343 - isodate: Date. Returns the date in ISO 8601 format.
344 344 - localdate: Date. Converts a date to local date.
345 345 - obfuscate: Any text. Returns the input text rendered as a
346 346 sequence of XML entities.
347 347 - person: Any text. Returns the text before an email address.
348 348 - rfc822date: Date. Returns a date using the same format used
349 349 in email headers.
350 350 - short: Changeset hash. Returns the short form of a changeset
351 351 hash, i.e. a 12-byte hexadecimal string.
352 352 - shortdate: Date. Returns a date like "2006-09-18".
353 353 - strip: Any text. Strips all leading and trailing whitespace.
354 354 - tabindent: Any text. Returns the text, with every line except
355 355 the first starting with a tab character.
356 356 - urlescape: Any text. Escapes all "special" characters. For
357 357 example, "foo bar" becomes "foo%20bar".
358 358 - user: Any text. Returns the user portion of an email address.
359 359 ''')),
360 360
361 361 (['urls'], _('URL Paths'),
362 362 _(r'''
363 363 Valid URLs are of the form:
364 364
365 365 local/filesystem/path (or file://local/filesystem/path)
366 366 http://[user[:pass]@]host[:port]/[path]
367 367 https://[user[:pass]@]host[:port]/[path]
368 368 ssh://[user[:pass]@]host[:port]/[path]
369 369
370 370 Paths in the local filesystem can either point to Mercurial
371 371 repositories or to bundle files (as created by 'hg bundle' or
372 372 'hg incoming --bundle').
373 373
374 374 An optional identifier after # indicates a particular branch, tag,
375 375 or changeset to use from the remote repository.
376 376
377 377 Some features, such as pushing to http:// and https:// URLs are
378 378 only possible if the feature is explicitly enabled on the remote
379 379 Mercurial server.
380 380
381 381 Some notes about using SSH with Mercurial:
382 382 - SSH requires an accessible shell account on the destination
383 383 machine and a copy of hg in the remote path or specified with as
384 384 remotecmd.
385 385 - path is relative to the remote user's home directory by default.
386 386 Use an extra slash at the start of a path to specify an absolute path:
387 387 ssh://example.com//tmp/repository
388 388 - Mercurial doesn't use its own compression via SSH; the right
389 389 thing to do is to configure it in your ~/.ssh/config, e.g.:
390 390 Host *.mylocalnetwork.example.com
391 391 Compression no
392 392 Host *
393 393 Compression yes
394 394 Alternatively specify "ssh -C" as your ssh command in your hgrc
395 395 or with the --ssh command line option.
396 396
397 397 These URLs can all be stored in your hgrc with path aliases under
398 398 the [paths] section like so:
399 399 [paths]
400 400 alias1 = URL1
401 401 alias2 = URL2
402 402 ...
403 403
404 404 You can then use the alias for any command that uses a URL (for
405 405 example 'hg pull alias1' would pull from the 'alias1' path).
406 406
407 407 Two path aliases are special because they are used as defaults
408 408 when you do not provide the URL to a command:
409 409
410 410 default:
411 411 When you create a repository with hg clone, the clone command
412 412 saves the location of the source repository as the new
413 413 repository's 'default' path. This is then used when you omit
414 414 path from push- and pull-like commands (including incoming and
415 415 outgoing).
416 416
417 417 default-push:
418 418 The push command will look for a path named 'default-push', and
419 419 prefer it over 'default' if both are defined.
420 420 ''')),
421 421 )
@@ -1,261 +1,261 b''
1 1 hg convert [OPTION]... SOURCE [DEST [REVMAP]]
2 2
3 3 convert a foreign SCM repository to a Mercurial one.
4 4
5 5 Accepted source formats [identifiers]:
6 6 - Mercurial [hg]
7 7 - CVS [cvs]
8 8 - Darcs [darcs]
9 9 - git [git]
10 10 - Subversion [svn]
11 11 - Monotone [mtn]
12 12 - GNU Arch [gnuarch]
13 13 - Bazaar [bzr]
14 14 - Perforce [p4]
15 15
16 16 Accepted destination formats [identifiers]:
17 17 - Mercurial [hg]
18 18 - Subversion [svn] (history on branches is not preserved)
19 19
20 20 If no revision is given, all revisions will be converted.
21 21 Otherwise, convert will only import up to the named revision
22 22 (given in a format understood by the source).
23 23
24 24 If no destination directory name is specified, it defaults to the
25 25 basename of the source with '-hg' appended. If the destination
26 26 repository doesn't exist, it will be created.
27 27
28 28 If <REVMAP> isn't given, it will be put in a default location
29 29 (<dest>/.hg/shamap by default). The <REVMAP> is a simple text file
30 30 that maps each source commit ID to the destination ID for that
31 31 revision, like so:
32 32 <source ID> <destination ID>
33 33
34 34 If the file doesn't exist, it's automatically created. It's
35 35 updated on each commit copied, so convert-repo can be interrupted
36 36 and can be run repeatedly to copy new commits.
37 37
38 38 The [username mapping] file is a simple text file that maps each
39 39 source commit author to a destination commit author. It is handy
40 40 for source SCMs that use unix logins to identify authors (eg:
41 41 CVS). One line per author mapping and the line format is:
42 42 srcauthor=whatever string you want
43 43
44 44 The filemap is a file that allows filtering and remapping of files
45 45 and directories. Comment lines start with '#'. Each line can
46 46 contain one of the following directives:
47 47
48 48 include path/to/file
49 49
50 50 exclude path/to/file
51 51
52 52 rename from/file to/file
53 53
54 54 The 'include' directive causes a file, or all files under a
55 55 directory, to be included in the destination repository, and the
56 exclusion of all other files and directories not explicitely included.
56 exclusion of all other files and directories not explicitly included.
57 57 The 'exclude' directive causes files or directories to be omitted.
58 58 The 'rename' directive renames a file or directory. To rename from
59 59 a subdirectory into the root of the repository, use '.' as the
60 60 path to rename to.
61 61
62 62 The splicemap is a file that allows insertion of synthetic
63 63 history, letting you specify the parents of a revision. This is
64 64 useful if you want to e.g. give a Subversion merge two parents, or
65 65 graft two disconnected series of history together. Each entry
66 66 contains a key, followed by a space, followed by one or two
67 67 comma-separated values. The key is the revision ID in the source
68 68 revision control system whose parents should be modified (same
69 69 format as a key in .hg/shamap). The values are the revision IDs
70 70 (in either the source or destination revision control system) that
71 71 should be used as the new parents for that node.
72 72
73 73 The branchmap is a file that allows you to rename a branch when it is
74 74 being brought in from whatever external repository. When used in
75 75 conjunction with a splicemap, it allows for a powerful combination
76 76 to help fix even the most badly mismanaged repositories and turn them
77 77 into nicely structured Mercurial repositories. The branchmap contains
78 78 lines of the form "original_branch_name new_branch_name".
79 79 "original_branch_name" is the name of the branch in the source
80 80 repository, and "new_branch_name" is the name of the branch is the
81 81 destination repository. This can be used to (for instance) move code
82 82 in one repository from "default" to a named branch.
83 83
84 84 Mercurial Source
85 85 -----------------
86 86
87 87 --config convert.hg.ignoreerrors=False (boolean)
88 88 ignore integrity errors when reading. Use it to fix Mercurial
89 89 repositories with missing revlogs, by converting from and to
90 90 Mercurial.
91 91 --config convert.hg.saverev=False (boolean)
92 92 store original revision ID in changeset (forces target IDs to
93 93 change)
94 94 --config convert.hg.startrev=0 (hg revision identifier)
95 95 convert start revision and its descendants
96 96
97 97 CVS Source
98 98 ----------
99 99
100 100 CVS source will use a sandbox (i.e. a checked-out copy) from CVS
101 101 to indicate the starting point of what will be converted. Direct
102 102 access to the repository files is not needed, unless of course the
103 103 repository is :local:. The conversion uses the top level directory
104 104 in the sandbox to find the CVS repository, and then uses CVS rlog
105 105 commands to find files to convert. This means that unless a
106 106 filemap is given, all files under the starting directory will be
107 converted, and that any directory reorganisation in the CVS
107 converted, and that any directory reorganization in the CVS
108 108 sandbox is ignored.
109 109
110 110 Because CVS does not have changesets, it is necessary to collect
111 111 individual commits to CVS and merge them into changesets. CVS
112 112 source uses its internal changeset merging code by default but can
113 113 be configured to call the external 'cvsps' program by setting:
114 114 --config convert.cvsps='cvsps -A -u --cvs-direct -q'
115 115 This option is deprecated and will be removed in Mercurial 1.4.
116 116
117 117 The options shown are the defaults.
118 118
119 119 Internal cvsps is selected by setting
120 120 --config convert.cvsps=builtin
121 121 and has a few more configurable options:
122 122 --config convert.cvsps.cache=True (boolean)
123 123 Set to False to disable remote log caching, for testing and
124 124 debugging purposes.
125 125 --config convert.cvsps.fuzz=60 (integer)
126 126 Specify the maximum time (in seconds) that is allowed
127 127 between commits with identical user and log message in a
128 128 single changeset. When very large files were checked in as
129 129 part of a changeset then the default may not be long
130 130 enough.
131 131 --config convert.cvsps.mergeto='{{mergetobranch ([-\w]+)}}'
132 132 Specify a regular expression to which commit log messages
133 133 are matched. If a match occurs, then the conversion
134 134 process will insert a dummy revision merging the branch on
135 135 which this log message occurs to the branch indicated in
136 136 the regex.
137 137 --config convert.cvsps.mergefrom='{{mergefrombranch ([-\w]+)}}'
138 138 Specify a regular expression to which commit log messages
139 139 are matched. If a match occurs, then the conversion
140 140 process will add the most recent revision on the branch
141 141 indicated in the regex as the second parent of the
142 142 changeset.
143 143
144 144 The hgext/convert/cvsps wrapper script allows the builtin
145 145 changeset merging code to be run without doing a conversion. Its
146 146 parameters and output are similar to that of cvsps 2.1.
147 147
148 148 Subversion Source
149 149 -----------------
150 150
151 151 Subversion source detects classical trunk/branches/tags layouts.
152 152 By default, the supplied "svn://repo/path/" source URL is
153 153 converted as a single branch. If "svn://repo/path/trunk" exists it
154 154 replaces the default branch. If "svn://repo/path/branches" exists,
155 155 its subdirectories are listed as possible branches. If
156 156 "svn://repo/path/tags" exists, it is looked for tags referencing
157 157 converted branches. Default "trunk", "branches" and "tags" values
158 can be overriden with following options. Set them to paths
159 relative to the source URL, or leave them blank to disable
160 autodetection.
158 can be overridden with following options. Set them to paths
159 relative to the source URL, or leave them blank to disable auto
160 detection.
161 161
162 162 --config convert.svn.branches=branches (directory name)
163 163 specify the directory containing branches
164 164 --config convert.svn.tags=tags (directory name)
165 165 specify the directory containing tags
166 166 --config convert.svn.trunk=trunk (directory name)
167 167 specify the name of the trunk branch
168 168
169 169 Source history can be retrieved starting at a specific revision,
170 170 instead of being integrally converted. Only single branch
171 171 conversions are supported.
172 172
173 173 --config convert.svn.startrev=0 (svn revision number)
174 174 specify start Subversion revision.
175 175
176 176 Perforce Source
177 177 ---------------
178 178
179 179 The Perforce (P4) importer can be given a p4 depot path or a
180 180 client specification as source. It will convert all files in the
181 181 source to a flat Mercurial repository, ignoring labels, branches
182 182 and integrations. Note that when a depot path is given you then
183 183 usually should specify a target directory, because otherwise the
184 184 target may be named ...-hg.
185 185
186 186 It is possible to limit the amount of source history to be
187 187 converted by specifying an initial Perforce revision.
188 188
189 189 --config convert.p4.startrev=0 (perforce changelist number)
190 190 specify initial Perforce revision.
191 191
192 192
193 193 Mercurial Destination
194 194 ---------------------
195 195
196 196 --config convert.hg.clonebranches=False (boolean)
197 197 dispatch source branches in separate clones.
198 198 --config convert.hg.tagsbranch=default (branch name)
199 199 tag revisions branch name
200 200 --config convert.hg.usebranchnames=True (boolean)
201 201 preserve branch names
202 202
203 203 options:
204 204
205 205 -A --authors username mapping filename
206 206 -d --dest-type destination repository type
207 207 --filemap remap file names using contents of file
208 208 -r --rev import up to target revision REV
209 209 -s --source-type source repository type
210 210 --splicemap splice synthesized history into place
211 211 --branchmap change branch names while converting
212 212 --datesort try to sort changesets by date
213 213
214 214 use "hg -v help convert" to show global options
215 215 adding a
216 216 assuming destination a-hg
217 217 initializing destination a-hg repository
218 218 scanning source...
219 219 sorting...
220 220 converting...
221 221 4 a
222 222 3 b
223 223 2 c
224 224 1 d
225 225 0 e
226 226 pulling from ../a
227 227 searching for changes
228 228 no changes found
229 229 % should fail
230 230 initializing destination bogusfile repository
231 231 abort: cannot create new bundle repository
232 232 % should fail
233 233 abort: Permission denied: bogusdir
234 234 % should succeed
235 235 initializing destination bogusdir repository
236 236 scanning source...
237 237 sorting...
238 238 converting...
239 239 4 a
240 240 3 b
241 241 2 c
242 242 1 d
243 243 0 e
244 244 % test pre and post conversion actions
245 245 run hg source pre-conversion action
246 246 run hg sink pre-conversion action
247 247 run hg sink post-conversion action
248 248 run hg source post-conversion action
249 249 % converting empty dir should fail nicely
250 250 assuming destination emptydir-hg
251 251 initializing destination emptydir-hg repository
252 252 emptydir does not look like a CVS checkout
253 253 emptydir does not look like a Git repo
254 254 emptydir does not look like a Subversion repo
255 255 emptydir is not a local Mercurial repo
256 256 emptydir does not look like a darcs repo
257 257 emptydir does not look like a monotone repo
258 258 emptydir does not look like a GNU Arch repo
259 259 emptydir does not look like a Bazaar repo
260 260 emptydir does not look like a P4 repo
261 261 abort: emptydir: missing or unsupported repository
@@ -1,502 +1,502 b''
1 1 % help
2 2 keyword extension - keyword expansion in local repositories
3 3
4 4 This extension expands RCS/CVS-like or self-customized $Keywords$ in
5 5 tracked text files selected by your configuration.
6 6
7 7 Keywords are only expanded in local repositories and not stored in the
8 8 change history. The mechanism can be regarded as a convenience for the
9 9 current user or for archive distribution.
10 10
11 11 Configuration is done in the [keyword] and [keywordmaps] sections of
12 12 hgrc files.
13 13
14 14 Example:
15 15
16 16 [keyword]
17 17 # expand keywords in every python file except those matching "x*"
18 18 **.py =
19 19 x* = ignore
20 20
21 21 Note: the more specific you are in your filename patterns
22 22 the less you lose speed in huge repositories.
23 23
24 24 For [keywordmaps] template mapping and expansion demonstration and
25 25 control run "hg kwdemo".
26 26
27 27 An additional date template filter {date|utcdate} is provided.
28 28
29 29 The default template mappings (view with "hg kwdemo -d") can be
30 30 replaced with customized keywords and templates. Again, run "hg
31 31 kwdemo" to control the results of your config changes.
32 32
33 33 Before changing/disabling active keywords, run "hg kwshrink" to avoid
34 the risk of inadvertedly storing expanded keywords in the change
34 the risk of inadvertently storing expanded keywords in the change
35 35 history.
36 36
37 37 To force expansion after enabling it, or a configuration change, run
38 38 "hg kwexpand".
39 39
40 40 Also, when committing with the record extension or using mq's qrecord,
41 41 be aware that keywords cannot be updated. Again, run "hg kwexpand" on
42 42 the files in question to update keyword expansions after all changes
43 43 have been checked in.
44 44
45 45 Expansions spanning more than one line and incremental expansions,
46 46 like CVS' $Log$, are not supported. A keyword template map
47 47 "Log = {desc}" expands to the first line of the changeset description.
48 48
49 49 list of commands:
50 50
51 51 kwdemo print [keywordmaps] configuration and an expansion example
52 52 kwexpand expand keywords in working directory
53 53 kwfiles print files currently configured for keyword expansion
54 54 kwshrink revert expanded keywords in working directory
55 55
56 56 enabled extensions:
57 57
58 58 keyword keyword expansion in local repositories
59 59 mq patch management and development
60 60 notify hook extension to email notifications on commits/pushes
61 61
62 62 use "hg -v help keyword" to show aliases and global options
63 63 % hg kwdemo
64 64 [extensions]
65 65 hgext.keyword =
66 66 [keyword]
67 67 * =
68 68 b = ignore
69 69 demo.txt =
70 70 [keywordmaps]
71 71 RCSFile = {file|basename},v
72 72 Author = {author|user}
73 73 Header = {root}/{file},v {node|short} {date|utcdate} {author|user}
74 74 Source = {root}/{file},v
75 75 Date = {date|utcdate}
76 76 Id = {file|basename},v {node|short} {date|utcdate} {author|user}
77 77 Revision = {node|short}
78 78 $RCSFile: demo.txt,v $
79 79 $Author: test $
80 80 $Header: /TMP/demo.txt,v xxxxxxxxxxxx 2000/00/00 00:00:00 test $
81 81 $Source: /TMP/demo.txt,v $
82 82 $Date: 2000/00/00 00:00:00 $
83 83 $Id: demo.txt,v xxxxxxxxxxxx 2000/00/00 00:00:00 test $
84 84 $Revision: xxxxxxxxxxxx $
85 85 [extensions]
86 86 hgext.keyword =
87 87 [keyword]
88 88 * =
89 89 b = ignore
90 90 demo.txt =
91 91 [keywordmaps]
92 92 Branch = {branches}
93 93 $Branch: demobranch $
94 94 % kwshrink should exit silently in empty/invalid repo
95 95 pulling from test-keyword.hg
96 96 requesting all changes
97 97 adding changesets
98 98 adding manifests
99 99 adding file changes
100 100 added 1 changesets with 1 changes to 1 files
101 101 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
102 102 % cat
103 103 expand $Id$
104 104 do not process $Id:
105 105 xxx $
106 106 ignore $Id$
107 107 % addremove
108 108 adding a
109 109 adding b
110 110 % status
111 111 A a
112 112 A b
113 113 % default keyword expansion including commit hook
114 114 % interrupted commit should not change state or run commit hook
115 115 abort: empty commit message
116 116 % status
117 117 A a
118 118 A b
119 119 % commit
120 120 a
121 121 b
122 122 overwriting a expanding keywords
123 123 running hook commit.test: cp a hooktest
124 124 committed changeset 1:ef63ca68695bc9495032c6fda1350c71e6d256e9
125 125 % status
126 126 ? hooktest
127 127 % identify
128 128 ef63ca68695b
129 129 % cat
130 130 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
131 131 do not process $Id:
132 132 xxx $
133 133 ignore $Id$
134 134 % hg cat
135 135 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
136 136 do not process $Id:
137 137 xxx $
138 138 ignore $Id$
139 139 a
140 140 % diff a hooktest
141 141 % removing commit hook from config
142 142 % bundle
143 143 2 changesets found
144 144 % notify on pull to check whether keywords stay as is in email
145 145 % ie. if patch.diff wrapper acts as it should
146 146 % pull from bundle
147 147 pulling from ../kw.hg
148 148 requesting all changes
149 149 adding changesets
150 150 adding manifests
151 151 adding file changes
152 152 added 2 changesets with 3 changes to 3 files
153 153
154 154 diff -r 000000000000 -r a2392c293916 sym
155 155 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
156 156 +++ b/sym Sat Feb 09 20:25:47 2008 +0100
157 157 @@ -0,0 +1,1 @@
158 158 +a
159 159 \ No newline at end of file
160 160
161 161 diff -r a2392c293916 -r ef63ca68695b a
162 162 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
163 163 +++ b/a Thu Jan 01 00:00:00 1970 +0000
164 164 @@ -0,0 +1,3 @@
165 165 +expand $Id$
166 166 +do not process $Id:
167 167 +xxx $
168 168 diff -r a2392c293916 -r ef63ca68695b b
169 169 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
170 170 +++ b/b Thu Jan 01 00:00:00 1970 +0000
171 171 @@ -0,0 +1,1 @@
172 172 +ignore $Id$
173 173 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
174 174 % remove notify config
175 175 % touch
176 176 % status
177 177 % update
178 178 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
179 179 % cat
180 180 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
181 181 do not process $Id:
182 182 xxx $
183 183 ignore $Id$
184 184 % check whether expansion is filewise
185 185 % commit c
186 186 adding c
187 187 % force expansion
188 188 overwriting a expanding keywords
189 189 overwriting c expanding keywords
190 190 % compare changenodes in a c
191 191 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
192 192 do not process $Id:
193 193 xxx $
194 194 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
195 195 tests for different changenodes
196 196 % qinit -c
197 197 % qimport
198 198 % qcommit
199 199 % keywords should not be expanded in patch
200 200 # HG changeset patch
201 201 # User User Name <user@example.com>
202 202 # Date 1 0
203 203 # Node ID 40a904bbbe4cd4ab0a1f28411e35db26341a40ad
204 204 # Parent ef63ca68695bc9495032c6fda1350c71e6d256e9
205 205 cndiff
206 206
207 207 diff -r ef63ca68695b -r 40a904bbbe4c c
208 208 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
209 209 +++ b/c Thu Jan 01 00:00:01 1970 +0000
210 210 @@ -0,0 +1,2 @@
211 211 +$Id$
212 212 +tests for different changenodes
213 213 % qpop
214 214 patch queue now empty
215 215 % qgoto - should imply qpush
216 216 applying mqtest.diff
217 217 now at: mqtest.diff
218 218 % cat
219 219 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
220 220 tests for different changenodes
221 221 % qpop and move on
222 222 patch queue now empty
223 223 % copy
224 224 % kwfiles added
225 225 a
226 226 c
227 227 % commit
228 228 c
229 229 c: copy a:0045e12f6c5791aac80ca6cbfd97709a88307292
230 230 overwriting c expanding keywords
231 231 committed changeset 2:e22d299ac0c2bd8897b3df5114374b9e4d4ca62f
232 232 % cat a c
233 233 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
234 234 do not process $Id:
235 235 xxx $
236 236 expand $Id: c,v e22d299ac0c2 1970/01/01 00:00:01 user $
237 237 do not process $Id:
238 238 xxx $
239 239 % touch copied c
240 240 % status
241 241 % kwfiles
242 242 a
243 243 c
244 244 % diff --rev
245 245 diff -r ef63ca68695b c
246 246 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
247 247 @@ -0,0 +1,3 @@
248 248 +expand $Id$
249 249 +do not process $Id:
250 250 +xxx $
251 251 % rollback
252 252 rolling back last transaction
253 253 % status
254 254 A c
255 255 % update -C
256 256 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
257 257 % custom keyword expansion
258 258 % try with kwdemo
259 259 [extensions]
260 260 hgext.keyword =
261 261 [keyword]
262 262 * =
263 263 b = ignore
264 264 demo.txt =
265 265 [keywordmaps]
266 266 Xinfo = {author}: {desc}
267 267 $Xinfo: test: hg keyword config and expansion example $
268 268 % cat
269 269 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
270 270 do not process $Id:
271 271 xxx $
272 272 ignore $Id$
273 273 % hg cat
274 274 expand $Id: a ef63ca68695b Thu, 01 Jan 1970 00:00:00 +0000 user $
275 275 do not process $Id:
276 276 xxx $
277 277 ignore $Id$
278 278 a
279 279 % interrupted commit should not change state
280 280 abort: empty commit message
281 281 % status
282 282 M a
283 283 ? c
284 284 ? log
285 285 % commit
286 286 a
287 287 overwriting a expanding keywords
288 288 committed changeset 2:bb948857c743469b22bbf51f7ec8112279ca5d83
289 289 % status
290 290 ? c
291 291 % verify
292 292 checking changesets
293 293 checking manifests
294 294 crosschecking files in changesets and manifests
295 295 checking files
296 296 3 files, 3 changesets, 4 total revisions
297 297 % cat
298 298 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
299 299 do not process $Id:
300 300 xxx $
301 301 $Xinfo: User Name <user@example.com>: firstline $
302 302 ignore $Id$
303 303 % hg cat
304 304 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
305 305 do not process $Id:
306 306 xxx $
307 307 $Xinfo: User Name <user@example.com>: firstline $
308 308 ignore $Id$
309 309 a
310 310 % annotate
311 311 1: expand $Id$
312 312 1: do not process $Id:
313 313 1: xxx $
314 314 2: $Xinfo$
315 315 % remove
316 316 committed changeset 3:d14c712653769de926994cf7fbb06c8fbd68f012
317 317 % status
318 318 ? c
319 319 % rollback
320 320 rolling back last transaction
321 321 % status
322 322 R a
323 323 ? c
324 324 % revert a
325 325 % cat a
326 326 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
327 327 do not process $Id:
328 328 xxx $
329 329 $Xinfo: User Name <user@example.com>: firstline $
330 330 % clone to test incoming
331 331 requesting all changes
332 332 adding changesets
333 333 adding manifests
334 334 adding file changes
335 335 added 2 changesets with 3 changes to 3 files
336 336 updating working directory
337 337 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
338 338 % incoming
339 339 comparing with test-keyword/Test
340 340 searching for changes
341 341 changeset: 2:bb948857c743
342 342 tag: tip
343 343 user: User Name <user@example.com>
344 344 date: Thu Jan 01 00:00:02 1970 +0000
345 345 summary: firstline
346 346
347 347 % commit rejecttest
348 348 a
349 349 overwriting a expanding keywords
350 350 committed changeset 2:85e279d709ffc28c9fdd1b868570985fc3d87082
351 351 % export
352 352 % import
353 353 applying ../rejecttest.diff
354 354 % cat
355 355 expand $Id: a 4e0994474d25 Thu, 01 Jan 1970 00:00:03 +0000 user $ rejecttest
356 356 do not process $Id: rejecttest
357 357 xxx $
358 358 $Xinfo: User Name <user@example.com>: rejects? $
359 359 ignore $Id$
360 360
361 361 % rollback
362 362 rolling back last transaction
363 363 % clean update
364 364 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
365 365 % kwexpand/kwshrink on selected files
366 366 % copy a x/a
367 367 % kwexpand a
368 368 overwriting a expanding keywords
369 369 % kwexpand x/a should abort
370 370 abort: outstanding uncommitted changes
371 371 x/a
372 372 x/a: copy a:779c764182ce5d43e2b1eb66ce06d7b47bfe342e
373 373 overwriting x/a expanding keywords
374 374 committed changeset 3:cfa68229c1167443337266ebac453c73b1d5d16e
375 375 % cat a
376 376 expand $Id: x/a cfa68229c116 Thu, 01 Jan 1970 00:00:03 +0000 user $
377 377 do not process $Id:
378 378 xxx $
379 379 $Xinfo: User Name <user@example.com>: xa $
380 380 % kwshrink a inside directory x
381 381 overwriting x/a shrinking keywords
382 382 % cat a
383 383 expand $Id$
384 384 do not process $Id:
385 385 xxx $
386 386 $Xinfo$
387 387 % kwexpand nonexistent
388 388 nonexistent:
389 389 % hg serve
390 390 % expansion
391 391 % hgweb file
392 392 200 Script output follows
393 393
394 394 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
395 395 do not process $Id:
396 396 xxx $
397 397 $Xinfo: User Name <user@example.com>: firstline $
398 398 % no expansion
399 399 % hgweb annotate
400 400 200 Script output follows
401 401
402 402
403 403 user@1: expand $Id$
404 404 user@1: do not process $Id:
405 405 user@1: xxx $
406 406 user@2: $Xinfo$
407 407
408 408
409 409
410 410
411 411 % hgweb changeset
412 412 200 Script output follows
413 413
414 414
415 415 # HG changeset patch
416 416 # User User Name <user@example.com>
417 417 # Date 3 0
418 418 # Node ID cfa68229c1167443337266ebac453c73b1d5d16e
419 419 # Parent bb948857c743469b22bbf51f7ec8112279ca5d83
420 420 xa
421 421
422 422 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
423 423 +++ b/x/a Thu Jan 01 00:00:03 1970 +0000
424 424 @@ -0,0 +1,4 @@
425 425 +expand $Id$
426 426 +do not process $Id:
427 427 +xxx $
428 428 +$Xinfo$
429 429
430 430 % hgweb filediff
431 431 200 Script output follows
432 432
433 433
434 434 --- a/a Thu Jan 01 00:00:00 1970 +0000
435 435 +++ b/a Thu Jan 01 00:00:02 1970 +0000
436 436 @@ -1,3 +1,4 @@
437 437 expand $Id$
438 438 do not process $Id:
439 439 xxx $
440 440 +$Xinfo$
441 441
442 442
443 443
444 444
445 445 % errors encountered
446 446 % merge/resolve
447 447 % simplemerge
448 448 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
449 449 created new head
450 450 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
451 451 (branch merge, don't forget to commit)
452 452 $Id: m 8731e1dadc99 Thu, 01 Jan 1970 00:00:00 +0000 test $
453 453 foo
454 454 % conflict
455 455 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
456 456 created new head
457 457 merging m
458 458 warning: conflicts during merge.
459 459 merging m failed!
460 460 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
461 461 use 'hg resolve' to retry unresolved file merges or 'hg up --clean' to abandon
462 462 % keyword stays outside conflict zone
463 463 $Id$
464 464 <<<<<<< local
465 465 bar
466 466 =======
467 467 foo
468 468 >>>>>>> other
469 469 % resolve to local
470 470 $Id: m 43dfd2854b5b Thu, 01 Jan 1970 00:00:00 +0000 test $
471 471 bar
472 472 % switch off expansion
473 473 % kwshrink with unknown file u
474 474 overwriting a shrinking keywords
475 475 overwriting m shrinking keywords
476 476 overwriting x/a shrinking keywords
477 477 % cat
478 478 expand $Id$
479 479 do not process $Id:
480 480 xxx $
481 481 $Xinfo$
482 482 ignore $Id$
483 483 % hg cat
484 484 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
485 485 do not process $Id:
486 486 xxx $
487 487 $Xinfo: User Name <user@example.com>: firstline $
488 488 ignore $Id$
489 489 a
490 490 % cat
491 491 expand $Id$
492 492 do not process $Id:
493 493 xxx $
494 494 $Xinfo$
495 495 ignore $Id$
496 496 % hg cat
497 497 expand $Id$
498 498 do not process $Id:
499 499 xxx $
500 500 $Xinfo$
501 501 ignore $Id$
502 502 a
General Comments 0
You need to be logged in to leave comments. Login now