##// END OF EJS Templates
hgcia, convert: escape backslashes in docstrings
Martin Geisler -
r8541:06ace504 default
parent child Browse files
Show More
@@ -1,274 +1,274 b''
1 # convert.py Foreign SCM converter
1 # convert.py Foreign SCM converter
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7
7
8 '''converting foreign VCS repositories to Mercurial'''
8 '''converting foreign VCS repositories to Mercurial'''
9
9
10 import convcmd
10 import convcmd
11 import cvsps
11 import cvsps
12 import subversion
12 import subversion
13 from mercurial import commands
13 from mercurial import commands
14 from mercurial.i18n import _
14 from mercurial.i18n import _
15
15
16 # Commands definition was moved elsewhere to ease demandload job.
16 # Commands definition was moved elsewhere to ease demandload job.
17
17
18 def convert(ui, src, dest=None, revmapfile=None, **opts):
18 def convert(ui, src, dest=None, revmapfile=None, **opts):
19 """convert a foreign SCM repository to a Mercurial one.
19 """convert a foreign SCM repository to a Mercurial one.
20
20
21 Accepted source formats [identifiers]:
21 Accepted source formats [identifiers]:
22 - Mercurial [hg]
22 - Mercurial [hg]
23 - CVS [cvs]
23 - CVS [cvs]
24 - Darcs [darcs]
24 - Darcs [darcs]
25 - git [git]
25 - git [git]
26 - Subversion [svn]
26 - Subversion [svn]
27 - Monotone [mtn]
27 - Monotone [mtn]
28 - GNU Arch [gnuarch]
28 - GNU Arch [gnuarch]
29 - Bazaar [bzr]
29 - Bazaar [bzr]
30 - Perforce [p4]
30 - Perforce [p4]
31
31
32 Accepted destination formats [identifiers]:
32 Accepted destination formats [identifiers]:
33 - Mercurial [hg]
33 - Mercurial [hg]
34 - Subversion [svn] (history on branches is not preserved)
34 - Subversion [svn] (history on branches is not preserved)
35
35
36 If no revision is given, all revisions will be converted.
36 If no revision is given, all revisions will be converted.
37 Otherwise, convert will only import up to the named revision
37 Otherwise, convert will only import up to the named revision
38 (given in a format understood by the source).
38 (given in a format understood by the source).
39
39
40 If no destination directory name is specified, it defaults to the
40 If no destination directory name is specified, it defaults to the
41 basename of the source with '-hg' appended. If the destination
41 basename of the source with '-hg' appended. If the destination
42 repository doesn't exist, it will be created.
42 repository doesn't exist, it will be created.
43
43
44 If <REVMAP> isn't given, it will be put in a default location
44 If <REVMAP> isn't given, it will be put in a default location
45 (<dest>/.hg/shamap by default). The <REVMAP> is a simple text file
45 (<dest>/.hg/shamap by default). The <REVMAP> is a simple text file
46 that maps each source commit ID to the destination ID for that
46 that maps each source commit ID to the destination ID for that
47 revision, like so:
47 revision, like so:
48 <source ID> <destination ID>
48 <source ID> <destination ID>
49
49
50 If the file doesn't exist, it's automatically created. It's
50 If the file doesn't exist, it's automatically created. It's
51 updated on each commit copied, so convert-repo can be interrupted
51 updated on each commit copied, so convert-repo can be interrupted
52 and can be run repeatedly to copy new commits.
52 and can be run repeatedly to copy new commits.
53
53
54 The [username mapping] file is a simple text file that maps each
54 The [username mapping] file is a simple text file that maps each
55 source commit author to a destination commit author. It is handy
55 source commit author to a destination commit author. It is handy
56 for source SCMs that use unix logins to identify authors (eg:
56 for source SCMs that use unix logins to identify authors (eg:
57 CVS). One line per author mapping and the line format is:
57 CVS). One line per author mapping and the line format is:
58 srcauthor=whatever string you want
58 srcauthor=whatever string you want
59
59
60 The filemap is a file that allows filtering and remapping of files
60 The filemap is a file that allows filtering and remapping of files
61 and directories. Comment lines start with '#'. Each line can
61 and directories. Comment lines start with '#'. Each line can
62 contain one of the following directives:
62 contain one of the following directives:
63
63
64 include path/to/file
64 include path/to/file
65
65
66 exclude path/to/file
66 exclude path/to/file
67
67
68 rename from/file to/file
68 rename from/file to/file
69
69
70 The 'include' directive causes a file, or all files under a
70 The 'include' directive causes a file, or all files under a
71 directory, to be included in the destination repository, and the
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 explicitely included.
73 The 'exclude' directive causes files or directories to be omitted.
73 The 'exclude' directive causes files or directories to be omitted.
74 The 'rename' directive renames a file or directory. To rename from
74 The 'rename' directive renames a file or directory. To rename from
75 a subdirectory into the root of the repository, use '.' as the
75 a subdirectory into the root of the repository, use '.' as the
76 path to rename to.
76 path to rename to.
77
77
78 The splicemap is a file that allows insertion of synthetic
78 The splicemap is a file that allows insertion of synthetic
79 history, letting you specify the parents of a revision. This is
79 history, letting you specify the parents of a revision. This is
80 useful if you want to e.g. give a Subversion merge two parents, or
80 useful if you want to e.g. give a Subversion merge two parents, or
81 graft two disconnected series of history together. Each entry
81 graft two disconnected series of history together. Each entry
82 contains a key, followed by a space, followed by one or two
82 contains a key, followed by a space, followed by one or two
83 comma-separated values. The key is the revision ID in the source
83 comma-separated values. The key is the revision ID in the source
84 revision control system whose parents should be modified (same
84 revision control system whose parents should be modified (same
85 format as a key in .hg/shamap). The values are the revision IDs
85 format as a key in .hg/shamap). The values are the revision IDs
86 (in either the source or destination revision control system) that
86 (in either the source or destination revision control system) that
87 should be used as the new parents for that node.
87 should be used as the new parents for that node.
88
88
89 The branchmap is a file that allows you to rename a branch when it is
89 The branchmap is a file that allows you to rename a branch when it is
90 being brought in from whatever external repository. When used in
90 being brought in from whatever external repository. When used in
91 conjunction with a splicemap, it allows for a powerful combination
91 conjunction with a splicemap, it allows for a powerful combination
92 to help fix even the most badly mismanaged repositories and turn them
92 to help fix even the most badly mismanaged repositories and turn them
93 into nicely structured Mercurial repositories. The branchmap contains
93 into nicely structured Mercurial repositories. The branchmap contains
94 lines of the form "original_branch_name new_branch_name".
94 lines of the form "original_branch_name new_branch_name".
95 "original_branch_name" is the name of the branch in the source
95 "original_branch_name" is the name of the branch in the source
96 repository, and "new_branch_name" is the name of the branch is the
96 repository, and "new_branch_name" is the name of the branch is the
97 destination repository. This can be used to (for instance) move code
97 destination repository. This can be used to (for instance) move code
98 in one repository from "default" to a named branch.
98 in one repository from "default" to a named branch.
99
99
100 Mercurial Source
100 Mercurial Source
101 -----------------
101 -----------------
102
102
103 --config convert.hg.ignoreerrors=False (boolean)
103 --config convert.hg.ignoreerrors=False (boolean)
104 ignore integrity errors when reading. Use it to fix Mercurial
104 ignore integrity errors when reading. Use it to fix Mercurial
105 repositories with missing revlogs, by converting from and to
105 repositories with missing revlogs, by converting from and to
106 Mercurial.
106 Mercurial.
107 --config convert.hg.saverev=False (boolean)
107 --config convert.hg.saverev=False (boolean)
108 store original revision ID in changeset (forces target IDs to
108 store original revision ID in changeset (forces target IDs to
109 change)
109 change)
110 --config convert.hg.startrev=0 (hg revision identifier)
110 --config convert.hg.startrev=0 (hg revision identifier)
111 convert start revision and its descendants
111 convert start revision and its descendants
112
112
113 CVS Source
113 CVS Source
114 ----------
114 ----------
115
115
116 CVS source will use a sandbox (i.e. a checked-out copy) from CVS
116 CVS source will use a sandbox (i.e. a checked-out copy) from CVS
117 to indicate the starting point of what will be converted. Direct
117 to indicate the starting point of what will be converted. Direct
118 access to the repository files is not needed, unless of course the
118 access to the repository files is not needed, unless of course the
119 repository is :local:. The conversion uses the top level directory
119 repository is :local:. The conversion uses the top level directory
120 in the sandbox to find the CVS repository, and then uses CVS rlog
120 in the sandbox to find the CVS repository, and then uses CVS rlog
121 commands to find files to convert. This means that unless a
121 commands to find files to convert. This means that unless a
122 filemap is given, all files under the starting directory will be
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 reorganisation in the CVS
124 sandbox is ignored.
124 sandbox is ignored.
125
125
126 Because CVS does not have changesets, it is necessary to collect
126 Because CVS does not have changesets, it is necessary to collect
127 individual commits to CVS and merge them into changesets. CVS
127 individual commits to CVS and merge them into changesets. CVS
128 source uses its internal changeset merging code by default but can
128 source uses its internal changeset merging code by default but can
129 be configured to call the external 'cvsps' program by setting:
129 be configured to call the external 'cvsps' program by setting:
130 --config convert.cvsps='cvsps -A -u --cvs-direct -q'
130 --config convert.cvsps='cvsps -A -u --cvs-direct -q'
131 This is a legacy option and may be removed in future.
131 This is a legacy option and may be removed in future.
132
132
133 The options shown are the defaults.
133 The options shown are the defaults.
134
134
135 Internal cvsps is selected by setting
135 Internal cvsps is selected by setting
136 --config convert.cvsps=builtin
136 --config convert.cvsps=builtin
137 and has a few more configurable options:
137 and has a few more configurable options:
138 --config convert.cvsps.cache=True (boolean)
138 --config convert.cvsps.cache=True (boolean)
139 Set to False to disable remote log caching, for testing and
139 Set to False to disable remote log caching, for testing and
140 debugging purposes.
140 debugging purposes.
141 --config convert.cvsps.fuzz=60 (integer)
141 --config convert.cvsps.fuzz=60 (integer)
142 Specify the maximum time (in seconds) that is allowed
142 Specify the maximum time (in seconds) that is allowed
143 between commits with identical user and log message in a
143 between commits with identical user and log message in a
144 single changeset. When very large files were checked in as
144 single changeset. When very large files were checked in as
145 part of a changeset then the default may not be long
145 part of a changeset then the default may not be long
146 enough.
146 enough.
147 --config convert.cvsps.mergeto='{{mergetobranch ([-\w]+)}}'
147 --config convert.cvsps.mergeto='{{mergetobranch ([-\\w]+)}}'
148 Specify a regular expression to which commit log messages
148 Specify a regular expression to which commit log messages
149 are matched. If a match occurs, then the conversion
149 are matched. If a match occurs, then the conversion
150 process will insert a dummy revision merging the branch on
150 process will insert a dummy revision merging the branch on
151 which this log message occurs to the branch indicated in
151 which this log message occurs to the branch indicated in
152 the regex.
152 the regex.
153 --config convert.cvsps.mergefrom='{{mergefrombranch ([-\w]+)}}'
153 --config convert.cvsps.mergefrom='{{mergefrombranch ([-\\w]+)}}'
154 Specify a regular expression to which commit log messages
154 Specify a regular expression to which commit log messages
155 are matched. If a match occurs, then the conversion
155 are matched. If a match occurs, then the conversion
156 process will add the most recent revision on the branch
156 process will add the most recent revision on the branch
157 indicated in the regex as the second parent of the
157 indicated in the regex as the second parent of the
158 changeset.
158 changeset.
159
159
160 The hgext/convert/cvsps wrapper script allows the builtin
160 The hgext/convert/cvsps wrapper script allows the builtin
161 changeset merging code to be run without doing a conversion. Its
161 changeset merging code to be run without doing a conversion. Its
162 parameters and output are similar to that of cvsps 2.1.
162 parameters and output are similar to that of cvsps 2.1.
163
163
164 Subversion Source
164 Subversion Source
165 -----------------
165 -----------------
166
166
167 Subversion source detects classical trunk/branches/tags layouts.
167 Subversion source detects classical trunk/branches/tags layouts.
168 By default, the supplied "svn://repo/path/" source URL is
168 By default, the supplied "svn://repo/path/" source URL is
169 converted as a single branch. If "svn://repo/path/trunk" exists it
169 converted as a single branch. If "svn://repo/path/trunk" exists it
170 replaces the default branch. If "svn://repo/path/branches" exists,
170 replaces the default branch. If "svn://repo/path/branches" exists,
171 its subdirectories are listed as possible branches. If
171 its subdirectories are listed as possible branches. If
172 "svn://repo/path/tags" exists, it is looked for tags referencing
172 "svn://repo/path/tags" exists, it is looked for tags referencing
173 converted branches. Default "trunk", "branches" and "tags" values
173 converted branches. Default "trunk", "branches" and "tags" values
174 can be overriden with following options. Set them to paths
174 can be overriden with following options. Set them to paths
175 relative to the source URL, or leave them blank to disable
175 relative to the source URL, or leave them blank to disable
176 autodetection.
176 autodetection.
177
177
178 --config convert.svn.branches=branches (directory name)
178 --config convert.svn.branches=branches (directory name)
179 specify the directory containing branches
179 specify the directory containing branches
180 --config convert.svn.tags=tags (directory name)
180 --config convert.svn.tags=tags (directory name)
181 specify the directory containing tags
181 specify the directory containing tags
182 --config convert.svn.trunk=trunk (directory name)
182 --config convert.svn.trunk=trunk (directory name)
183 specify the name of the trunk branch
183 specify the name of the trunk branch
184
184
185 Source history can be retrieved starting at a specific revision,
185 Source history can be retrieved starting at a specific revision,
186 instead of being integrally converted. Only single branch
186 instead of being integrally converted. Only single branch
187 conversions are supported.
187 conversions are supported.
188
188
189 --config convert.svn.startrev=0 (svn revision number)
189 --config convert.svn.startrev=0 (svn revision number)
190 specify start Subversion revision.
190 specify start Subversion revision.
191
191
192 Perforce Source
192 Perforce Source
193 ---------------
193 ---------------
194
194
195 The Perforce (P4) importer can be given a p4 depot path or a
195 The Perforce (P4) importer can be given a p4 depot path or a
196 client specification as source. It will convert all files in the
196 client specification as source. It will convert all files in the
197 source to a flat Mercurial repository, ignoring labels, branches
197 source to a flat Mercurial repository, ignoring labels, branches
198 and integrations. Note that when a depot path is given you then
198 and integrations. Note that when a depot path is given you then
199 usually should specify a target directory, because otherwise the
199 usually should specify a target directory, because otherwise the
200 target may be named ...-hg.
200 target may be named ...-hg.
201
201
202 It is possible to limit the amount of source history to be
202 It is possible to limit the amount of source history to be
203 converted by specifying an initial Perforce revision.
203 converted by specifying an initial Perforce revision.
204
204
205 --config convert.p4.startrev=0 (perforce changelist number)
205 --config convert.p4.startrev=0 (perforce changelist number)
206 specify initial Perforce revision.
206 specify initial Perforce revision.
207
207
208
208
209 Mercurial Destination
209 Mercurial Destination
210 ---------------------
210 ---------------------
211
211
212 --config convert.hg.clonebranches=False (boolean)
212 --config convert.hg.clonebranches=False (boolean)
213 dispatch source branches in separate clones.
213 dispatch source branches in separate clones.
214 --config convert.hg.tagsbranch=default (branch name)
214 --config convert.hg.tagsbranch=default (branch name)
215 tag revisions branch name
215 tag revisions branch name
216 --config convert.hg.usebranchnames=True (boolean)
216 --config convert.hg.usebranchnames=True (boolean)
217 preserve branch names
217 preserve branch names
218
218
219 """
219 """
220 return convcmd.convert(ui, src, dest, revmapfile, **opts)
220 return convcmd.convert(ui, src, dest, revmapfile, **opts)
221
221
222 def debugsvnlog(ui, **opts):
222 def debugsvnlog(ui, **opts):
223 return subversion.debugsvnlog(ui, **opts)
223 return subversion.debugsvnlog(ui, **opts)
224
224
225 def debugcvsps(ui, *args, **opts):
225 def debugcvsps(ui, *args, **opts):
226 '''create changeset information from CVS
226 '''create changeset information from CVS
227
227
228 This command is intended as a debugging tool for the CVS to
228 This command is intended as a debugging tool for the CVS to
229 Mercurial converter, and can be used as a direct replacement for
229 Mercurial converter, and can be used as a direct replacement for
230 cvsps.
230 cvsps.
231
231
232 Hg debugcvsps reads the CVS rlog for current directory (or any
232 Hg debugcvsps reads the CVS rlog for current directory (or any
233 named directory) in the CVS repository, and converts the log to a
233 named directory) in the CVS repository, and converts the log to a
234 series of changesets based on matching commit log entries and
234 series of changesets based on matching commit log entries and
235 dates.'''
235 dates.'''
236 return cvsps.debugcvsps(ui, *args, **opts)
236 return cvsps.debugcvsps(ui, *args, **opts)
237
237
238 commands.norepo += " convert debugsvnlog debugcvsps"
238 commands.norepo += " convert debugsvnlog debugcvsps"
239
239
240 cmdtable = {
240 cmdtable = {
241 "convert":
241 "convert":
242 (convert,
242 (convert,
243 [('A', 'authors', '', _('username mapping filename')),
243 [('A', 'authors', '', _('username mapping filename')),
244 ('d', 'dest-type', '', _('destination repository type')),
244 ('d', 'dest-type', '', _('destination repository type')),
245 ('', 'filemap', '', _('remap file names using contents of file')),
245 ('', 'filemap', '', _('remap file names using contents of file')),
246 ('r', 'rev', '', _('import up to target revision REV')),
246 ('r', 'rev', '', _('import up to target revision REV')),
247 ('s', 'source-type', '', _('source repository type')),
247 ('s', 'source-type', '', _('source repository type')),
248 ('', 'splicemap', '', _('splice synthesized history into place')),
248 ('', 'splicemap', '', _('splice synthesized history into place')),
249 ('', 'branchmap', '', _('change branch names while converting')),
249 ('', 'branchmap', '', _('change branch names while converting')),
250 ('', 'datesort', None, _('try to sort changesets by date'))],
250 ('', 'datesort', None, _('try to sort changesets by date'))],
251 _('hg convert [OPTION]... SOURCE [DEST [REVMAP]]')),
251 _('hg convert [OPTION]... SOURCE [DEST [REVMAP]]')),
252 "debugsvnlog":
252 "debugsvnlog":
253 (debugsvnlog,
253 (debugsvnlog,
254 [],
254 [],
255 'hg debugsvnlog'),
255 'hg debugsvnlog'),
256 "debugcvsps":
256 "debugcvsps":
257 (debugcvsps,
257 (debugcvsps,
258 [
258 [
259 # Main options shared with cvsps-2.1
259 # Main options shared with cvsps-2.1
260 ('b', 'branches', [], _('only return changes on specified branches')),
260 ('b', 'branches', [], _('only return changes on specified branches')),
261 ('p', 'prefix', '', _('prefix to remove from file names')),
261 ('p', 'prefix', '', _('prefix to remove from file names')),
262 ('r', 'revisions', [], _('only return changes after or between specified tags')),
262 ('r', 'revisions', [], _('only return changes after or between specified tags')),
263 ('u', 'update-cache', None, _("update cvs log cache")),
263 ('u', 'update-cache', None, _("update cvs log cache")),
264 ('x', 'new-cache', None, _("create new cvs log cache")),
264 ('x', 'new-cache', None, _("create new cvs log cache")),
265 ('z', 'fuzz', 60, _('set commit time fuzz in seconds')),
265 ('z', 'fuzz', 60, _('set commit time fuzz in seconds')),
266 ('', 'root', '', _('specify cvsroot')),
266 ('', 'root', '', _('specify cvsroot')),
267 # Options specific to builtin cvsps
267 # Options specific to builtin cvsps
268 ('', 'parents', '', _('show parent changesets')),
268 ('', 'parents', '', _('show parent changesets')),
269 ('', 'ancestors', '', _('show current changeset in ancestor branches')),
269 ('', 'ancestors', '', _('show current changeset in ancestor branches')),
270 # Options that are ignored for compatibility with cvsps-2.1
270 # Options that are ignored for compatibility with cvsps-2.1
271 ('A', 'cvs-direct', None, _('ignored for compatibility')),
271 ('A', 'cvs-direct', None, _('ignored for compatibility')),
272 ],
272 ],
273 _('hg debugcvsps [OPTION]... [PATH]...')),
273 _('hg debugcvsps [OPTION]... [PATH]...')),
274 }
274 }
@@ -1,246 +1,246 b''
1 # Copyright (C) 2007-8 Brendan Cully <brendan@kublai.com>
1 # Copyright (C) 2007-8 Brendan Cully <brendan@kublai.com>
2 # Published under the GNU GPL
2 # Published under the GNU GPL
3
3
4 """CIA notification
4 """CIA notification
5
5
6 This is meant to be run as a changegroup or incoming hook.
6 This is meant to be run as a changegroup or incoming hook.
7 To configure it, set the following options in your hgrc:
7 To configure it, set the following options in your hgrc:
8
8
9 [cia]
9 [cia]
10 # your registered CIA user name
10 # your registered CIA user name
11 user = foo
11 user = foo
12 # the name of the project in CIA
12 # the name of the project in CIA
13 project = foo
13 project = foo
14 # the module (subproject) (optional)
14 # the module (subproject) (optional)
15 #module = foo
15 #module = foo
16 # Append a diffstat to the log message (optional)
16 # Append a diffstat to the log message (optional)
17 #diffstat = False
17 #diffstat = False
18 # Template to use for log messages (optional)
18 # Template to use for log messages (optional)
19 #template = {desc}\n{baseurl}/rev/{node}-- {diffstat}
19 #template = {desc}\\n{baseurl}/rev/{node}-- {diffstat}
20 # Style to use (optional)
20 # Style to use (optional)
21 #style = foo
21 #style = foo
22 # The URL of the CIA notification service (optional)
22 # The URL of the CIA notification service (optional)
23 # You can use mailto: URLs to send by email, eg
23 # You can use mailto: URLs to send by email, eg
24 # mailto:cia@cia.vc
24 # mailto:cia@cia.vc
25 # Make sure to set email.from if you do this.
25 # Make sure to set email.from if you do this.
26 #url = http://cia.vc/
26 #url = http://cia.vc/
27 # print message instead of sending it (optional)
27 # print message instead of sending it (optional)
28 #test = False
28 #test = False
29
29
30 [hooks]
30 [hooks]
31 # one of these:
31 # one of these:
32 changegroup.cia = python:hgcia.hook
32 changegroup.cia = python:hgcia.hook
33 #incoming.cia = python:hgcia.hook
33 #incoming.cia = python:hgcia.hook
34
34
35 [web]
35 [web]
36 # If you want hyperlinks (optional)
36 # If you want hyperlinks (optional)
37 baseurl = http://server/path/to/repo
37 baseurl = http://server/path/to/repo
38 """
38 """
39
39
40 from mercurial.i18n import _
40 from mercurial.i18n import _
41 from mercurial.node import *
41 from mercurial.node import *
42 from mercurial import cmdutil, patch, templater, util, mail
42 from mercurial import cmdutil, patch, templater, util, mail
43 import email.Parser
43 import email.Parser
44
44
45 import xmlrpclib
45 import xmlrpclib
46 from xml.sax import saxutils
46 from xml.sax import saxutils
47
47
48 socket_timeout = 30 # seconds
48 socket_timeout = 30 # seconds
49 try:
49 try:
50 # set a timeout for the socket so you don't have to wait so looooong
50 # set a timeout for the socket so you don't have to wait so looooong
51 # when cia.vc is having problems. requires python >= 2.3:
51 # when cia.vc is having problems. requires python >= 2.3:
52 import socket
52 import socket
53 socket.setdefaulttimeout(socket_timeout)
53 socket.setdefaulttimeout(socket_timeout)
54 except:
54 except:
55 pass
55 pass
56
56
57 HGCIA_VERSION = '0.1'
57 HGCIA_VERSION = '0.1'
58 HGCIA_URL = 'http://hg.kublai.com/mercurial/hgcia'
58 HGCIA_URL = 'http://hg.kublai.com/mercurial/hgcia'
59
59
60
60
61 class ciamsg(object):
61 class ciamsg(object):
62 """ A CIA message """
62 """ A CIA message """
63 def __init__(self, cia, ctx):
63 def __init__(self, cia, ctx):
64 self.cia = cia
64 self.cia = cia
65 self.ctx = ctx
65 self.ctx = ctx
66 self.url = self.cia.url
66 self.url = self.cia.url
67
67
68 def fileelem(self, path, uri, action):
68 def fileelem(self, path, uri, action):
69 if uri:
69 if uri:
70 uri = ' uri=%s' % saxutils.quoteattr(uri)
70 uri = ' uri=%s' % saxutils.quoteattr(uri)
71 return '<file%s action=%s>%s</file>' % (
71 return '<file%s action=%s>%s</file>' % (
72 uri, saxutils.quoteattr(action), saxutils.escape(path))
72 uri, saxutils.quoteattr(action), saxutils.escape(path))
73
73
74 def fileelems(self):
74 def fileelems(self):
75 n = self.ctx.node()
75 n = self.ctx.node()
76 f = self.cia.repo.status(self.ctx.parents()[0].node(), n)
76 f = self.cia.repo.status(self.ctx.parents()[0].node(), n)
77 url = self.url or ''
77 url = self.url or ''
78 elems = []
78 elems = []
79 for path in f[0]:
79 for path in f[0]:
80 uri = '%s/diff/%s/%s' % (url, short(n), path)
80 uri = '%s/diff/%s/%s' % (url, short(n), path)
81 elems.append(self.fileelem(path, url and uri, 'modify'))
81 elems.append(self.fileelem(path, url and uri, 'modify'))
82 for path in f[1]:
82 for path in f[1]:
83 # TODO: copy/rename ?
83 # TODO: copy/rename ?
84 uri = '%s/file/%s/%s' % (url, short(n), path)
84 uri = '%s/file/%s/%s' % (url, short(n), path)
85 elems.append(self.fileelem(path, url and uri, 'add'))
85 elems.append(self.fileelem(path, url and uri, 'add'))
86 for path in f[2]:
86 for path in f[2]:
87 elems.append(self.fileelem(path, '', 'remove'))
87 elems.append(self.fileelem(path, '', 'remove'))
88
88
89 return '\n'.join(elems)
89 return '\n'.join(elems)
90
90
91 def sourceelem(self, project, module=None, branch=None):
91 def sourceelem(self, project, module=None, branch=None):
92 msg = ['<source>', '<project>%s</project>' % saxutils.escape(project)]
92 msg = ['<source>', '<project>%s</project>' % saxutils.escape(project)]
93 if module:
93 if module:
94 msg.append('<module>%s</module>' % saxutils.escape(module))
94 msg.append('<module>%s</module>' % saxutils.escape(module))
95 if branch:
95 if branch:
96 msg.append('<branch>%s</branch>' % saxutils.escape(branch))
96 msg.append('<branch>%s</branch>' % saxutils.escape(branch))
97 msg.append('</source>')
97 msg.append('</source>')
98
98
99 return '\n'.join(msg)
99 return '\n'.join(msg)
100
100
101 def diffstat(self):
101 def diffstat(self):
102 class patchbuf:
102 class patchbuf:
103 def __init__(self):
103 def __init__(self):
104 self.lines = []
104 self.lines = []
105 # diffstat is stupid
105 # diffstat is stupid
106 self.name = 'cia'
106 self.name = 'cia'
107 def write(self, data):
107 def write(self, data):
108 self.lines.append(data)
108 self.lines.append(data)
109 def close(self):
109 def close(self):
110 pass
110 pass
111
111
112 n = self.ctx.node()
112 n = self.ctx.node()
113 pbuf = patchbuf()
113 pbuf = patchbuf()
114 patch.export(self.cia.repo, [n], fp=pbuf)
114 patch.export(self.cia.repo, [n], fp=pbuf)
115 return patch.diffstat(pbuf.lines) or ''
115 return patch.diffstat(pbuf.lines) or ''
116
116
117 def logmsg(self):
117 def logmsg(self):
118 diffstat = self.cia.diffstat and self.diffstat() or ''
118 diffstat = self.cia.diffstat and self.diffstat() or ''
119 self.cia.ui.pushbuffer()
119 self.cia.ui.pushbuffer()
120 self.cia.templater.show(self.ctx, changes=self.ctx.changeset(),
120 self.cia.templater.show(self.ctx, changes=self.ctx.changeset(),
121 url=self.cia.url, diffstat=diffstat)
121 url=self.cia.url, diffstat=diffstat)
122 return self.cia.ui.popbuffer()
122 return self.cia.ui.popbuffer()
123
123
124 def xml(self):
124 def xml(self):
125 n = short(self.ctx.node())
125 n = short(self.ctx.node())
126 src = self.sourceelem(self.cia.project, module=self.cia.module,
126 src = self.sourceelem(self.cia.project, module=self.cia.module,
127 branch=self.ctx.branch())
127 branch=self.ctx.branch())
128 # unix timestamp
128 # unix timestamp
129 dt = self.ctx.date()
129 dt = self.ctx.date()
130 timestamp = dt[0]
130 timestamp = dt[0]
131
131
132 author = saxutils.escape(self.ctx.user())
132 author = saxutils.escape(self.ctx.user())
133 rev = '%d:%s' % (self.ctx.rev(), n)
133 rev = '%d:%s' % (self.ctx.rev(), n)
134 log = saxutils.escape(self.logmsg())
134 log = saxutils.escape(self.logmsg())
135
135
136 url = self.url and '<url>%s/rev/%s</url>' % (saxutils.escape(self.url),
136 url = self.url and '<url>%s/rev/%s</url>' % (saxutils.escape(self.url),
137 n) or ''
137 n) or ''
138
138
139 msg = """
139 msg = """
140 <message>
140 <message>
141 <generator>
141 <generator>
142 <name>Mercurial (hgcia)</name>
142 <name>Mercurial (hgcia)</name>
143 <version>%s</version>
143 <version>%s</version>
144 <url>%s</url>
144 <url>%s</url>
145 <user>%s</user>
145 <user>%s</user>
146 </generator>
146 </generator>
147 %s
147 %s
148 <body>
148 <body>
149 <commit>
149 <commit>
150 <author>%s</author>
150 <author>%s</author>
151 <version>%s</version>
151 <version>%s</version>
152 <log>%s</log>
152 <log>%s</log>
153 %s
153 %s
154 <files>%s</files>
154 <files>%s</files>
155 </commit>
155 </commit>
156 </body>
156 </body>
157 <timestamp>%d</timestamp>
157 <timestamp>%d</timestamp>
158 </message>
158 </message>
159 """ % \
159 """ % \
160 (HGCIA_VERSION, saxutils.escape(HGCIA_URL),
160 (HGCIA_VERSION, saxutils.escape(HGCIA_URL),
161 saxutils.escape(self.cia.user), src, author, rev, log, url,
161 saxutils.escape(self.cia.user), src, author, rev, log, url,
162 self.fileelems(), timestamp)
162 self.fileelems(), timestamp)
163
163
164 return msg
164 return msg
165
165
166
166
167 class hgcia(object):
167 class hgcia(object):
168 """ CIA notification class """
168 """ CIA notification class """
169
169
170 deftemplate = '{desc}'
170 deftemplate = '{desc}'
171 dstemplate = '{desc}\n-- \n{diffstat}'
171 dstemplate = '{desc}\n-- \n{diffstat}'
172
172
173 def __init__(self, ui, repo):
173 def __init__(self, ui, repo):
174 self.ui = ui
174 self.ui = ui
175 self.repo = repo
175 self.repo = repo
176
176
177 self.ciaurl = self.ui.config('cia', 'url', 'http://cia.vc')
177 self.ciaurl = self.ui.config('cia', 'url', 'http://cia.vc')
178 self.user = self.ui.config('cia', 'user')
178 self.user = self.ui.config('cia', 'user')
179 self.project = self.ui.config('cia', 'project')
179 self.project = self.ui.config('cia', 'project')
180 self.module = self.ui.config('cia', 'module')
180 self.module = self.ui.config('cia', 'module')
181 self.diffstat = self.ui.configbool('cia', 'diffstat')
181 self.diffstat = self.ui.configbool('cia', 'diffstat')
182 self.emailfrom = self.ui.config('email', 'from')
182 self.emailfrom = self.ui.config('email', 'from')
183 self.dryrun = self.ui.configbool('cia', 'test')
183 self.dryrun = self.ui.configbool('cia', 'test')
184 self.url = self.ui.config('web', 'baseurl')
184 self.url = self.ui.config('web', 'baseurl')
185
185
186 style = self.ui.config('cia', 'style')
186 style = self.ui.config('cia', 'style')
187 template = self.ui.config('cia', 'template')
187 template = self.ui.config('cia', 'template')
188 if not template:
188 if not template:
189 template = self.diffstat and self.dstemplate or self.deftemplate
189 template = self.diffstat and self.dstemplate or self.deftemplate
190 template = templater.parsestring(template, quoted=False)
190 template = templater.parsestring(template, quoted=False)
191 t = cmdutil.changeset_templater(self.ui, self.repo, False, None,
191 t = cmdutil.changeset_templater(self.ui, self.repo, False, None,
192 style, False)
192 style, False)
193 t.use_template(template)
193 t.use_template(template)
194 self.templater = t
194 self.templater = t
195
195
196 def sendrpc(self, msg):
196 def sendrpc(self, msg):
197 srv = xmlrpclib.Server(self.ciaurl)
197 srv = xmlrpclib.Server(self.ciaurl)
198 srv.hub.deliver(msg)
198 srv.hub.deliver(msg)
199
199
200 def sendemail(self, address, data):
200 def sendemail(self, address, data):
201 p = email.Parser.Parser()
201 p = email.Parser.Parser()
202 msg = p.parsestr(data)
202 msg = p.parsestr(data)
203 msg['Date'] = util.datestr(format="%a, %d %b %Y %H:%M:%S %1%2")
203 msg['Date'] = util.datestr(format="%a, %d %b %Y %H:%M:%S %1%2")
204 msg['To'] = address
204 msg['To'] = address
205 msg['From'] = self.emailfrom
205 msg['From'] = self.emailfrom
206 msg['Subject'] = 'DeliverXML'
206 msg['Subject'] = 'DeliverXML'
207 msg['Content-type'] = 'text/xml'
207 msg['Content-type'] = 'text/xml'
208 msgtext = msg.as_string(0)
208 msgtext = msg.as_string(0)
209
209
210 self.ui.status(_('hgcia: sending update to %s\n') % address)
210 self.ui.status(_('hgcia: sending update to %s\n') % address)
211 mail.sendmail(self.ui, util.email(self.emailfrom),
211 mail.sendmail(self.ui, util.email(self.emailfrom),
212 [address], msgtext)
212 [address], msgtext)
213
213
214
214
215 def hook(ui, repo, hooktype, node=None, url=None, **kwargs):
215 def hook(ui, repo, hooktype, node=None, url=None, **kwargs):
216 """ send CIA notification """
216 """ send CIA notification """
217 def sendmsg(cia, ctx):
217 def sendmsg(cia, ctx):
218 msg = ciamsg(cia, ctx).xml()
218 msg = ciamsg(cia, ctx).xml()
219 if cia.dryrun:
219 if cia.dryrun:
220 ui.write(msg)
220 ui.write(msg)
221 elif cia.ciaurl.startswith('mailto:'):
221 elif cia.ciaurl.startswith('mailto:'):
222 if not cia.emailfrom:
222 if not cia.emailfrom:
223 raise util.Abort(_('email.from must be defined when '
223 raise util.Abort(_('email.from must be defined when '
224 'sending by email'))
224 'sending by email'))
225 cia.sendemail(cia.ciaurl[7:], msg)
225 cia.sendemail(cia.ciaurl[7:], msg)
226 else:
226 else:
227 cia.sendrpc(msg)
227 cia.sendrpc(msg)
228
228
229 n = bin(node)
229 n = bin(node)
230 cia = hgcia(ui, repo)
230 cia = hgcia(ui, repo)
231 if not cia.user:
231 if not cia.user:
232 ui.debug(_('cia: no user specified'))
232 ui.debug(_('cia: no user specified'))
233 return
233 return
234 if not cia.project:
234 if not cia.project:
235 ui.debug(_('cia: no project specified'))
235 ui.debug(_('cia: no project specified'))
236 return
236 return
237 if hooktype == 'changegroup':
237 if hooktype == 'changegroup':
238 start = repo.changelog.rev(n)
238 start = repo.changelog.rev(n)
239 end = len(repo.changelog)
239 end = len(repo.changelog)
240 for rev in xrange(start, end):
240 for rev in xrange(start, end):
241 n = repo.changelog.node(rev)
241 n = repo.changelog.node(rev)
242 ctx = repo.changectx(n)
242 ctx = repo.changectx(n)
243 sendmsg(cia, ctx)
243 sendmsg(cia, ctx)
244 else:
244 else:
245 ctx = repo.changectx(n)
245 ctx = repo.changectx(n)
246 sendmsg(cia, ctx)
246 sendmsg(cia, ctx)
General Comments 0
You need to be logged in to leave comments. Login now