##// END OF EJS Templates
Merge with stable
Steve Borho -
r10512:261cc6b0 merge default
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -0,0 +1,31 b''
1 WiX installer source files
2 ==========================
3
4 The files in this folder are used by the thg-winbuild [1] package
5 building architecture to create a Mercurial MSI installer. These files
6 are versioned within the Mercurial source tree because the WXS files
7 must kept up to date with distribution changes within their branch. In
8 other words, the default branch WXS files are expected to diverge from
9 the stable branch WXS files. Storing them within the same repository is
10 the only sane way to keep the the source tree and the installer in sync.
11
12 The MSI installer builder uses only the mercurial.ini file from the
13 contrib/win32 folder, the contents of which have been historically used
14 to create an InnoSetup based installer. The rest of the files there are
15 ignored.
16
17 The MSI packages built by thg-winbuild require elevated (admin)
18 privileges to be installed due to the installation of MSVC CRT libraries
19 under the C:\WINDOWS\WinSxS folder. Thus the InnoSetup installers may
20 still be useful to some users.
21
22 To build your own MSI packages, clone the thg-winbuild [1] repository
23 and follow the README.txt [2] instructions closely. There are fewer
24 prerequisites for a WiX [3] installer than an InnoSetup installer, but
25 they are more specific.
26
27 Direct questions or comments to Steve Borho <steve@borho.org>
28
29 [1] http://bitbucket.org/tortoisehg/thg-winbuild
30 [2] http://bitbucket.org/tortoisehg/thg-winbuild/src/tip/README.txt
31 [3] http://wix.sourceforge.net/
@@ -0,0 +1,218 b''
1 <?xml version="1.0" encoding="utf-8"?>
2 <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
3 <Fragment>
4 <ComponentGroup Id="contribFolder">
5 <Component Id="cmp807F426F34EC5B808607BA4007244E84" Directory="dirCA1E322A30AE0A461BB6A083C7A87627" Guid="{4D96B8DF-A686-42A8-AD81-E2CB4324C697}">
6 <File Id="fil0BF2562805D37A6C57219F945DC7C3B4" KeyPath="yes" Source="$(var.SourceDir)\bash_completion" />
7 </Component>
8 <Component Id="cmpB28139F1930563D83220AD2C1128B030" Directory="dirCA1E322A30AE0A461BB6A083C7A87627" Guid="{89D69124-1ED8-429B-AAC8-BC3F2B4F2B80}">
9 <File Id="fil8A6DCD57765E3E3FEF179102A0906054" KeyPath="yes" Source="$(var.SourceDir)\hgk" />
10 </Component>
11 <Component Id="cmpB7DA2E5DE9E5C7DDB7696E436D98B142" Directory="dirCA1E322A30AE0A461BB6A083C7A87627" Guid="{C1B3D111-3A62-4B6A-A39B-1DCECEDA08EC}">
12 <File Id="fil18816C74E6F00D731B7DBE0870BDE2D9" KeyPath="yes" Source="$(var.SourceDir)\hgwebdir.fcgi" />
13 </Component>
14 <Component Id="cmpB1DF769846D452FC44EBA6763F12D29F" Directory="dirCA1E322A30AE0A461BB6A083C7A87627" Guid="{63CCAE00-5F02-4AB2-84A9-B11780E618AA}">
15 <File Id="fil5896BB6DFC36A92D41E0AB8C7DB80A97" KeyPath="yes" Source="$(var.SourceDir)\hgwebdir.wsgi" />
16 </Component>
17 <Component Id="cmp7C407D087BF1947C9EC8AFE24B42CD9B" Directory="dirCA1E322A30AE0A461BB6A083C7A87627" Guid="{1200C67E-0A52-462E-AC7B-CDC933F43ACD}">
18 <File Id="fil81E357676284E64EDFEBE9FE5CEB872F" KeyPath="yes" Source="$(var.SourceDir)\logo-droplets.svg" />
19 </Component>
20 <Component Id="cmp6946EABE287930CA55C76B0961DE17E7" Directory="dirCA1E322A30AE0A461BB6A083C7A87627" Guid="{E014BC0E-B6CA-4020-9302-CB503904A4FB}">
21 <File Id="filA77C0B15558EB3894B9E728D2FE3AF9F" KeyPath="yes" Source="$(var.SourceDir)\mercurial.el" />
22 </Component>
23 <Component Id="cmpBEDE8604BDE5144025036F4BB04679B2" Directory="dirCA1E322A30AE0A461BB6A083C7A87627" Guid="{3BB12008-A453-4A1B-B427-F97ADD127ED7}">
24 <File Id="fil3F209A8D711EA35E5B38E05678D0D5E2" KeyPath="yes" Source="$(var.SourceDir)\sample.hgrc" />
25 </Component>
26 <Component Id="cmpED03CB84DEE74E99BB62BE0F8AC356F1" Directory="dirCA1E322A30AE0A461BB6A083C7A87627" Guid="{BE0C6DC7-A91A-4390-A111-54D109148514}">
27 <File Id="filCD2BF173510DF04EA722FE4924B3DAE7" KeyPath="yes" Source="$(var.SourceDir)\tcsh_completion" />
28 </Component>
29 <Component Id="cmp7395336B1B93AB450D822FA0DE0C5FCB" Directory="dirCA1E322A30AE0A461BB6A083C7A87627" Guid="{8F376341-1DEF-419D-8D88-17F393C5F3CA}">
30 <File Id="fil2C004105E9F354CA4156678C4FB76823" KeyPath="yes" Source="$(var.SourceDir)\tcsh_completion_build.sh" />
31 </Component>
32 <Component Id="cmpA914A0A1896C5E91A8381B2B5491564C" Directory="dirCA1E322A30AE0A461BB6A083C7A87627" Guid="{87C90FF2-43D5-4F81-93C2-295E4C7D7718}">
33 <File Id="filBF6BED4F17C843169CEBE289E39FC8FB" KeyPath="yes" Source="$(var.SourceDir)\xml.rnc" />
34 </Component>
35 <Component Id="cmp512D2C19167594DBED53CF4A5896DAC1" Directory="dirCA1E322A30AE0A461BB6A083C7A87627" Guid="{6BAA6FEC-4C90-4B97-A330-A60B34BE0A5A}">
36 <File Id="filA8F4DBB6CE376C77AA79D9E9D715A519" KeyPath="yes" Source="$(var.SourceDir)\zsh_completion" />
37 </Component>
38 <Component Id="cmp82B1C0B5D90172AD872BBE93AD1A4746" Directory="dirBED34C623F6A770B7C7A5C815434688D" Guid="{7EAE9937-D488-43A4-8EED-A726711C02D5}">
39 <File Id="filBB7FA67E9542AD28BADC33AE9FE32A34" KeyPath="yes" Source="$(var.SourceDir)\vim\hg-menu.vim" />
40 </Component>
41 <Component Id="cmp86D915E62A0C3FF73A370E0D48DDE5F1" Directory="dirBED34C623F6A770B7C7A5C815434688D" Guid="{1ECD4227-1385-4073-95E9-5346A21E92F2}">
42 <File Id="filC508D5BC3A9EAE1F33C95F8EF07EF431" KeyPath="yes" Source="$(var.SourceDir)\vim\HGAnnotate.vim" />
43 </Component>
44 <Component Id="cmp6F11D903E1DB0DFC0C3EB42F63394E60" Directory="dirBED34C623F6A770B7C7A5C815434688D" Guid="{41DE72AC-B7BC-489F-82D8-EFB39E280910}">
45 <File Id="filDAC324AFC5566758101A7423165FE7FA" KeyPath="yes" Source="$(var.SourceDir)\vim\hgcommand.vim" />
46 </Component>
47 <Component Id="cmpA55369F11281B52F404E0724200C115F" Directory="dirBED34C623F6A770B7C7A5C815434688D" Guid="{F72CA80F-E453-40C5-AFFD-875C46B7C7D7}">
48 <File Id="fil982E8F72370A711566A7F7645AC8204D" KeyPath="yes" Source="$(var.SourceDir)\vim\patchreview.txt" />
49 </Component>
50 <Component Id="cmp56F529456B47B91250785C4D0A205ED2" Directory="dirBED34C623F6A770B7C7A5C815434688D" Guid="{02A22346-ECA7-4C70-AF97-974550F0D8D3}">
51 <File Id="filC59985C7F91EC21A42FEF97AD3CDD65D" KeyPath="yes" Source="$(var.SourceDir)\vim\patchreview.vim" />
52 </Component>
53 </ComponentGroup>
54 </Fragment>
55 <Fragment>
56 <DirectoryRef Id="INSTALLDIR">
57 <Directory Id="dirCA1E322A30AE0A461BB6A083C7A87627" Name="contrib" />
58 </DirectoryRef>
59 </Fragment>
60 <Fragment>
61 <DirectoryRef Id="dirCA1E322A30AE0A461BB6A083C7A87627" />
62 </Fragment>
63 <Fragment>
64 <DirectoryRef Id="dir0AAB909DA146B054F15679730FA6EDC4" />
65 </Fragment>
66 <Fragment>
67 <DirectoryRef Id="dirCA1E322A30AE0A461BB6A083C7A87627" />
68 </Fragment>
69 <Fragment>
70 <DirectoryRef Id="dirCA1E322A30AE0A461BB6A083C7A87627" />
71 </Fragment>
72 <Fragment>
73 <DirectoryRef Id="dirCA1E322A30AE0A461BB6A083C7A87627" />
74 </Fragment>
75 <Fragment>
76 <DirectoryRef Id="dir0AAB909DA146B054F15679730FA6EDC4" />
77 </Fragment>
78 <Fragment>
79 <DirectoryRef Id="dir0AAB909DA146B054F15679730FA6EDC4" />
80 </Fragment>
81 <Fragment>
82 <DirectoryRef Id="dirCA1E322A30AE0A461BB6A083C7A87627" />
83 </Fragment>
84 <Fragment>
85 <DirectoryRef Id="dirCA1E322A30AE0A461BB6A083C7A87627" />
86 </Fragment>
87 <Fragment>
88 <DirectoryRef Id="dirCA1E322A30AE0A461BB6A083C7A87627" />
89 </Fragment>
90 <Fragment>
91 <DirectoryRef Id="dirCA1E322A30AE0A461BB6A083C7A87627" />
92 </Fragment>
93 <Fragment>
94 <DirectoryRef Id="dirBED34C623F6A770B7C7A5C815434688D" />
95 </Fragment>
96 <Fragment>
97 <DirectoryRef Id="dirCA1E322A30AE0A461BB6A083C7A87627" />
98 </Fragment>
99 <Fragment>
100 <DirectoryRef Id="dirD2A98CA486D03F46DB07EE76569468D8" />
101 </Fragment>
102 <Fragment>
103 <DirectoryRef Id="dir0AAB909DA146B054F15679730FA6EDC4" />
104 </Fragment>
105 <Fragment>
106 <DirectoryRef Id="dirCA1E322A30AE0A461BB6A083C7A87627" />
107 </Fragment>
108 <Fragment>
109 <DirectoryRef Id="dirCA1E322A30AE0A461BB6A083C7A87627" />
110 </Fragment>
111 <Fragment>
112 <DirectoryRef Id="dirCA1E322A30AE0A461BB6A083C7A87627" />
113 </Fragment>
114 <Fragment>
115 <DirectoryRef Id="dirBED34C623F6A770B7C7A5C815434688D" />
116 </Fragment>
117 <Fragment>
118 <DirectoryRef Id="dirCA1E322A30AE0A461BB6A083C7A87627" />
119 </Fragment>
120 <Fragment>
121 <DirectoryRef Id="dir0AAB909DA146B054F15679730FA6EDC4" />
122 </Fragment>
123 <Fragment>
124 <DirectoryRef Id="dirCA1E322A30AE0A461BB6A083C7A87627" />
125 </Fragment>
126 <Fragment>
127 <DirectoryRef Id="dirCA1E322A30AE0A461BB6A083C7A87627" />
128 </Fragment>
129 <Fragment>
130 <DirectoryRef Id="dir51C59DA466E26BFDB5855AE6BE2E4BDD" />
131 </Fragment>
132 <Fragment>
133 <DirectoryRef Id="dir0AAB909DA146B054F15679730FA6EDC4" />
134 </Fragment>
135 <Fragment>
136 <DirectoryRef Id="dir0AAB909DA146B054F15679730FA6EDC4" />
137 </Fragment>
138 <Fragment>
139 <DirectoryRef Id="dirCA1E322A30AE0A461BB6A083C7A87627" />
140 </Fragment>
141 <Fragment>
142 <DirectoryRef Id="dirCA1E322A30AE0A461BB6A083C7A87627" />
143 </Fragment>
144 <Fragment>
145 <DirectoryRef Id="dirCA1E322A30AE0A461BB6A083C7A87627" />
146 </Fragment>
147 <Fragment>
148 <DirectoryRef Id="dirBED34C623F6A770B7C7A5C815434688D" />
149 </Fragment>
150 <Fragment>
151 <DirectoryRef Id="dirBED34C623F6A770B7C7A5C815434688D" />
152 </Fragment>
153 <Fragment>
154 <DirectoryRef Id="dir3F9A2BE78E3069AB8A904DF734C62404" />
155 </Fragment>
156 <Fragment>
157 <DirectoryRef Id="dir3F9A2BE78E3069AB8A904DF734C62404" />
158 </Fragment>
159 <Fragment>
160 <DirectoryRef Id="dirD2A98CA486D03F46DB07EE76569468D8" />
161 </Fragment>
162 <Fragment>
163 <DirectoryRef Id="dir3F9A2BE78E3069AB8A904DF734C62404" />
164 </Fragment>
165 <Fragment>
166 <DirectoryRef Id="dirBED34C623F6A770B7C7A5C815434688D" />
167 </Fragment>
168 <Fragment>
169 <DirectoryRef Id="dirCA1E322A30AE0A461BB6A083C7A87627" />
170 </Fragment>
171 <Fragment>
172 <DirectoryRef Id="dirCA1E322A30AE0A461BB6A083C7A87627" />
173 </Fragment>
174 <Fragment>
175 <DirectoryRef Id="dirCA1E322A30AE0A461BB6A083C7A87627" />
176 </Fragment>
177 <Fragment>
178 <DirectoryRef Id="dir3F9A2BE78E3069AB8A904DF734C62404" />
179 </Fragment>
180 <Fragment>
181 <DirectoryRef Id="dirCA1E322A30AE0A461BB6A083C7A87627" />
182 </Fragment>
183 <Fragment>
184 <DirectoryRef Id="dirCA1E322A30AE0A461BB6A083C7A87627" />
185 </Fragment>
186 <Fragment>
187 <DirectoryRef Id="dirCA1E322A30AE0A461BB6A083C7A87627" />
188 </Fragment>
189 <Fragment>
190 <DirectoryRef Id="dirCA1E322A30AE0A461BB6A083C7A87627" />
191 </Fragment>
192 <Fragment>
193 <DirectoryRef Id="dirCA1E322A30AE0A461BB6A083C7A87627" />
194 </Fragment>
195 <Fragment>
196 <DirectoryRef Id="dir51C59DA466E26BFDB5855AE6BE2E4BDD" />
197 </Fragment>
198 <Fragment>
199 <DirectoryRef Id="dir3F9A2BE78E3069AB8A904DF734C62404" />
200 </Fragment>
201 <Fragment>
202 <DirectoryRef Id="dir0AAB909DA146B054F15679730FA6EDC4" />
203 </Fragment>
204 <Fragment>
205 <DirectoryRef Id="dir51C59DA466E26BFDB5855AE6BE2E4BDD" />
206 </Fragment>
207 <Fragment>
208 <DirectoryRef Id="dirCA1E322A30AE0A461BB6A083C7A87627" />
209 </Fragment>
210 <Fragment>
211 <DirectoryRef Id="dirCA1E322A30AE0A461BB6A083C7A87627" />
212 </Fragment>
213 <Fragment>
214 <DirectoryRef Id="dirCA1E322A30AE0A461BB6A083C7A87627">
215 <Directory Id="dirBED34C623F6A770B7C7A5C815434688D" Name="vim" />
216 </DirectoryRef>
217 </Fragment>
218 </Wix>
@@ -0,0 +1,33 b''
1 <?xml version="1.0" encoding="utf-8"?>
2 <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
3 <Fragment>
4 <ComponentGroup Id="docFolder">
5 <Component Id="cmpD8AD113FB988F56EF745AF287E14D1BE" Directory="dir7C3635117912185773B74CE69A101DF6" Guid="{F7DDE641-6FD6-48B9-B869-C92927437CF0}">
6 <File Id="filFC7EC893C906DE77DE4D96687109D8E9" KeyPath="yes" Source="$(var.SourceDir)\hg.1.html">
7 <Shortcut Id="hg1StartMenu" Directory="ProgramMenuDir" Name="Mercurial Command Reference"
8 Icon="hgIcon.ico" IconIndex="0" Advertise="yes"/>
9 </File>
10 </Component>
11 <Component Id="cmpB89B9653438B9A899D3AF741EF3D50E7" Directory="dir7C3635117912185773B74CE69A101DF6" Guid="{6289969D-8286-47AE-97B2-2DCB3B6A9EE3}">
12 <File Id="filA7A1E3181858EAEA214687DBA9AEFB27" KeyPath="yes" Source="$(var.SourceDir)\hgignore.5.html" >
13 <Shortcut Id="hgignore5StartMenu" Directory="ProgramMenuDir" Name="Mercurial Ignore Files"
14 Icon="hgIcon.ico" IconIndex="0" Advertise="yes"/>
15 </File>
16 </Component>
17 <Component Id="cmp2759E68E80E0FCEA1ACE704650E93C2A" Directory="dir7C3635117912185773B74CE69A101DF6" Guid="{2B13EF1C-5B3A-4A36-85B8-59D8155FB454}">
18 <File Id="fil4B171255CC318D73C35538CAA0AFFC24" KeyPath="yes" Source="$(var.SourceDir)\hgrc.5.html" >
19 <Shortcut Id="hgrc5StartMenu" Directory="ProgramMenuDir" Name="Mercurial Configuration Files"
20 Icon="hgIcon.ico" IconIndex="0" Advertise="yes"/>
21 </File>
22 </Component>
23 <Component Id="cmp9ADC9EFD76613D01E16146107BBE6590" Directory="dir7C3635117912185773B74CE69A101DF6" Guid="{673C6217-F669-4845-B5A6-56EA061C38AF}">
24 <File Id="fil7994C9041F416FEA568FA98CA6891FB6" KeyPath="yes" Source="$(var.SourceDir)\style.css" />
25 </Component>
26 </ComponentGroup>
27 </Fragment>
28 <Fragment>
29 <DirectoryRef Id="INSTALLDIR">
30 <Directory Id="dir7C3635117912185773B74CE69A101DF6" Name="doc" />
31 </DirectoryRef>
32 </Fragment>
33 </Wix>
@@ -0,0 +1,72 b''
1 <?xml version="1.0" encoding="utf-8"?>
2 <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
3 <Fragment>
4 <ComponentGroup Id="helpFolder">
5 <Component Id="cmpC57BF45B775FC47D736ECD6944E64F6A" Directory="dirFC9FDBD71FA305BA6ECD4D639DB49784" Guid="{7189D54F-D55A-420C-9B2E-65A1CC522329}">
6 <File Id="filA01F5D0329BE64B4368A85C0B3671502" KeyPath="yes" Source="$(var.SourceDir)\config.txt" />
7 </Component>
8 <Component Id="cmp06DE93F7EEC0B3D1B698D92336E2AB11" Directory="dirFC9FDBD71FA305BA6ECD4D639DB49784" Guid="{15D85D50-B81D-41A7-9BDB-0DB57F655E83}">
9 <File Id="fil340415BFF8DB5A5977A364C422E731FF" KeyPath="yes" Source="$(var.SourceDir)\dates.txt" />
10 </Component>
11 <Component Id="cmp19C67CC3F7666886D7413E6F81DF923C" Directory="dirFC9FDBD71FA305BA6ECD4D639DB49784" Guid="{AA3D9BAB-C337-4726-9DAA-3EAAF3563378}">
12 <File Id="filDF5D1C01F4E5DFDF9ADC1B35D27FD592" KeyPath="yes" Source="$(var.SourceDir)\diffs.txt" />
13 </Component>
14 <Component Id="cmp0A78423CDE879648435F77671E14A6C3" Directory="dirFC9FDBD71FA305BA6ECD4D639DB49784" Guid="{EDFD63E7-69F6-4330-8226-07E5C3C5403D}">
15 <File Id="fil826C9369DF9AD54F6EBD9A10CA302D19" KeyPath="yes" Source="$(var.SourceDir)\environment.txt" />
16 </Component>
17 <Component Id="cmpA2B37D3B1AF89288970F0714F8A9AF39" Directory="dirFC9FDBD71FA305BA6ECD4D639DB49784" Guid="{B0D96B76-52E7-4D4E-A91D-24EC66373500}">
18 <File Id="filE8F682CC0E4F56B880D18017BC38DE50" KeyPath="yes" Source="$(var.SourceDir)\extensions.txt" />
19 </Component>
20 <Component Id="cmp417868B7DC07F14CE6B327271D65E5CC" Directory="dirFC9FDBD71FA305BA6ECD4D639DB49784" Guid="{0D01D7B8-8418-4FE6-9D37-0685FCB5DEE1}">
21 <File Id="filA0E27F4B12C85FA269523DC3A3CDC575" KeyPath="yes" Source="$(var.SourceDir)\multirevs.txt" />
22 </Component>
23 <Component Id="cmp339A70D8F3BF8B3AD679BE214A1984C7" Directory="dirFC9FDBD71FA305BA6ECD4D639DB49784" Guid="{EF91B612-EC51-4FC2-B3B1-C00ECBC13A4A}">
24 <File Id="fil6B14DD18C2EEDDDE767B9D83279A2ADA" KeyPath="yes" Source="$(var.SourceDir)\patterns.txt" />
25 </Component>
26 <Component Id="cmp177C53DE57B01391F4C5FDD15FA14EC6" Directory="dirFC9FDBD71FA305BA6ECD4D639DB49784" Guid="{AE7781B5-0F6E-4CF6-943D-B63F9511F274}">
27 <File Id="fil824583354419DB2A4F0AD312B3331FBB" KeyPath="yes" Source="$(var.SourceDir)\revisions.txt" />
28 </Component>
29 <Component Id="cmp69860FA1CEA8FE029A13DEF05E4832DF" Directory="dirFC9FDBD71FA305BA6ECD4D639DB49784" Guid="{895AB77F-A66E-42EF-B3FB-66E96737713D}">
30 <File Id="filF26222C0B0B107CF754108CD2B1C8CCF" KeyPath="yes" Source="$(var.SourceDir)\templates.txt" />
31 </Component>
32 <Component Id="cmpC1E444987188E87D749757D6E686B596" Directory="dirFC9FDBD71FA305BA6ECD4D639DB49784" Guid="{88938242-3A31-4F8E-9074-3B859DE150CD}">
33 <File Id="filCE7C1FF1BA40B646398A71851EB1EF7C" KeyPath="yes" Source="$(var.SourceDir)\urls.txt" />
34 </Component>
35 </ComponentGroup>
36 </Fragment>
37 <Fragment>
38 <DirectoryRef Id="INSTALLDIR">
39 <Directory Id="dirFC9FDBD71FA305BA6ECD4D639DB49784" Name="help" />
40 </DirectoryRef>
41 </Fragment>
42 <Fragment>
43 <DirectoryRef Id="dirFC9FDBD71FA305BA6ECD4D639DB49784" />
44 </Fragment>
45 <Fragment>
46 <DirectoryRef Id="dirFC9FDBD71FA305BA6ECD4D639DB49784" />
47 </Fragment>
48 <Fragment>
49 <DirectoryRef Id="dirFC9FDBD71FA305BA6ECD4D639DB49784" />
50 </Fragment>
51 <Fragment>
52 <DirectoryRef Id="dirFC9FDBD71FA305BA6ECD4D639DB49784" />
53 </Fragment>
54 <Fragment>
55 <DirectoryRef Id="dirFC9FDBD71FA305BA6ECD4D639DB49784" />
56 </Fragment>
57 <Fragment>
58 <DirectoryRef Id="dirFC9FDBD71FA305BA6ECD4D639DB49784" />
59 </Fragment>
60 <Fragment>
61 <DirectoryRef Id="dirFC9FDBD71FA305BA6ECD4D639DB49784" />
62 </Fragment>
63 <Fragment>
64 <DirectoryRef Id="dirFC9FDBD71FA305BA6ECD4D639DB49784" />
65 </Fragment>
66 <Fragment>
67 <DirectoryRef Id="dirFC9FDBD71FA305BA6ECD4D639DB49784" />
68 </Fragment>
69 <Fragment>
70 <DirectoryRef Id="dirFC9FDBD71FA305BA6ECD4D639DB49784" />
71 </Fragment>
72 </Wix> No newline at end of file
@@ -0,0 +1,78 b''
1 <?xml version="1.0" encoding="utf-8"?>
2 <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
3 <Fragment>
4 <ComponentGroup Id="i18nFolder">
5 <Component Id="cmp34947721B29BBB2D9942A0FC95D1F588" Directory="dirE238460D44340499CA44F8E5739FEA52" Guid="{511A94DD-0893-4E5B-A1CF-6DD9CDB1F797}">
6 <File Id="filF2E58DD4817D0D0AF53F4B8AB9836B47" KeyPath="yes" Source="$(var.SourceDir)\da.po" />
7 </Component>
8 <Component Id="cmpDC59717569F92122A034EC7E35A4F80D" Directory="dirE238460D44340499CA44F8E5739FEA52" Guid="{F6FD5F94-81D6-49AD-9D91-563E56224408}">
9 <File Id="fil39753E48327D06086C63EFA7711BB750" KeyPath="yes" Source="$(var.SourceDir)\de.po" />
10 </Component>
11 <Component Id="cmp7E5E80C4438C32CF7A419FDC63FB002D" Directory="dirE238460D44340499CA44F8E5739FEA52" Guid="{861DE2B9-B47D-4ACA-AEB2-645C69148B6B}">
12 <File Id="fil9B0C044DD27F2EE57B045B48BB697C0D" KeyPath="yes" Source="$(var.SourceDir)\el.po" />
13 </Component>
14 <Component Id="cmpA9C546113FA1B46B0C9498FA69F29B13" Directory="dirE238460D44340499CA44F8E5739FEA52" Guid="{756F0EEF-18AE-496F-A037-A063442CFE29}">
15 <File Id="fil6DF0C7BA972338067807AE385588F48A" KeyPath="yes" Source="$(var.SourceDir)\fr.po" />
16 </Component>
17 <Component Id="cmp6046EB31B43E28CD554FDD8D8E8AD420" Directory="dirE238460D44340499CA44F8E5739FEA52" Guid="{0404483D-818E-459B-902B-956D84869AE1}">
18 <File Id="filE99CA9E94849D4956606F72FD0B5FC5E" KeyPath="yes" Source="$(var.SourceDir)\hggettext" />
19 </Component>
20 <Component Id="cmp6133B01CC2B9B358CBF9327F700BD00A" Directory="dirE238460D44340499CA44F8E5739FEA52" Guid="{9B5289AE-FD8B-48ED-9E1C-2DB8BB495D0E}">
21 <File Id="filC69EEB86E4ED9073F34871B19155FED5" KeyPath="yes" Source="$(var.SourceDir)\it.po" />
22 </Component>
23 <Component Id="cmp97F2EE7A098F00025CB2FB889C5F6253" Directory="dirE238460D44340499CA44F8E5739FEA52" Guid="{9139E885-C29E-41C4-A9A5-52B3D1A2D0A0}">
24 <File Id="filA39E9F30340253C998EC80AE177CE43C" KeyPath="yes" Source="$(var.SourceDir)\ja.po" />
25 </Component>
26 <Component Id="cmpF3727AA82BA7F68310A26BB75A6647AD" Directory="dirE238460D44340499CA44F8E5739FEA52" Guid="{721683B1-6F5D-4F4F-A7FC-A134B4023B00}">
27 <File Id="fil80CA8EF53276179809903975CB3979F3" KeyPath="yes" Source="$(var.SourceDir)\pt_BR.po" />
28 </Component>
29 <Component Id="cmp939C74001F1FBEB147E5ADCED4770AC8" Directory="dirE238460D44340499CA44F8E5739FEA52" Guid="{69358529-D448-48EE-AD75-ADC7342F5EB2}">
30 <File Id="fil8F5BDF00539EE96B5270F71C7923CDC6" KeyPath="yes" Source="$(var.SourceDir)\sv.po" />
31 </Component>
32 <Component Id="cmpE23A15AD782608B1EDD835CF6B829378" Directory="dirE238460D44340499CA44F8E5739FEA52" Guid="{BB6C915D-20F7-4A90-91CA-F95629AF9992}">
33 <File Id="filC2CD3385CC6C9F85A582C8BEFF6A36FD" KeyPath="yes" Source="$(var.SourceDir)\zh_CN.po" />
34 </Component>
35 <Component Id="cmp3C3523DBA0ED9315E204C24B28BBECB1" Directory="dirE238460D44340499CA44F8E5739FEA52" Guid="{9A57C9EE-3A56-461C-A2B9-5CCB89080339}">
36 <File Id="fil717A0C3D62D332C48BAC66536FC1B6A0" KeyPath="yes" Source="$(var.SourceDir)\zh_TW.po" />
37 </Component>
38 </ComponentGroup>
39 </Fragment>
40 <Fragment>
41 <DirectoryRef Id="INSTALLDIR">
42 <Directory Id="dirE238460D44340499CA44F8E5739FEA52" Name="i18n" />
43 </DirectoryRef>
44 </Fragment>
45 <Fragment>
46 <DirectoryRef Id="dirE238460D44340499CA44F8E5739FEA52" />
47 </Fragment>
48 <Fragment>
49 <DirectoryRef Id="dirE238460D44340499CA44F8E5739FEA52" />
50 </Fragment>
51 <Fragment>
52 <DirectoryRef Id="dirE238460D44340499CA44F8E5739FEA52" />
53 </Fragment>
54 <Fragment>
55 <DirectoryRef Id="dirE238460D44340499CA44F8E5739FEA52" />
56 </Fragment>
57 <Fragment>
58 <DirectoryRef Id="dirE238460D44340499CA44F8E5739FEA52" />
59 </Fragment>
60 <Fragment>
61 <DirectoryRef Id="dirE238460D44340499CA44F8E5739FEA52" />
62 </Fragment>
63 <Fragment>
64 <DirectoryRef Id="dirE238460D44340499CA44F8E5739FEA52" />
65 </Fragment>
66 <Fragment>
67 <DirectoryRef Id="dirE238460D44340499CA44F8E5739FEA52" />
68 </Fragment>
69 <Fragment>
70 <DirectoryRef Id="dirE238460D44340499CA44F8E5739FEA52" />
71 </Fragment>
72 <Fragment>
73 <DirectoryRef Id="dirE238460D44340499CA44F8E5739FEA52" />
74 </Fragment>
75 <Fragment>
76 <DirectoryRef Id="dirE238460D44340499CA44F8E5739FEA52" />
77 </Fragment>
78 </Wix> No newline at end of file
@@ -0,0 +1,172 b''
1 <?xml version="1.0" encoding="utf-8"?>
2 <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
3 <Fragment>
4 <ComponentGroup Id="localeFolder">
5 <Component Id="cmp83E7ADE01D9D898727106DBD965E0E17" Directory="dir37691BFCCBD5A038C2B503A0CA9FFDCC" Guid="{45A0DC87-8C6C-4BDA-907A-52F93D183C18}">
6 <File Id="fil9262D6374AF5065C3CD975127E419FD5" KeyPath="yes" Source="$(var.SourceDir)\da\LC_MESSAGES\hg.mo" />
7 </Component>
8 <Component Id="cmp52A447EBEC4DE496C672ED4285986FC2" Directory="dirFD5FFD33B2F5044881CA967A9603AD80" Guid="{D5929D29-3597-4BFA-970C-1336B74AED0D}">
9 <File Id="fil324023D432B614A0F6AD7C1795A47663" KeyPath="yes" Source="$(var.SourceDir)\de\LC_MESSAGES\hg.mo" />
10 </Component>
11 <Component Id="cmpD14AFBC2AB851E0FF50AB01087D37C37" Directory="dir11F21566046EDDA54202617BDCA27A33" Guid="{AEEB8542-91FA-4FDB-A976-C210608D45C4}">
12 <File Id="fil3263B9C209A995DEDAB99C915ADFEDF2" KeyPath="yes" Source="$(var.SourceDir)\el\LC_MESSAGES\hg.mo" />
13 </Component>
14 <Component Id="cmpF8D03402F9CC15628D08BD1547931E33" Directory="dir76A360BADC007F88A0B6C012DBA071F0" Guid="{F983F836-213C-4A8F-84C6-9DE3DDFFC4EF}">
15 <File Id="fil61661147957312ACE1F53D8B7B6FC20C" KeyPath="yes" Source="$(var.SourceDir)\fr\LC_MESSAGES\hg.mo" />
16 </Component>
17 <Component Id="cmpB7A86C8460D0C05C63F89961A89C32C2" Directory="dir84C2343136F7D4B857D28FDACE545F33" Guid="{E53EEBD5-5F54-426A-83C0-18C479C526D5}">
18 <File Id="filBA4BA4851E74E108136A44069A96DB8A" KeyPath="yes" Source="$(var.SourceDir)\it\LC_MESSAGES\hg.mo" />
19 </Component>
20 <Component Id="cmpA2E1D600E985FB335E18566CD5230E38" Directory="dir29F26642414C1248A97432E9C4947B87" Guid="{27F33F75-B5D6-47AD-AE23-DAC135401540}">
21 <File Id="filB4C1367F8EC03C778E275E7C6A4F882B" KeyPath="yes" Source="$(var.SourceDir)\ja\LC_MESSAGES\hg.mo" />
22 </Component>
23 <Component Id="cmp113B5931CE4199C5A5BF62728EA3A755" Directory="dir669486A97BE07EA6B1A33203A89256A6" Guid="{63F05AD9-CABA-420E-9A50-123F0803A8CE}">
24 <File Id="fil0B3347DB0C4538334E8346A9B7844458" KeyPath="yes" Source="$(var.SourceDir)\pt_BR\LC_MESSAGES\hg.mo" />
25 </Component>
26 <Component Id="cmpE35EFB6166609188B151E512DD832276" Directory="dir8637F62A0436F8880FACE0053CC26075" Guid="{81FC0E03-9956-4476-AFEE-1FCD0E98F860}">
27 <File Id="fil518947DB16E6B4E58C52D16C3C8559C1" KeyPath="yes" Source="$(var.SourceDir)\sv\LC_MESSAGES\hg.mo" />
28 </Component>
29 <Component Id="cmpC6F51D89D671AC7F66692F402B916E3D" Directory="dir402295F841398275038CC187DA8A3179" Guid="{198031AC-5DC5-44F9-AF77-A11AC15B82DE}">
30 <File Id="fil1905BB31038C24BBF8557DA796E65A2D" KeyPath="yes" Source="$(var.SourceDir)\zh_CN\LC_MESSAGES\hg.mo" />
31 </Component>
32 <Component Id="cmp2C7860CA004402234F7F1883F1093A1D" Directory="dirF619ECAC819DE44FC21E11EB1715B156" Guid="{EDF8F748-735A-4BC2-B683-14247D691F5C}">
33 <File Id="fil997E71C3D4C6318F0EF7048E50C5C0AA" KeyPath="yes" Source="$(var.SourceDir)\zh_TW\LC_MESSAGES\hg.mo" />
34 </Component>
35 </ComponentGroup>
36 </Fragment>
37 <Fragment>
38 <DirectoryRef Id="INSTALLDIR">
39 <Directory Id="dirA42EA32F4A238F2C40AD5ACF4C831152" Name="locale" />
40 </DirectoryRef>
41 </Fragment>
42 <Fragment>
43 <DirectoryRef Id="dir669486A97BE07EA6B1A33203A89256A6" />
44 </Fragment>
45 <Fragment>
46 <DirectoryRef Id="dirF619ECAC819DE44FC21E11EB1715B156" />
47 </Fragment>
48 <Fragment>
49 <DirectoryRef Id="dirFD5FFD33B2F5044881CA967A9603AD80" />
50 </Fragment>
51 <Fragment>
52 <DirectoryRef Id="dir37691BFCCBD5A038C2B503A0CA9FFDCC" />
53 </Fragment>
54 <Fragment>
55 <DirectoryRef Id="dir29F26642414C1248A97432E9C4947B87" />
56 </Fragment>
57 <Fragment>
58 <DirectoryRef Id="dir84C2343136F7D4B857D28FDACE545F33" />
59 </Fragment>
60 <Fragment>
61 <DirectoryRef Id="dir402295F841398275038CC187DA8A3179" />
62 </Fragment>
63 <Fragment>
64 <DirectoryRef Id="dir11F21566046EDDA54202617BDCA27A33" />
65 </Fragment>
66 <Fragment>
67 <DirectoryRef Id="dir8637F62A0436F8880FACE0053CC26075" />
68 </Fragment>
69 <Fragment>
70 <DirectoryRef Id="dir76A360BADC007F88A0B6C012DBA071F0" />
71 </Fragment>
72 <Fragment>
73 <DirectoryRef Id="dirA42EA32F4A238F2C40AD5ACF4C831152">
74 <Directory Id="dir0863D143DD8AA8C439D73CEC9FD66C1E" Name="fr" />
75 </DirectoryRef>
76 </Fragment>
77 <Fragment>
78 <DirectoryRef Id="dirA42EA32F4A238F2C40AD5ACF4C831152">
79 <Directory Id="dir0FE904A675AA86182DE42F092B60D5CB" Name="zh_CN" />
80 </DirectoryRef>
81 </Fragment>
82 <Fragment>
83 <DirectoryRef Id="dirABB7B0E1FB87A3FA812818303D9504D3">
84 <Directory Id="dir11F21566046EDDA54202617BDCA27A33" Name="LC_MESSAGES" />
85 </DirectoryRef>
86 </Fragment>
87 <Fragment>
88 <DirectoryRef Id="dirA42EA32F4A238F2C40AD5ACF4C831152">
89 <Directory Id="dir22C8B725631FC803704E5AB85CE4AB85" Name="da" />
90 </DirectoryRef>
91 </Fragment>
92 <Fragment>
93 <DirectoryRef Id="dirF7EFDAD22D2FB858CCFF6C617192E3F0">
94 <Directory Id="dir29F26642414C1248A97432E9C4947B87" Name="LC_MESSAGES" />
95 </DirectoryRef>
96 </Fragment>
97 <Fragment>
98 <DirectoryRef Id="dir22C8B725631FC803704E5AB85CE4AB85">
99 <Directory Id="dir37691BFCCBD5A038C2B503A0CA9FFDCC" Name="LC_MESSAGES" />
100 </DirectoryRef>
101 </Fragment>
102 <Fragment>
103 <DirectoryRef Id="dirA42EA32F4A238F2C40AD5ACF4C831152">
104 <Directory Id="dir40014079D6FD208765D49B2B6E77B444" Name="pt_BR" />
105 </DirectoryRef>
106 </Fragment>
107 <Fragment>
108 <DirectoryRef Id="dir0FE904A675AA86182DE42F092B60D5CB">
109 <Directory Id="dir402295F841398275038CC187DA8A3179" Name="LC_MESSAGES" />
110 </DirectoryRef>
111 </Fragment>
112 <Fragment>
113 <DirectoryRef Id="dir40014079D6FD208765D49B2B6E77B444">
114 <Directory Id="dir669486A97BE07EA6B1A33203A89256A6" Name="LC_MESSAGES" />
115 </DirectoryRef>
116 </Fragment>
117 <Fragment>
118 <DirectoryRef Id="dir0863D143DD8AA8C439D73CEC9FD66C1E">
119 <Directory Id="dir76A360BADC007F88A0B6C012DBA071F0" Name="LC_MESSAGES" />
120 </DirectoryRef>
121 </Fragment>
122 <Fragment>
123 <DirectoryRef Id="dir8CB5F92AB36FF6BF8CDA8AF1BD4BB402">
124 <Directory Id="dir84C2343136F7D4B857D28FDACE545F33" Name="LC_MESSAGES" />
125 </DirectoryRef>
126 </Fragment>
127 <Fragment>
128 <DirectoryRef Id="dirE6EE5833D8D4174BB8C9193A44E67309">
129 <Directory Id="dir8637F62A0436F8880FACE0053CC26075" Name="LC_MESSAGES" />
130 </DirectoryRef>
131 </Fragment>
132 <Fragment>
133 <DirectoryRef Id="dirA42EA32F4A238F2C40AD5ACF4C831152">
134 <Directory Id="dir8CB5F92AB36FF6BF8CDA8AF1BD4BB402" Name="it" />
135 </DirectoryRef>
136 </Fragment>
137 <Fragment>
138 <DirectoryRef Id="dirA42EA32F4A238F2C40AD5ACF4C831152">
139 <Directory Id="dir97CB62B25E0D4335D79170C310016140" Name="de" />
140 </DirectoryRef>
141 </Fragment>
142 <Fragment>
143 <DirectoryRef Id="dirA42EA32F4A238F2C40AD5ACF4C831152">
144 <Directory Id="dirABB7B0E1FB87A3FA812818303D9504D3" Name="el" />
145 </DirectoryRef>
146 </Fragment>
147 <Fragment>
148 <DirectoryRef Id="dirA42EA32F4A238F2C40AD5ACF4C831152">
149 <Directory Id="dirE6EE5833D8D4174BB8C9193A44E67309" Name="sv" />
150 </DirectoryRef>
151 </Fragment>
152 <Fragment>
153 <DirectoryRef Id="dirA42EA32F4A238F2C40AD5ACF4C831152">
154 <Directory Id="dirF24C7B7028912CC69CE9E1E38F2FEE54" Name="zh_TW" />
155 </DirectoryRef>
156 </Fragment>
157 <Fragment>
158 <DirectoryRef Id="dirF24C7B7028912CC69CE9E1E38F2FEE54">
159 <Directory Id="dirF619ECAC819DE44FC21E11EB1715B156" Name="LC_MESSAGES" />
160 </DirectoryRef>
161 </Fragment>
162 <Fragment>
163 <DirectoryRef Id="dirA42EA32F4A238F2C40AD5ACF4C831152">
164 <Directory Id="dirF7EFDAD22D2FB858CCFF6C617192E3F0" Name="ja" />
165 </DirectoryRef>
166 </Fragment>
167 <Fragment>
168 <DirectoryRef Id="dir97CB62B25E0D4335D79170C310016140">
169 <Directory Id="dirFD5FFD33B2F5044881CA967A9603AD80" Name="LC_MESSAGES" />
170 </DirectoryRef>
171 </Fragment>
172 </Wix>
@@ -0,0 +1,133 b''
1 <?xml version='1.0' encoding='windows-1252'?>
2 <Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>
3
4 <!-- Copyright 2010 Steve Borho <steve@borho.org>
5
6 This software may be used and distributed according to the terms of the
7 GNU General Public License version 2 or any later version. -->
8
9 <?define ComponentMainExecutableGUID = D102B8FA-059B-4ACC-9FA3-8C78C3B58EEF ?>
10 <?define ProductUpgradeCode = A1CC6134-E945-4399-BE36-EB0017FDF7CF ?>
11
12 <Product Name='Mercurial' Id='*'
13 UpgradeCode='$(var.ProductUpgradeCode)'
14 Language='1033' Codepage='1252' Version='$(var.Version)' Manufacturer='Matt Mackall and others.'>
15
16 <Package Id='*' Keywords='Installer' Description="Mercurial distributed SCM (version $(var.Version))"
17 Comments='$(var.Comments)' Manufacturer='Matt Mackall and others.'
18 InstallerVersion='300' Languages='1033' Compressed='yes' SummaryCodepage='1252' />
19
20 <Media Id='1' Cabinet='mercurial.cab' EmbedCab='yes' DiskPrompt='CD-ROM #1' CompressionLevel='high' />
21 <Property Id='DiskPrompt' Value="Mercurial $(var.Version) Installation [1]" />
22
23 <Condition Message='Mercurial requires Windows XP or higher'>VersionNT >= 501</Condition>
24
25 <Property Id="INSTALLDIR">
26 <ComponentSearch Id='SearchForMainExecutableComponent' Guid='$(var.ComponentMainExecutableGUID)' />
27 </Property>
28
29 <!--Property Id='ARPCOMMENTS'>any comments</Property-->
30 <Property Id='ARPCONTACT'>mercurial@selenic.com</Property>
31 <Property Id='ARPHELPLINK'>http://mercurial.selenic.com/wiki/</Property>
32 <Property Id='ARPURLINFOABOUT'>http://mercurial.selenic.com/about/</Property>
33 <Property Id='ARPURLUPDATEINFO'>http://mercurial.selenic.com/downloads/</Property>
34 <Property Id='ARPHELPTELEPHONE'>http://mercurial.selenic.com/wiki/Support</Property>
35 <Property Id='ARPPRODUCTICON'>hgIcon.ico</Property>
36
37 <Property Id='INSTALLEDMERCURIALPRODUCTS' Secure='yes'></Property>
38 <Property Id='REINSTALLMODE'>amus</Property>
39
40 <Directory Id='TARGETDIR' Name='SourceDir'>
41 <Directory Id='ProgramFilesFolder' Name='PFiles'>
42 <Directory Id='INSTALLDIR' Name='Mercurial'>
43 <Component Id='MainExecutable' Guid='$(var.ComponentMainExecutableGUID)'>
44 <File Id='hgEXE' Name='hg.exe' DiskId='1' Source='dist\hg.exe' KeyPath='yes' />
45 <File Id='libraryZIP' Name='library.zip' DiskId='1' Source='dist\library.zip' />
46 <File Id='pythonDLL' Name='python26.dll' DiskId='1' Source='dist\python26.dll' />
47 <Environment Id="Environment" Name="PATH" Part="last" System="yes"
48 Permanent="no" Value="[INSTALLDIR]" Action="set" />
49 </Component>
50 <Component Id='ReadMe' Guid='56A8E372-991D-4DCA-B91D-93D775974CF5'>
51 <File Id='ReadMe' Name='ReadMe.html' DiskId='1' Source='contrib\win32\ReadMe.html' KeyPath='yes'/>
52 </Component>
53 <Component Id='COPYING' Guid='B7801DBA-1C49-4BF4-91AD-33C65F5C7895'>
54 <File Id='COPYING' Name='COPYING.rtf' DiskId='1' Source='..\misc\COPYING.rtf' />
55 </Component>
56 <Directory Id='HGRCD' Name='hgrc.d'>
57 <Component Id='mercurial.rc' Guid='1D5FAEEE-7E6E-43B1-9F7F-802714316B15'>
58 <File Id='mercurial.rc' Name='Mercurial.rc' DiskId='1' Source='contrib\win32\mercurial.ini'
59 ReadOnly='yes'/>
60 </Component>
61 <Component Id='mergetools.rc' Guid='E8A1DC29-FF40-4B5F-BD12-80B9F7BF0CCD'>
62 <File Id='mergetools.rc' Name='MergeTools.rc' DiskId='1' Source='contrib\mergetools.hgrc'
63 ReadOnly='yes'/>
64 </Component>
65 </Directory>
66 </Directory>
67 </Directory>
68
69 <Directory Id="ProgramMenuFolder" Name="Programs">
70 <Directory Id="ProgramMenuDir" Name="Mercurial $(var.Version)">
71 <Component Id="ProgramMenuDir" Guid="D5A63320-1238-489B-B68B-CF053E9577CA">
72 <RemoveFolder Id='ProgramMenuDir' On='uninstall' />
73 <RegistryValue Root='HKCU' Key='Software\Mercurial\InstallDir' Type='string'
74 Value='[INSTALLDIR]' KeyPath='yes' />
75 <Shortcut Id='UrlShortcut' Directory='ProgramMenuDir' Name='Mercurial Web Site' Target='[ARPHELPLINK]'
76 Icon="hgIcon.ico" IconIndex='0' />
77 </Component>
78 </Directory>
79 </Directory>
80
81 <Directory Id="DesktopFolder" Name="Desktop" />
82 <Merge Id='VCRuntime' DiskId='1' Language='1033'
83 SourceFile='C:\Program Files\Microsoft SDKs\Windows\v7.0\Redist\VC\microsoft.vcxx.crt.x86_msm.msm' />
84 <Merge Id='VCRuntimePolicy' DiskId='1' Language='1033'
85 SourceFile='C:\Program Files\Microsoft SDKs\Windows\v7.0\Redist\VC\policy.x.xx.microsoft.vcxx.crt.x86_msm.msm' />
86 </Directory>
87
88 <Feature Id='Complete' Title='Mercurial $(var.Version)' Description='The complete package'
89 Display='expand' Level='1' ConfigurableDirectory='INSTALLDIR' >
90 <Feature Id='MainProgram' Title='Program' Description='Mercurial command line app'
91 Level='1' Absent='disallow' >
92 <ComponentRef Id='MainExecutable' />
93 <ComponentRef Id='ProgramMenuDir' />
94 <ComponentRef Id='ReadMe' />
95 <ComponentRef Id='COPYING' />
96 <ComponentRef Id='mercurial.rc' />
97 <ComponentRef Id='mergetools.rc' />
98 <ComponentGroupRef Id='helpFolder' />
99 <ComponentGroupRef Id='templatesFolder' />
100 <MergeRef Id='VCRuntime' />
101 <MergeRef Id='VCRuntimePolicy' />
102 </Feature>
103 <Feature Id='Locales' Title='Translations' Description='Translations' Level='1'>
104 <ComponentGroupRef Id='localeFolder' />
105 <ComponentGroupRef Id='i18nFolder' />
106 </Feature>
107 <Feature Id='Documentation' Title='Documentation' Description='HTML man pages' Level='1'>
108 <ComponentGroupRef Id='docFolder' />
109 </Feature>
110 <Feature Id='Misc' Title='Miscellaneous' Description='Contributed scripts' Level='1'>
111 <ComponentGroupRef Id='contribFolder' />
112 </Feature>
113 </Feature>
114
115 <UIRef Id="WixUI_FeatureTree" />
116 <UIRef Id="WixUI_ErrorProgressText" />
117
118 <WixVariable Id="WixUILicenseRtf" Value="..\misc\COPYING.rtf" />
119
120 <Icon Id="hgIcon.ico" SourceFile="contrib/win32/mercurial.ico" />
121
122 <Upgrade Id='$(var.ProductUpgradeCode)'>
123 <UpgradeVersion
124 IncludeMinimum='yes' Minimum='0.0.0' IncludeMaximum='no' OnlyDetect='no'
125 Property='INSTALLEDMERCURIALPRODUCTS' />
126 </Upgrade>
127
128 <InstallExecuteSequence>
129 <RemoveExistingProducts After='InstallInitialize'/>
130 </InstallExecuteSequence>
131
132 </Product>
133 </Wix>
This diff has been collapsed as it changes many lines, (801 lines changed) Show them Hide them
@@ -0,0 +1,801 b''
1 <?xml version="1.0" encoding="utf-8"?>
2 <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
3 <Fragment>
4 <ComponentGroup Id="templatesFolder">
5 <Component Id="cmp245D27C2F7D49A9E8A10E551A4913BD8" Directory="dirBFB9D3FD33CE2C696D390C94E4E1B4D8" Guid="{67C4810A-F6CA-4512-9A43-43DD72A525BF}">
6 <File Id="filC46EB4977E850DDE52085CD81647B147" KeyPath="yes" Source="$(var.SourceDir)\map-cmdline.changelog" />
7 </Component>
8 <Component Id="cmpC6E57D5F80AEF6636B609AFDDC1B34C3" Directory="dirBFB9D3FD33CE2C696D390C94E4E1B4D8" Guid="{212A9079-1B49-4DB7-A49E-BACCBCD8AA33}">
9 <File Id="fil66141877E998E34A73C78F52F5525D79" KeyPath="yes" Source="$(var.SourceDir)\map-cmdline.compact" />
10 </Component>
11 <Component Id="cmp3E8935E93A2F8142E0F30B7654DEDB7B" Directory="dirBFB9D3FD33CE2C696D390C94E4E1B4D8" Guid="{70544E9C-F575-4039-94F9-19DAA12BA38B}">
12 <File Id="filB6CE3FEEF27A46E979CF71DFD5071876" KeyPath="yes" Source="$(var.SourceDir)\map-cmdline.default" />
13 </Component>
14 <Component Id="cmpA98C695C9A852CA08358744DE71BBCB4" Directory="dirBFB9D3FD33CE2C696D390C94E4E1B4D8" Guid="{A0123134-AB72-4793-BAAC-109F78D4A35B}">
15 <File Id="fil603AB867DCF6C98CF4E8C908F627AD53" KeyPath="yes" Source="$(var.SourceDir)\map-cmdline.xml" />
16 </Component>
17 <Component Id="cmpDE1FEC5413B37B6300493CAF819462DC" Directory="dirBFB9D3FD33CE2C696D390C94E4E1B4D8" Guid="{F89E0BE4-DEAF-4132-BE18-86A171111051}">
18 <File Id="filFFC56D533BF5BA670CAD613427C30029" KeyPath="yes" Source="$(var.SourceDir)\template-vars.txt" />
19 </Component>
20 <Component Id="cmpD7A2B04E9CF5E810D00331B0D14890C1" Directory="dir35F8598B197DFC5580317F5E7C2E9AE9" Guid="{EEB7B220-90C2-4049-BE87-592C3B5A23D4}">
21 <File Id="filAF2A94DF56A6D0C95DE82C39C151A195" KeyPath="yes" Source="$(var.SourceDir)\atom\changelog.tmpl" />
22 </Component>
23 <Component Id="cmpB2016007359D825073A8ED67A1827DF9" Directory="dir35F8598B197DFC5580317F5E7C2E9AE9" Guid="{EDD3F2A2-160D-4C2C-9C92-E41FA1B5D474}">
24 <File Id="fil417417DF06043E5780F4B5C267352CF9" KeyPath="yes" Source="$(var.SourceDir)\atom\changelogentry.tmpl" />
25 </Component>
26 <Component Id="cmp4AF55640C671810FB1ADF0351BB90E17" Directory="dir35F8598B197DFC5580317F5E7C2E9AE9" Guid="{E15B5539-56E0-4D48-A89A-4E6E286AAC68}">
27 <File Id="filABC1E3A8F08963D46947F5DD71204159" KeyPath="yes" Source="$(var.SourceDir)\atom\error.tmpl" />
28 </Component>
29 <Component Id="cmpEAFBDC0898D5AB411B21780B0B8F2C65" Directory="dir35F8598B197DFC5580317F5E7C2E9AE9" Guid="{3F4C35A0-3860-4C9C-9C4F-96D601C60B0F}">
30 <File Id="filFD94D63312BF0A454BCC74F559F26AC5" KeyPath="yes" Source="$(var.SourceDir)\atom\filelog.tmpl" />
31 </Component>
32 <Component Id="cmp09A2C49717F147CA23C43FA4BE19CB89" Directory="dir35F8598B197DFC5580317F5E7C2E9AE9" Guid="{EFDA7618-5C27-449F-BBDF-13B512CA774D}">
33 <File Id="filD79697217EB1368CCFBAC93891022BA4" KeyPath="yes" Source="$(var.SourceDir)\atom\header.tmpl" />
34 </Component>
35 <Component Id="cmpA1BD3C8A4A8B57A1A224DC625A939716" Directory="dir35F8598B197DFC5580317F5E7C2E9AE9" Guid="{8AFBBBD2-D743-4C72-9C4C-8EB7F9F5E4BD}">
36 <File Id="filC93C0483D9AB074A860A448C8AE7A311" KeyPath="yes" Source="$(var.SourceDir)\atom\map" />
37 </Component>
38 <Component Id="cmp1F8380EC4EB90B7E2893B8ABAFBA3C6C" Directory="dir35F8598B197DFC5580317F5E7C2E9AE9" Guid="{691B5260-2697-43AC-8D71-7E6CBC9784BA}">
39 <File Id="fil38C516EE8F0ED4FFB814F8A78FE04B5B" KeyPath="yes" Source="$(var.SourceDir)\atom\tagentry.tmpl" />
40 </Component>
41 <Component Id="cmp87896A208C9063C4E0F127D6C124A97A" Directory="dir35F8598B197DFC5580317F5E7C2E9AE9" Guid="{BB5DECFE-313E-44FD-B354-FB8DB781199E}">
42 <File Id="fil01F9F5804BA8E9E7D37A07043B4EE7B7" KeyPath="yes" Source="$(var.SourceDir)\atom\tags.tmpl" />
43 </Component>
44 <Component Id="cmpF5D9AB7FE9A07C307EE33CD2AFA719AD" Directory="dir2EDFFCA56732C858816EF19F127E8D0F" Guid="{5D4CD000-740A-445D-B22A-413D27B2D424}">
45 <File Id="fil1B50FD60A3ADF7A829E34C97198D1D52" KeyPath="yes" Source="$(var.SourceDir)\coal\header.tmpl" />
46 </Component>
47 <Component Id="cmpC34DFD4C8EBD5BCABA6F10B70B1D28B1" Directory="dir2EDFFCA56732C858816EF19F127E8D0F" Guid="{5FC3B465-E22F-4B49-8733-2D6D8D5457C4}">
48 <File Id="fil483848B756E76EE840BFB45A62C00DB3" KeyPath="yes" Source="$(var.SourceDir)\coal\map" />
49 </Component>
50 <Component Id="cmpC5067895295DA39B9B11AD3DD4E21B52" Directory="dir3D22B249045A1473460D0D5D104478F2" Guid="{4061F72F-DA6A-4A2E-B917-0179C577EC91}">
51 <File Id="filAB35B91E5DC594F9F0BE979B5C938650" KeyPath="yes" Source="$(var.SourceDir)\gitweb\branches.tmpl" />
52 </Component>
53 <Component Id="cmp7F9AD93FB91A0E4622F452B0EF9352C2" Directory="dir3D22B249045A1473460D0D5D104478F2" Guid="{1B73BD55-F6F1-40B7-99F6-7BFB9252CC85}">
54 <File Id="filB65F6983E85F6327A20D92F0E50C8F3A" KeyPath="yes" Source="$(var.SourceDir)\gitweb\changelog.tmpl" />
55 </Component>
56 <Component Id="cmpB186C4D7FF025E46CE87EA66461F775C" Directory="dir3D22B249045A1473460D0D5D104478F2" Guid="{0F9A4AAA-B82D-4DE4-B475-BDCCA08A81BB}">
57 <File Id="filBBAE80D203E7C7DE353764E8D8ED5CF4" KeyPath="yes" Source="$(var.SourceDir)\gitweb\changelogentry.tmpl" />
58 </Component>
59 <Component Id="cmp4C205C07FEA1B04A79AB62A672FB7034" Directory="dir3D22B249045A1473460D0D5D104478F2" Guid="{0BB9DA75-8FE3-48B1-9F7A-E377C36F7EBB}">
60 <File Id="filD737DB25A84CF6BAC537C01A4C1FD441" KeyPath="yes" Source="$(var.SourceDir)\gitweb\changeset.tmpl" />
61 </Component>
62 <Component Id="cmp4F1AEA9E1182AA3FEB959501A4524E2C" Directory="dir3D22B249045A1473460D0D5D104478F2" Guid="{4CA8A2C5-2401-476C-8863-2AEAC3178B87}">
63 <File Id="filAC2F74FB38AD114EE60B8A858195AAA5" KeyPath="yes" Source="$(var.SourceDir)\gitweb\error.tmpl" />
64 </Component>
65 <Component Id="cmp6D28475C0E5C0EF2C7368197A2543E04" Directory="dir3D22B249045A1473460D0D5D104478F2" Guid="{3E7A0738-9ADA-409F-82D7-63C5212D9FEF}">
66 <File Id="fil7E719E65B937150669EC7A65D931A42E" KeyPath="yes" Source="$(var.SourceDir)\gitweb\fileannotate.tmpl" />
67 </Component>
68 <Component Id="cmp41DDE65FBD7F6C62DE2F904E8BB70D52" Directory="dir3D22B249045A1473460D0D5D104478F2" Guid="{D82ECE81-F30E-4A32-BB33-A5ED30242266}">
69 <File Id="filE5314FDBD1A83FD2071ABDBEB5D75966" KeyPath="yes" Source="$(var.SourceDir)\gitweb\filediff.tmpl" />
70 </Component>
71 <Component Id="cmp73B2345AE1A62971C248A4200125105E" Directory="dir3D22B249045A1473460D0D5D104478F2" Guid="{88E6B5AF-F996-4676-9DD2-755803ED6ADD}">
72 <File Id="fil813DE4E9B8637CB0C9DEEE04871B7154" KeyPath="yes" Source="$(var.SourceDir)\gitweb\filelog.tmpl" />
73 </Component>
74 <Component Id="cmp74429C97DF03C076BB1976374DB4393A" Directory="dir3D22B249045A1473460D0D5D104478F2" Guid="{12709310-806B-443B-8A7D-49A7C2AC83AA}">
75 <File Id="fil80D6BDD10936FB214225A8A9C385CE36" KeyPath="yes" Source="$(var.SourceDir)\gitweb\filerevision.tmpl" />
76 </Component>
77 <Component Id="cmpAC64876FBE3380283CFA6A488C4610E5" Directory="dir3D22B249045A1473460D0D5D104478F2" Guid="{FC6F127E-4BB9-40AE-B3AC-0B0EB4F8315D}">
78 <File Id="fil05FF66A70E6AC220F28CCC9C93327329" KeyPath="yes" Source="$(var.SourceDir)\gitweb\footer.tmpl" />
79 </Component>
80 <Component Id="cmp30DB23C31D0DD655BA9E3F2D0D6C8582" Directory="dir3D22B249045A1473460D0D5D104478F2" Guid="{55E52857-56BD-46E3-BC4D-597CA09CB6C5}">
81 <File Id="filEE919C1EA37972DA70DECD2DE78563C2" KeyPath="yes" Source="$(var.SourceDir)\gitweb\graph.tmpl" />
82 </Component>
83 <Component Id="cmp1D6387539D230EC1EE3F68EC430EAB56" Directory="dir3D22B249045A1473460D0D5D104478F2" Guid="{F6E15E2E-80A2-4194-9763-EA900D9D4517}">
84 <File Id="filD3FF2C0986F56DE61C70A80D6FCD2963" KeyPath="yes" Source="$(var.SourceDir)\gitweb\header.tmpl" />
85 </Component>
86 <Component Id="cmpF4CB95EC9E3B863756FEBD0A4AE3B573" Directory="dir3D22B249045A1473460D0D5D104478F2" Guid="{7098B211-3164-4303-B719-8897458E7474}">
87 <File Id="fil02B1E9DBF857B7D72BF76AADDDB1C11D" KeyPath="yes" Source="$(var.SourceDir)\gitweb\index.tmpl" />
88 </Component>
89 <Component Id="cmp4BFEAC2B08F975649AC2660D56EFA4B7" Directory="dir3D22B249045A1473460D0D5D104478F2" Guid="{BFB237EE-79F5-45E5-B182-61BC41FD366D}">
90 <File Id="fil95099724846977404F6A38059E39F6EE" KeyPath="yes" Source="$(var.SourceDir)\gitweb\manifest.tmpl" />
91 </Component>
92 <Component Id="cmpEF1EB2713EEA62B1F81C5872116A1CEC" Directory="dir3D22B249045A1473460D0D5D104478F2" Guid="{210BD2AF-E37E-4506-9723-AB284A0A2105}">
93 <File Id="fil15115F4F87328A9540B3644B3475A878" KeyPath="yes" Source="$(var.SourceDir)\gitweb\map" />
94 </Component>
95 <Component Id="cmp9F186B6C977DFA7712F92E9FB2DE3891" Directory="dir3D22B249045A1473460D0D5D104478F2" Guid="{5A4A880B-F777-4C4F-96AD-AD18787A2DA1}">
96 <File Id="fil7F7A105718DAA19575A155EBE946F511" KeyPath="yes" Source="$(var.SourceDir)\gitweb\notfound.tmpl" />
97 </Component>
98 <Component Id="cmp1DA3F0160E3BCFE235A3C6F752FB2D12" Directory="dir3D22B249045A1473460D0D5D104478F2" Guid="{85C86EC9-8328-4FDC-9DA4-009A3CBE1421}">
99 <File Id="fil645D98D0A60CFDC1C5D6AABEFCF7F120" KeyPath="yes" Source="$(var.SourceDir)\gitweb\search.tmpl" />
100 </Component>
101 <Component Id="cmpECF912909627794036B7DF5594B928F4" Directory="dir3D22B249045A1473460D0D5D104478F2" Guid="{5F0E4FE5-59D6-48EA-9BBF-0D1D8381E270}">
102 <File Id="fil7A9B36B1AAC6F76DB84BD2DA8FFF44C6" KeyPath="yes" Source="$(var.SourceDir)\gitweb\shortlog.tmpl" />
103 </Component>
104 <Component Id="cmpDA71A47B23710A56F1A612AC01E1A0D7" Directory="dir3D22B249045A1473460D0D5D104478F2" Guid="{D913EF9E-0F02-4C11-B0E9-9F8945854F6E}">
105 <File Id="fil1EDFE0E741E8E14C9A1DB7E66BCC4C9C" KeyPath="yes" Source="$(var.SourceDir)\gitweb\summary.tmpl" />
106 </Component>
107 <Component Id="cmp209D055DDBF11DC7688C0A8765559E32" Directory="dir3D22B249045A1473460D0D5D104478F2" Guid="{9BD8D802-796C-4347-9D21-3D2FC568DA94}">
108 <File Id="fil604852E1873B9EB0D5E2F7A8680CB3D2" KeyPath="yes" Source="$(var.SourceDir)\gitweb\tags.tmpl" />
109 </Component>
110 <Component Id="cmp1823075725AEFF65DFA5A8216B1BBAD1" Directory="dirA4D7AC0F636AD49029D4D19861392983" Guid="{4F380BC7-0658-46A2-8309-6179462CFBA3}">
111 <File Id="filDA3E8C8E479D5D924DD741002D22369B" KeyPath="yes" Source="$(var.SourceDir)\monoblue\branches.tmpl" />
112 </Component>
113 <Component Id="cmpDBF9A5ECD1ABDCF5C28F35FB7B4F7001" Directory="dirA4D7AC0F636AD49029D4D19861392983" Guid="{2522DE9A-08A5-425F-8AF5-499FA6205B85}">
114 <File Id="fil8E8F17BD7047E2FA8D6D83E3C561A136" KeyPath="yes" Source="$(var.SourceDir)\monoblue\changelog.tmpl" />
115 </Component>
116 <Component Id="cmp88FAE67F5F5D776AB06D0DFD7AF6B321" Directory="dirA4D7AC0F636AD49029D4D19861392983" Guid="{7F1857A6-1AB6-40BA-ADD9-D9AD10E86F86}">
117 <File Id="filB923707738590DC420DC9EC081FCD19E" KeyPath="yes" Source="$(var.SourceDir)\monoblue\changelogentry.tmpl" />
118 </Component>
119 <Component Id="cmpD9CA247FB5FD87215801EF6FA1862855" Directory="dirA4D7AC0F636AD49029D4D19861392983" Guid="{DE0C16EC-89C1-4553-9FC2-08BDD881411C}">
120 <File Id="fil40ED0CD11F2D78CC17557BADFA4CE6C7" KeyPath="yes" Source="$(var.SourceDir)\monoblue\changeset.tmpl" />
121 </Component>
122 <Component Id="cmp4BE8112F99779C0F09A4F1FC00C2EF1E" Directory="dirA4D7AC0F636AD49029D4D19861392983" Guid="{31991907-3468-41C5-8B9B-73730BBE6064}">
123 <File Id="fil6D7DFED2D3692BBD082FB0D28D889EA6" KeyPath="yes" Source="$(var.SourceDir)\monoblue\error.tmpl" />
124 </Component>
125 <Component Id="cmp7ABEE036B7DAAD0D1600DDBB43F2C671" Directory="dirA4D7AC0F636AD49029D4D19861392983" Guid="{F9F8FADE-2F02-457B-899E-BB18DFD0AE44}">
126 <File Id="filF2B59AD2AB4DE9C3101DC96F8867A4D5" KeyPath="yes" Source="$(var.SourceDir)\monoblue\fileannotate.tmpl" />
127 </Component>
128 <Component Id="cmpE4F3DB56FD3C5BB582CA58C7BACB0506" Directory="dirA4D7AC0F636AD49029D4D19861392983" Guid="{6E0D083A-19BE-4C8F-8CCF-339395DC638D}">
129 <File Id="fil6BBA7BBD0890DFCC52E62A948092EFB6" KeyPath="yes" Source="$(var.SourceDir)\monoblue\filediff.tmpl" />
130 </Component>
131 <Component Id="cmpEE32268B28EEC3EB3A572522C71905AE" Directory="dirA4D7AC0F636AD49029D4D19861392983" Guid="{493A745D-5342-4436-8C27-A8A6F6AC88EA}">
132 <File Id="filA85CD6E3008BD751106AB7B9A480C492" KeyPath="yes" Source="$(var.SourceDir)\monoblue\filelog.tmpl" />
133 </Component>
134 <Component Id="cmpC761C78428E72FFB5558162BB37B61BD" Directory="dirA4D7AC0F636AD49029D4D19861392983" Guid="{3083DBF6-1661-4722-B30B-1E3B3B90595D}">
135 <File Id="fil0BBD8320FE88C3AC548D940A0C435A05" KeyPath="yes" Source="$(var.SourceDir)\monoblue\filerevision.tmpl" />
136 </Component>
137 <Component Id="cmp17DC3C81B3ABC398360C2007A40ED1E6" Directory="dirA4D7AC0F636AD49029D4D19861392983" Guid="{E3059DE2-D513-473A-9720-DC7FDE13C29F}">
138 <File Id="filCA536CAA1C01B426565F20331B68760B" KeyPath="yes" Source="$(var.SourceDir)\monoblue\footer.tmpl" />
139 </Component>
140 <Component Id="cmp7F175F65C4834FE6069C99E338BDA7E5" Directory="dirA4D7AC0F636AD49029D4D19861392983" Guid="{F04EEDB1-0506-48A8-A4CB-B6A17188FDFE}">
141 <File Id="filC02F1442F76CB500AEB3840EF7356FD6" KeyPath="yes" Source="$(var.SourceDir)\monoblue\graph.tmpl" />
142 </Component>
143 <Component Id="cmpFC525BC58FC3D5CBABF272044899809D" Directory="dirA4D7AC0F636AD49029D4D19861392983" Guid="{2830CCC3-2E3A-4F54-9834-88E61755FDC1}">
144 <File Id="fil8B196B20D79CED85511A9369E5410E12" KeyPath="yes" Source="$(var.SourceDir)\monoblue\header.tmpl" />
145 </Component>
146 <Component Id="cmp5D58BCED0887E9ABCF56A03A3F766C05" Directory="dirA4D7AC0F636AD49029D4D19861392983" Guid="{67B47C67-E7C2-4D2E-BF1E-35072896BF66}">
147 <File Id="fil0F66655D0D9C0515498C736CA70EF2E7" KeyPath="yes" Source="$(var.SourceDir)\monoblue\index.tmpl" />
148 </Component>
149 <Component Id="cmp069EBBE6EAC918EFD74E47E30707A6B1" Directory="dirA4D7AC0F636AD49029D4D19861392983" Guid="{578111BD-AB5C-4E97-8873-F13EF2AB50A6}">
150 <File Id="filB70BAB1BDAC9E692CBD3DF2107E53358" KeyPath="yes" Source="$(var.SourceDir)\monoblue\manifest.tmpl" />
151 </Component>
152 <Component Id="cmp7B96FAA138AD6D0C5632C9AC08396CC7" Directory="dirA4D7AC0F636AD49029D4D19861392983" Guid="{0765336B-6756-4F68-804E-593D19D85696}">
153 <File Id="fil28523FA8BEB5DBE9EB187AC3D8FDFEBA" KeyPath="yes" Source="$(var.SourceDir)\monoblue\map" />
154 </Component>
155 <Component Id="cmp2F6A7B795CB0B56C4FA02196BB6C7DC6" Directory="dirA4D7AC0F636AD49029D4D19861392983" Guid="{A97A84F4-6360-405E-AB51-6604AD513107}">
156 <File Id="fil7AF04A6D9109C9B041BB38DC59E17AF9" KeyPath="yes" Source="$(var.SourceDir)\monoblue\notfound.tmpl" />
157 </Component>
158 <Component Id="cmp0ABEB61C4B9DB9E2FA98E5CD114D7BF3" Directory="dirA4D7AC0F636AD49029D4D19861392983" Guid="{4F97F211-D002-4241-A3F7-A39987524C82}">
159 <File Id="fil0D19104380B37D9A5821C8F0E7C5E77E" KeyPath="yes" Source="$(var.SourceDir)\monoblue\search.tmpl" />
160 </Component>
161 <Component Id="cmp113433EEDE668F21E50B0BFE83C69EEA" Directory="dirA4D7AC0F636AD49029D4D19861392983" Guid="{130A1D16-509D-442B-A990-8E9013D8BD05}">
162 <File Id="filB23212E48881879A57ACC2C8D328E43A" KeyPath="yes" Source="$(var.SourceDir)\monoblue\shortlog.tmpl" />
163 </Component>
164 <Component Id="cmp7F0B6DB5B3B965F203EF66F4BCA0CE8C" Directory="dirA4D7AC0F636AD49029D4D19861392983" Guid="{C452F9A9-67C4-43DC-B0B1-1A833A4F9095}">
165 <File Id="fil696E3BCDF3D23D04000016B5E5B255A2" KeyPath="yes" Source="$(var.SourceDir)\monoblue\summary.tmpl" />
166 </Component>
167 <Component Id="cmpE504AE3BC09BD33B8042C57812C4D1ED" Directory="dirA4D7AC0F636AD49029D4D19861392983" Guid="{9126CF0F-8EE1-4136-B27D-F1773EE2E2AB}">
168 <File Id="filC76BB94267C1E4FFF294F47D87A298D0" KeyPath="yes" Source="$(var.SourceDir)\monoblue\tags.tmpl" />
169 </Component>
170 <Component Id="cmp6A1BCBCE2BB6301A8437B67971E31BBD" Directory="dirCCAE13CC8442C655BA01764423E1EBB7" Guid="{C1507C7D-9D45-4A0E-AF3F-8E52F60E9A49}">
171 <File Id="filE8D51B053A29EE93BF3E1B0AEE6B5604" KeyPath="yes" Source="$(var.SourceDir)\paper\branches.tmpl" />
172 </Component>
173 <Component Id="cmp7FCF7EC491FB8A8C77281BBF71B21092" Directory="dirCCAE13CC8442C655BA01764423E1EBB7" Guid="{8F619F2F-A83A-4B76-986E-7B10826E0EEB}">
174 <File Id="filC65F1E9916A7F8ADC2E07A192FBC5E48" KeyPath="yes" Source="$(var.SourceDir)\paper\changeset.tmpl" />
175 </Component>
176 <Component Id="cmp2601220A329A40F193104A27B1EDD50F" Directory="dirCCAE13CC8442C655BA01764423E1EBB7" Guid="{2CA3DD6E-1BCD-4ED0-95B3-69BD2C723E8F}">
177 <File Id="fil5883423E0C42B6B99C9E22DAEFAEE97D" KeyPath="yes" Source="$(var.SourceDir)\paper\error.tmpl" />
178 </Component>
179 <Component Id="cmp6D80FA8F113931AFEDE452F25CE759C0" Directory="dirCCAE13CC8442C655BA01764423E1EBB7" Guid="{D5E5A96F-CE04-4FA2-82ED-0BB7AEFFEC4A}">
180 <File Id="filFDEDF9406F5102D8471419766BE46784" KeyPath="yes" Source="$(var.SourceDir)\paper\fileannotate.tmpl" />
181 </Component>
182 <Component Id="cmpF4FCB6A530B40B81345B2FF34128015E" Directory="dirCCAE13CC8442C655BA01764423E1EBB7" Guid="{D3307DF3-96CA-4E8B-95C2-5636EC9A71B2}">
183 <File Id="filCB283DE6BE999D0B91EC88F1F05380BA" KeyPath="yes" Source="$(var.SourceDir)\paper\filediff.tmpl" />
184 </Component>
185 <Component Id="cmp928B9D51180E9D420CFB1EDBAE91A65B" Directory="dirCCAE13CC8442C655BA01764423E1EBB7" Guid="{0157773E-9259-41AA-81A0-B032B8114DB0}">
186 <File Id="fil480C6A2482FB8457C07B0B4982C6B2F3" KeyPath="yes" Source="$(var.SourceDir)\paper\filelog.tmpl" />
187 </Component>
188 <Component Id="cmp7294A4A1FDA1C3BABFE78197A25A369D" Directory="dirCCAE13CC8442C655BA01764423E1EBB7" Guid="{DA8F5765-9DFB-425C-964B-7E6EB38BADB3}">
189 <File Id="filE64DC2B6DF6C1636DC6EB3F0EF988F23" KeyPath="yes" Source="$(var.SourceDir)\paper\filelogentry.tmpl" />
190 </Component>
191 <Component Id="cmp3F36EF7AE95420BC80A34E9C24293791" Directory="dirCCAE13CC8442C655BA01764423E1EBB7" Guid="{AB07E3D8-7276-416C-85A6-06555FF945EA}">
192 <File Id="filCDB52360E0BEB00BEE8776FA76441783" KeyPath="yes" Source="$(var.SourceDir)\paper\filerevision.tmpl" />
193 </Component>
194 <Component Id="cmpCFCDDD5FA4AE732AA051C60F8787554D" Directory="dirCCAE13CC8442C655BA01764423E1EBB7" Guid="{C900C800-0660-4B36-ACA3-BDD3BB783CE1}">
195 <File Id="filEF3D2835F8AB4488B82C88BFE4A69D6E" KeyPath="yes" Source="$(var.SourceDir)\paper\footer.tmpl" />
196 </Component>
197 <Component Id="cmp30B241A606B4AD5E3162C0B2CC09DDF9" Directory="dirCCAE13CC8442C655BA01764423E1EBB7" Guid="{71501492-BCD0-4B2F-B653-83D27CCEFAAD}">
198 <File Id="filE02A4F87D721B4B6B68235AF4A8863A6" KeyPath="yes" Source="$(var.SourceDir)\paper\graph.tmpl" />
199 </Component>
200 <Component Id="cmpB668F2549A35840953001CE413A4C827" Directory="dirCCAE13CC8442C655BA01764423E1EBB7" Guid="{CDE48712-F5C9-4BCC-9790-B2910F87992A}">
201 <File Id="filD235AD247D72C6F35605F9AD3D3ACFE6" KeyPath="yes" Source="$(var.SourceDir)\paper\header.tmpl" />
202 </Component>
203 <Component Id="cmp2E517204CC91CC55F5080C62F8C74CB3" Directory="dirCCAE13CC8442C655BA01764423E1EBB7" Guid="{C2BDB761-525B-436D-946D-8327ADCB0762}">
204 <File Id="filE647110321294198E994033415240DE2" KeyPath="yes" Source="$(var.SourceDir)\paper\index.tmpl" />
205 </Component>
206 <Component Id="cmp1169DE962AAC1A97C8820F60C85C2AB5" Directory="dirCCAE13CC8442C655BA01764423E1EBB7" Guid="{8AA0022C-9AE2-41D8-9F43-8AC426DCF7F0}">
207 <File Id="fil3A3927F8011BABF73AF2BF55FD2B9FD2" KeyPath="yes" Source="$(var.SourceDir)\paper\manifest.tmpl" />
208 </Component>
209 <Component Id="cmp563BA8E85BA6CABCF9AEBD11CB585EBD" Directory="dirCCAE13CC8442C655BA01764423E1EBB7" Guid="{74408780-F9AE-4067-B1F8-0A1B12BB1826}">
210 <File Id="fil1FCC19A801EB82E395B5A8EF7E869C62" KeyPath="yes" Source="$(var.SourceDir)\paper\map" />
211 </Component>
212 <Component Id="cmpA9A7FE93F240404E1161E70007AC6A78" Directory="dirCCAE13CC8442C655BA01764423E1EBB7" Guid="{CAE40619-B777-4C7A-8622-CB2DD6756FAA}">
213 <File Id="filFEBD530E5D4F4F2FD23A9E746297721D" KeyPath="yes" Source="$(var.SourceDir)\paper\notfound.tmpl" />
214 </Component>
215 <Component Id="cmpBA354AABF9A7304F201B7C71121A7900" Directory="dirCCAE13CC8442C655BA01764423E1EBB7" Guid="{23095279-8206-46CB-82E4-AFC007E16C63}">
216 <File Id="fil9F42800F509E8B8E019992B77410F683" KeyPath="yes" Source="$(var.SourceDir)\paper\search.tmpl" />
217 </Component>
218 <Component Id="cmp3AB3B632EBF9F9FD18D38605C343A6B0" Directory="dirCCAE13CC8442C655BA01764423E1EBB7" Guid="{107E31F0-B7AB-4757-A1F6-86C17E8C384B}">
219 <File Id="filD59540CF5AFFFD872F212B2B1AE6212A" KeyPath="yes" Source="$(var.SourceDir)\paper\shortlog.tmpl" />
220 </Component>
221 <Component Id="cmpB3F6B93705B00E2E3B0459048506D283" Directory="dirCCAE13CC8442C655BA01764423E1EBB7" Guid="{2F3B2692-5B58-40F8-AE29-844BA58FB312}">
222 <File Id="fil03DA9AB1567E165864A87437FEE91FB8" KeyPath="yes" Source="$(var.SourceDir)\paper\shortlogentry.tmpl" />
223 </Component>
224 <Component Id="cmpEF4594F7D285174D5A356ADEAAFC61ED" Directory="dirCCAE13CC8442C655BA01764423E1EBB7" Guid="{1BE60A48-7183-4886-BC75-3CFF6C51EC3F}">
225 <File Id="fil671A5C071B2BE506FA431651133EFE0A" KeyPath="yes" Source="$(var.SourceDir)\paper\tags.tmpl" />
226 </Component>
227 <Component Id="cmpB25518BB895E3CB4186909EBE6E99C4C" Directory="dir10F9FCD5760425E499FA92F2CAA7A3EA" Guid="{DBB01FA1-EFC7-42E3-81F2-190C060DC035}">
228 <File Id="fil475FD65FC1DCA47748FF999739657468" KeyPath="yes" Source="$(var.SourceDir)\raw\changeset.tmpl" />
229 </Component>
230 <Component Id="cmp17DC05518B1190BEEF5761C10C71854A" Directory="dir10F9FCD5760425E499FA92F2CAA7A3EA" Guid="{4501B072-4375-4458-94C1-2C4E9E991ACD}">
231 <File Id="filFA9899D1B1C1E72B406307FDB9AF7BAF" KeyPath="yes" Source="$(var.SourceDir)\raw\error.tmpl" />
232 </Component>
233 <Component Id="cmpECA65D83D6634896FDB8534E4175FD4F" Directory="dir10F9FCD5760425E499FA92F2CAA7A3EA" Guid="{747870B2-1590-41D5-9112-EB5FFBF50796}">
234 <File Id="fil3CDE49640C240CF6C2C4E1660F904DE9" KeyPath="yes" Source="$(var.SourceDir)\raw\fileannotate.tmpl" />
235 </Component>
236 <Component Id="cmp20CF2D57316DC85D6C9A5B3475F84E80" Directory="dir10F9FCD5760425E499FA92F2CAA7A3EA" Guid="{01ACEDCC-BACF-4D06-B875-0520723B9D37}">
237 <File Id="fil988220F2BAD1CE4CBC544A29C02E82E6" KeyPath="yes" Source="$(var.SourceDir)\raw\filediff.tmpl" />
238 </Component>
239 <Component Id="cmp43DB97D87FF0A11266474A5725C84AC1" Directory="dir10F9FCD5760425E499FA92F2CAA7A3EA" Guid="{CBAFB505-70F0-40BD-9415-AAE3C3FE5C88}">
240 <File Id="fil5066029ABD205C7759437E120CB7018C" KeyPath="yes" Source="$(var.SourceDir)\raw\index.tmpl" />
241 </Component>
242 <Component Id="cmp0C3504452BA0E5A32AE919D1FB100BD3" Directory="dir10F9FCD5760425E499FA92F2CAA7A3EA" Guid="{2EBF0786-F18F-4787-9863-4075D16CAB8F}">
243 <File Id="filAAC49A49F4D6E3A6EAB4EDE1EE2A5808" KeyPath="yes" Source="$(var.SourceDir)\raw\manifest.tmpl" />
244 </Component>
245 <Component Id="cmpE610EB581D7A11C798F107E00759EE30" Directory="dir10F9FCD5760425E499FA92F2CAA7A3EA" Guid="{8834AC2E-11C2-4890-A49B-59BD5CB34953}">
246 <File Id="fil985E1967B655AE5DA8779D2A8E69D986" KeyPath="yes" Source="$(var.SourceDir)\raw\map" />
247 </Component>
248 <Component Id="cmp708C16629B8102ED107B6C79265E1A61" Directory="dir10F9FCD5760425E499FA92F2CAA7A3EA" Guid="{C78C08CA-6F0D-4E00-ADDF-94DA6BFD385A}">
249 <File Id="fil52C7F355B2B8B57D8D8D26F373EED4A4" KeyPath="yes" Source="$(var.SourceDir)\raw\notfound.tmpl" />
250 </Component>
251 <Component Id="cmpEED23BA7882C00E45C04648CCF07C72A" Directory="dirCB380721FE38FED97C74A9E48945B5BC" Guid="{6AFE12A7-D858-4D04-A263-AF21890783F2}">
252 <File Id="fil38EF6BFECB3EA61C18AD8510C540E030" KeyPath="yes" Source="$(var.SourceDir)\rss\changelog.tmpl" />
253 </Component>
254 <Component Id="cmp9293236C7475213E8741C193885BC370" Directory="dirCB380721FE38FED97C74A9E48945B5BC" Guid="{FF79D239-640A-4337-8DC8-C7710DCF6952}">
255 <File Id="fil124495614FA234F165FDFAFB5FCC8B41" KeyPath="yes" Source="$(var.SourceDir)\rss\changelogentry.tmpl" />
256 </Component>
257 <Component Id="cmpF300C87CBE61D8E4F9139A655CC77F2A" Directory="dirCB380721FE38FED97C74A9E48945B5BC" Guid="{76C2B1FF-C1F8-49FC-BEC7-8689FB2676D7}">
258 <File Id="fil975AA04A3938C6B3FA67DE73AD8D8A62" KeyPath="yes" Source="$(var.SourceDir)\rss\error.tmpl" />
259 </Component>
260 <Component Id="cmp3EFC01264101EB280A920E7543FC6BA7" Directory="dirCB380721FE38FED97C74A9E48945B5BC" Guid="{7A44A6EA-6ECB-4235-8EFC-0EFEB2F82E1C}">
261 <File Id="fil15C5D0A18D58A500E90E198CD25799D2" KeyPath="yes" Source="$(var.SourceDir)\rss\filelog.tmpl" />
262 </Component>
263 <Component Id="cmpE86BFF1FA4FE20689AD82969DFD9EF04" Directory="dirCB380721FE38FED97C74A9E48945B5BC" Guid="{F3DE2D70-217D-4DDB-A8F8-C973AAF85D06}">
264 <File Id="fil4516B6122DFF1FAD28410E4F68D37FB4" KeyPath="yes" Source="$(var.SourceDir)\rss\filelogentry.tmpl" />
265 </Component>
266 <Component Id="cmpA55F157640DFAA982E2410BFF396F838" Directory="dirCB380721FE38FED97C74A9E48945B5BC" Guid="{4A467D62-3231-4A5C-B455-EF3C962B1F46}">
267 <File Id="fil0F3AAA97082D3FB22338711F77A7C8C4" KeyPath="yes" Source="$(var.SourceDir)\rss\header.tmpl" />
268 </Component>
269 <Component Id="cmp69196B3D4F0DAC971FC2D765F781B671" Directory="dirCB380721FE38FED97C74A9E48945B5BC" Guid="{26329996-79F4-4E76-8553-1BDFC0873639}">
270 <File Id="filA937974673590803746240DB3A232A66" KeyPath="yes" Source="$(var.SourceDir)\rss\map" />
271 </Component>
272 <Component Id="cmpC696B6493CA1B63C192B073B0D728564" Directory="dirCB380721FE38FED97C74A9E48945B5BC" Guid="{E0719339-E59A-428B-8600-845314285599}">
273 <File Id="filAA576F47CA9747367AF8C4528FD8A217" KeyPath="yes" Source="$(var.SourceDir)\rss\tagentry.tmpl" />
274 </Component>
275 <Component Id="cmp47B99ED43B673977AE8606E3451D8674" Directory="dirCB380721FE38FED97C74A9E48945B5BC" Guid="{F785D87D-0BE0-41C5-BF47-4A7B23CE7BB1}">
276 <File Id="filF3D72D7B79A517201EA2B568C64C19C8" KeyPath="yes" Source="$(var.SourceDir)\rss\tags.tmpl" />
277 </Component>
278 <Component Id="cmp8C4153B109FF248C582A307E7575BDC7" Directory="dirB8FE74E96715E770736FEAFA391ABF15" Guid="{2C281D9F-09BD-43C7-BEB8-89F63217D01E}">
279 <File Id="fil45D9BD7CD3073F10FED6A9FDD57C6303" KeyPath="yes" Source="$(var.SourceDir)\spartan\branches.tmpl" />
280 </Component>
281 <Component Id="cmp258992A63D8561862F924B2F445F480E" Directory="dirB8FE74E96715E770736FEAFA391ABF15" Guid="{9640D202-3867-49AF-B648-6F8B515376FD}">
282 <File Id="filDCE126EE702579CD325AFA89C6040B3D" KeyPath="yes" Source="$(var.SourceDir)\spartan\changelog.tmpl" />
283 </Component>
284 <Component Id="cmpCBC90D7649B7D877F5A4BBAD723F7F2F" Directory="dirB8FE74E96715E770736FEAFA391ABF15" Guid="{DF2B7556-BBFB-489D-908B-6220191EC620}">
285 <File Id="fil9FBF8720F94C113556DDE1C97DF9DDE3" KeyPath="yes" Source="$(var.SourceDir)\spartan\changelogentry.tmpl" />
286 </Component>
287 <Component Id="cmp983EB84EE3C3FA79C2B4B0E126900FF8" Directory="dirB8FE74E96715E770736FEAFA391ABF15" Guid="{78ED06E6-41D9-4598-9697-2142016885D7}">
288 <File Id="filE54688ED547D1CF7DF8EB2E53E47C1C0" KeyPath="yes" Source="$(var.SourceDir)\spartan\changeset.tmpl" />
289 </Component>
290 <Component Id="cmp87B16F191F9EE8A923BD50FFAB5AF8A1" Directory="dirB8FE74E96715E770736FEAFA391ABF15" Guid="{0002AF55-6A71-4F51-8C50-B39F6215F2B0}">
291 <File Id="filCF590744894C82D12672745CA8694FDA" KeyPath="yes" Source="$(var.SourceDir)\spartan\error.tmpl" />
292 </Component>
293 <Component Id="cmp24FC541496FA7F2BB64E40CE1881E9BD" Directory="dirB8FE74E96715E770736FEAFA391ABF15" Guid="{F2E08FF3-744B-4D45-AFA1-5C33A1604F91}">
294 <File Id="filD8A4D1634CFB34AE2A52DD98EFE22B29" KeyPath="yes" Source="$(var.SourceDir)\spartan\fileannotate.tmpl" />
295 </Component>
296 <Component Id="cmp942F916336F4CBA936A065A28D3EADD3" Directory="dirB8FE74E96715E770736FEAFA391ABF15" Guid="{905E4307-E9B4-45E3-AE1C-E7D01A4120F4}">
297 <File Id="fil568DC2C47109CC77F23F9593A16B44F0" KeyPath="yes" Source="$(var.SourceDir)\spartan\filediff.tmpl" />
298 </Component>
299 <Component Id="cmp597CDAFF17F11DA3FED725E5829DD73C" Directory="dirB8FE74E96715E770736FEAFA391ABF15" Guid="{84FBA0CE-7E2C-4504-94D5-9C383DA4BFDE}">
300 <File Id="fil580676C3E18FDF8919786C15B92FDBD2" KeyPath="yes" Source="$(var.SourceDir)\spartan\filelog.tmpl" />
301 </Component>
302 <Component Id="cmpC4ACD09B8854124364CBB8CDAD38FB08" Directory="dirB8FE74E96715E770736FEAFA391ABF15" Guid="{0D0E0B26-EA07-4ADE-96F3-3F30DBC4AA9D}">
303 <File Id="filC736FBCC66C5D91989FC91C2E5931182" KeyPath="yes" Source="$(var.SourceDir)\spartan\filelogentry.tmpl" />
304 </Component>
305 <Component Id="cmpEB22906EBBA0857AECD19E74CE893749" Directory="dirB8FE74E96715E770736FEAFA391ABF15" Guid="{A588ED57-CDFE-4316-AF15-C81E40A67343}">
306 <File Id="filDA153719D47EE0165DB1A908ECF1D9CF" KeyPath="yes" Source="$(var.SourceDir)\spartan\filerevision.tmpl" />
307 </Component>
308 <Component Id="cmp30475C52A92194CBC64BED509F7D7B64" Directory="dirB8FE74E96715E770736FEAFA391ABF15" Guid="{B795B3F2-394E-4F23-B0B7-BD6191E17714}">
309 <File Id="filBAF07E4ACAECCC417DCF35B01D053357" KeyPath="yes" Source="$(var.SourceDir)\spartan\footer.tmpl" />
310 </Component>
311 <Component Id="cmp679ECC15E459B542AA894CD7F0B3690D" Directory="dirB8FE74E96715E770736FEAFA391ABF15" Guid="{82E57B98-1C5F-43D5-81FF-7952EFAAAB62}">
312 <File Id="filABBF8D15F4DE387F2648004FAB23B078" KeyPath="yes" Source="$(var.SourceDir)\spartan\graph.tmpl" />
313 </Component>
314 <Component Id="cmp31A95170275A0F3CEA2EFA0B3EE699EA" Directory="dirB8FE74E96715E770736FEAFA391ABF15" Guid="{382BA9AC-67BC-44BA-8004-F72EFEB68007}">
315 <File Id="fil69E45EBB4E4C69CD7482C487DF64C3D8" KeyPath="yes" Source="$(var.SourceDir)\spartan\header.tmpl" />
316 </Component>
317 <Component Id="cmp4363ACEAD4BD8845B0C9AC2D03465A5E" Directory="dirB8FE74E96715E770736FEAFA391ABF15" Guid="{CF8B8556-687B-4E82-BC6B-62A4916A7E4A}">
318 <File Id="fil5FE55A8E4659024B58AA7C8256CA9439" KeyPath="yes" Source="$(var.SourceDir)\spartan\index.tmpl" />
319 </Component>
320 <Component Id="cmp1B77757C5443F6AA7EED502370927E92" Directory="dirB8FE74E96715E770736FEAFA391ABF15" Guid="{0AB066EA-64CF-4434-9752-2E900396EFFF}">
321 <File Id="filE71CF1D4A57966CE89FED594B3D9E430" KeyPath="yes" Source="$(var.SourceDir)\spartan\manifest.tmpl" />
322 </Component>
323 <Component Id="cmp269E53402BD0050DE38109E3BF5C3945" Directory="dirB8FE74E96715E770736FEAFA391ABF15" Guid="{6B03B3A5-49D5-413B-9DA5-A32BA30F88BC}">
324 <File Id="fil9D51DC7A5EB908A1EECA883E06EB8B31" KeyPath="yes" Source="$(var.SourceDir)\spartan\map" />
325 </Component>
326 <Component Id="cmp73F5CB8A13C8C57CD15DB9064BD9E2BF" Directory="dirB8FE74E96715E770736FEAFA391ABF15" Guid="{3B9DB632-BDDC-4DDF-81FD-1DC6751A3467}">
327 <File Id="filE079FF0AA52D0BCCDB9A5B49A80D5504" KeyPath="yes" Source="$(var.SourceDir)\spartan\notfound.tmpl" />
328 </Component>
329 <Component Id="cmpECE54442CE4E6FD27717A8D578125A2B" Directory="dirB8FE74E96715E770736FEAFA391ABF15" Guid="{886E4B52-C284-4493-B2E5-FA35A9CD3ABA}">
330 <File Id="filE14A89B3EA724AAB48354DEE70DF69C8" KeyPath="yes" Source="$(var.SourceDir)\spartan\search.tmpl" />
331 </Component>
332 <Component Id="cmpFF1D26AC267CDED40E1D2AC476D7F09F" Directory="dirB8FE74E96715E770736FEAFA391ABF15" Guid="{6359A1B3-48B2-4092-A7C4-BE53F05870AB}">
333 <File Id="fil868AF4DF25AC25F53793411E9365297D" KeyPath="yes" Source="$(var.SourceDir)\spartan\shortlog.tmpl" />
334 </Component>
335 <Component Id="cmp2D8D54467D34BA5055A5FF2E67254EAD" Directory="dirB8FE74E96715E770736FEAFA391ABF15" Guid="{F26E20E2-7A04-489B-8F83-183D898174DD}">
336 <File Id="filF4ABFCC411E50C0042C1848016EE5327" KeyPath="yes" Source="$(var.SourceDir)\spartan\shortlogentry.tmpl" />
337 </Component>
338 <Component Id="cmp83ECAEFDCC6805EEFC78A54EF1000146" Directory="dirB8FE74E96715E770736FEAFA391ABF15" Guid="{7A021045-DA97-4A34-87AB-0882F071573C}">
339 <File Id="fil1D8DB7506E3BBD7449459C9BAB25CDAD" KeyPath="yes" Source="$(var.SourceDir)\spartan\tags.tmpl" />
340 </Component>
341 <Component Id="cmpBFBB6F3B9C29C43E991077D6BFC33944" Directory="dirC5A30981F5453B69928F61CEC4F6D629" Guid="{5883A510-590E-4ADE-85A7-B94F373B63CA}">
342 <File Id="filF8DE93E9297B5CAE60372749245F15B2" KeyPath="yes" Source="$(var.SourceDir)\static\background.png" />
343 </Component>
344 <Component Id="cmp0F63F938946462583D4DEE74873DC408" Directory="dirC5A30981F5453B69928F61CEC4F6D629" Guid="{A0C70D4C-E2BE-4173-A95F-A2FB690A65D5}">
345 <File Id="fil00A8D7AEA87233FED8A360F2D862572E" KeyPath="yes" Source="$(var.SourceDir)\static\coal-file.png" />
346 </Component>
347 <Component Id="cmpDE70C42580801A22F32CFD04C7EF4CF9" Directory="dirC5A30981F5453B69928F61CEC4F6D629" Guid="{81A48619-3740-4874-83C3-AE58D74E30A6}">
348 <File Id="fil42C97A38CD59CED3713E93E6CBCDBFFE" KeyPath="yes" Source="$(var.SourceDir)\static\coal-folder.png" />
349 </Component>
350 <Component Id="cmpA45BEDD14DCA07C7A92A417386445E25" Directory="dirC5A30981F5453B69928F61CEC4F6D629" Guid="{030E40CB-67A6-4EB5-ACC2-18B14BC31178}">
351 <File Id="filCC692DA67C62144B4AF34F10014F83FD" KeyPath="yes" Source="$(var.SourceDir)\static\excanvas.js" />
352 </Component>
353 <Component Id="cmp72015354E78AA8A481B54F81AD8F9682" Directory="dirC5A30981F5453B69928F61CEC4F6D629" Guid="{894DAD59-0224-4CD1-8984-CBCB1464E44A}">
354 <File Id="fil690EAE877AB9F8C70C93E471115C52F2" KeyPath="yes" Source="$(var.SourceDir)\static\graph.js" />
355 </Component>
356 <Component Id="cmpB467CD481FE78A9CD052183FE5F3D0C6" Directory="dirC5A30981F5453B69928F61CEC4F6D629" Guid="{DC08B50D-F37D-42FF-A382-037D7DE80008}">
357 <File Id="fil28C1A9E02AE19A9988E8F1BAFDBEAD91" KeyPath="yes" Source="$(var.SourceDir)\static\hgicon.png" />
358 </Component>
359 <Component Id="cmp6B247043D5188A72BAFBCC81DB10B97C" Directory="dirC5A30981F5453B69928F61CEC4F6D629" Guid="{4EC7856F-BA03-4DE8-A84B-7DCF12A9393C}">
360 <File Id="fil8860DD8D5DA074A5BE9F314F442285EC" KeyPath="yes" Source="$(var.SourceDir)\static\hglogo.png" />
361 </Component>
362 <Component Id="cmp4F8B4BF7E8F4DDF9C224F45A94BAE0AA" Directory="dirC5A30981F5453B69928F61CEC4F6D629" Guid="{2363D738-3F01-4C02-95A1-AF17841FC087}">
363 <File Id="filA6C8F4FB422C911ABE0F82752E383E99" KeyPath="yes" Source="$(var.SourceDir)\static\style-coal.css" />
364 </Component>
365 <Component Id="cmpF69080B6DF1E2AA23990F61B4ED884EB" Directory="dirC5A30981F5453B69928F61CEC4F6D629" Guid="{5ED9E813-DE59-45C9-9CCB-1A3248E348E5}">
366 <File Id="fil5078818944548B0CCE1C317796CF7D4A" KeyPath="yes" Source="$(var.SourceDir)\static\style-gitweb.css" />
367 </Component>
368 <Component Id="cmp39B7B1D88ED5B2E8E878FEF7FB7D0F89" Directory="dirC5A30981F5453B69928F61CEC4F6D629" Guid="{5F49B8D0-F497-4848-B95D-040BE4CC3296}">
369 <File Id="filB788208E124431C056B605A46EC7A643" KeyPath="yes" Source="$(var.SourceDir)\static\style-monoblue.css" />
370 </Component>
371 <Component Id="cmp08A2992654457D67292FF9C2B96B5172" Directory="dirC5A30981F5453B69928F61CEC4F6D629" Guid="{474A04D3-84A1-435F-99DB-21BA3079CE55}">
372 <File Id="filE567FA56A8009FB9853EA69E01A0A410" KeyPath="yes" Source="$(var.SourceDir)\static\style-paper.css" />
373 </Component>
374 <Component Id="cmpDABBFD12710BF06B024350932B45E606" Directory="dirC5A30981F5453B69928F61CEC4F6D629" Guid="{54BB78EA-5B06-42EA-8F30-9C5277A8590E}">
375 <File Id="filE018265028110198DEF0E278341C9EB3" KeyPath="yes" Source="$(var.SourceDir)\static\style.css" />
376 </Component>
377 </ComponentGroup>
378 </Fragment>
379 <Fragment>
380 <DirectoryRef Id="INSTALLDIR">
381 <Directory Id="dirBFB9D3FD33CE2C696D390C94E4E1B4D8" Name="templates" />
382 </DirectoryRef>
383 </Fragment>
384 <Fragment>
385 <DirectoryRef Id="dirA4D7AC0F636AD49029D4D19861392983" />
386 </Fragment>
387 <Fragment>
388 <DirectoryRef Id="dirC5A30981F5453B69928F61CEC4F6D629" />
389 </Fragment>
390 <Fragment>
391 <DirectoryRef Id="dir35F8598B197DFC5580317F5E7C2E9AE9" />
392 </Fragment>
393 <Fragment>
394 <DirectoryRef Id="dirA4D7AC0F636AD49029D4D19861392983" />
395 </Fragment>
396 <Fragment>
397 <DirectoryRef Id="dir10F9FCD5760425E499FA92F2CAA7A3EA" />
398 </Fragment>
399 <Fragment>
400 <DirectoryRef Id="dirC5A30981F5453B69928F61CEC4F6D629" />
401 </Fragment>
402 <Fragment>
403 <DirectoryRef Id="dirA4D7AC0F636AD49029D4D19861392983" />
404 </Fragment>
405 <Fragment>
406 <DirectoryRef Id="dirCCAE13CC8442C655BA01764423E1EBB7" />
407 </Fragment>
408 <Fragment>
409 <DirectoryRef Id="dir10F9FCD5760425E499FA92F2CAA7A3EA" />
410 </Fragment>
411 <Fragment>
412 <DirectoryRef Id="dirA4D7AC0F636AD49029D4D19861392983" />
413 </Fragment>
414 <Fragment>
415 <DirectoryRef Id="dirA4D7AC0F636AD49029D4D19861392983" />
416 </Fragment>
417 <Fragment>
418 <DirectoryRef Id="dirB8FE74E96715E770736FEAFA391ABF15" />
419 </Fragment>
420 <Fragment>
421 <DirectoryRef Id="dir3D22B249045A1473460D0D5D104478F2" />
422 </Fragment>
423 <Fragment>
424 <DirectoryRef Id="dir3D22B249045A1473460D0D5D104478F2" />
425 </Fragment>
426 <Fragment>
427 <DirectoryRef Id="dir35F8598B197DFC5580317F5E7C2E9AE9" />
428 </Fragment>
429 <Fragment>
430 <DirectoryRef Id="dir3D22B249045A1473460D0D5D104478F2" />
431 </Fragment>
432 <Fragment>
433 <DirectoryRef Id="dir10F9FCD5760425E499FA92F2CAA7A3EA" />
434 </Fragment>
435 <Fragment>
436 <DirectoryRef Id="dirBFB9D3FD33CE2C696D390C94E4E1B4D8" />
437 </Fragment>
438 <Fragment>
439 <DirectoryRef Id="dirB8FE74E96715E770736FEAFA391ABF15" />
440 </Fragment>
441 <Fragment>
442 <DirectoryRef Id="dirB8FE74E96715E770736FEAFA391ABF15" />
443 </Fragment>
444 <Fragment>
445 <DirectoryRef Id="dirCCAE13CC8442C655BA01764423E1EBB7" />
446 </Fragment>
447 <Fragment>
448 <DirectoryRef Id="dirB8FE74E96715E770736FEAFA391ABF15" />
449 </Fragment>
450 <Fragment>
451 <DirectoryRef Id="dirB8FE74E96715E770736FEAFA391ABF15" />
452 </Fragment>
453 <Fragment>
454 <DirectoryRef Id="dirCCAE13CC8442C655BA01764423E1EBB7" />
455 </Fragment>
456 <Fragment>
457 <DirectoryRef Id="dirA4D7AC0F636AD49029D4D19861392983" />
458 </Fragment>
459 <Fragment>
460 <DirectoryRef Id="dirB8FE74E96715E770736FEAFA391ABF15" />
461 </Fragment>
462 <Fragment>
463 <DirectoryRef Id="dirCCAE13CC8442C655BA01764423E1EBB7" />
464 </Fragment>
465 <Fragment>
466 <DirectoryRef Id="dir3D22B249045A1473460D0D5D104478F2" />
467 </Fragment>
468 <Fragment>
469 <DirectoryRef Id="dirB8FE74E96715E770736FEAFA391ABF15" />
470 </Fragment>
471 <Fragment>
472 <DirectoryRef Id="dirC5A30981F5453B69928F61CEC4F6D629" />
473 </Fragment>
474 <Fragment>
475 <DirectoryRef Id="dirCCAE13CC8442C655BA01764423E1EBB7" />
476 </Fragment>
477 <Fragment>
478 <DirectoryRef Id="dirBFB9D3FD33CE2C696D390C94E4E1B4D8" />
479 </Fragment>
480 <Fragment>
481 <DirectoryRef Id="dirCB380721FE38FED97C74A9E48945B5BC" />
482 </Fragment>
483 <Fragment>
484 <DirectoryRef Id="dirCCAE13CC8442C655BA01764423E1EBB7" />
485 </Fragment>
486 <Fragment>
487 <DirectoryRef Id="dir3D22B249045A1473460D0D5D104478F2" />
488 </Fragment>
489 <Fragment>
490 <DirectoryRef Id="dirB8FE74E96715E770736FEAFA391ABF15" />
491 </Fragment>
492 <Fragment>
493 <DirectoryRef Id="dir10F9FCD5760425E499FA92F2CAA7A3EA" />
494 </Fragment>
495 <Fragment>
496 <DirectoryRef Id="dirCB380721FE38FED97C74A9E48945B5BC" />
497 </Fragment>
498 <Fragment>
499 <DirectoryRef Id="dir35F8598B197DFC5580317F5E7C2E9AE9" />
500 </Fragment>
501 <Fragment>
502 <DirectoryRef Id="dirA4D7AC0F636AD49029D4D19861392983" />
503 </Fragment>
504 <Fragment>
505 <DirectoryRef Id="dir3D22B249045A1473460D0D5D104478F2" />
506 </Fragment>
507 <Fragment>
508 <DirectoryRef Id="dir3D22B249045A1473460D0D5D104478F2" />
509 </Fragment>
510 <Fragment>
511 <DirectoryRef Id="dir3D22B249045A1473460D0D5D104478F2" />
512 </Fragment>
513 <Fragment>
514 <DirectoryRef Id="dirC5A30981F5453B69928F61CEC4F6D629" />
515 </Fragment>
516 <Fragment>
517 <DirectoryRef Id="dirCCAE13CC8442C655BA01764423E1EBB7" />
518 </Fragment>
519 <Fragment>
520 <DirectoryRef Id="dirB8FE74E96715E770736FEAFA391ABF15" />
521 </Fragment>
522 <Fragment>
523 <DirectoryRef Id="dirA4D7AC0F636AD49029D4D19861392983" />
524 </Fragment>
525 <Fragment>
526 <DirectoryRef Id="dirB8FE74E96715E770736FEAFA391ABF15" />
527 </Fragment>
528 <Fragment>
529 <DirectoryRef Id="dirCB380721FE38FED97C74A9E48945B5BC" />
530 </Fragment>
531 <Fragment>
532 <DirectoryRef Id="dirCCAE13CC8442C655BA01764423E1EBB7" />
533 </Fragment>
534 <Fragment>
535 <DirectoryRef Id="dirC5A30981F5453B69928F61CEC4F6D629" />
536 </Fragment>
537 <Fragment>
538 <DirectoryRef Id="dir3D22B249045A1473460D0D5D104478F2" />
539 </Fragment>
540 <Fragment>
541 <DirectoryRef Id="dirCCAE13CC8442C655BA01764423E1EBB7" />
542 </Fragment>
543 <Fragment>
544 <DirectoryRef Id="dir10F9FCD5760425E499FA92F2CAA7A3EA" />
545 </Fragment>
546 <Fragment>
547 <DirectoryRef Id="dirC5A30981F5453B69928F61CEC4F6D629" />
548 </Fragment>
549 <Fragment>
550 <DirectoryRef Id="dirCCAE13CC8442C655BA01764423E1EBB7" />
551 </Fragment>
552 <Fragment>
553 <DirectoryRef Id="dir3D22B249045A1473460D0D5D104478F2" />
554 </Fragment>
555 <Fragment>
556 <DirectoryRef Id="dirB8FE74E96715E770736FEAFA391ABF15" />
557 </Fragment>
558 <Fragment>
559 <DirectoryRef Id="dir3D22B249045A1473460D0D5D104478F2" />
560 </Fragment>
561 <Fragment>
562 <DirectoryRef Id="dirA4D7AC0F636AD49029D4D19861392983" />
563 </Fragment>
564 <Fragment>
565 <DirectoryRef Id="dirA4D7AC0F636AD49029D4D19861392983" />
566 </Fragment>
567 <Fragment>
568 <DirectoryRef Id="dirA4D7AC0F636AD49029D4D19861392983" />
569 </Fragment>
570 <Fragment>
571 <DirectoryRef Id="dirA4D7AC0F636AD49029D4D19861392983" />
572 </Fragment>
573 <Fragment>
574 <DirectoryRef Id="dir3D22B249045A1473460D0D5D104478F2" />
575 </Fragment>
576 <Fragment>
577 <DirectoryRef Id="dirCCAE13CC8442C655BA01764423E1EBB7" />
578 </Fragment>
579 <Fragment>
580 <DirectoryRef Id="dirB8FE74E96715E770736FEAFA391ABF15" />
581 </Fragment>
582 <Fragment>
583 <DirectoryRef Id="dir35F8598B197DFC5580317F5E7C2E9AE9" />
584 </Fragment>
585 <Fragment>
586 <DirectoryRef Id="dirB8FE74E96715E770736FEAFA391ABF15" />
587 </Fragment>
588 <Fragment>
589 <DirectoryRef Id="dirA4D7AC0F636AD49029D4D19861392983" />
590 </Fragment>
591 <Fragment>
592 <DirectoryRef Id="dirB8FE74E96715E770736FEAFA391ABF15" />
593 </Fragment>
594 <Fragment>
595 <DirectoryRef Id="dirCCAE13CC8442C655BA01764423E1EBB7" />
596 </Fragment>
597 <Fragment>
598 <DirectoryRef Id="dirCB380721FE38FED97C74A9E48945B5BC" />
599 </Fragment>
600 <Fragment>
601 <DirectoryRef Id="dirB8FE74E96715E770736FEAFA391ABF15" />
602 </Fragment>
603 <Fragment>
604 <DirectoryRef Id="dirB8FE74E96715E770736FEAFA391ABF15" />
605 </Fragment>
606 <Fragment>
607 <DirectoryRef Id="dir3D22B249045A1473460D0D5D104478F2" />
608 </Fragment>
609 <Fragment>
610 <DirectoryRef Id="dir35F8598B197DFC5580317F5E7C2E9AE9" />
611 </Fragment>
612 <Fragment>
613 <DirectoryRef Id="dirC5A30981F5453B69928F61CEC4F6D629" />
614 </Fragment>
615 <Fragment>
616 <DirectoryRef Id="dirCB380721FE38FED97C74A9E48945B5BC" />
617 </Fragment>
618 <Fragment>
619 <DirectoryRef Id="dirBFB9D3FD33CE2C696D390C94E4E1B4D8" />
620 </Fragment>
621 <Fragment>
622 <DirectoryRef Id="dirCCAE13CC8442C655BA01764423E1EBB7" />
623 </Fragment>
624 <Fragment>
625 <DirectoryRef Id="dir3D22B249045A1473460D0D5D104478F2" />
626 </Fragment>
627 <Fragment>
628 <DirectoryRef Id="dir3D22B249045A1473460D0D5D104478F2" />
629 </Fragment>
630 <Fragment>
631 <DirectoryRef Id="dir35F8598B197DFC5580317F5E7C2E9AE9" />
632 </Fragment>
633 <Fragment>
634 <DirectoryRef Id="dir10F9FCD5760425E499FA92F2CAA7A3EA" />
635 </Fragment>
636 <Fragment>
637 <DirectoryRef Id="dirCCAE13CC8442C655BA01764423E1EBB7" />
638 </Fragment>
639 <Fragment>
640 <DirectoryRef Id="dirC5A30981F5453B69928F61CEC4F6D629" />
641 </Fragment>
642 <Fragment>
643 <DirectoryRef Id="dirCCAE13CC8442C655BA01764423E1EBB7" />
644 </Fragment>
645 <Fragment>
646 <DirectoryRef Id="dirCCAE13CC8442C655BA01764423E1EBB7" />
647 </Fragment>
648 <Fragment>
649 <DirectoryRef Id="dirC5A30981F5453B69928F61CEC4F6D629" />
650 </Fragment>
651 <Fragment>
652 <DirectoryRef Id="dir2EDFFCA56732C858816EF19F127E8D0F" />
653 </Fragment>
654 <Fragment>
655 <DirectoryRef Id="dirB8FE74E96715E770736FEAFA391ABF15" />
656 </Fragment>
657 <Fragment>
658 <DirectoryRef Id="dir3D22B249045A1473460D0D5D104478F2" />
659 </Fragment>
660 <Fragment>
661 <DirectoryRef Id="dirCB380721FE38FED97C74A9E48945B5BC" />
662 </Fragment>
663 <Fragment>
664 <DirectoryRef Id="dirBFB9D3FD33CE2C696D390C94E4E1B4D8" />
665 </Fragment>
666 <Fragment>
667 <DirectoryRef Id="dirA4D7AC0F636AD49029D4D19861392983" />
668 </Fragment>
669 <Fragment>
670 <DirectoryRef Id="dirB8FE74E96715E770736FEAFA391ABF15" />
671 </Fragment>
672 <Fragment>
673 <DirectoryRef Id="dirCCAE13CC8442C655BA01764423E1EBB7" />
674 </Fragment>
675 <Fragment>
676 <DirectoryRef Id="dir35F8598B197DFC5580317F5E7C2E9AE9" />
677 </Fragment>
678 <Fragment>
679 <DirectoryRef Id="dirA4D7AC0F636AD49029D4D19861392983" />
680 </Fragment>
681 <Fragment>
682 <DirectoryRef Id="dir3D22B249045A1473460D0D5D104478F2" />
683 </Fragment>
684 <Fragment>
685 <DirectoryRef Id="dirC5A30981F5453B69928F61CEC4F6D629" />
686 </Fragment>
687 <Fragment>
688 <DirectoryRef Id="dirA4D7AC0F636AD49029D4D19861392983" />
689 </Fragment>
690 <Fragment>
691 <DirectoryRef Id="dirBFB9D3FD33CE2C696D390C94E4E1B4D8" />
692 </Fragment>
693 <Fragment>
694 <DirectoryRef Id="dirC5A30981F5453B69928F61CEC4F6D629" />
695 </Fragment>
696 <Fragment>
697 <DirectoryRef Id="dirA4D7AC0F636AD49029D4D19861392983" />
698 </Fragment>
699 <Fragment>
700 <DirectoryRef Id="dirA4D7AC0F636AD49029D4D19861392983" />
701 </Fragment>
702 <Fragment>
703 <DirectoryRef Id="dir10F9FCD5760425E499FA92F2CAA7A3EA" />
704 </Fragment>
705 <Fragment>
706 <DirectoryRef Id="dirCB380721FE38FED97C74A9E48945B5BC" />
707 </Fragment>
708 <Fragment>
709 <DirectoryRef Id="dir35F8598B197DFC5580317F5E7C2E9AE9" />
710 </Fragment>
711 <Fragment>
712 <DirectoryRef Id="dirB8FE74E96715E770736FEAFA391ABF15" />
713 </Fragment>
714 <Fragment>
715 <DirectoryRef Id="dir10F9FCD5760425E499FA92F2CAA7A3EA" />
716 </Fragment>
717 <Fragment>
718 <DirectoryRef Id="dirB8FE74E96715E770736FEAFA391ABF15" />
719 </Fragment>
720 <Fragment>
721 <DirectoryRef Id="dir3D22B249045A1473460D0D5D104478F2" />
722 </Fragment>
723 <Fragment>
724 <DirectoryRef Id="dirA4D7AC0F636AD49029D4D19861392983" />
725 </Fragment>
726 <Fragment>
727 <DirectoryRef Id="dirCB380721FE38FED97C74A9E48945B5BC" />
728 </Fragment>
729 <Fragment>
730 <DirectoryRef Id="dir3D22B249045A1473460D0D5D104478F2" />
731 </Fragment>
732 <Fragment>
733 <DirectoryRef Id="dirCCAE13CC8442C655BA01764423E1EBB7" />
734 </Fragment>
735 <Fragment>
736 <DirectoryRef Id="dirCB380721FE38FED97C74A9E48945B5BC" />
737 </Fragment>
738 <Fragment>
739 <DirectoryRef Id="dir3D22B249045A1473460D0D5D104478F2" />
740 </Fragment>
741 <Fragment>
742 <DirectoryRef Id="dirCCAE13CC8442C655BA01764423E1EBB7" />
743 </Fragment>
744 <Fragment>
745 <DirectoryRef Id="dir2EDFFCA56732C858816EF19F127E8D0F" />
746 </Fragment>
747 <Fragment>
748 <DirectoryRef Id="dirC5A30981F5453B69928F61CEC4F6D629" />
749 </Fragment>
750 <Fragment>
751 <DirectoryRef Id="dirA4D7AC0F636AD49029D4D19861392983" />
752 </Fragment>
753 <Fragment>
754 <DirectoryRef Id="dirB8FE74E96715E770736FEAFA391ABF15" />
755 </Fragment>
756 <Fragment>
757 <DirectoryRef Id="dirBFB9D3FD33CE2C696D390C94E4E1B4D8">
758 <Directory Id="dir10F9FCD5760425E499FA92F2CAA7A3EA" Name="raw" />
759 </DirectoryRef>
760 </Fragment>
761 <Fragment>
762 <DirectoryRef Id="dirBFB9D3FD33CE2C696D390C94E4E1B4D8">
763 <Directory Id="dir2EDFFCA56732C858816EF19F127E8D0F" Name="coal" />
764 </DirectoryRef>
765 </Fragment>
766 <Fragment>
767 <DirectoryRef Id="dirBFB9D3FD33CE2C696D390C94E4E1B4D8">
768 <Directory Id="dir35F8598B197DFC5580317F5E7C2E9AE9" Name="atom" />
769 </DirectoryRef>
770 </Fragment>
771 <Fragment>
772 <DirectoryRef Id="dirBFB9D3FD33CE2C696D390C94E4E1B4D8">
773 <Directory Id="dir3D22B249045A1473460D0D5D104478F2" Name="gitweb" />
774 </DirectoryRef>
775 </Fragment>
776 <Fragment>
777 <DirectoryRef Id="dirBFB9D3FD33CE2C696D390C94E4E1B4D8">
778 <Directory Id="dirA4D7AC0F636AD49029D4D19861392983" Name="monoblue" />
779 </DirectoryRef>
780 </Fragment>
781 <Fragment>
782 <DirectoryRef Id="dirBFB9D3FD33CE2C696D390C94E4E1B4D8">
783 <Directory Id="dirB8FE74E96715E770736FEAFA391ABF15" Name="spartan" />
784 </DirectoryRef>
785 </Fragment>
786 <Fragment>
787 <DirectoryRef Id="dirBFB9D3FD33CE2C696D390C94E4E1B4D8">
788 <Directory Id="dirC5A30981F5453B69928F61CEC4F6D629" Name="static" />
789 </DirectoryRef>
790 </Fragment>
791 <Fragment>
792 <DirectoryRef Id="dirBFB9D3FD33CE2C696D390C94E4E1B4D8">
793 <Directory Id="dirCB380721FE38FED97C74A9E48945B5BC" Name="rss" />
794 </DirectoryRef>
795 </Fragment>
796 <Fragment>
797 <DirectoryRef Id="dirBFB9D3FD33CE2C696D390C94E4E1B4D8">
798 <Directory Id="dirCCAE13CC8442C655BA01764423E1EBB7" Name="paper" />
799 </DirectoryRef>
800 </Fragment>
801 </Wix> No newline at end of file
@@ -1,219 +1,218 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2
2
3 """\
3 """\
4 reorder a revlog (the manifest by default) to save space
4 reorder a revlog (the manifest by default) to save space
5
5
6 Specifically, this topologically sorts the revisions in the revlog so that
6 Specifically, this topologically sorts the revisions in the revlog so that
7 revisions on the same branch are adjacent as much as possible. This is a
7 revisions on the same branch are adjacent as much as possible. This is a
8 workaround for the fact that Mercurial computes deltas relative to the
8 workaround for the fact that Mercurial computes deltas relative to the
9 previous revision rather than relative to a parent revision.
9 previous revision rather than relative to a parent revision.
10
10
11 This is *not* safe to run on a changelog.
11 This is *not* safe to run on a changelog.
12 """
12 """
13
13
14 # Originally written by Benoit Boissinot <benoit.boissinot at ens-lyon.org>
14 # Originally written by Benoit Boissinot <benoit.boissinot at ens-lyon.org>
15 # as a patch to rewrite-log. Cleaned up, refactored, documented, and
15 # as a patch to rewrite-log. Cleaned up, refactored, documented, and
16 # renamed by Greg Ward <greg at gerg.ca>.
16 # renamed by Greg Ward <greg at gerg.ca>.
17
17
18 # XXX would be nice to have a way to verify the repository after shrinking,
18 # XXX would be nice to have a way to verify the repository after shrinking,
19 # e.g. by comparing "before" and "after" states of random changesets
19 # e.g. by comparing "before" and "after" states of random changesets
20 # (maybe: export before, shrink, export after, diff).
20 # (maybe: export before, shrink, export after, diff).
21
21
22 import sys, os, tempfile
22 import os, tempfile
23 import optparse
23 from mercurial import revlog, transaction, node, util
24 from mercurial import ui as ui_, hg, revlog, transaction, node, util
25 from mercurial import changegroup
24 from mercurial import changegroup
25 from mercurial.i18n import _
26
26
27 def toposort(ui, rl):
27 def toposort(ui, rl):
28
28
29 children = {}
29 children = {}
30 root = []
30 root = []
31 # build children and roots
31 # build children and roots
32 ui.status('reading revs\n')
32 ui.status(_('reading revs\n'))
33 try:
33 try:
34 for i in rl:
34 for i in rl:
35 ui.progress(_('reading'), i, total=len(rl))
35 ui.progress(_('reading'), i, total=len(rl))
36 children[i] = []
36 children[i] = []
37 parents = [p for p in rl.parentrevs(i) if p != node.nullrev]
37 parents = [p for p in rl.parentrevs(i) if p != node.nullrev]
38 # in case of duplicate parents
38 # in case of duplicate parents
39 if len(parents) == 2 and parents[0] == parents[1]:
39 if len(parents) == 2 and parents[0] == parents[1]:
40 del parents[1]
40 del parents[1]
41 for p in parents:
41 for p in parents:
42 assert p in children
42 assert p in children
43 children[p].append(i)
43 children[p].append(i)
44
44
45 if len(parents) == 0:
45 if len(parents) == 0:
46 root.append(i)
46 root.append(i)
47 finally:
47 finally:
48 ui.progress(_('reading'), None, total=len(rl))
48 ui.progress(_('reading'), None, total=len(rl))
49
49
50 # XXX this is a reimplementation of the 'branchsort' topo sort
50 # XXX this is a reimplementation of the 'branchsort' topo sort
51 # algorithm in hgext.convert.convcmd... would be nice not to duplicate
51 # algorithm in hgext.convert.convcmd... would be nice not to duplicate
52 # the algorithm
52 # the algorithm
53 ui.status('sorting revs\n')
53 ui.status(_('sorting revs\n'))
54 visit = root
54 visit = root
55 ret = []
55 ret = []
56 while visit:
56 while visit:
57 i = visit.pop(0)
57 i = visit.pop(0)
58 ret.append(i)
58 ret.append(i)
59 if i not in children:
59 if i not in children:
60 # This only happens if some node's p1 == p2, which can
60 # This only happens if some node's p1 == p2, which can
61 # happen in the manifest in certain circumstances.
61 # happen in the manifest in certain circumstances.
62 continue
62 continue
63 next = []
63 next = []
64 for c in children.pop(i):
64 for c in children.pop(i):
65 parents_unseen = [p for p in rl.parentrevs(c)
65 parents_unseen = [p for p in rl.parentrevs(c)
66 if p != node.nullrev and p in children]
66 if p != node.nullrev and p in children]
67 if len(parents_unseen) == 0:
67 if len(parents_unseen) == 0:
68 next.append(c)
68 next.append(c)
69 visit = next + visit
69 visit = next + visit
70 return ret
70 return ret
71
71
72 def writerevs(ui, r1, r2, order, tr):
72 def writerevs(ui, r1, r2, order, tr):
73
73
74 ui.status('writing revs\n')
74 ui.status(_('writing revs\n'))
75
75
76 count = [0]
76 count = [0]
77 def progress(*args):
77 def progress(*args):
78 ui.progress(_('writing'), count[0], total=len(order))
78 ui.progress(_('writing'), count[0], total=len(order))
79 count[0] += 1
79 count[0] += 1
80
80
81 order = [r1.node(r) for r in order]
81 order = [r1.node(r) for r in order]
82
82
83 # this is a bit ugly, but it works
83 # this is a bit ugly, but it works
84 lookup = lambda x: "%020d" % r1.linkrev(r1.rev(x))
84 lookup = lambda x: "%020d" % r1.linkrev(r1.rev(x))
85 unlookup = lambda x: int(x, 10)
85 unlookup = lambda x: int(x, 10)
86
86
87 try:
87 try:
88 group = util.chunkbuffer(r1.group(order, lookup, progress))
88 group = util.chunkbuffer(r1.group(order, lookup, progress))
89 chunkiter = changegroup.chunkiter(group)
89 chunkiter = changegroup.chunkiter(group)
90 r2.addgroup(chunkiter, unlookup, tr)
90 r2.addgroup(chunkiter, unlookup, tr)
91 finally:
91 finally:
92 ui.progress(_('writing'), None, len(order))
92 ui.progress(_('writing'), None, len(order))
93
93
94 def report(ui, olddatafn, newdatafn):
94 def report(ui, olddatafn, newdatafn):
95 oldsize = float(os.stat(olddatafn).st_size)
95 oldsize = float(os.stat(olddatafn).st_size)
96 newsize = float(os.stat(newdatafn).st_size)
96 newsize = float(os.stat(newdatafn).st_size)
97
97
98 # argh: have to pass an int to %d, because a float >= 2^32
98 # argh: have to pass an int to %d, because a float >= 2^32
99 # blows up under Python 2.5 or earlier
99 # blows up under Python 2.5 or earlier
100 ui.write('old file size: %12d bytes (%6.1f MiB)\n'
100 ui.write(_('old file size: %12d bytes (%6.1f MiB)\n')
101 % (int(oldsize), oldsize / 1024 / 1024))
101 % (int(oldsize), oldsize / 1024 / 1024))
102 ui.write('new file size: %12d bytes (%6.1f MiB)\n'
102 ui.write(_('new file size: %12d bytes (%6.1f MiB)\n')
103 % (int(newsize), newsize / 1024 / 1024))
103 % (int(newsize), newsize / 1024 / 1024))
104
104
105 shrink_percent = (oldsize - newsize) / oldsize * 100
105 shrink_percent = (oldsize - newsize) / oldsize * 100
106 shrink_factor = oldsize / newsize
106 shrink_factor = oldsize / newsize
107 ui.write('shrinkage: %.1f%% (%.1fx)\n' % (shrink_percent, shrink_factor))
107 ui.write(_('shrinkage: %.1f%% (%.1fx)\n')
108 % (shrink_percent, shrink_factor))
108
109
109 def shrink(ui, repo, **opts):
110 def shrink(ui, repo, **opts):
110 """
111 """
111 Shrink revlog by re-ordering revisions. Will operate on manifest for
112 Shrink revlog by re-ordering revisions. Will operate on manifest for
112 the given repository if no other revlog is specified."""
113 the given repository if no other revlog is specified."""
113
114
114 # Unbuffer stdout for nice progress output.
115 sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
116
117 if not repo.local():
115 if not repo.local():
118 raise util.Abort('not a local repository: %s' % repo.root)
116 raise util.Abort(_('not a local repository: %s') % repo.root)
119
117
120 fn = opts.get('revlog')
118 fn = opts.get('revlog')
121 if not fn:
119 if not fn:
122 indexfn = repo.sjoin('00manifest.i')
120 indexfn = repo.sjoin('00manifest.i')
123 else:
121 else:
124 if not fn.endswith('.i'):
122 if not fn.endswith('.i'):
125 raise util.Abort('--revlog option must specify the revlog index '
123 raise util.Abort(_('--revlog option must specify the revlog index '
126 'file (*.i), not %s' % opts.get('revlog'))
124 'file (*.i), not %s') % opts.get('revlog'))
127
125
128 indexfn = os.path.realpath(fn)
126 indexfn = os.path.realpath(fn)
129 store = repo.sjoin('')
127 store = repo.sjoin('')
130 if not indexfn.startswith(store):
128 if not indexfn.startswith(store):
131 raise util.Abort('--revlog option must specify a revlog in %s, '
129 raise util.Abort(_('--revlog option must specify a revlog in %s, '
132 'not %s' % (store, indexfn))
130 'not %s') % (store, indexfn))
133
131
134 datafn = indexfn[:-2] + '.d'
132 datafn = indexfn[:-2] + '.d'
135 if not os.path.exists(indexfn):
133 if not os.path.exists(indexfn):
136 raise util.Abort('no such file: %s' % indexfn)
134 raise util.Abort(_('no such file: %s') % indexfn)
137 if '00changelog' in indexfn:
135 if '00changelog' in indexfn:
138 raise util.Abort('shrinking the changelog will corrupt your repository')
136 raise util.Abort(_('shrinking the changelog '
137 'will corrupt your repository'))
139 if not os.path.exists(datafn):
138 if not os.path.exists(datafn):
140 # This is just a lazy shortcut because I can't be bothered to
139 # This is just a lazy shortcut because I can't be bothered to
141 # handle all the special cases that entail from no .d file.
140 # handle all the special cases that entail from no .d file.
142 raise util.Abort('%s does not exist: revlog not big enough '
141 raise util.Abort(_('%s does not exist: revlog not big enough '
143 'to be worth shrinking' % datafn)
142 'to be worth shrinking') % datafn)
144
143
145 oldindexfn = indexfn + '.old'
144 oldindexfn = indexfn + '.old'
146 olddatafn = datafn + '.old'
145 olddatafn = datafn + '.old'
147 if os.path.exists(oldindexfn) or os.path.exists(olddatafn):
146 if os.path.exists(oldindexfn) or os.path.exists(olddatafn):
148 raise util.Abort('one or both of\n'
147 raise util.Abort(_('one or both of\n'
149 ' %s\n'
148 ' %s\n'
150 ' %s\n'
149 ' %s\n'
151 'exists from a previous run; please clean up before '
150 'exists from a previous run; please clean up '
152 'running again' % (oldindexfn, olddatafn))
151 'before running again') % (oldindexfn, olddatafn))
153
152
154 ui.write('shrinking %s\n' % indexfn)
153 ui.write(_('shrinking %s\n') % indexfn)
155 prefix = os.path.basename(indexfn)[:-1]
154 prefix = os.path.basename(indexfn)[:-1]
156 (tmpfd, tmpindexfn) = tempfile.mkstemp(dir=os.path.dirname(indexfn),
155 (tmpfd, tmpindexfn) = tempfile.mkstemp(dir=os.path.dirname(indexfn),
157 prefix=prefix,
156 prefix=prefix,
158 suffix='.i')
157 suffix='.i')
159 tmpdatafn = tmpindexfn[:-2] + '.d'
158 tmpdatafn = tmpindexfn[:-2] + '.d'
160 os.close(tmpfd)
159 os.close(tmpfd)
161
160
162 r1 = revlog.revlog(util.opener(os.getcwd(), audit=False), indexfn)
161 r1 = revlog.revlog(util.opener(os.getcwd(), audit=False), indexfn)
163 r2 = revlog.revlog(util.opener(os.getcwd(), audit=False), tmpindexfn)
162 r2 = revlog.revlog(util.opener(os.getcwd(), audit=False), tmpindexfn)
164
163
165 # Don't use repo.transaction(), because then things get hairy with
164 # Don't use repo.transaction(), because then things get hairy with
166 # paths: some need to be relative to .hg, and some need to be
165 # paths: some need to be relative to .hg, and some need to be
167 # absolute. Doing it this way keeps things simple: everything is an
166 # absolute. Doing it this way keeps things simple: everything is an
168 # absolute path.
167 # absolute path.
169 lock = repo.lock(wait=False)
168 lock = repo.lock(wait=False)
170 tr = transaction.transaction(ui.warn,
169 tr = transaction.transaction(ui.warn,
171 open,
170 open,
172 repo.sjoin('journal'))
171 repo.sjoin('journal'))
173
172
174 try:
173 try:
175 try:
174 try:
176 order = toposort(ui, r1)
175 order = toposort(ui, r1)
177 writerevs(ui, r1, r2, order, tr)
176 writerevs(ui, r1, r2, order, tr)
178 report(ui, datafn, tmpdatafn)
177 report(ui, datafn, tmpdatafn)
179 tr.close()
178 tr.close()
180 except:
179 except:
181 # Abort transaction first, so we truncate the files before
180 # Abort transaction first, so we truncate the files before
182 # deleting them.
181 # deleting them.
183 tr.abort()
182 tr.abort()
184 if os.path.exists(tmpindexfn):
183 if os.path.exists(tmpindexfn):
185 os.unlink(tmpindexfn)
184 os.unlink(tmpindexfn)
186 if os.path.exists(tmpdatafn):
185 if os.path.exists(tmpdatafn):
187 os.unlink(tmpdatafn)
186 os.unlink(tmpdatafn)
188 raise
187 raise
189 if not opts.get('dry_run'):
188 if not opts.get('dry_run'):
190 # Racy since both files cannot be renamed atomically
189 # Racy since both files cannot be renamed atomically
191 util.os_link(indexfn, oldindexfn)
190 util.os_link(indexfn, oldindexfn)
192 util.os_link(datafn, olddatafn)
191 util.os_link(datafn, olddatafn)
193 util.rename(tmpindexfn, indexfn)
192 util.rename(tmpindexfn, indexfn)
194 util.rename(tmpdatafn, datafn)
193 util.rename(tmpdatafn, datafn)
195 else:
194 else:
196 os.unlink(tmpindexfn)
195 os.unlink(tmpindexfn)
197 os.unlink(tmpdatafn)
196 os.unlink(tmpdatafn)
198 finally:
197 finally:
199 lock.release()
198 lock.release()
200
199
201 if not opts.get('dry_run'):
200 if not opts.get('dry_run'):
202 ui.write('note: old revlog saved in:\n'
201 ui.write(_('note: old revlog saved in:\n'
203 ' %s\n'
202 ' %s\n'
204 ' %s\n'
203 ' %s\n'
205 '(You can delete those files when you are satisfied that your\n'
204 '(You can delete those files when you are satisfied that your\n'
206 'repository is still sane. '
205 'repository is still sane. '
207 'Running \'hg verify\' is strongly recommended.)\n'
206 'Running \'hg verify\' is strongly recommended.)\n')
208 % (oldindexfn, olddatafn))
207 % (oldindexfn, olddatafn))
209
208
210 cmdtable = {
209 cmdtable = {
211 'shrink': (shrink,
210 'shrink': (shrink,
212 [('', 'revlog', '', 'index (.i) file of the revlog to shrink'),
211 [('', 'revlog', '', _('index (.i) file of the revlog to shrink')),
213 ('n', 'dry-run', None, 'do not shrink, simulate only'),
212 ('n', 'dry-run', None, _('do not shrink, simulate only')),
214 ],
213 ],
215 'hg shrink [--revlog PATH]')
214 _('hg shrink [--revlog PATH]'))
216 }
215 }
217
216
218 if __name__ == "__main__":
217 if __name__ == "__main__":
219 print "shrink-revlog.py is now an extension (see hg help extensions)"
218 print "shrink-revlog.py is now an extension (see hg help extensions)"
@@ -1,327 +1,327 b''
1 # Mercurial extension to provide the 'hg bookmark' command
1 # Mercurial extension to provide the 'hg bookmark' command
2 #
2 #
3 # Copyright 2008 David Soria Parra <dsp@php.net>
3 # Copyright 2008 David Soria Parra <dsp@php.net>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 '''track a line of development with movable markers
8 '''track a line of development with movable markers
9
9
10 Bookmarks are local movable markers to changesets. Every bookmark
10 Bookmarks are local movable markers to changesets. Every bookmark
11 points to a changeset identified by its hash. If you commit a
11 points to a changeset identified by its hash. If you commit a
12 changeset that is based on a changeset that has a bookmark on it, the
12 changeset that is based on a changeset that has a bookmark on it, the
13 bookmark shifts to the new changeset.
13 bookmark shifts to the new changeset.
14
14
15 It is possible to use bookmark names in every revision lookup (e.g. hg
15 It is possible to use bookmark names in every revision lookup (e.g. hg
16 merge, hg update).
16 merge, hg update).
17
17
18 By default, when several bookmarks point to the same changeset, they
18 By default, when several bookmarks point to the same changeset, they
19 will all move forward together. It is possible to obtain a more
19 will all move forward together. It is possible to obtain a more
20 git-like experience by adding the following configuration option to
20 git-like experience by adding the following configuration option to
21 your .hgrc::
21 your .hgrc::
22
22
23 [bookmarks]
23 [bookmarks]
24 track.current = True
24 track.current = True
25
25
26 This will cause Mercurial to track the bookmark that you are currently
26 This will cause Mercurial to track the bookmark that you are currently
27 using, and only update it. This is similar to git's approach to
27 using, and only update it. This is similar to git's approach to
28 branching.
28 branching.
29 '''
29 '''
30
30
31 from mercurial.i18n import _
31 from mercurial.i18n import _
32 from mercurial.node import nullid, nullrev, hex, short
32 from mercurial.node import nullid, nullrev, hex, short
33 from mercurial import util, commands, repair, extensions
33 from mercurial import util, commands, repair, extensions
34 import os
34 import os
35
35
36 def write(repo):
36 def write(repo):
37 '''Write bookmarks
37 '''Write bookmarks
38
38
39 Write the given bookmark => hash dictionary to the .hg/bookmarks file
39 Write the given bookmark => hash dictionary to the .hg/bookmarks file
40 in a format equal to those of localtags.
40 in a format equal to those of localtags.
41
41
42 We also store a backup of the previous state in undo.bookmarks that
42 We also store a backup of the previous state in undo.bookmarks that
43 can be copied back on rollback.
43 can be copied back on rollback.
44 '''
44 '''
45 refs = repo._bookmarks
45 refs = repo._bookmarks
46 if os.path.exists(repo.join('bookmarks')):
46 if os.path.exists(repo.join('bookmarks')):
47 util.copyfile(repo.join('bookmarks'), repo.join('undo.bookmarks'))
47 util.copyfile(repo.join('bookmarks'), repo.join('undo.bookmarks'))
48 if repo._bookmarkcurrent not in refs:
48 if repo._bookmarkcurrent not in refs:
49 setcurrent(repo, None)
49 setcurrent(repo, None)
50 wlock = repo.wlock()
50 wlock = repo.wlock()
51 try:
51 try:
52 file = repo.opener('bookmarks', 'w', atomictemp=True)
52 file = repo.opener('bookmarks', 'w', atomictemp=True)
53 for refspec, node in refs.iteritems():
53 for refspec, node in refs.iteritems():
54 file.write("%s %s\n" % (hex(node), refspec))
54 file.write("%s %s\n" % (hex(node), refspec))
55 file.rename()
55 file.rename()
56 finally:
56 finally:
57 wlock.release()
57 wlock.release()
58
58
59 def setcurrent(repo, mark):
59 def setcurrent(repo, mark):
60 '''Set the name of the bookmark that we are currently on
60 '''Set the name of the bookmark that we are currently on
61
61
62 Set the name of the bookmark that we are on (hg update <bookmark>).
62 Set the name of the bookmark that we are on (hg update <bookmark>).
63 The name is recorded in .hg/bookmarks.current
63 The name is recorded in .hg/bookmarks.current
64 '''
64 '''
65 current = repo._bookmarkcurrent
65 current = repo._bookmarkcurrent
66 if current == mark:
66 if current == mark:
67 return
67 return
68
68
69 refs = repo._bookmarks
69 refs = repo._bookmarks
70
70
71 # do not update if we do update to a rev equal to the current bookmark
71 # do not update if we do update to a rev equal to the current bookmark
72 if (mark and mark not in refs and
72 if (mark and mark not in refs and
73 current and refs[current] == repo.changectx('.').node()):
73 current and refs[current] == repo.changectx('.').node()):
74 return
74 return
75 if mark not in refs:
75 if mark not in refs:
76 mark = ''
76 mark = ''
77 wlock = repo.wlock()
77 wlock = repo.wlock()
78 try:
78 try:
79 file = repo.opener('bookmarks.current', 'w', atomictemp=True)
79 file = repo.opener('bookmarks.current', 'w', atomictemp=True)
80 file.write(mark)
80 file.write(mark)
81 file.rename()
81 file.rename()
82 finally:
82 finally:
83 wlock.release()
83 wlock.release()
84 repo._bookmarkcurrent = mark
84 repo._bookmarkcurrent = mark
85
85
86 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False, rename=None):
86 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False, rename=None):
87 '''track a line of development with movable markers
87 '''track a line of development with movable markers
88
88
89 Bookmarks are pointers to certain commits that move when
89 Bookmarks are pointers to certain commits that move when
90 committing. Bookmarks are local. They can be renamed, copied and
90 committing. Bookmarks are local. They can be renamed, copied and
91 deleted. It is possible to use bookmark names in 'hg merge' and
91 deleted. It is possible to use bookmark names in 'hg merge' and
92 'hg update' to merge and update respectively to a given bookmark.
92 'hg update' to merge and update respectively to a given bookmark.
93
93
94 You can use 'hg bookmark NAME' to set a bookmark on the working
94 You can use 'hg bookmark NAME' to set a bookmark on the working
95 directory's parent revision with the given name. If you specify
95 directory's parent revision with the given name. If you specify
96 a revision using -r REV (where REV may be an existing bookmark),
96 a revision using -r REV (where REV may be an existing bookmark),
97 the bookmark is assigned to that revision.
97 the bookmark is assigned to that revision.
98 '''
98 '''
99 hexfn = ui.debugflag and hex or short
99 hexfn = ui.debugflag and hex or short
100 marks = repo._bookmarks
100 marks = repo._bookmarks
101 cur = repo.changectx('.').node()
101 cur = repo.changectx('.').node()
102
102
103 if rename:
103 if rename:
104 if rename not in marks:
104 if rename not in marks:
105 raise util.Abort(_("a bookmark of this name does not exist"))
105 raise util.Abort(_("a bookmark of this name does not exist"))
106 if mark in marks and not force:
106 if mark in marks and not force:
107 raise util.Abort(_("a bookmark of the same name already exists"))
107 raise util.Abort(_("a bookmark of the same name already exists"))
108 if mark is None:
108 if mark is None:
109 raise util.Abort(_("new bookmark name required"))
109 raise util.Abort(_("new bookmark name required"))
110 marks[mark] = marks[rename]
110 marks[mark] = marks[rename]
111 del marks[rename]
111 del marks[rename]
112 if repo._bookmarkcurrent == rename:
112 if repo._bookmarkcurrent == rename:
113 setcurrent(repo, mark)
113 setcurrent(repo, mark)
114 write(repo)
114 write(repo)
115 return
115 return
116
116
117 if delete:
117 if delete:
118 if mark is None:
118 if mark is None:
119 raise util.Abort(_("bookmark name required"))
119 raise util.Abort(_("bookmark name required"))
120 if mark not in marks:
120 if mark not in marks:
121 raise util.Abort(_("a bookmark of this name does not exist"))
121 raise util.Abort(_("a bookmark of this name does not exist"))
122 if mark == repo._bookmarkcurrent:
122 if mark == repo._bookmarkcurrent:
123 setcurrent(repo, None)
123 setcurrent(repo, None)
124 del marks[mark]
124 del marks[mark]
125 write(repo)
125 write(repo)
126 return
126 return
127
127
128 if mark != None:
128 if mark != None:
129 if "\n" in mark:
129 if "\n" in mark:
130 raise util.Abort(_("bookmark name cannot contain newlines"))
130 raise util.Abort(_("bookmark name cannot contain newlines"))
131 mark = mark.strip()
131 mark = mark.strip()
132 if mark in marks and not force:
132 if mark in marks and not force:
133 raise util.Abort(_("a bookmark of the same name already exists"))
133 raise util.Abort(_("a bookmark of the same name already exists"))
134 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
134 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
135 and not force):
135 and not force):
136 raise util.Abort(
136 raise util.Abort(
137 _("a bookmark cannot have the name of an existing branch"))
137 _("a bookmark cannot have the name of an existing branch"))
138 if rev:
138 if rev:
139 marks[mark] = repo.lookup(rev)
139 marks[mark] = repo.lookup(rev)
140 else:
140 else:
141 marks[mark] = repo.changectx('.').node()
141 marks[mark] = repo.changectx('.').node()
142 setcurrent(repo, mark)
142 setcurrent(repo, mark)
143 write(repo)
143 write(repo)
144 return
144 return
145
145
146 if mark is None:
146 if mark is None:
147 if rev:
147 if rev:
148 raise util.Abort(_("bookmark name required"))
148 raise util.Abort(_("bookmark name required"))
149 if len(marks) == 0:
149 if len(marks) == 0:
150 ui.status("no bookmarks set\n")
150 ui.status(_("no bookmarks set\n"))
151 else:
151 else:
152 for bmark, n in marks.iteritems():
152 for bmark, n in marks.iteritems():
153 if ui.configbool('bookmarks', 'track.current'):
153 if ui.configbool('bookmarks', 'track.current'):
154 current = repo._bookmarkcurrent
154 current = repo._bookmarkcurrent
155 prefix = (bmark == current and n == cur) and '*' or ' '
155 prefix = (bmark == current and n == cur) and '*' or ' '
156 else:
156 else:
157 prefix = (n == cur) and '*' or ' '
157 prefix = (n == cur) and '*' or ' '
158
158
159 if ui.quiet:
159 if ui.quiet:
160 ui.write("%s\n" % bmark)
160 ui.write("%s\n" % bmark)
161 else:
161 else:
162 ui.write(" %s %-25s %d:%s\n" % (
162 ui.write(" %s %-25s %d:%s\n" % (
163 prefix, bmark, repo.changelog.rev(n), hexfn(n)))
163 prefix, bmark, repo.changelog.rev(n), hexfn(n)))
164 return
164 return
165
165
166 def _revstostrip(changelog, node):
166 def _revstostrip(changelog, node):
167 srev = changelog.rev(node)
167 srev = changelog.rev(node)
168 tostrip = [srev]
168 tostrip = [srev]
169 saveheads = []
169 saveheads = []
170 for r in xrange(srev, len(changelog)):
170 for r in xrange(srev, len(changelog)):
171 parents = changelog.parentrevs(r)
171 parents = changelog.parentrevs(r)
172 if parents[0] in tostrip or parents[1] in tostrip:
172 if parents[0] in tostrip or parents[1] in tostrip:
173 tostrip.append(r)
173 tostrip.append(r)
174 if parents[1] != nullrev:
174 if parents[1] != nullrev:
175 for p in parents:
175 for p in parents:
176 if p not in tostrip and p > srev:
176 if p not in tostrip and p > srev:
177 saveheads.append(p)
177 saveheads.append(p)
178 return [r for r in tostrip if r not in saveheads]
178 return [r for r in tostrip if r not in saveheads]
179
179
180 def strip(oldstrip, ui, repo, node, backup="all"):
180 def strip(oldstrip, ui, repo, node, backup="all"):
181 """Strip bookmarks if revisions are stripped using
181 """Strip bookmarks if revisions are stripped using
182 the mercurial.strip method. This usually happens during
182 the mercurial.strip method. This usually happens during
183 qpush and qpop"""
183 qpush and qpop"""
184 revisions = _revstostrip(repo.changelog, node)
184 revisions = _revstostrip(repo.changelog, node)
185 marks = repo._bookmarks
185 marks = repo._bookmarks
186 update = []
186 update = []
187 for mark, n in marks.iteritems():
187 for mark, n in marks.iteritems():
188 if repo.changelog.rev(n) in revisions:
188 if repo.changelog.rev(n) in revisions:
189 update.append(mark)
189 update.append(mark)
190 oldstrip(ui, repo, node, backup)
190 oldstrip(ui, repo, node, backup)
191 if len(update) > 0:
191 if len(update) > 0:
192 for m in update:
192 for m in update:
193 marks[m] = repo.changectx('.').node()
193 marks[m] = repo.changectx('.').node()
194 write(repo)
194 write(repo)
195
195
196 def reposetup(ui, repo):
196 def reposetup(ui, repo):
197 if not repo.local():
197 if not repo.local():
198 return
198 return
199
199
200 class bookmark_repo(repo.__class__):
200 class bookmark_repo(repo.__class__):
201
201
202 @util.propertycache
202 @util.propertycache
203 def _bookmarks(self):
203 def _bookmarks(self):
204 '''Parse .hg/bookmarks file and return a dictionary
204 '''Parse .hg/bookmarks file and return a dictionary
205
205
206 Bookmarks are stored as {HASH}\\s{NAME}\\n (localtags format) values
206 Bookmarks are stored as {HASH}\\s{NAME}\\n (localtags format) values
207 in the .hg/bookmarks file. They are read returned as a dictionary
207 in the .hg/bookmarks file. They are read returned as a dictionary
208 with name => hash values.
208 with name => hash values.
209 '''
209 '''
210 try:
210 try:
211 bookmarks = {}
211 bookmarks = {}
212 for line in self.opener('bookmarks'):
212 for line in self.opener('bookmarks'):
213 sha, refspec = line.strip().split(' ', 1)
213 sha, refspec = line.strip().split(' ', 1)
214 bookmarks[refspec] = super(bookmark_repo, self).lookup(sha)
214 bookmarks[refspec] = super(bookmark_repo, self).lookup(sha)
215 except:
215 except:
216 pass
216 pass
217 return bookmarks
217 return bookmarks
218
218
219 @util.propertycache
219 @util.propertycache
220 def _bookmarkcurrent(self):
220 def _bookmarkcurrent(self):
221 '''Get the current bookmark
221 '''Get the current bookmark
222
222
223 If we use gittishsh branches we have a current bookmark that
223 If we use gittishsh branches we have a current bookmark that
224 we are on. This function returns the name of the bookmark. It
224 we are on. This function returns the name of the bookmark. It
225 is stored in .hg/bookmarks.current
225 is stored in .hg/bookmarks.current
226 '''
226 '''
227 mark = None
227 mark = None
228 if os.path.exists(self.join('bookmarks.current')):
228 if os.path.exists(self.join('bookmarks.current')):
229 file = self.opener('bookmarks.current')
229 file = self.opener('bookmarks.current')
230 # No readline() in posixfile_nt, reading everything is cheap
230 # No readline() in posixfile_nt, reading everything is cheap
231 mark = (file.readlines() or [''])[0]
231 mark = (file.readlines() or [''])[0]
232 if mark == '':
232 if mark == '':
233 mark = None
233 mark = None
234 file.close()
234 file.close()
235 return mark
235 return mark
236
236
237 def rollback(self):
237 def rollback(self):
238 if os.path.exists(self.join('undo.bookmarks')):
238 if os.path.exists(self.join('undo.bookmarks')):
239 util.rename(self.join('undo.bookmarks'), self.join('bookmarks'))
239 util.rename(self.join('undo.bookmarks'), self.join('bookmarks'))
240 return super(bookmark_repo, self).rollback()
240 return super(bookmark_repo, self).rollback()
241
241
242 def lookup(self, key):
242 def lookup(self, key):
243 if key in self._bookmarks:
243 if key in self._bookmarks:
244 key = self._bookmarks[key]
244 key = self._bookmarks[key]
245 return super(bookmark_repo, self).lookup(key)
245 return super(bookmark_repo, self).lookup(key)
246
246
247 def _bookmarksupdate(self, parents, node):
247 def _bookmarksupdate(self, parents, node):
248 marks = self._bookmarks
248 marks = self._bookmarks
249 update = False
249 update = False
250 if ui.configbool('bookmarks', 'track.current'):
250 if ui.configbool('bookmarks', 'track.current'):
251 mark = self._bookmarkcurrent
251 mark = self._bookmarkcurrent
252 if mark and marks[mark] in parents:
252 if mark and marks[mark] in parents:
253 marks[mark] = node
253 marks[mark] = node
254 update = True
254 update = True
255 else:
255 else:
256 for mark, n in marks.items():
256 for mark, n in marks.items():
257 if n in parents:
257 if n in parents:
258 marks[mark] = node
258 marks[mark] = node
259 update = True
259 update = True
260 if update:
260 if update:
261 write(self)
261 write(self)
262
262
263 def commitctx(self, ctx, error=False):
263 def commitctx(self, ctx, error=False):
264 """Add a revision to the repository and
264 """Add a revision to the repository and
265 move the bookmark"""
265 move the bookmark"""
266 wlock = self.wlock() # do both commit and bookmark with lock held
266 wlock = self.wlock() # do both commit and bookmark with lock held
267 try:
267 try:
268 node = super(bookmark_repo, self).commitctx(ctx, error)
268 node = super(bookmark_repo, self).commitctx(ctx, error)
269 if node is None:
269 if node is None:
270 return None
270 return None
271 parents = self.changelog.parents(node)
271 parents = self.changelog.parents(node)
272 if parents[1] == nullid:
272 if parents[1] == nullid:
273 parents = (parents[0],)
273 parents = (parents[0],)
274
274
275 self._bookmarksupdate(parents, node)
275 self._bookmarksupdate(parents, node)
276 return node
276 return node
277 finally:
277 finally:
278 wlock.release()
278 wlock.release()
279
279
280 def addchangegroup(self, source, srctype, url, emptyok=False):
280 def addchangegroup(self, source, srctype, url, emptyok=False):
281 parents = self.dirstate.parents()
281 parents = self.dirstate.parents()
282
282
283 result = super(bookmark_repo, self).addchangegroup(
283 result = super(bookmark_repo, self).addchangegroup(
284 source, srctype, url, emptyok)
284 source, srctype, url, emptyok)
285 if result > 1:
285 if result > 1:
286 # We have more heads than before
286 # We have more heads than before
287 return result
287 return result
288 node = self.changelog.tip()
288 node = self.changelog.tip()
289
289
290 self._bookmarksupdate(parents, node)
290 self._bookmarksupdate(parents, node)
291 return result
291 return result
292
292
293 def _findtags(self):
293 def _findtags(self):
294 """Merge bookmarks with normal tags"""
294 """Merge bookmarks with normal tags"""
295 (tags, tagtypes) = super(bookmark_repo, self)._findtags()
295 (tags, tagtypes) = super(bookmark_repo, self)._findtags()
296 tags.update(self._bookmarks)
296 tags.update(self._bookmarks)
297 return (tags, tagtypes)
297 return (tags, tagtypes)
298
298
299 repo.__class__ = bookmark_repo
299 repo.__class__ = bookmark_repo
300
300
301 def uisetup(ui):
301 def uisetup(ui):
302 extensions.wrapfunction(repair, "strip", strip)
302 extensions.wrapfunction(repair, "strip", strip)
303 if ui.configbool('bookmarks', 'track.current'):
303 if ui.configbool('bookmarks', 'track.current'):
304 extensions.wrapcommand(commands.table, 'update', updatecurbookmark)
304 extensions.wrapcommand(commands.table, 'update', updatecurbookmark)
305
305
306 def updatecurbookmark(orig, ui, repo, *args, **opts):
306 def updatecurbookmark(orig, ui, repo, *args, **opts):
307 '''Set the current bookmark
307 '''Set the current bookmark
308
308
309 If the user updates to a bookmark we update the .hg/bookmarks.current
309 If the user updates to a bookmark we update the .hg/bookmarks.current
310 file.
310 file.
311 '''
311 '''
312 res = orig(ui, repo, *args, **opts)
312 res = orig(ui, repo, *args, **opts)
313 rev = opts['rev']
313 rev = opts['rev']
314 if not rev and len(args) > 0:
314 if not rev and len(args) > 0:
315 rev = args[0]
315 rev = args[0]
316 setcurrent(repo, rev)
316 setcurrent(repo, rev)
317 return res
317 return res
318
318
319 cmdtable = {
319 cmdtable = {
320 "bookmarks":
320 "bookmarks":
321 (bookmark,
321 (bookmark,
322 [('f', 'force', False, _('force')),
322 [('f', 'force', False, _('force')),
323 ('r', 'rev', '', _('revision')),
323 ('r', 'rev', '', _('revision')),
324 ('d', 'delete', False, _('delete a given bookmark')),
324 ('d', 'delete', False, _('delete a given bookmark')),
325 ('m', 'rename', '', _('rename a given bookmark'))],
325 ('m', 'rename', '', _('rename a given bookmark'))],
326 _('hg bookmarks [-f] [-d] [-m NAME] [-r REV] [NAME]')),
326 _('hg bookmarks [-f] [-d] [-m NAME] [-r REV] [NAME]')),
327 }
327 }
@@ -1,286 +1,286 b''
1 # Copyright 2005, 2006 Benoit Boissinot <benoit.boissinot@ens-lyon.org>
1 # Copyright 2005, 2006 Benoit Boissinot <benoit.boissinot@ens-lyon.org>
2 #
2 #
3 # This software may be used and distributed according to the terms of the
3 # This software may be used and distributed according to the terms of the
4 # GNU General Public License version 2 or any later version.
4 # GNU General Public License version 2 or any later version.
5
5
6 '''commands to sign and verify changesets'''
6 '''commands to sign and verify changesets'''
7
7
8 import os, tempfile, binascii
8 import os, tempfile, binascii
9 from mercurial import util, commands, match
9 from mercurial import util, commands, match
10 from mercurial import node as hgnode
10 from mercurial import node as hgnode
11 from mercurial.i18n import _
11 from mercurial.i18n import _
12
12
13 class gpg(object):
13 class gpg(object):
14 def __init__(self, path, key=None):
14 def __init__(self, path, key=None):
15 self.path = path
15 self.path = path
16 self.key = (key and " --local-user \"%s\"" % key) or ""
16 self.key = (key and " --local-user \"%s\"" % key) or ""
17
17
18 def sign(self, data):
18 def sign(self, data):
19 gpgcmd = "%s --sign --detach-sign%s" % (self.path, self.key)
19 gpgcmd = "%s --sign --detach-sign%s" % (self.path, self.key)
20 return util.filter(data, gpgcmd)
20 return util.filter(data, gpgcmd)
21
21
22 def verify(self, data, sig):
22 def verify(self, data, sig):
23 """ returns of the good and bad signatures"""
23 """ returns of the good and bad signatures"""
24 sigfile = datafile = None
24 sigfile = datafile = None
25 try:
25 try:
26 # create temporary files
26 # create temporary files
27 fd, sigfile = tempfile.mkstemp(prefix="hg-gpg-", suffix=".sig")
27 fd, sigfile = tempfile.mkstemp(prefix="hg-gpg-", suffix=".sig")
28 fp = os.fdopen(fd, 'wb')
28 fp = os.fdopen(fd, 'wb')
29 fp.write(sig)
29 fp.write(sig)
30 fp.close()
30 fp.close()
31 fd, datafile = tempfile.mkstemp(prefix="hg-gpg-", suffix=".txt")
31 fd, datafile = tempfile.mkstemp(prefix="hg-gpg-", suffix=".txt")
32 fp = os.fdopen(fd, 'wb')
32 fp = os.fdopen(fd, 'wb')
33 fp.write(data)
33 fp.write(data)
34 fp.close()
34 fp.close()
35 gpgcmd = ("%s --logger-fd 1 --status-fd 1 --verify "
35 gpgcmd = ("%s --logger-fd 1 --status-fd 1 --verify "
36 "\"%s\" \"%s\"" % (self.path, sigfile, datafile))
36 "\"%s\" \"%s\"" % (self.path, sigfile, datafile))
37 ret = util.filter("", gpgcmd)
37 ret = util.filter("", gpgcmd)
38 finally:
38 finally:
39 for f in (sigfile, datafile):
39 for f in (sigfile, datafile):
40 try:
40 try:
41 if f:
41 if f:
42 os.unlink(f)
42 os.unlink(f)
43 except:
43 except:
44 pass
44 pass
45 keys = []
45 keys = []
46 key, fingerprint = None, None
46 key, fingerprint = None, None
47 err = ""
47 err = ""
48 for l in ret.splitlines():
48 for l in ret.splitlines():
49 # see DETAILS in the gnupg documentation
49 # see DETAILS in the gnupg documentation
50 # filter the logger output
50 # filter the logger output
51 if not l.startswith("[GNUPG:]"):
51 if not l.startswith("[GNUPG:]"):
52 continue
52 continue
53 l = l[9:]
53 l = l[9:]
54 if l.startswith("ERRSIG"):
54 if l.startswith("ERRSIG"):
55 err = _("error while verifying signature")
55 err = _("error while verifying signature")
56 break
56 break
57 elif l.startswith("VALIDSIG"):
57 elif l.startswith("VALIDSIG"):
58 # fingerprint of the primary key
58 # fingerprint of the primary key
59 fingerprint = l.split()[10]
59 fingerprint = l.split()[10]
60 elif (l.startswith("GOODSIG") or
60 elif (l.startswith("GOODSIG") or
61 l.startswith("EXPSIG") or
61 l.startswith("EXPSIG") or
62 l.startswith("EXPKEYSIG") or
62 l.startswith("EXPKEYSIG") or
63 l.startswith("BADSIG")):
63 l.startswith("BADSIG")):
64 if key is not None:
64 if key is not None:
65 keys.append(key + [fingerprint])
65 keys.append(key + [fingerprint])
66 key = l.split(" ", 2)
66 key = l.split(" ", 2)
67 fingerprint = None
67 fingerprint = None
68 if err:
68 if err:
69 return err, []
69 return err, []
70 if key is not None:
70 if key is not None:
71 keys.append(key + [fingerprint])
71 keys.append(key + [fingerprint])
72 return err, keys
72 return err, keys
73
73
74 def newgpg(ui, **opts):
74 def newgpg(ui, **opts):
75 """create a new gpg instance"""
75 """create a new gpg instance"""
76 gpgpath = ui.config("gpg", "cmd", "gpg")
76 gpgpath = ui.config("gpg", "cmd", "gpg")
77 gpgkey = opts.get('key')
77 gpgkey = opts.get('key')
78 if not gpgkey:
78 if not gpgkey:
79 gpgkey = ui.config("gpg", "key", None)
79 gpgkey = ui.config("gpg", "key", None)
80 return gpg(gpgpath, gpgkey)
80 return gpg(gpgpath, gpgkey)
81
81
82 def sigwalk(repo):
82 def sigwalk(repo):
83 """
83 """
84 walk over every sigs, yields a couple
84 walk over every sigs, yields a couple
85 ((node, version, sig), (filename, linenumber))
85 ((node, version, sig), (filename, linenumber))
86 """
86 """
87 def parsefile(fileiter, context):
87 def parsefile(fileiter, context):
88 ln = 1
88 ln = 1
89 for l in fileiter:
89 for l in fileiter:
90 if not l:
90 if not l:
91 continue
91 continue
92 yield (l.split(" ", 2), (context, ln))
92 yield (l.split(" ", 2), (context, ln))
93 ln += 1
93 ln += 1
94
94
95 # read the heads
95 # read the heads
96 fl = repo.file(".hgsigs")
96 fl = repo.file(".hgsigs")
97 for r in reversed(fl.heads()):
97 for r in reversed(fl.heads()):
98 fn = ".hgsigs|%s" % hgnode.short(r)
98 fn = ".hgsigs|%s" % hgnode.short(r)
99 for item in parsefile(fl.read(r).splitlines(), fn):
99 for item in parsefile(fl.read(r).splitlines(), fn):
100 yield item
100 yield item
101 try:
101 try:
102 # read local signatures
102 # read local signatures
103 fn = "localsigs"
103 fn = "localsigs"
104 for item in parsefile(repo.opener(fn), fn):
104 for item in parsefile(repo.opener(fn), fn):
105 yield item
105 yield item
106 except IOError:
106 except IOError:
107 pass
107 pass
108
108
109 def getkeys(ui, repo, mygpg, sigdata, context):
109 def getkeys(ui, repo, mygpg, sigdata, context):
110 """get the keys who signed a data"""
110 """get the keys who signed a data"""
111 fn, ln = context
111 fn, ln = context
112 node, version, sig = sigdata
112 node, version, sig = sigdata
113 prefix = "%s:%d" % (fn, ln)
113 prefix = "%s:%d" % (fn, ln)
114 node = hgnode.bin(node)
114 node = hgnode.bin(node)
115
115
116 data = node2txt(repo, node, version)
116 data = node2txt(repo, node, version)
117 sig = binascii.a2b_base64(sig)
117 sig = binascii.a2b_base64(sig)
118 err, keys = mygpg.verify(data, sig)
118 err, keys = mygpg.verify(data, sig)
119 if err:
119 if err:
120 ui.warn("%s:%d %s\n" % (fn, ln , err))
120 ui.warn("%s:%d %s\n" % (fn, ln , err))
121 return None
121 return None
122
122
123 validkeys = []
123 validkeys = []
124 # warn for expired key and/or sigs
124 # warn for expired key and/or sigs
125 for key in keys:
125 for key in keys:
126 if key[0] == "BADSIG":
126 if key[0] == "BADSIG":
127 ui.write(_("%s Bad signature from \"%s\"\n") % (prefix, key[2]))
127 ui.write(_("%s Bad signature from \"%s\"\n") % (prefix, key[2]))
128 continue
128 continue
129 if key[0] == "EXPSIG":
129 if key[0] == "EXPSIG":
130 ui.write(_("%s Note: Signature has expired"
130 ui.write(_("%s Note: Signature has expired"
131 " (signed by: \"%s\")\n") % (prefix, key[2]))
131 " (signed by: \"%s\")\n") % (prefix, key[2]))
132 elif key[0] == "EXPKEYSIG":
132 elif key[0] == "EXPKEYSIG":
133 ui.write(_("%s Note: This key has expired"
133 ui.write(_("%s Note: This key has expired"
134 " (signed by: \"%s\")\n") % (prefix, key[2]))
134 " (signed by: \"%s\")\n") % (prefix, key[2]))
135 validkeys.append((key[1], key[2], key[3]))
135 validkeys.append((key[1], key[2], key[3]))
136 return validkeys
136 return validkeys
137
137
138 def sigs(ui, repo):
138 def sigs(ui, repo):
139 """list signed changesets"""
139 """list signed changesets"""
140 mygpg = newgpg(ui)
140 mygpg = newgpg(ui)
141 revs = {}
141 revs = {}
142
142
143 for data, context in sigwalk(repo):
143 for data, context in sigwalk(repo):
144 node, version, sig = data
144 node, version, sig = data
145 fn, ln = context
145 fn, ln = context
146 try:
146 try:
147 n = repo.lookup(node)
147 n = repo.lookup(node)
148 except KeyError:
148 except KeyError:
149 ui.warn(_("%s:%d node does not exist\n") % (fn, ln))
149 ui.warn(_("%s:%d node does not exist\n") % (fn, ln))
150 continue
150 continue
151 r = repo.changelog.rev(n)
151 r = repo.changelog.rev(n)
152 keys = getkeys(ui, repo, mygpg, data, context)
152 keys = getkeys(ui, repo, mygpg, data, context)
153 if not keys:
153 if not keys:
154 continue
154 continue
155 revs.setdefault(r, [])
155 revs.setdefault(r, [])
156 revs[r].extend(keys)
156 revs[r].extend(keys)
157 for rev in sorted(revs, reverse=True):
157 for rev in sorted(revs, reverse=True):
158 for k in revs[rev]:
158 for k in revs[rev]:
159 r = "%5d:%s" % (rev, hgnode.hex(repo.changelog.node(rev)))
159 r = "%5d:%s" % (rev, hgnode.hex(repo.changelog.node(rev)))
160 ui.write("%-30s %s\n" % (keystr(ui, k), r))
160 ui.write("%-30s %s\n" % (keystr(ui, k), r))
161
161
162 def check(ui, repo, rev):
162 def check(ui, repo, rev):
163 """verify all the signatures there may be for a particular revision"""
163 """verify all the signatures there may be for a particular revision"""
164 mygpg = newgpg(ui)
164 mygpg = newgpg(ui)
165 rev = repo.lookup(rev)
165 rev = repo.lookup(rev)
166 hexrev = hgnode.hex(rev)
166 hexrev = hgnode.hex(rev)
167 keys = []
167 keys = []
168
168
169 for data, context in sigwalk(repo):
169 for data, context in sigwalk(repo):
170 node, version, sig = data
170 node, version, sig = data
171 if node == hexrev:
171 if node == hexrev:
172 k = getkeys(ui, repo, mygpg, data, context)
172 k = getkeys(ui, repo, mygpg, data, context)
173 if k:
173 if k:
174 keys.extend(k)
174 keys.extend(k)
175
175
176 if not keys:
176 if not keys:
177 ui.write(_("No valid signature for %s\n") % hgnode.short(rev))
177 ui.write(_("No valid signature for %s\n") % hgnode.short(rev))
178 return
178 return
179
179
180 # print summary
180 # print summary
181 ui.write("%s is signed by:\n" % hgnode.short(rev))
181 ui.write("%s is signed by:\n" % hgnode.short(rev))
182 for key in keys:
182 for key in keys:
183 ui.write(" %s\n" % keystr(ui, key))
183 ui.write(" %s\n" % keystr(ui, key))
184
184
185 def keystr(ui, key):
185 def keystr(ui, key):
186 """associate a string to a key (username, comment)"""
186 """associate a string to a key (username, comment)"""
187 keyid, user, fingerprint = key
187 keyid, user, fingerprint = key
188 comment = ui.config("gpg", fingerprint, None)
188 comment = ui.config("gpg", fingerprint, None)
189 if comment:
189 if comment:
190 return "%s (%s)" % (user, comment)
190 return "%s (%s)" % (user, comment)
191 else:
191 else:
192 return user
192 return user
193
193
194 def sign(ui, repo, *revs, **opts):
194 def sign(ui, repo, *revs, **opts):
195 """add a signature for the current or given revision
195 """add a signature for the current or given revision
196
196
197 If no revision is given, the parent of the working directory is used,
197 If no revision is given, the parent of the working directory is used,
198 or tip if no revision is checked out.
198 or tip if no revision is checked out.
199
199
200 See 'hg help dates' for a list of formats valid for -d/--date.
200 See 'hg help dates' for a list of formats valid for -d/--date.
201 """
201 """
202
202
203 mygpg = newgpg(ui, **opts)
203 mygpg = newgpg(ui, **opts)
204 sigver = "0"
204 sigver = "0"
205 sigmessage = ""
205 sigmessage = ""
206
206
207 date = opts.get('date')
207 date = opts.get('date')
208 if date:
208 if date:
209 opts['date'] = util.parsedate(date)
209 opts['date'] = util.parsedate(date)
210
210
211 if revs:
211 if revs:
212 nodes = [repo.lookup(n) for n in revs]
212 nodes = [repo.lookup(n) for n in revs]
213 else:
213 else:
214 nodes = [node for node in repo.dirstate.parents()
214 nodes = [node for node in repo.dirstate.parents()
215 if node != hgnode.nullid]
215 if node != hgnode.nullid]
216 if len(nodes) > 1:
216 if len(nodes) > 1:
217 raise util.Abort(_('uncommitted merge - please provide a '
217 raise util.Abort(_('uncommitted merge - please provide a '
218 'specific revision'))
218 'specific revision'))
219 if not nodes:
219 if not nodes:
220 nodes = [repo.changelog.tip()]
220 nodes = [repo.changelog.tip()]
221
221
222 for n in nodes:
222 for n in nodes:
223 hexnode = hgnode.hex(n)
223 hexnode = hgnode.hex(n)
224 ui.write("Signing %d:%s\n" % (repo.changelog.rev(n),
224 ui.write(_("Signing %d:%s\n") % (repo.changelog.rev(n),
225 hgnode.short(n)))
225 hgnode.short(n)))
226 # build data
226 # build data
227 data = node2txt(repo, n, sigver)
227 data = node2txt(repo, n, sigver)
228 sig = mygpg.sign(data)
228 sig = mygpg.sign(data)
229 if not sig:
229 if not sig:
230 raise util.Abort(_("Error while signing"))
230 raise util.Abort(_("Error while signing"))
231 sig = binascii.b2a_base64(sig)
231 sig = binascii.b2a_base64(sig)
232 sig = sig.replace("\n", "")
232 sig = sig.replace("\n", "")
233 sigmessage += "%s %s %s\n" % (hexnode, sigver, sig)
233 sigmessage += "%s %s %s\n" % (hexnode, sigver, sig)
234
234
235 # write it
235 # write it
236 if opts['local']:
236 if opts['local']:
237 repo.opener("localsigs", "ab").write(sigmessage)
237 repo.opener("localsigs", "ab").write(sigmessage)
238 return
238 return
239
239
240 for x in repo.status(unknown=True)[:5]:
240 for x in repo.status(unknown=True)[:5]:
241 if ".hgsigs" in x and not opts["force"]:
241 if ".hgsigs" in x and not opts["force"]:
242 raise util.Abort(_("working copy of .hgsigs is changed "
242 raise util.Abort(_("working copy of .hgsigs is changed "
243 "(please commit .hgsigs manually "
243 "(please commit .hgsigs manually "
244 "or use --force)"))
244 "or use --force)"))
245
245
246 repo.wfile(".hgsigs", "ab").write(sigmessage)
246 repo.wfile(".hgsigs", "ab").write(sigmessage)
247
247
248 if '.hgsigs' not in repo.dirstate:
248 if '.hgsigs' not in repo.dirstate:
249 repo.add([".hgsigs"])
249 repo.add([".hgsigs"])
250
250
251 if opts["no_commit"]:
251 if opts["no_commit"]:
252 return
252 return
253
253
254 message = opts['message']
254 message = opts['message']
255 if not message:
255 if not message:
256 # we don't translate commit messages
256 # we don't translate commit messages
257 message = "\n".join(["Added signature for changeset %s"
257 message = "\n".join(["Added signature for changeset %s"
258 % hgnode.short(n)
258 % hgnode.short(n)
259 for n in nodes])
259 for n in nodes])
260 try:
260 try:
261 m = match.exact(repo.root, '', ['.hgsigs'])
261 m = match.exact(repo.root, '', ['.hgsigs'])
262 repo.commit(message, opts['user'], opts['date'], match=m)
262 repo.commit(message, opts['user'], opts['date'], match=m)
263 except ValueError, inst:
263 except ValueError, inst:
264 raise util.Abort(str(inst))
264 raise util.Abort(str(inst))
265
265
266 def node2txt(repo, node, ver):
266 def node2txt(repo, node, ver):
267 """map a manifest into some text"""
267 """map a manifest into some text"""
268 if ver == "0":
268 if ver == "0":
269 return "%s\n" % hgnode.hex(node)
269 return "%s\n" % hgnode.hex(node)
270 else:
270 else:
271 raise util.Abort(_("unknown signature version"))
271 raise util.Abort(_("unknown signature version"))
272
272
273 cmdtable = {
273 cmdtable = {
274 "sign":
274 "sign":
275 (sign,
275 (sign,
276 [('l', 'local', None, _('make the signature local')),
276 [('l', 'local', None, _('make the signature local')),
277 ('f', 'force', None, _('sign even if the sigfile is modified')),
277 ('f', 'force', None, _('sign even if the sigfile is modified')),
278 ('', 'no-commit', None, _('do not commit the sigfile after signing')),
278 ('', 'no-commit', None, _('do not commit the sigfile after signing')),
279 ('k', 'key', '', _('the key id to sign with')),
279 ('k', 'key', '', _('the key id to sign with')),
280 ('m', 'message', '', _('commit message')),
280 ('m', 'message', '', _('commit message')),
281 ] + commands.commitopts2,
281 ] + commands.commitopts2,
282 _('hg sign [OPTION]... [REVISION]...')),
282 _('hg sign [OPTION]... [REVISION]...')),
283 "sigcheck": (check, [], _('hg sigcheck REVISION')),
283 "sigcheck": (check, [], _('hg sigcheck REVISION')),
284 "sigs": (sigs, [], _('hg sigs')),
284 "sigs": (sigs, [], _('hg sigs')),
285 }
285 }
286
286
@@ -1,550 +1,550 b''
1 # keyword.py - $Keyword$ expansion for Mercurial
1 # keyword.py - $Keyword$ expansion for Mercurial
2 #
2 #
3 # Copyright 2007-2009 Christian Ebert <blacktrash@gmx.net>
3 # Copyright 2007-2009 Christian Ebert <blacktrash@gmx.net>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7 #
7 #
8 # $Id$
8 # $Id$
9 #
9 #
10 # Keyword expansion hack against the grain of a DSCM
10 # Keyword expansion hack against the grain of a DSCM
11 #
11 #
12 # There are many good reasons why this is not needed in a distributed
12 # There are many good reasons why this is not needed in a distributed
13 # SCM, still it may be useful in very small projects based on single
13 # SCM, still it may be useful in very small projects based on single
14 # files (like LaTeX packages), that are mostly addressed to an
14 # files (like LaTeX packages), that are mostly addressed to an
15 # audience not running a version control system.
15 # audience not running a version control system.
16 #
16 #
17 # For in-depth discussion refer to
17 # For in-depth discussion refer to
18 # <http://mercurial.selenic.com/wiki/KeywordPlan>.
18 # <http://mercurial.selenic.com/wiki/KeywordPlan>.
19 #
19 #
20 # Keyword expansion is based on Mercurial's changeset template mappings.
20 # Keyword expansion is based on Mercurial's changeset template mappings.
21 #
21 #
22 # Binary files are not touched.
22 # Binary files are not touched.
23 #
23 #
24 # Files to act upon/ignore are specified in the [keyword] section.
24 # Files to act upon/ignore are specified in the [keyword] section.
25 # Customized keyword template mappings in the [keywordmaps] section.
25 # Customized keyword template mappings in the [keywordmaps] section.
26 #
26 #
27 # Run "hg help keyword" and "hg kwdemo" to get info on configuration.
27 # Run "hg help keyword" and "hg kwdemo" to get info on configuration.
28
28
29 '''expand keywords in tracked files
29 '''expand keywords in tracked files
30
30
31 This extension expands RCS/CVS-like or self-customized $Keywords$ in
31 This extension expands RCS/CVS-like or self-customized $Keywords$ in
32 tracked text files selected by your configuration.
32 tracked text files selected by your configuration.
33
33
34 Keywords are only expanded in local repositories and not stored in the
34 Keywords are only expanded in local repositories and not stored in the
35 change history. The mechanism can be regarded as a convenience for the
35 change history. The mechanism can be regarded as a convenience for the
36 current user or for archive distribution.
36 current user or for archive distribution.
37
37
38 Configuration is done in the [keyword] and [keywordmaps] sections of
38 Configuration is done in the [keyword] and [keywordmaps] sections of
39 hgrc files.
39 hgrc files.
40
40
41 Example::
41 Example::
42
42
43 [keyword]
43 [keyword]
44 # expand keywords in every python file except those matching "x*"
44 # expand keywords in every python file except those matching "x*"
45 **.py =
45 **.py =
46 x* = ignore
46 x* = ignore
47
47
48 NOTE: the more specific you are in your filename patterns the less you
48 NOTE: the more specific you are in your filename patterns the less you
49 lose speed in huge repositories.
49 lose speed in huge repositories.
50
50
51 For [keywordmaps] template mapping and expansion demonstration and
51 For [keywordmaps] template mapping and expansion demonstration and
52 control run "hg kwdemo". See "hg help templates" for a list of
52 control run "hg kwdemo". See "hg help templates" for a list of
53 available templates and filters.
53 available templates and filters.
54
54
55 An additional date template filter {date|utcdate} is provided. It
55 An additional date template filter {date|utcdate} is provided. It
56 returns a date like "2006/09/18 15:13:13".
56 returns a date like "2006/09/18 15:13:13".
57
57
58 The default template mappings (view with "hg kwdemo -d") can be
58 The default template mappings (view with "hg kwdemo -d") can be
59 replaced with customized keywords and templates. Again, run "hg
59 replaced with customized keywords and templates. Again, run "hg
60 kwdemo" to control the results of your config changes.
60 kwdemo" to control the results of your config changes.
61
61
62 Before changing/disabling active keywords, run "hg kwshrink" to avoid
62 Before changing/disabling active keywords, run "hg kwshrink" to avoid
63 the risk of inadvertently storing expanded keywords in the change
63 the risk of inadvertently storing expanded keywords in the change
64 history.
64 history.
65
65
66 To force expansion after enabling it, or a configuration change, run
66 To force expansion after enabling it, or a configuration change, run
67 "hg kwexpand".
67 "hg kwexpand".
68
68
69 Also, when committing with the record extension or using mq's qrecord,
69 Also, when committing with the record extension or using mq's qrecord,
70 be aware that keywords cannot be updated. Again, run "hg kwexpand" on
70 be aware that keywords cannot be updated. Again, run "hg kwexpand" on
71 the files in question to update keyword expansions after all changes
71 the files in question to update keyword expansions after all changes
72 have been checked in.
72 have been checked in.
73
73
74 Expansions spanning more than one line and incremental expansions,
74 Expansions spanning more than one line and incremental expansions,
75 like CVS' $Log$, are not supported. A keyword template map "Log =
75 like CVS' $Log$, are not supported. A keyword template map "Log =
76 {desc}" expands to the first line of the changeset description.
76 {desc}" expands to the first line of the changeset description.
77 '''
77 '''
78
78
79 from mercurial import commands, cmdutil, dispatch, filelog, revlog, extensions
79 from mercurial import commands, cmdutil, dispatch, filelog, revlog, extensions
80 from mercurial import patch, localrepo, templater, templatefilters, util, match
80 from mercurial import patch, localrepo, templater, templatefilters, util, match
81 from mercurial.hgweb import webcommands
81 from mercurial.hgweb import webcommands
82 from mercurial.lock import release
82 from mercurial.lock import release
83 from mercurial.node import nullid
83 from mercurial.node import nullid
84 from mercurial.i18n import _
84 from mercurial.i18n import _
85 import re, shutil, tempfile
85 import re, shutil, tempfile
86
86
87 commands.optionalrepo += ' kwdemo'
87 commands.optionalrepo += ' kwdemo'
88
88
89 # hg commands that do not act on keywords
89 # hg commands that do not act on keywords
90 nokwcommands = ('add addremove annotate bundle copy export grep incoming init'
90 nokwcommands = ('add addremove annotate bundle copy export grep incoming init'
91 ' log outgoing push rename rollback tip verify'
91 ' log outgoing push rename rollback tip verify'
92 ' convert email glog')
92 ' convert email glog')
93
93
94 # hg commands that trigger expansion only when writing to working dir,
94 # hg commands that trigger expansion only when writing to working dir,
95 # not when reading filelog, and unexpand when reading from working dir
95 # not when reading filelog, and unexpand when reading from working dir
96 restricted = ('merge record resolve qfold qimport qnew qpush qrefresh qrecord'
96 restricted = ('merge record resolve qfold qimport qnew qpush qrefresh qrecord'
97 ' transplant')
97 ' transplant')
98
98
99 # provide cvs-like UTC date filter
99 # provide cvs-like UTC date filter
100 utcdate = lambda x: util.datestr((x[0], 0), '%Y/%m/%d %H:%M:%S')
100 utcdate = lambda x: util.datestr((x[0], 0), '%Y/%m/%d %H:%M:%S')
101
101
102 # make keyword tools accessible
102 # make keyword tools accessible
103 kwtools = {'templater': None, 'hgcmd': '', 'inc': [], 'exc': ['.hg*']}
103 kwtools = {'templater': None, 'hgcmd': '', 'inc': [], 'exc': ['.hg*']}
104
104
105
105
106 class kwtemplater(object):
106 class kwtemplater(object):
107 '''
107 '''
108 Sets up keyword templates, corresponding keyword regex, and
108 Sets up keyword templates, corresponding keyword regex, and
109 provides keyword substitution functions.
109 provides keyword substitution functions.
110 '''
110 '''
111 templates = {
111 templates = {
112 'Revision': '{node|short}',
112 'Revision': '{node|short}',
113 'Author': '{author|user}',
113 'Author': '{author|user}',
114 'Date': '{date|utcdate}',
114 'Date': '{date|utcdate}',
115 'RCSfile': '{file|basename},v',
115 'RCSfile': '{file|basename},v',
116 'RCSFile': '{file|basename},v', # kept for backwards compatibility
116 'RCSFile': '{file|basename},v', # kept for backwards compatibility
117 # with hg-keyword
117 # with hg-keyword
118 'Source': '{root}/{file},v',
118 'Source': '{root}/{file},v',
119 'Id': '{file|basename},v {node|short} {date|utcdate} {author|user}',
119 'Id': '{file|basename},v {node|short} {date|utcdate} {author|user}',
120 'Header': '{root}/{file},v {node|short} {date|utcdate} {author|user}',
120 'Header': '{root}/{file},v {node|short} {date|utcdate} {author|user}',
121 }
121 }
122
122
123 def __init__(self, ui, repo):
123 def __init__(self, ui, repo):
124 self.ui = ui
124 self.ui = ui
125 self.repo = repo
125 self.repo = repo
126 self.match = match.match(repo.root, '', [],
126 self.match = match.match(repo.root, '', [],
127 kwtools['inc'], kwtools['exc'])
127 kwtools['inc'], kwtools['exc'])
128 self.restrict = kwtools['hgcmd'] in restricted.split()
128 self.restrict = kwtools['hgcmd'] in restricted.split()
129
129
130 kwmaps = self.ui.configitems('keywordmaps')
130 kwmaps = self.ui.configitems('keywordmaps')
131 if kwmaps: # override default templates
131 if kwmaps: # override default templates
132 self.templates = dict((k, templater.parsestring(v, False))
132 self.templates = dict((k, templater.parsestring(v, False))
133 for k, v in kwmaps)
133 for k, v in kwmaps)
134 escaped = map(re.escape, self.templates.keys())
134 escaped = map(re.escape, self.templates.keys())
135 kwpat = r'\$(%s)(: [^$\n\r]*? )??\$' % '|'.join(escaped)
135 kwpat = r'\$(%s)(: [^$\n\r]*? )??\$' % '|'.join(escaped)
136 self.re_kw = re.compile(kwpat)
136 self.re_kw = re.compile(kwpat)
137
137
138 templatefilters.filters['utcdate'] = utcdate
138 templatefilters.filters['utcdate'] = utcdate
139 self.ct = cmdutil.changeset_templater(self.ui, self.repo,
139 self.ct = cmdutil.changeset_templater(self.ui, self.repo,
140 False, None, '', False)
140 False, None, '', False)
141
141
142 def substitute(self, data, path, ctx, subfunc):
142 def substitute(self, data, path, ctx, subfunc):
143 '''Replaces keywords in data with expanded template.'''
143 '''Replaces keywords in data with expanded template.'''
144 def kwsub(mobj):
144 def kwsub(mobj):
145 kw = mobj.group(1)
145 kw = mobj.group(1)
146 self.ct.use_template(self.templates[kw])
146 self.ct.use_template(self.templates[kw])
147 self.ui.pushbuffer()
147 self.ui.pushbuffer()
148 self.ct.show(ctx, root=self.repo.root, file=path)
148 self.ct.show(ctx, root=self.repo.root, file=path)
149 ekw = templatefilters.firstline(self.ui.popbuffer())
149 ekw = templatefilters.firstline(self.ui.popbuffer())
150 return '$%s: %s $' % (kw, ekw)
150 return '$%s: %s $' % (kw, ekw)
151 return subfunc(kwsub, data)
151 return subfunc(kwsub, data)
152
152
153 def expand(self, path, node, data):
153 def expand(self, path, node, data):
154 '''Returns data with keywords expanded.'''
154 '''Returns data with keywords expanded.'''
155 if not self.restrict and self.match(path) and not util.binary(data):
155 if not self.restrict and self.match(path) and not util.binary(data):
156 ctx = self.repo.filectx(path, fileid=node).changectx()
156 ctx = self.repo.filectx(path, fileid=node).changectx()
157 return self.substitute(data, path, ctx, self.re_kw.sub)
157 return self.substitute(data, path, ctx, self.re_kw.sub)
158 return data
158 return data
159
159
160 def iskwfile(self, path, flagfunc):
160 def iskwfile(self, path, flagfunc):
161 '''Returns true if path matches [keyword] pattern
161 '''Returns true if path matches [keyword] pattern
162 and is not a symbolic link.
162 and is not a symbolic link.
163 Caveat: localrepository._link fails on Windows.'''
163 Caveat: localrepository._link fails on Windows.'''
164 return self.match(path) and not 'l' in flagfunc(path)
164 return self.match(path) and not 'l' in flagfunc(path)
165
165
166 def overwrite(self, node, expand, files):
166 def overwrite(self, node, expand, files):
167 '''Overwrites selected files expanding/shrinking keywords.'''
167 '''Overwrites selected files expanding/shrinking keywords.'''
168 ctx = self.repo[node]
168 ctx = self.repo[node]
169 mf = ctx.manifest()
169 mf = ctx.manifest()
170 if node is not None: # commit
170 if node is not None: # commit
171 files = [f for f in ctx.files() if f in mf]
171 files = [f for f in ctx.files() if f in mf]
172 notify = self.ui.debug
172 notify = self.ui.debug
173 else: # kwexpand/kwshrink
173 else: # kwexpand/kwshrink
174 notify = self.ui.note
174 notify = self.ui.note
175 candidates = [f for f in files if self.iskwfile(f, ctx.flags)]
175 candidates = [f for f in files if self.iskwfile(f, ctx.flags)]
176 if candidates:
176 if candidates:
177 self.restrict = True # do not expand when reading
177 self.restrict = True # do not expand when reading
178 msg = (expand and _('overwriting %s expanding keywords\n')
178 msg = (expand and _('overwriting %s expanding keywords\n')
179 or _('overwriting %s shrinking keywords\n'))
179 or _('overwriting %s shrinking keywords\n'))
180 for f in candidates:
180 for f in candidates:
181 fp = self.repo.file(f)
181 fp = self.repo.file(f)
182 data = fp.read(mf[f])
182 data = fp.read(mf[f])
183 if util.binary(data):
183 if util.binary(data):
184 continue
184 continue
185 if expand:
185 if expand:
186 if node is None:
186 if node is None:
187 ctx = self.repo.filectx(f, fileid=mf[f]).changectx()
187 ctx = self.repo.filectx(f, fileid=mf[f]).changectx()
188 data, found = self.substitute(data, f, ctx,
188 data, found = self.substitute(data, f, ctx,
189 self.re_kw.subn)
189 self.re_kw.subn)
190 else:
190 else:
191 found = self.re_kw.search(data)
191 found = self.re_kw.search(data)
192 if found:
192 if found:
193 notify(msg % f)
193 notify(msg % f)
194 self.repo.wwrite(f, data, mf.flags(f))
194 self.repo.wwrite(f, data, mf.flags(f))
195 if node is None:
195 if node is None:
196 self.repo.dirstate.normal(f)
196 self.repo.dirstate.normal(f)
197 self.restrict = False
197 self.restrict = False
198
198
199 def shrinktext(self, text):
199 def shrinktext(self, text):
200 '''Unconditionally removes all keyword substitutions from text.'''
200 '''Unconditionally removes all keyword substitutions from text.'''
201 return self.re_kw.sub(r'$\1$', text)
201 return self.re_kw.sub(r'$\1$', text)
202
202
203 def shrink(self, fname, text):
203 def shrink(self, fname, text):
204 '''Returns text with all keyword substitutions removed.'''
204 '''Returns text with all keyword substitutions removed.'''
205 if self.match(fname) and not util.binary(text):
205 if self.match(fname) and not util.binary(text):
206 return self.shrinktext(text)
206 return self.shrinktext(text)
207 return text
207 return text
208
208
209 def shrinklines(self, fname, lines):
209 def shrinklines(self, fname, lines):
210 '''Returns lines with keyword substitutions removed.'''
210 '''Returns lines with keyword substitutions removed.'''
211 if self.match(fname):
211 if self.match(fname):
212 text = ''.join(lines)
212 text = ''.join(lines)
213 if not util.binary(text):
213 if not util.binary(text):
214 return self.shrinktext(text).splitlines(True)
214 return self.shrinktext(text).splitlines(True)
215 return lines
215 return lines
216
216
217 def wread(self, fname, data):
217 def wread(self, fname, data):
218 '''If in restricted mode returns data read from wdir with
218 '''If in restricted mode returns data read from wdir with
219 keyword substitutions removed.'''
219 keyword substitutions removed.'''
220 return self.restrict and self.shrink(fname, data) or data
220 return self.restrict and self.shrink(fname, data) or data
221
221
222 class kwfilelog(filelog.filelog):
222 class kwfilelog(filelog.filelog):
223 '''
223 '''
224 Subclass of filelog to hook into its read, add, cmp methods.
224 Subclass of filelog to hook into its read, add, cmp methods.
225 Keywords are "stored" unexpanded, and processed on reading.
225 Keywords are "stored" unexpanded, and processed on reading.
226 '''
226 '''
227 def __init__(self, opener, kwt, path):
227 def __init__(self, opener, kwt, path):
228 super(kwfilelog, self).__init__(opener, path)
228 super(kwfilelog, self).__init__(opener, path)
229 self.kwt = kwt
229 self.kwt = kwt
230 self.path = path
230 self.path = path
231
231
232 def read(self, node):
232 def read(self, node):
233 '''Expands keywords when reading filelog.'''
233 '''Expands keywords when reading filelog.'''
234 data = super(kwfilelog, self).read(node)
234 data = super(kwfilelog, self).read(node)
235 return self.kwt.expand(self.path, node, data)
235 return self.kwt.expand(self.path, node, data)
236
236
237 def add(self, text, meta, tr, link, p1=None, p2=None):
237 def add(self, text, meta, tr, link, p1=None, p2=None):
238 '''Removes keyword substitutions when adding to filelog.'''
238 '''Removes keyword substitutions when adding to filelog.'''
239 text = self.kwt.shrink(self.path, text)
239 text = self.kwt.shrink(self.path, text)
240 return super(kwfilelog, self).add(text, meta, tr, link, p1, p2)
240 return super(kwfilelog, self).add(text, meta, tr, link, p1, p2)
241
241
242 def cmp(self, node, text):
242 def cmp(self, node, text):
243 '''Removes keyword substitutions for comparison.'''
243 '''Removes keyword substitutions for comparison.'''
244 text = self.kwt.shrink(self.path, text)
244 text = self.kwt.shrink(self.path, text)
245 if self.renamed(node):
245 if self.renamed(node):
246 t2 = super(kwfilelog, self).read(node)
246 t2 = super(kwfilelog, self).read(node)
247 return t2 != text
247 return t2 != text
248 return revlog.revlog.cmp(self, node, text)
248 return revlog.revlog.cmp(self, node, text)
249
249
250 def _status(ui, repo, kwt, *pats, **opts):
250 def _status(ui, repo, kwt, *pats, **opts):
251 '''Bails out if [keyword] configuration is not active.
251 '''Bails out if [keyword] configuration is not active.
252 Returns status of working directory.'''
252 Returns status of working directory.'''
253 if kwt:
253 if kwt:
254 unknown = (opts.get('unknown') or opts.get('all')
254 unknown = (opts.get('unknown') or opts.get('all')
255 or opts.get('untracked'))
255 or opts.get('untracked'))
256 return repo.status(match=cmdutil.match(repo, pats, opts), clean=True,
256 return repo.status(match=cmdutil.match(repo, pats, opts), clean=True,
257 unknown=unknown)
257 unknown=unknown)
258 if ui.configitems('keyword'):
258 if ui.configitems('keyword'):
259 raise util.Abort(_('[keyword] patterns cannot match'))
259 raise util.Abort(_('[keyword] patterns cannot match'))
260 raise util.Abort(_('no [keyword] patterns configured'))
260 raise util.Abort(_('no [keyword] patterns configured'))
261
261
262 def _kwfwrite(ui, repo, expand, *pats, **opts):
262 def _kwfwrite(ui, repo, expand, *pats, **opts):
263 '''Selects files and passes them to kwtemplater.overwrite.'''
263 '''Selects files and passes them to kwtemplater.overwrite.'''
264 if repo.dirstate.parents()[1] != nullid:
264 if repo.dirstate.parents()[1] != nullid:
265 raise util.Abort(_('outstanding uncommitted merge'))
265 raise util.Abort(_('outstanding uncommitted merge'))
266 kwt = kwtools['templater']
266 kwt = kwtools['templater']
267 status = _status(ui, repo, kwt, *pats, **opts)
267 status = _status(ui, repo, kwt, *pats, **opts)
268 modified, added, removed, deleted = status[:4]
268 modified, added, removed, deleted = status[:4]
269 if modified or added or removed or deleted:
269 if modified or added or removed or deleted:
270 raise util.Abort(_('outstanding uncommitted changes'))
270 raise util.Abort(_('outstanding uncommitted changes'))
271 wlock = lock = None
271 wlock = lock = None
272 try:
272 try:
273 wlock = repo.wlock()
273 wlock = repo.wlock()
274 lock = repo.lock()
274 lock = repo.lock()
275 kwt.overwrite(None, expand, status[6])
275 kwt.overwrite(None, expand, status[6])
276 finally:
276 finally:
277 release(lock, wlock)
277 release(lock, wlock)
278
278
279 def demo(ui, repo, *args, **opts):
279 def demo(ui, repo, *args, **opts):
280 '''print [keywordmaps] configuration and an expansion example
280 '''print [keywordmaps] configuration and an expansion example
281
281
282 Show current, custom, or default keyword template maps and their
282 Show current, custom, or default keyword template maps and their
283 expansions.
283 expansions.
284
284
285 Extend the current configuration by specifying maps as arguments
285 Extend the current configuration by specifying maps as arguments
286 and using -f/--rcfile to source an external hgrc file.
286 and using -f/--rcfile to source an external hgrc file.
287
287
288 Use -d/--default to disable current configuration.
288 Use -d/--default to disable current configuration.
289
289
290 See "hg help templates" for information on templates and filters.
290 See "hg help templates" for information on templates and filters.
291 '''
291 '''
292 def demoitems(section, items):
292 def demoitems(section, items):
293 ui.write('[%s]\n' % section)
293 ui.write('[%s]\n' % section)
294 for k, v in sorted(items):
294 for k, v in sorted(items):
295 ui.write('%s = %s\n' % (k, v))
295 ui.write('%s = %s\n' % (k, v))
296
296
297 msg = 'hg keyword config and expansion example'
298 fn = 'demo.txt'
297 fn = 'demo.txt'
299 branchname = 'demobranch'
298 branchname = 'demobranch'
300 tmpdir = tempfile.mkdtemp('', 'kwdemo.')
299 tmpdir = tempfile.mkdtemp('', 'kwdemo.')
301 ui.note(_('creating temporary repository at %s\n') % tmpdir)
300 ui.note(_('creating temporary repository at %s\n') % tmpdir)
302 repo = localrepo.localrepository(ui, tmpdir, True)
301 repo = localrepo.localrepository(ui, tmpdir, True)
303 ui.setconfig('keyword', fn, '')
302 ui.setconfig('keyword', fn, '')
304
303
305 uikwmaps = ui.configitems('keywordmaps')
304 uikwmaps = ui.configitems('keywordmaps')
306 if args or opts.get('rcfile'):
305 if args or opts.get('rcfile'):
307 ui.status(_('\n\tconfiguration using custom keyword template maps\n'))
306 ui.status(_('\n\tconfiguration using custom keyword template maps\n'))
308 if uikwmaps:
307 if uikwmaps:
309 ui.status(_('\textending current template maps\n'))
308 ui.status(_('\textending current template maps\n'))
310 if opts.get('default') or not uikwmaps:
309 if opts.get('default') or not uikwmaps:
311 ui.status(_('\toverriding default template maps\n'))
310 ui.status(_('\toverriding default template maps\n'))
312 if opts.get('rcfile'):
311 if opts.get('rcfile'):
313 ui.readconfig(opts.get('rcfile'))
312 ui.readconfig(opts.get('rcfile'))
314 if args:
313 if args:
315 # simulate hgrc parsing
314 # simulate hgrc parsing
316 rcmaps = ['[keywordmaps]\n'] + [a + '\n' for a in args]
315 rcmaps = ['[keywordmaps]\n'] + [a + '\n' for a in args]
317 fp = repo.opener('hgrc', 'w')
316 fp = repo.opener('hgrc', 'w')
318 fp.writelines(rcmaps)
317 fp.writelines(rcmaps)
319 fp.close()
318 fp.close()
320 ui.readconfig(repo.join('hgrc'))
319 ui.readconfig(repo.join('hgrc'))
321 kwmaps = dict(ui.configitems('keywordmaps'))
320 kwmaps = dict(ui.configitems('keywordmaps'))
322 elif opts.get('default'):
321 elif opts.get('default'):
323 ui.status(_('\n\tconfiguration using default keyword template maps\n'))
322 ui.status(_('\n\tconfiguration using default keyword template maps\n'))
324 kwmaps = kwtemplater.templates
323 kwmaps = kwtemplater.templates
325 if uikwmaps:
324 if uikwmaps:
326 ui.status(_('\tdisabling current template maps\n'))
325 ui.status(_('\tdisabling current template maps\n'))
327 for k, v in kwmaps.iteritems():
326 for k, v in kwmaps.iteritems():
328 ui.setconfig('keywordmaps', k, v)
327 ui.setconfig('keywordmaps', k, v)
329 else:
328 else:
330 ui.status(_('\n\tconfiguration using current keyword template maps\n'))
329 ui.status(_('\n\tconfiguration using current keyword template maps\n'))
331 kwmaps = dict(uikwmaps) or kwtemplater.templates
330 kwmaps = dict(uikwmaps) or kwtemplater.templates
332
331
333 uisetup(ui)
332 uisetup(ui)
334 reposetup(ui, repo)
333 reposetup(ui, repo)
335 for k, v in ui.configitems('extensions'):
334 for k, v in ui.configitems('extensions'):
336 if k.endswith('keyword'):
335 if k.endswith('keyword'):
337 extension = '%s = %s' % (k, v)
336 extension = '%s = %s' % (k, v)
338 break
337 break
339 ui.write('[extensions]\n%s\n' % extension)
338 ui.write('[extensions]\n%s\n' % extension)
340 demoitems('keyword', ui.configitems('keyword'))
339 demoitems('keyword', ui.configitems('keyword'))
341 demoitems('keywordmaps', kwmaps.iteritems())
340 demoitems('keywordmaps', kwmaps.iteritems())
342 keywords = '$' + '$\n$'.join(sorted(kwmaps.keys())) + '$\n'
341 keywords = '$' + '$\n$'.join(sorted(kwmaps.keys())) + '$\n'
343 repo.wopener(fn, 'w').write(keywords)
342 repo.wopener(fn, 'w').write(keywords)
344 repo.add([fn])
343 repo.add([fn])
345 path = repo.wjoin(fn)
344 path = repo.wjoin(fn)
346 ui.note(_('\nkeywords written to %s:\n') % path)
345 ui.note(_('\nkeywords written to %s:\n') % path)
347 ui.note(keywords)
346 ui.note(keywords)
348 ui.note('\nhg -R "%s" branch "%s"\n' % (tmpdir, branchname))
347 ui.note('\nhg -R "%s" branch "%s"\n' % (tmpdir, branchname))
349 # silence branch command if not verbose
348 # silence branch command if not verbose
350 quiet = ui.quiet
349 quiet = ui.quiet
351 ui.quiet = not ui.verbose
350 ui.quiet = not ui.verbose
352 commands.branch(ui, repo, branchname)
351 commands.branch(ui, repo, branchname)
353 ui.quiet = quiet
352 ui.quiet = quiet
354 for name, cmd in ui.configitems('hooks'):
353 for name, cmd in ui.configitems('hooks'):
355 if name.split('.', 1)[0].find('commit') > -1:
354 if name.split('.', 1)[0].find('commit') > -1:
356 repo.ui.setconfig('hooks', name, '')
355 repo.ui.setconfig('hooks', name, '')
357 ui.note(_('unhooked all commit hooks\n'))
356 ui.note(_('unhooked all commit hooks\n'))
358 ui.note('hg -R "%s" ci -m "%s"\n' % (tmpdir, msg))
357 msg = _('hg keyword configuration and expansion example')
358 ui.note("hg -R '%s' ci -m '%s'\n" % (tmpdir, msg))
359 repo.commit(text=msg)
359 repo.commit(text=msg)
360 ui.status(_('\n\tkeywords expanded\n'))
360 ui.status(_('\n\tkeywords expanded\n'))
361 ui.write(repo.wread(fn))
361 ui.write(repo.wread(fn))
362 ui.debug('\nremoving temporary repository %s\n' % tmpdir)
362 ui.debug('\nremoving temporary repository %s\n' % tmpdir)
363 shutil.rmtree(tmpdir, ignore_errors=True)
363 shutil.rmtree(tmpdir, ignore_errors=True)
364
364
365 def expand(ui, repo, *pats, **opts):
365 def expand(ui, repo, *pats, **opts):
366 '''expand keywords in the working directory
366 '''expand keywords in the working directory
367
367
368 Run after (re)enabling keyword expansion.
368 Run after (re)enabling keyword expansion.
369
369
370 kwexpand refuses to run if given files contain local changes.
370 kwexpand refuses to run if given files contain local changes.
371 '''
371 '''
372 # 3rd argument sets expansion to True
372 # 3rd argument sets expansion to True
373 _kwfwrite(ui, repo, True, *pats, **opts)
373 _kwfwrite(ui, repo, True, *pats, **opts)
374
374
375 def files(ui, repo, *pats, **opts):
375 def files(ui, repo, *pats, **opts):
376 '''show files configured for keyword expansion
376 '''show files configured for keyword expansion
377
377
378 List which files in the working directory are matched by the
378 List which files in the working directory are matched by the
379 [keyword] configuration patterns.
379 [keyword] configuration patterns.
380
380
381 Useful to prevent inadvertent keyword expansion and to speed up
381 Useful to prevent inadvertent keyword expansion and to speed up
382 execution by including only files that are actual candidates for
382 execution by including only files that are actual candidates for
383 expansion.
383 expansion.
384
384
385 See "hg help keyword" on how to construct patterns both for
385 See "hg help keyword" on how to construct patterns both for
386 inclusion and exclusion of files.
386 inclusion and exclusion of files.
387
387
388 With -A/--all and -v/--verbose the codes used to show the status
388 With -A/--all and -v/--verbose the codes used to show the status
389 of files are::
389 of files are::
390
390
391 K = keyword expansion candidate
391 K = keyword expansion candidate
392 k = keyword expansion candidate (not tracked)
392 k = keyword expansion candidate (not tracked)
393 I = ignored
393 I = ignored
394 i = ignored (not tracked)
394 i = ignored (not tracked)
395 '''
395 '''
396 kwt = kwtools['templater']
396 kwt = kwtools['templater']
397 status = _status(ui, repo, kwt, *pats, **opts)
397 status = _status(ui, repo, kwt, *pats, **opts)
398 cwd = pats and repo.getcwd() or ''
398 cwd = pats and repo.getcwd() or ''
399 modified, added, removed, deleted, unknown, ignored, clean = status
399 modified, added, removed, deleted, unknown, ignored, clean = status
400 files = []
400 files = []
401 if not (opts.get('unknown') or opts.get('untracked')) or opts.get('all'):
401 if not (opts.get('unknown') or opts.get('untracked')) or opts.get('all'):
402 files = sorted(modified + added + clean)
402 files = sorted(modified + added + clean)
403 wctx = repo[None]
403 wctx = repo[None]
404 kwfiles = [f for f in files if kwt.iskwfile(f, wctx.flags)]
404 kwfiles = [f for f in files if kwt.iskwfile(f, wctx.flags)]
405 kwunknown = [f for f in unknown if kwt.iskwfile(f, wctx.flags)]
405 kwunknown = [f for f in unknown if kwt.iskwfile(f, wctx.flags)]
406 if not opts.get('ignore') or opts.get('all'):
406 if not opts.get('ignore') or opts.get('all'):
407 showfiles = kwfiles, kwunknown
407 showfiles = kwfiles, kwunknown
408 else:
408 else:
409 showfiles = [], []
409 showfiles = [], []
410 if opts.get('all') or opts.get('ignore'):
410 if opts.get('all') or opts.get('ignore'):
411 showfiles += ([f for f in files if f not in kwfiles],
411 showfiles += ([f for f in files if f not in kwfiles],
412 [f for f in unknown if f not in kwunknown])
412 [f for f in unknown if f not in kwunknown])
413 for char, filenames in zip('KkIi', showfiles):
413 for char, filenames in zip('KkIi', showfiles):
414 fmt = (opts.get('all') or ui.verbose) and '%s %%s\n' % char or '%s\n'
414 fmt = (opts.get('all') or ui.verbose) and '%s %%s\n' % char or '%s\n'
415 for f in filenames:
415 for f in filenames:
416 ui.write(fmt % repo.pathto(f, cwd))
416 ui.write(fmt % repo.pathto(f, cwd))
417
417
418 def shrink(ui, repo, *pats, **opts):
418 def shrink(ui, repo, *pats, **opts):
419 '''revert expanded keywords in the working directory
419 '''revert expanded keywords in the working directory
420
420
421 Run before changing/disabling active keywords or if you experience
421 Run before changing/disabling active keywords or if you experience
422 problems with "hg import" or "hg merge".
422 problems with "hg import" or "hg merge".
423
423
424 kwshrink refuses to run if given files contain local changes.
424 kwshrink refuses to run if given files contain local changes.
425 '''
425 '''
426 # 3rd argument sets expansion to False
426 # 3rd argument sets expansion to False
427 _kwfwrite(ui, repo, False, *pats, **opts)
427 _kwfwrite(ui, repo, False, *pats, **opts)
428
428
429
429
430 def uisetup(ui):
430 def uisetup(ui):
431 '''Collects [keyword] config in kwtools.
431 '''Collects [keyword] config in kwtools.
432 Monkeypatches dispatch._parse if needed.'''
432 Monkeypatches dispatch._parse if needed.'''
433
433
434 for pat, opt in ui.configitems('keyword'):
434 for pat, opt in ui.configitems('keyword'):
435 if opt != 'ignore':
435 if opt != 'ignore':
436 kwtools['inc'].append(pat)
436 kwtools['inc'].append(pat)
437 else:
437 else:
438 kwtools['exc'].append(pat)
438 kwtools['exc'].append(pat)
439
439
440 if kwtools['inc']:
440 if kwtools['inc']:
441 def kwdispatch_parse(orig, ui, args):
441 def kwdispatch_parse(orig, ui, args):
442 '''Monkeypatch dispatch._parse to obtain running hg command.'''
442 '''Monkeypatch dispatch._parse to obtain running hg command.'''
443 cmd, func, args, options, cmdoptions = orig(ui, args)
443 cmd, func, args, options, cmdoptions = orig(ui, args)
444 kwtools['hgcmd'] = cmd
444 kwtools['hgcmd'] = cmd
445 return cmd, func, args, options, cmdoptions
445 return cmd, func, args, options, cmdoptions
446
446
447 extensions.wrapfunction(dispatch, '_parse', kwdispatch_parse)
447 extensions.wrapfunction(dispatch, '_parse', kwdispatch_parse)
448
448
449 def reposetup(ui, repo):
449 def reposetup(ui, repo):
450 '''Sets up repo as kwrepo for keyword substitution.
450 '''Sets up repo as kwrepo for keyword substitution.
451 Overrides file method to return kwfilelog instead of filelog
451 Overrides file method to return kwfilelog instead of filelog
452 if file matches user configuration.
452 if file matches user configuration.
453 Wraps commit to overwrite configured files with updated
453 Wraps commit to overwrite configured files with updated
454 keyword substitutions.
454 keyword substitutions.
455 Monkeypatches patch and webcommands.'''
455 Monkeypatches patch and webcommands.'''
456
456
457 try:
457 try:
458 if (not repo.local() or not kwtools['inc']
458 if (not repo.local() or not kwtools['inc']
459 or kwtools['hgcmd'] in nokwcommands.split()
459 or kwtools['hgcmd'] in nokwcommands.split()
460 or '.hg' in util.splitpath(repo.root)
460 or '.hg' in util.splitpath(repo.root)
461 or repo._url.startswith('bundle:')):
461 or repo._url.startswith('bundle:')):
462 return
462 return
463 except AttributeError:
463 except AttributeError:
464 pass
464 pass
465
465
466 kwtools['templater'] = kwt = kwtemplater(ui, repo)
466 kwtools['templater'] = kwt = kwtemplater(ui, repo)
467
467
468 class kwrepo(repo.__class__):
468 class kwrepo(repo.__class__):
469 def file(self, f):
469 def file(self, f):
470 if f[0] == '/':
470 if f[0] == '/':
471 f = f[1:]
471 f = f[1:]
472 return kwfilelog(self.sopener, kwt, f)
472 return kwfilelog(self.sopener, kwt, f)
473
473
474 def wread(self, filename):
474 def wread(self, filename):
475 data = super(kwrepo, self).wread(filename)
475 data = super(kwrepo, self).wread(filename)
476 return kwt.wread(filename, data)
476 return kwt.wread(filename, data)
477
477
478 def commit(self, *args, **opts):
478 def commit(self, *args, **opts):
479 # use custom commitctx for user commands
479 # use custom commitctx for user commands
480 # other extensions can still wrap repo.commitctx directly
480 # other extensions can still wrap repo.commitctx directly
481 self.commitctx = self.kwcommitctx
481 self.commitctx = self.kwcommitctx
482 try:
482 try:
483 return super(kwrepo, self).commit(*args, **opts)
483 return super(kwrepo, self).commit(*args, **opts)
484 finally:
484 finally:
485 del self.commitctx
485 del self.commitctx
486
486
487 def kwcommitctx(self, ctx, error=False):
487 def kwcommitctx(self, ctx, error=False):
488 wlock = lock = None
488 wlock = lock = None
489 try:
489 try:
490 wlock = self.wlock()
490 wlock = self.wlock()
491 lock = self.lock()
491 lock = self.lock()
492 n = super(kwrepo, self).commitctx(ctx, error)
492 n = super(kwrepo, self).commitctx(ctx, error)
493 kwt.overwrite(n, True, None)
493 kwt.overwrite(n, True, None)
494 return n
494 return n
495 finally:
495 finally:
496 release(lock, wlock)
496 release(lock, wlock)
497
497
498 # monkeypatches
498 # monkeypatches
499 def kwpatchfile_init(orig, self, ui, fname, opener,
499 def kwpatchfile_init(orig, self, ui, fname, opener,
500 missing=False, eol=None):
500 missing=False, eol=None):
501 '''Monkeypatch/wrap patch.patchfile.__init__ to avoid
501 '''Monkeypatch/wrap patch.patchfile.__init__ to avoid
502 rejects or conflicts due to expanded keywords in working dir.'''
502 rejects or conflicts due to expanded keywords in working dir.'''
503 orig(self, ui, fname, opener, missing, eol)
503 orig(self, ui, fname, opener, missing, eol)
504 # shrink keywords read from working dir
504 # shrink keywords read from working dir
505 self.lines = kwt.shrinklines(self.fname, self.lines)
505 self.lines = kwt.shrinklines(self.fname, self.lines)
506
506
507 def kw_diff(orig, repo, node1=None, node2=None, match=None, changes=None,
507 def kw_diff(orig, repo, node1=None, node2=None, match=None, changes=None,
508 opts=None):
508 opts=None):
509 '''Monkeypatch patch.diff to avoid expansion except when
509 '''Monkeypatch patch.diff to avoid expansion except when
510 comparing against working dir.'''
510 comparing against working dir.'''
511 if node2 is not None:
511 if node2 is not None:
512 kwt.match = util.never
512 kwt.match = util.never
513 elif node1 is not None and node1 != repo['.'].node():
513 elif node1 is not None and node1 != repo['.'].node():
514 kwt.restrict = True
514 kwt.restrict = True
515 return orig(repo, node1, node2, match, changes, opts)
515 return orig(repo, node1, node2, match, changes, opts)
516
516
517 def kwweb_skip(orig, web, req, tmpl):
517 def kwweb_skip(orig, web, req, tmpl):
518 '''Wraps webcommands.x turning off keyword expansion.'''
518 '''Wraps webcommands.x turning off keyword expansion.'''
519 kwt.match = util.never
519 kwt.match = util.never
520 return orig(web, req, tmpl)
520 return orig(web, req, tmpl)
521
521
522 repo.__class__ = kwrepo
522 repo.__class__ = kwrepo
523
523
524 extensions.wrapfunction(patch.patchfile, '__init__', kwpatchfile_init)
524 extensions.wrapfunction(patch.patchfile, '__init__', kwpatchfile_init)
525 if not kwt.restrict:
525 if not kwt.restrict:
526 extensions.wrapfunction(patch, 'diff', kw_diff)
526 extensions.wrapfunction(patch, 'diff', kw_diff)
527 for c in 'annotate changeset rev filediff diff'.split():
527 for c in 'annotate changeset rev filediff diff'.split():
528 extensions.wrapfunction(webcommands, c, kwweb_skip)
528 extensions.wrapfunction(webcommands, c, kwweb_skip)
529
529
530 cmdtable = {
530 cmdtable = {
531 'kwdemo':
531 'kwdemo':
532 (demo,
532 (demo,
533 [('d', 'default', None, _('show default keyword template maps')),
533 [('d', 'default', None, _('show default keyword template maps')),
534 ('f', 'rcfile', '', _('read maps from rcfile'))],
534 ('f', 'rcfile', '', _('read maps from rcfile'))],
535 _('hg kwdemo [-d] [-f RCFILE] [TEMPLATEMAP]...')),
535 _('hg kwdemo [-d] [-f RCFILE] [TEMPLATEMAP]...')),
536 'kwexpand': (expand, commands.walkopts,
536 'kwexpand': (expand, commands.walkopts,
537 _('hg kwexpand [OPTION]... [FILE]...')),
537 _('hg kwexpand [OPTION]... [FILE]...')),
538 'kwfiles':
538 'kwfiles':
539 (files,
539 (files,
540 [('A', 'all', None, _('show keyword status flags of all files')),
540 [('A', 'all', None, _('show keyword status flags of all files')),
541 ('i', 'ignore', None, _('show files excluded from expansion')),
541 ('i', 'ignore', None, _('show files excluded from expansion')),
542 ('u', 'unknown', None, _('only show unknown (not tracked) files')),
542 ('u', 'unknown', None, _('only show unknown (not tracked) files')),
543 ('a', 'all', None,
543 ('a', 'all', None,
544 _('show keyword status flags of all files (DEPRECATED)')),
544 _('show keyword status flags of all files (DEPRECATED)')),
545 ('u', 'untracked', None, _('only show untracked files (DEPRECATED)')),
545 ('u', 'untracked', None, _('only show untracked files (DEPRECATED)')),
546 ] + commands.walkopts,
546 ] + commands.walkopts,
547 _('hg kwfiles [OPTION]... [FILE]...')),
547 _('hg kwfiles [OPTION]... [FILE]...')),
548 'kwshrink': (shrink, commands.walkopts,
548 'kwshrink': (shrink, commands.walkopts,
549 _('hg kwshrink [OPTION]... [FILE]...')),
549 _('hg kwshrink [OPTION]... [FILE]...')),
550 }
550 }
@@ -1,2816 +1,2815 b''
1 # mq.py - patch queues for mercurial
1 # mq.py - patch queues for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 '''manage a stack of patches
8 '''manage a stack of patches
9
9
10 This extension lets you work with a stack of patches in a Mercurial
10 This extension lets you work with a stack of patches in a Mercurial
11 repository. It manages two stacks of patches - all known patches, and
11 repository. It manages two stacks of patches - all known patches, and
12 applied patches (subset of known patches).
12 applied patches (subset of known patches).
13
13
14 Known patches are represented as patch files in the .hg/patches
14 Known patches are represented as patch files in the .hg/patches
15 directory. Applied patches are both patch files and changesets.
15 directory. Applied patches are both patch files and changesets.
16
16
17 Common tasks (use "hg help command" for more details)::
17 Common tasks (use "hg help command" for more details)::
18
18
19 prepare repository to work with patches qinit
19 prepare repository to work with patches qinit
20 create new patch qnew
20 create new patch qnew
21 import existing patch qimport
21 import existing patch qimport
22
22
23 print patch series qseries
23 print patch series qseries
24 print applied patches qapplied
24 print applied patches qapplied
25
25
26 add known patch to applied stack qpush
26 add known patch to applied stack qpush
27 remove patch from applied stack qpop
27 remove patch from applied stack qpop
28 refresh contents of top applied patch qrefresh
28 refresh contents of top applied patch qrefresh
29
29
30 By default, mq will automatically use git patches when required to
30 By default, mq will automatically use git patches when required to
31 avoid losing file mode changes, copy records, binary files or empty
31 avoid losing file mode changes, copy records, binary files or empty
32 files creations or deletions. This behaviour can be configured with::
32 files creations or deletions. This behaviour can be configured with::
33
33
34 [mq]
34 [mq]
35 git = auto/keep/yes/no
35 git = auto/keep/yes/no
36
36
37 If set to 'keep', mq will obey the [diff] section configuration while
37 If set to 'keep', mq will obey the [diff] section configuration while
38 preserving existing git patches upon qrefresh. If set to 'yes' or
38 preserving existing git patches upon qrefresh. If set to 'yes' or
39 'no', mq will override the [diff] section and always generate git or
39 'no', mq will override the [diff] section and always generate git or
40 regular patches, possibly losing data in the second case.
40 regular patches, possibly losing data in the second case.
41 '''
41 '''
42
42
43 from mercurial.i18n import _
43 from mercurial.i18n import _
44 from mercurial.node import bin, hex, short, nullid, nullrev
44 from mercurial.node import bin, hex, short, nullid, nullrev
45 from mercurial.lock import release
45 from mercurial.lock import release
46 from mercurial import commands, cmdutil, hg, patch, util
46 from mercurial import commands, cmdutil, hg, patch, util
47 from mercurial import repair, extensions, url, error
47 from mercurial import repair, extensions, url, error
48 import os, sys, re, errno
48 import os, sys, re, errno
49
49
50 commands.norepo += " qclone"
50 commands.norepo += " qclone"
51
51
52 # Patch names looks like unix-file names.
52 # Patch names looks like unix-file names.
53 # They must be joinable with queue directory and result in the patch path.
53 # They must be joinable with queue directory and result in the patch path.
54 normname = util.normpath
54 normname = util.normpath
55
55
56 class statusentry(object):
56 class statusentry(object):
57 def __init__(self, rev, name=None):
57 def __init__(self, rev, name=None):
58 if not name:
58 if not name:
59 fields = rev.split(':', 1)
59 fields = rev.split(':', 1)
60 if len(fields) == 2:
60 if len(fields) == 2:
61 self.rev, self.name = fields
61 self.rev, self.name = fields
62 else:
62 else:
63 self.rev, self.name = None, None
63 self.rev, self.name = None, None
64 else:
64 else:
65 self.rev, self.name = rev, name
65 self.rev, self.name = rev, name
66
66
67 def __str__(self):
67 def __str__(self):
68 return self.rev + ':' + self.name
68 return self.rev + ':' + self.name
69
69
70 class patchheader(object):
70 class patchheader(object):
71 def __init__(self, pf, plainmode=False):
71 def __init__(self, pf, plainmode=False):
72 def eatdiff(lines):
72 def eatdiff(lines):
73 while lines:
73 while lines:
74 l = lines[-1]
74 l = lines[-1]
75 if (l.startswith("diff -") or
75 if (l.startswith("diff -") or
76 l.startswith("Index:") or
76 l.startswith("Index:") or
77 l.startswith("===========")):
77 l.startswith("===========")):
78 del lines[-1]
78 del lines[-1]
79 else:
79 else:
80 break
80 break
81 def eatempty(lines):
81 def eatempty(lines):
82 while lines:
82 while lines:
83 l = lines[-1]
83 l = lines[-1]
84 if re.match('\s*$', l):
84 if re.match('\s*$', l):
85 del lines[-1]
85 del lines[-1]
86 else:
86 else:
87 break
87 break
88
88
89 message = []
89 message = []
90 comments = []
90 comments = []
91 user = None
91 user = None
92 date = None
92 date = None
93 parent = None
93 parent = None
94 format = None
94 format = None
95 subject = None
95 subject = None
96 diffstart = 0
96 diffstart = 0
97
97
98 for line in file(pf):
98 for line in file(pf):
99 line = line.rstrip()
99 line = line.rstrip()
100 if line.startswith('diff --git'):
100 if line.startswith('diff --git'):
101 diffstart = 2
101 diffstart = 2
102 break
102 break
103 if diffstart:
103 if diffstart:
104 if line.startswith('+++ '):
104 if line.startswith('+++ '):
105 diffstart = 2
105 diffstart = 2
106 break
106 break
107 if line.startswith("--- "):
107 if line.startswith("--- "):
108 diffstart = 1
108 diffstart = 1
109 continue
109 continue
110 elif format == "hgpatch":
110 elif format == "hgpatch":
111 # parse values when importing the result of an hg export
111 # parse values when importing the result of an hg export
112 if line.startswith("# User "):
112 if line.startswith("# User "):
113 user = line[7:]
113 user = line[7:]
114 elif line.startswith("# Date "):
114 elif line.startswith("# Date "):
115 date = line[7:]
115 date = line[7:]
116 elif line.startswith("# Parent "):
116 elif line.startswith("# Parent "):
117 parent = line[9:]
117 parent = line[9:]
118 elif not line.startswith("# ") and line:
118 elif not line.startswith("# ") and line:
119 message.append(line)
119 message.append(line)
120 format = None
120 format = None
121 elif line == '# HG changeset patch':
121 elif line == '# HG changeset patch':
122 message = []
122 message = []
123 format = "hgpatch"
123 format = "hgpatch"
124 elif (format != "tagdone" and (line.startswith("Subject: ") or
124 elif (format != "tagdone" and (line.startswith("Subject: ") or
125 line.startswith("subject: "))):
125 line.startswith("subject: "))):
126 subject = line[9:]
126 subject = line[9:]
127 format = "tag"
127 format = "tag"
128 elif (format != "tagdone" and (line.startswith("From: ") or
128 elif (format != "tagdone" and (line.startswith("From: ") or
129 line.startswith("from: "))):
129 line.startswith("from: "))):
130 user = line[6:]
130 user = line[6:]
131 format = "tag"
131 format = "tag"
132 elif (format != "tagdone" and (line.startswith("Date: ") or
132 elif (format != "tagdone" and (line.startswith("Date: ") or
133 line.startswith("date: "))):
133 line.startswith("date: "))):
134 date = line[6:]
134 date = line[6:]
135 format = "tag"
135 format = "tag"
136 elif format == "tag" and line == "":
136 elif format == "tag" and line == "":
137 # when looking for tags (subject: from: etc) they
137 # when looking for tags (subject: from: etc) they
138 # end once you find a blank line in the source
138 # end once you find a blank line in the source
139 format = "tagdone"
139 format = "tagdone"
140 elif message or line:
140 elif message or line:
141 message.append(line)
141 message.append(line)
142 comments.append(line)
142 comments.append(line)
143
143
144 eatdiff(message)
144 eatdiff(message)
145 eatdiff(comments)
145 eatdiff(comments)
146 eatempty(message)
146 eatempty(message)
147 eatempty(comments)
147 eatempty(comments)
148
148
149 # make sure message isn't empty
149 # make sure message isn't empty
150 if format and format.startswith("tag") and subject:
150 if format and format.startswith("tag") and subject:
151 message.insert(0, "")
151 message.insert(0, "")
152 message.insert(0, subject)
152 message.insert(0, subject)
153
153
154 self.message = message
154 self.message = message
155 self.comments = comments
155 self.comments = comments
156 self.user = user
156 self.user = user
157 self.date = date
157 self.date = date
158 self.parent = parent
158 self.parent = parent
159 self.haspatch = diffstart > 1
159 self.haspatch = diffstart > 1
160 self.plainmode = plainmode
160 self.plainmode = plainmode
161
161
162 def setuser(self, user):
162 def setuser(self, user):
163 if not self.updateheader(['From: ', '# User '], user):
163 if not self.updateheader(['From: ', '# User '], user):
164 try:
164 try:
165 patchheaderat = self.comments.index('# HG changeset patch')
165 patchheaderat = self.comments.index('# HG changeset patch')
166 self.comments.insert(patchheaderat + 1, '# User ' + user)
166 self.comments.insert(patchheaderat + 1, '# User ' + user)
167 except ValueError:
167 except ValueError:
168 if self.plainmode or self._hasheader(['Date: ']):
168 if self.plainmode or self._hasheader(['Date: ']):
169 self.comments = ['From: ' + user] + self.comments
169 self.comments = ['From: ' + user] + self.comments
170 else:
170 else:
171 tmp = ['# HG changeset patch', '# User ' + user, '']
171 tmp = ['# HG changeset patch', '# User ' + user, '']
172 self.comments = tmp + self.comments
172 self.comments = tmp + self.comments
173 self.user = user
173 self.user = user
174
174
175 def setdate(self, date):
175 def setdate(self, date):
176 if not self.updateheader(['Date: ', '# Date '], date):
176 if not self.updateheader(['Date: ', '# Date '], date):
177 try:
177 try:
178 patchheaderat = self.comments.index('# HG changeset patch')
178 patchheaderat = self.comments.index('# HG changeset patch')
179 self.comments.insert(patchheaderat + 1, '# Date ' + date)
179 self.comments.insert(patchheaderat + 1, '# Date ' + date)
180 except ValueError:
180 except ValueError:
181 if self.plainmode or self._hasheader(['From: ']):
181 if self.plainmode or self._hasheader(['From: ']):
182 self.comments = ['Date: ' + date] + self.comments
182 self.comments = ['Date: ' + date] + self.comments
183 else:
183 else:
184 tmp = ['# HG changeset patch', '# Date ' + date, '']
184 tmp = ['# HG changeset patch', '# Date ' + date, '']
185 self.comments = tmp + self.comments
185 self.comments = tmp + self.comments
186 self.date = date
186 self.date = date
187
187
188 def setparent(self, parent):
188 def setparent(self, parent):
189 if not self.updateheader(['# Parent '], parent):
189 if not self.updateheader(['# Parent '], parent):
190 try:
190 try:
191 patchheaderat = self.comments.index('# HG changeset patch')
191 patchheaderat = self.comments.index('# HG changeset patch')
192 self.comments.insert(patchheaderat + 1, '# Parent ' + parent)
192 self.comments.insert(patchheaderat + 1, '# Parent ' + parent)
193 except ValueError:
193 except ValueError:
194 pass
194 pass
195 self.parent = parent
195 self.parent = parent
196
196
197 def setmessage(self, message):
197 def setmessage(self, message):
198 if self.comments:
198 if self.comments:
199 self._delmsg()
199 self._delmsg()
200 self.message = [message]
200 self.message = [message]
201 self.comments += self.message
201 self.comments += self.message
202
202
203 def updateheader(self, prefixes, new):
203 def updateheader(self, prefixes, new):
204 '''Update all references to a field in the patch header.
204 '''Update all references to a field in the patch header.
205 Return whether the field is present.'''
205 Return whether the field is present.'''
206 res = False
206 res = False
207 for prefix in prefixes:
207 for prefix in prefixes:
208 for i in xrange(len(self.comments)):
208 for i in xrange(len(self.comments)):
209 if self.comments[i].startswith(prefix):
209 if self.comments[i].startswith(prefix):
210 self.comments[i] = prefix + new
210 self.comments[i] = prefix + new
211 res = True
211 res = True
212 break
212 break
213 return res
213 return res
214
214
215 def _hasheader(self, prefixes):
215 def _hasheader(self, prefixes):
216 '''Check if a header starts with any of the given prefixes.'''
216 '''Check if a header starts with any of the given prefixes.'''
217 for prefix in prefixes:
217 for prefix in prefixes:
218 for comment in self.comments:
218 for comment in self.comments:
219 if comment.startswith(prefix):
219 if comment.startswith(prefix):
220 return True
220 return True
221 return False
221 return False
222
222
223 def __str__(self):
223 def __str__(self):
224 if not self.comments:
224 if not self.comments:
225 return ''
225 return ''
226 return '\n'.join(self.comments) + '\n\n'
226 return '\n'.join(self.comments) + '\n\n'
227
227
228 def _delmsg(self):
228 def _delmsg(self):
229 '''Remove existing message, keeping the rest of the comments fields.
229 '''Remove existing message, keeping the rest of the comments fields.
230 If comments contains 'subject: ', message will prepend
230 If comments contains 'subject: ', message will prepend
231 the field and a blank line.'''
231 the field and a blank line.'''
232 if self.message:
232 if self.message:
233 subj = 'subject: ' + self.message[0].lower()
233 subj = 'subject: ' + self.message[0].lower()
234 for i in xrange(len(self.comments)):
234 for i in xrange(len(self.comments)):
235 if subj == self.comments[i].lower():
235 if subj == self.comments[i].lower():
236 del self.comments[i]
236 del self.comments[i]
237 self.message = self.message[2:]
237 self.message = self.message[2:]
238 break
238 break
239 ci = 0
239 ci = 0
240 for mi in self.message:
240 for mi in self.message:
241 while mi != self.comments[ci]:
241 while mi != self.comments[ci]:
242 ci += 1
242 ci += 1
243 del self.comments[ci]
243 del self.comments[ci]
244
244
245 class queue(object):
245 class queue(object):
246 def __init__(self, ui, path, patchdir=None):
246 def __init__(self, ui, path, patchdir=None):
247 self.basepath = path
247 self.basepath = path
248 self.path = patchdir or os.path.join(path, "patches")
248 self.path = patchdir or os.path.join(path, "patches")
249 self.opener = util.opener(self.path)
249 self.opener = util.opener(self.path)
250 self.ui = ui
250 self.ui = ui
251 self.applied_dirty = 0
251 self.applied_dirty = 0
252 self.series_dirty = 0
252 self.series_dirty = 0
253 self.series_path = "series"
253 self.series_path = "series"
254 self.status_path = "status"
254 self.status_path = "status"
255 self.guards_path = "guards"
255 self.guards_path = "guards"
256 self.active_guards = None
256 self.active_guards = None
257 self.guards_dirty = False
257 self.guards_dirty = False
258 # Handle mq.git as a bool with extended values
258 # Handle mq.git as a bool with extended values
259 try:
259 try:
260 gitmode = ui.configbool('mq', 'git', None)
260 gitmode = ui.configbool('mq', 'git', None)
261 if gitmode is None:
261 if gitmode is None:
262 raise error.ConfigError()
262 raise error.ConfigError()
263 self.gitmode = gitmode and 'yes' or 'no'
263 self.gitmode = gitmode and 'yes' or 'no'
264 except error.ConfigError:
264 except error.ConfigError:
265 self.gitmode = ui.config('mq', 'git', 'auto').lower()
265 self.gitmode = ui.config('mq', 'git', 'auto').lower()
266 self.plainmode = ui.configbool('mq', 'plain', False)
266 self.plainmode = ui.configbool('mq', 'plain', False)
267
267
268 @util.propertycache
268 @util.propertycache
269 def applied(self):
269 def applied(self):
270 if os.path.exists(self.join(self.status_path)):
270 if os.path.exists(self.join(self.status_path)):
271 lines = self.opener(self.status_path).read().splitlines()
271 lines = self.opener(self.status_path).read().splitlines()
272 return [statusentry(l) for l in lines]
272 return [statusentry(l) for l in lines]
273 return []
273 return []
274
274
275 @util.propertycache
275 @util.propertycache
276 def full_series(self):
276 def full_series(self):
277 if os.path.exists(self.join(self.series_path)):
277 if os.path.exists(self.join(self.series_path)):
278 return self.opener(self.series_path).read().splitlines()
278 return self.opener(self.series_path).read().splitlines()
279 return []
279 return []
280
280
281 @util.propertycache
281 @util.propertycache
282 def series(self):
282 def series(self):
283 self.parse_series()
283 self.parse_series()
284 return self.series
284 return self.series
285
285
286 @util.propertycache
286 @util.propertycache
287 def series_guards(self):
287 def series_guards(self):
288 self.parse_series()
288 self.parse_series()
289 return self.series_guards
289 return self.series_guards
290
290
291 def invalidate(self):
291 def invalidate(self):
292 for a in 'applied full_series series series_guards'.split():
292 for a in 'applied full_series series series_guards'.split():
293 if a in self.__dict__:
293 if a in self.__dict__:
294 delattr(self, a)
294 delattr(self, a)
295 self.applied_dirty = 0
295 self.applied_dirty = 0
296 self.series_dirty = 0
296 self.series_dirty = 0
297 self.guards_dirty = False
297 self.guards_dirty = False
298 self.active_guards = None
298 self.active_guards = None
299
299
300 def diffopts(self, opts={}, patchfn=None):
300 def diffopts(self, opts={}, patchfn=None):
301 diffopts = patch.diffopts(self.ui, opts)
301 diffopts = patch.diffopts(self.ui, opts)
302 if self.gitmode == 'auto':
302 if self.gitmode == 'auto':
303 diffopts.upgrade = True
303 diffopts.upgrade = True
304 elif self.gitmode == 'keep':
304 elif self.gitmode == 'keep':
305 pass
305 pass
306 elif self.gitmode in ('yes', 'no'):
306 elif self.gitmode in ('yes', 'no'):
307 diffopts.git = self.gitmode == 'yes'
307 diffopts.git = self.gitmode == 'yes'
308 else:
308 else:
309 raise util.Abort(_('mq.git option can be auto/keep/yes/no'
309 raise util.Abort(_('mq.git option can be auto/keep/yes/no'
310 ' got %s') % self.gitmode)
310 ' got %s') % self.gitmode)
311 if patchfn:
311 if patchfn:
312 diffopts = self.patchopts(diffopts, patchfn)
312 diffopts = self.patchopts(diffopts, patchfn)
313 return diffopts
313 return diffopts
314
314
315 def patchopts(self, diffopts, *patches):
315 def patchopts(self, diffopts, *patches):
316 """Return a copy of input diff options with git set to true if
316 """Return a copy of input diff options with git set to true if
317 referenced patch is a git patch and should be preserved as such.
317 referenced patch is a git patch and should be preserved as such.
318 """
318 """
319 diffopts = diffopts.copy()
319 diffopts = diffopts.copy()
320 if not diffopts.git and self.gitmode == 'keep':
320 if not diffopts.git and self.gitmode == 'keep':
321 for patchfn in patches:
321 for patchfn in patches:
322 patchf = self.opener(patchfn, 'r')
322 patchf = self.opener(patchfn, 'r')
323 # if the patch was a git patch, refresh it as a git patch
323 # if the patch was a git patch, refresh it as a git patch
324 for line in patchf:
324 for line in patchf:
325 if line.startswith('diff --git'):
325 if line.startswith('diff --git'):
326 diffopts.git = True
326 diffopts.git = True
327 break
327 break
328 patchf.close()
328 patchf.close()
329 return diffopts
329 return diffopts
330
330
331 def join(self, *p):
331 def join(self, *p):
332 return os.path.join(self.path, *p)
332 return os.path.join(self.path, *p)
333
333
334 def find_series(self, patch):
334 def find_series(self, patch):
335 pre = re.compile("(\s*)([^#]+)")
335 pre = re.compile("(\s*)([^#]+)")
336 index = 0
336 index = 0
337 for l in self.full_series:
337 for l in self.full_series:
338 m = pre.match(l)
338 m = pre.match(l)
339 if m:
339 if m:
340 s = m.group(2)
340 s = m.group(2)
341 s = s.rstrip()
341 s = s.rstrip()
342 if s == patch:
342 if s == patch:
343 return index
343 return index
344 index += 1
344 index += 1
345 return None
345 return None
346
346
347 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
347 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
348
348
349 def parse_series(self):
349 def parse_series(self):
350 self.series = []
350 self.series = []
351 self.series_guards = []
351 self.series_guards = []
352 for l in self.full_series:
352 for l in self.full_series:
353 h = l.find('#')
353 h = l.find('#')
354 if h == -1:
354 if h == -1:
355 patch = l
355 patch = l
356 comment = ''
356 comment = ''
357 elif h == 0:
357 elif h == 0:
358 continue
358 continue
359 else:
359 else:
360 patch = l[:h]
360 patch = l[:h]
361 comment = l[h:]
361 comment = l[h:]
362 patch = patch.strip()
362 patch = patch.strip()
363 if patch:
363 if patch:
364 if patch in self.series:
364 if patch in self.series:
365 raise util.Abort(_('%s appears more than once in %s') %
365 raise util.Abort(_('%s appears more than once in %s') %
366 (patch, self.join(self.series_path)))
366 (patch, self.join(self.series_path)))
367 self.series.append(patch)
367 self.series.append(patch)
368 self.series_guards.append(self.guard_re.findall(comment))
368 self.series_guards.append(self.guard_re.findall(comment))
369
369
370 def check_guard(self, guard):
370 def check_guard(self, guard):
371 if not guard:
371 if not guard:
372 return _('guard cannot be an empty string')
372 return _('guard cannot be an empty string')
373 bad_chars = '# \t\r\n\f'
373 bad_chars = '# \t\r\n\f'
374 first = guard[0]
374 first = guard[0]
375 if first in '-+':
375 if first in '-+':
376 return (_('guard %r starts with invalid character: %r') %
376 return (_('guard %r starts with invalid character: %r') %
377 (guard, first))
377 (guard, first))
378 for c in bad_chars:
378 for c in bad_chars:
379 if c in guard:
379 if c in guard:
380 return _('invalid character in guard %r: %r') % (guard, c)
380 return _('invalid character in guard %r: %r') % (guard, c)
381
381
382 def set_active(self, guards):
382 def set_active(self, guards):
383 for guard in guards:
383 for guard in guards:
384 bad = self.check_guard(guard)
384 bad = self.check_guard(guard)
385 if bad:
385 if bad:
386 raise util.Abort(bad)
386 raise util.Abort(bad)
387 guards = sorted(set(guards))
387 guards = sorted(set(guards))
388 self.ui.debug('active guards: %s\n' % ' '.join(guards))
388 self.ui.debug('active guards: %s\n' % ' '.join(guards))
389 self.active_guards = guards
389 self.active_guards = guards
390 self.guards_dirty = True
390 self.guards_dirty = True
391
391
392 def active(self):
392 def active(self):
393 if self.active_guards is None:
393 if self.active_guards is None:
394 self.active_guards = []
394 self.active_guards = []
395 try:
395 try:
396 guards = self.opener(self.guards_path).read().split()
396 guards = self.opener(self.guards_path).read().split()
397 except IOError, err:
397 except IOError, err:
398 if err.errno != errno.ENOENT:
398 if err.errno != errno.ENOENT:
399 raise
399 raise
400 guards = []
400 guards = []
401 for i, guard in enumerate(guards):
401 for i, guard in enumerate(guards):
402 bad = self.check_guard(guard)
402 bad = self.check_guard(guard)
403 if bad:
403 if bad:
404 self.ui.warn('%s:%d: %s\n' %
404 self.ui.warn('%s:%d: %s\n' %
405 (self.join(self.guards_path), i + 1, bad))
405 (self.join(self.guards_path), i + 1, bad))
406 else:
406 else:
407 self.active_guards.append(guard)
407 self.active_guards.append(guard)
408 return self.active_guards
408 return self.active_guards
409
409
410 def set_guards(self, idx, guards):
410 def set_guards(self, idx, guards):
411 for g in guards:
411 for g in guards:
412 if len(g) < 2:
412 if len(g) < 2:
413 raise util.Abort(_('guard %r too short') % g)
413 raise util.Abort(_('guard %r too short') % g)
414 if g[0] not in '-+':
414 if g[0] not in '-+':
415 raise util.Abort(_('guard %r starts with invalid char') % g)
415 raise util.Abort(_('guard %r starts with invalid char') % g)
416 bad = self.check_guard(g[1:])
416 bad = self.check_guard(g[1:])
417 if bad:
417 if bad:
418 raise util.Abort(bad)
418 raise util.Abort(bad)
419 drop = self.guard_re.sub('', self.full_series[idx])
419 drop = self.guard_re.sub('', self.full_series[idx])
420 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
420 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
421 self.parse_series()
421 self.parse_series()
422 self.series_dirty = True
422 self.series_dirty = True
423
423
424 def pushable(self, idx):
424 def pushable(self, idx):
425 if isinstance(idx, str):
425 if isinstance(idx, str):
426 idx = self.series.index(idx)
426 idx = self.series.index(idx)
427 patchguards = self.series_guards[idx]
427 patchguards = self.series_guards[idx]
428 if not patchguards:
428 if not patchguards:
429 return True, None
429 return True, None
430 guards = self.active()
430 guards = self.active()
431 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
431 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
432 if exactneg:
432 if exactneg:
433 return False, exactneg[0]
433 return False, exactneg[0]
434 pos = [g for g in patchguards if g[0] == '+']
434 pos = [g for g in patchguards if g[0] == '+']
435 exactpos = [g for g in pos if g[1:] in guards]
435 exactpos = [g for g in pos if g[1:] in guards]
436 if pos:
436 if pos:
437 if exactpos:
437 if exactpos:
438 return True, exactpos[0]
438 return True, exactpos[0]
439 return False, pos
439 return False, pos
440 return True, ''
440 return True, ''
441
441
442 def explain_pushable(self, idx, all_patches=False):
442 def explain_pushable(self, idx, all_patches=False):
443 write = all_patches and self.ui.write or self.ui.warn
443 write = all_patches and self.ui.write or self.ui.warn
444 if all_patches or self.ui.verbose:
444 if all_patches or self.ui.verbose:
445 if isinstance(idx, str):
445 if isinstance(idx, str):
446 idx = self.series.index(idx)
446 idx = self.series.index(idx)
447 pushable, why = self.pushable(idx)
447 pushable, why = self.pushable(idx)
448 if all_patches and pushable:
448 if all_patches and pushable:
449 if why is None:
449 if why is None:
450 write(_('allowing %s - no guards in effect\n') %
450 write(_('allowing %s - no guards in effect\n') %
451 self.series[idx])
451 self.series[idx])
452 else:
452 else:
453 if not why:
453 if not why:
454 write(_('allowing %s - no matching negative guards\n') %
454 write(_('allowing %s - no matching negative guards\n') %
455 self.series[idx])
455 self.series[idx])
456 else:
456 else:
457 write(_('allowing %s - guarded by %r\n') %
457 write(_('allowing %s - guarded by %r\n') %
458 (self.series[idx], why))
458 (self.series[idx], why))
459 if not pushable:
459 if not pushable:
460 if why:
460 if why:
461 write(_('skipping %s - guarded by %r\n') %
461 write(_('skipping %s - guarded by %r\n') %
462 (self.series[idx], why))
462 (self.series[idx], why))
463 else:
463 else:
464 write(_('skipping %s - no matching guards\n') %
464 write(_('skipping %s - no matching guards\n') %
465 self.series[idx])
465 self.series[idx])
466
466
467 def save_dirty(self):
467 def save_dirty(self):
468 def write_list(items, path):
468 def write_list(items, path):
469 fp = self.opener(path, 'w')
469 fp = self.opener(path, 'w')
470 for i in items:
470 for i in items:
471 fp.write("%s\n" % i)
471 fp.write("%s\n" % i)
472 fp.close()
472 fp.close()
473 if self.applied_dirty:
473 if self.applied_dirty:
474 write_list(map(str, self.applied), self.status_path)
474 write_list(map(str, self.applied), self.status_path)
475 if self.series_dirty:
475 if self.series_dirty:
476 write_list(self.full_series, self.series_path)
476 write_list(self.full_series, self.series_path)
477 if self.guards_dirty:
477 if self.guards_dirty:
478 write_list(self.active_guards, self.guards_path)
478 write_list(self.active_guards, self.guards_path)
479
479
480 def removeundo(self, repo):
480 def removeundo(self, repo):
481 undo = repo.sjoin('undo')
481 undo = repo.sjoin('undo')
482 if not os.path.exists(undo):
482 if not os.path.exists(undo):
483 return
483 return
484 try:
484 try:
485 os.unlink(undo)
485 os.unlink(undo)
486 except OSError, inst:
486 except OSError, inst:
487 self.ui.warn(_('error removing undo: %s\n') % str(inst))
487 self.ui.warn(_('error removing undo: %s\n') % str(inst))
488
488
489 def printdiff(self, repo, diffopts, node1, node2=None, files=None,
489 def printdiff(self, repo, diffopts, node1, node2=None, files=None,
490 fp=None, changes=None, opts={}):
490 fp=None, changes=None, opts={}):
491 stat = opts.get('stat')
491 stat = opts.get('stat')
492 if stat:
492 if stat:
493 opts['unified'] = '0'
493 opts['unified'] = '0'
494
494
495 m = cmdutil.match(repo, files, opts)
495 m = cmdutil.match(repo, files, opts)
496 chunks = patch.diff(repo, node1, node2, m, changes, diffopts)
496 chunks = patch.diff(repo, node1, node2, m, changes, diffopts)
497 write = fp is None and repo.ui.write or fp.write
497 write = fp is None and repo.ui.write or fp.write
498 if stat:
498 if stat:
499 width = self.ui.interactive() and util.termwidth() or 80
499 width = self.ui.interactive() and util.termwidth() or 80
500 write(patch.diffstat(util.iterlines(chunks), width=width,
500 write(patch.diffstat(util.iterlines(chunks), width=width,
501 git=diffopts.git))
501 git=diffopts.git))
502 else:
502 else:
503 for chunk in chunks:
503 for chunk in chunks:
504 write(chunk)
504 write(chunk)
505
505
506 def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
506 def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
507 # first try just applying the patch
507 # first try just applying the patch
508 (err, n) = self.apply(repo, [patch], update_status=False,
508 (err, n) = self.apply(repo, [patch], update_status=False,
509 strict=True, merge=rev)
509 strict=True, merge=rev)
510
510
511 if err == 0:
511 if err == 0:
512 return (err, n)
512 return (err, n)
513
513
514 if n is None:
514 if n is None:
515 raise util.Abort(_("apply failed for patch %s") % patch)
515 raise util.Abort(_("apply failed for patch %s") % patch)
516
516
517 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
517 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
518
518
519 # apply failed, strip away that rev and merge.
519 # apply failed, strip away that rev and merge.
520 hg.clean(repo, head)
520 hg.clean(repo, head)
521 self.strip(repo, n, update=False, backup='strip')
521 self.strip(repo, n, update=False, backup='strip')
522
522
523 ctx = repo[rev]
523 ctx = repo[rev]
524 ret = hg.merge(repo, rev)
524 ret = hg.merge(repo, rev)
525 if ret:
525 if ret:
526 raise util.Abort(_("update returned %d") % ret)
526 raise util.Abort(_("update returned %d") % ret)
527 n = repo.commit(ctx.description(), ctx.user(), force=True)
527 n = repo.commit(ctx.description(), ctx.user(), force=True)
528 if n is None:
528 if n is None:
529 raise util.Abort(_("repo commit failed"))
529 raise util.Abort(_("repo commit failed"))
530 try:
530 try:
531 ph = patchheader(mergeq.join(patch), self.plainmode)
531 ph = patchheader(mergeq.join(patch), self.plainmode)
532 except:
532 except:
533 raise util.Abort(_("unable to read %s") % patch)
533 raise util.Abort(_("unable to read %s") % patch)
534
534
535 diffopts = self.patchopts(diffopts, patch)
535 diffopts = self.patchopts(diffopts, patch)
536 patchf = self.opener(patch, "w")
536 patchf = self.opener(patch, "w")
537 comments = str(ph)
537 comments = str(ph)
538 if comments:
538 if comments:
539 patchf.write(comments)
539 patchf.write(comments)
540 self.printdiff(repo, diffopts, head, n, fp=patchf)
540 self.printdiff(repo, diffopts, head, n, fp=patchf)
541 patchf.close()
541 patchf.close()
542 self.removeundo(repo)
542 self.removeundo(repo)
543 return (0, n)
543 return (0, n)
544
544
545 def qparents(self, repo, rev=None):
545 def qparents(self, repo, rev=None):
546 if rev is None:
546 if rev is None:
547 (p1, p2) = repo.dirstate.parents()
547 (p1, p2) = repo.dirstate.parents()
548 if p2 == nullid:
548 if p2 == nullid:
549 return p1
549 return p1
550 if len(self.applied) == 0:
550 if len(self.applied) == 0:
551 return None
551 return None
552 return bin(self.applied[-1].rev)
552 return bin(self.applied[-1].rev)
553 pp = repo.changelog.parents(rev)
553 pp = repo.changelog.parents(rev)
554 if pp[1] != nullid:
554 if pp[1] != nullid:
555 arevs = [x.rev for x in self.applied]
555 arevs = [x.rev for x in self.applied]
556 p0 = hex(pp[0])
556 p0 = hex(pp[0])
557 p1 = hex(pp[1])
557 p1 = hex(pp[1])
558 if p0 in arevs:
558 if p0 in arevs:
559 return pp[0]
559 return pp[0]
560 if p1 in arevs:
560 if p1 in arevs:
561 return pp[1]
561 return pp[1]
562 return pp[0]
562 return pp[0]
563
563
564 def mergepatch(self, repo, mergeq, series, diffopts):
564 def mergepatch(self, repo, mergeq, series, diffopts):
565 if len(self.applied) == 0:
565 if len(self.applied) == 0:
566 # each of the patches merged in will have two parents. This
566 # each of the patches merged in will have two parents. This
567 # can confuse the qrefresh, qdiff, and strip code because it
567 # can confuse the qrefresh, qdiff, and strip code because it
568 # needs to know which parent is actually in the patch queue.
568 # needs to know which parent is actually in the patch queue.
569 # so, we insert a merge marker with only one parent. This way
569 # so, we insert a merge marker with only one parent. This way
570 # the first patch in the queue is never a merge patch
570 # the first patch in the queue is never a merge patch
571 #
571 #
572 pname = ".hg.patches.merge.marker"
572 pname = ".hg.patches.merge.marker"
573 n = repo.commit('[mq]: merge marker', force=True)
573 n = repo.commit('[mq]: merge marker', force=True)
574 self.removeundo(repo)
574 self.removeundo(repo)
575 self.applied.append(statusentry(hex(n), pname))
575 self.applied.append(statusentry(hex(n), pname))
576 self.applied_dirty = 1
576 self.applied_dirty = 1
577
577
578 head = self.qparents(repo)
578 head = self.qparents(repo)
579
579
580 for patch in series:
580 for patch in series:
581 patch = mergeq.lookup(patch, strict=True)
581 patch = mergeq.lookup(patch, strict=True)
582 if not patch:
582 if not patch:
583 self.ui.warn(_("patch %s does not exist\n") % patch)
583 self.ui.warn(_("patch %s does not exist\n") % patch)
584 return (1, None)
584 return (1, None)
585 pushable, reason = self.pushable(patch)
585 pushable, reason = self.pushable(patch)
586 if not pushable:
586 if not pushable:
587 self.explain_pushable(patch, all_patches=True)
587 self.explain_pushable(patch, all_patches=True)
588 continue
588 continue
589 info = mergeq.isapplied(patch)
589 info = mergeq.isapplied(patch)
590 if not info:
590 if not info:
591 self.ui.warn(_("patch %s is not applied\n") % patch)
591 self.ui.warn(_("patch %s is not applied\n") % patch)
592 return (1, None)
592 return (1, None)
593 rev = bin(info[1])
593 rev = bin(info[1])
594 err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
594 err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
595 if head:
595 if head:
596 self.applied.append(statusentry(hex(head), patch))
596 self.applied.append(statusentry(hex(head), patch))
597 self.applied_dirty = 1
597 self.applied_dirty = 1
598 if err:
598 if err:
599 return (err, head)
599 return (err, head)
600 self.save_dirty()
600 self.save_dirty()
601 return (0, head)
601 return (0, head)
602
602
603 def patch(self, repo, patchfile):
603 def patch(self, repo, patchfile):
604 '''Apply patchfile to the working directory.
604 '''Apply patchfile to the working directory.
605 patchfile: name of patch file'''
605 patchfile: name of patch file'''
606 files = {}
606 files = {}
607 try:
607 try:
608 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
608 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
609 files=files, eolmode=None)
609 files=files, eolmode=None)
610 except Exception, inst:
610 except Exception, inst:
611 self.ui.note(str(inst) + '\n')
611 self.ui.note(str(inst) + '\n')
612 if not self.ui.verbose:
612 if not self.ui.verbose:
613 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
613 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
614 return (False, files, False)
614 return (False, files, False)
615
615
616 return (True, files, fuzz)
616 return (True, files, fuzz)
617
617
618 def apply(self, repo, series, list=False, update_status=True,
618 def apply(self, repo, series, list=False, update_status=True,
619 strict=False, patchdir=None, merge=None, all_files={}):
619 strict=False, patchdir=None, merge=None, all_files={}):
620 wlock = lock = tr = None
620 wlock = lock = tr = None
621 try:
621 try:
622 wlock = repo.wlock()
622 wlock = repo.wlock()
623 lock = repo.lock()
623 lock = repo.lock()
624 tr = repo.transaction()
624 tr = repo.transaction()
625 try:
625 try:
626 ret = self._apply(repo, series, list, update_status,
626 ret = self._apply(repo, series, list, update_status,
627 strict, patchdir, merge, all_files=all_files)
627 strict, patchdir, merge, all_files=all_files)
628 tr.close()
628 tr.close()
629 self.save_dirty()
629 self.save_dirty()
630 return ret
630 return ret
631 except:
631 except:
632 try:
632 try:
633 tr.abort()
633 tr.abort()
634 finally:
634 finally:
635 repo.invalidate()
635 repo.invalidate()
636 repo.dirstate.invalidate()
636 repo.dirstate.invalidate()
637 raise
637 raise
638 finally:
638 finally:
639 del tr
639 del tr
640 release(lock, wlock)
640 release(lock, wlock)
641 self.removeundo(repo)
641 self.removeundo(repo)
642
642
643 def _apply(self, repo, series, list=False, update_status=True,
643 def _apply(self, repo, series, list=False, update_status=True,
644 strict=False, patchdir=None, merge=None, all_files={}):
644 strict=False, patchdir=None, merge=None, all_files={}):
645 '''returns (error, hash)
645 '''returns (error, hash)
646 error = 1 for unable to read, 2 for patch failed, 3 for patch fuzz'''
646 error = 1 for unable to read, 2 for patch failed, 3 for patch fuzz'''
647 # TODO unify with commands.py
647 # TODO unify with commands.py
648 if not patchdir:
648 if not patchdir:
649 patchdir = self.path
649 patchdir = self.path
650 err = 0
650 err = 0
651 n = None
651 n = None
652 for patchname in series:
652 for patchname in series:
653 pushable, reason = self.pushable(patchname)
653 pushable, reason = self.pushable(patchname)
654 if not pushable:
654 if not pushable:
655 self.explain_pushable(patchname, all_patches=True)
655 self.explain_pushable(patchname, all_patches=True)
656 continue
656 continue
657 self.ui.status(_("applying %s\n") % patchname)
657 self.ui.status(_("applying %s\n") % patchname)
658 pf = os.path.join(patchdir, patchname)
658 pf = os.path.join(patchdir, patchname)
659
659
660 try:
660 try:
661 ph = patchheader(self.join(patchname), self.plainmode)
661 ph = patchheader(self.join(patchname), self.plainmode)
662 except:
662 except:
663 self.ui.warn(_("unable to read %s\n") % patchname)
663 self.ui.warn(_("unable to read %s\n") % patchname)
664 err = 1
664 err = 1
665 break
665 break
666
666
667 message = ph.message
667 message = ph.message
668 if not message:
668 if not message:
669 message = "imported patch %s\n" % patchname
669 message = "imported patch %s\n" % patchname
670 else:
670 else:
671 if list:
671 if list:
672 message.append("\nimported patch %s" % patchname)
672 message.append("\nimported patch %s" % patchname)
673 message = '\n'.join(message)
673 message = '\n'.join(message)
674
674
675 if ph.haspatch:
675 if ph.haspatch:
676 (patcherr, files, fuzz) = self.patch(repo, pf)
676 (patcherr, files, fuzz) = self.patch(repo, pf)
677 all_files.update(files)
677 all_files.update(files)
678 patcherr = not patcherr
678 patcherr = not patcherr
679 else:
679 else:
680 self.ui.warn(_("patch %s is empty\n") % patchname)
680 self.ui.warn(_("patch %s is empty\n") % patchname)
681 patcherr, files, fuzz = 0, [], 0
681 patcherr, files, fuzz = 0, [], 0
682
682
683 if merge and files:
683 if merge and files:
684 # Mark as removed/merged and update dirstate parent info
684 # Mark as removed/merged and update dirstate parent info
685 removed = []
685 removed = []
686 merged = []
686 merged = []
687 for f in files:
687 for f in files:
688 if os.path.exists(repo.wjoin(f)):
688 if os.path.exists(repo.wjoin(f)):
689 merged.append(f)
689 merged.append(f)
690 else:
690 else:
691 removed.append(f)
691 removed.append(f)
692 for f in removed:
692 for f in removed:
693 repo.dirstate.remove(f)
693 repo.dirstate.remove(f)
694 for f in merged:
694 for f in merged:
695 repo.dirstate.merge(f)
695 repo.dirstate.merge(f)
696 p1, p2 = repo.dirstate.parents()
696 p1, p2 = repo.dirstate.parents()
697 repo.dirstate.setparents(p1, merge)
697 repo.dirstate.setparents(p1, merge)
698
698
699 files = patch.updatedir(self.ui, repo, files)
699 files = patch.updatedir(self.ui, repo, files)
700 match = cmdutil.matchfiles(repo, files or [])
700 match = cmdutil.matchfiles(repo, files or [])
701 n = repo.commit(message, ph.user, ph.date, match=match, force=True)
701 n = repo.commit(message, ph.user, ph.date, match=match, force=True)
702
702
703 if n is None:
703 if n is None:
704 raise util.Abort(_("repo commit failed"))
704 raise util.Abort(_("repo commit failed"))
705
705
706 if update_status:
706 if update_status:
707 self.applied.append(statusentry(hex(n), patchname))
707 self.applied.append(statusentry(hex(n), patchname))
708
708
709 if patcherr:
709 if patcherr:
710 self.ui.warn(_("patch failed, rejects left in working dir\n"))
710 self.ui.warn(_("patch failed, rejects left in working dir\n"))
711 err = 2
711 err = 2
712 break
712 break
713
713
714 if fuzz and strict:
714 if fuzz and strict:
715 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
715 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
716 err = 3
716 err = 3
717 break
717 break
718 return (err, n)
718 return (err, n)
719
719
720 def _cleanup(self, patches, numrevs, keep=False):
720 def _cleanup(self, patches, numrevs, keep=False):
721 if not keep:
721 if not keep:
722 r = self.qrepo()
722 r = self.qrepo()
723 if r:
723 if r:
724 r.remove(patches, True)
724 r.remove(patches, True)
725 else:
725 else:
726 for p in patches:
726 for p in patches:
727 os.unlink(self.join(p))
727 os.unlink(self.join(p))
728
728
729 if numrevs:
729 if numrevs:
730 del self.applied[:numrevs]
730 del self.applied[:numrevs]
731 self.applied_dirty = 1
731 self.applied_dirty = 1
732
732
733 for i in sorted([self.find_series(p) for p in patches], reverse=True):
733 for i in sorted([self.find_series(p) for p in patches], reverse=True):
734 del self.full_series[i]
734 del self.full_series[i]
735 self.parse_series()
735 self.parse_series()
736 self.series_dirty = 1
736 self.series_dirty = 1
737
737
738 def _revpatches(self, repo, revs):
738 def _revpatches(self, repo, revs):
739 firstrev = repo[self.applied[0].rev].rev()
739 firstrev = repo[self.applied[0].rev].rev()
740 patches = []
740 patches = []
741 for i, rev in enumerate(revs):
741 for i, rev in enumerate(revs):
742
742
743 if rev < firstrev:
743 if rev < firstrev:
744 raise util.Abort(_('revision %d is not managed') % rev)
744 raise util.Abort(_('revision %d is not managed') % rev)
745
745
746 ctx = repo[rev]
746 ctx = repo[rev]
747 base = bin(self.applied[i].rev)
747 base = bin(self.applied[i].rev)
748 if ctx.node() != base:
748 if ctx.node() != base:
749 msg = _('cannot delete revision %d above applied patches')
749 msg = _('cannot delete revision %d above applied patches')
750 raise util.Abort(msg % rev)
750 raise util.Abort(msg % rev)
751
751
752 patch = self.applied[i].name
752 patch = self.applied[i].name
753 for fmt in ('[mq]: %s', 'imported patch %s'):
753 for fmt in ('[mq]: %s', 'imported patch %s'):
754 if ctx.description() == fmt % patch:
754 if ctx.description() == fmt % patch:
755 msg = _('patch %s finalized without changeset message\n')
755 msg = _('patch %s finalized without changeset message\n')
756 repo.ui.status(msg % patch)
756 repo.ui.status(msg % patch)
757 break
757 break
758
758
759 patches.append(patch)
759 patches.append(patch)
760 return patches
760 return patches
761
761
762 def finish(self, repo, revs):
762 def finish(self, repo, revs):
763 patches = self._revpatches(repo, sorted(revs))
763 patches = self._revpatches(repo, sorted(revs))
764 self._cleanup(patches, len(patches))
764 self._cleanup(patches, len(patches))
765
765
766 def delete(self, repo, patches, opts):
766 def delete(self, repo, patches, opts):
767 if not patches and not opts.get('rev'):
767 if not patches and not opts.get('rev'):
768 raise util.Abort(_('qdelete requires at least one revision or '
768 raise util.Abort(_('qdelete requires at least one revision or '
769 'patch name'))
769 'patch name'))
770
770
771 realpatches = []
771 realpatches = []
772 for patch in patches:
772 for patch in patches:
773 patch = self.lookup(patch, strict=True)
773 patch = self.lookup(patch, strict=True)
774 info = self.isapplied(patch)
774 info = self.isapplied(patch)
775 if info:
775 if info:
776 raise util.Abort(_("cannot delete applied patch %s") % patch)
776 raise util.Abort(_("cannot delete applied patch %s") % patch)
777 if patch not in self.series:
777 if patch not in self.series:
778 raise util.Abort(_("patch %s not in series file") % patch)
778 raise util.Abort(_("patch %s not in series file") % patch)
779 realpatches.append(patch)
779 realpatches.append(patch)
780
780
781 numrevs = 0
781 numrevs = 0
782 if opts.get('rev'):
782 if opts.get('rev'):
783 if not self.applied:
783 if not self.applied:
784 raise util.Abort(_('no patches applied'))
784 raise util.Abort(_('no patches applied'))
785 revs = cmdutil.revrange(repo, opts['rev'])
785 revs = cmdutil.revrange(repo, opts['rev'])
786 if len(revs) > 1 and revs[0] > revs[1]:
786 if len(revs) > 1 and revs[0] > revs[1]:
787 revs.reverse()
787 revs.reverse()
788 revpatches = self._revpatches(repo, revs)
788 revpatches = self._revpatches(repo, revs)
789 realpatches += revpatches
789 realpatches += revpatches
790 numrevs = len(revpatches)
790 numrevs = len(revpatches)
791
791
792 self._cleanup(realpatches, numrevs, opts.get('keep'))
792 self._cleanup(realpatches, numrevs, opts.get('keep'))
793
793
794 def check_toppatch(self, repo):
794 def check_toppatch(self, repo):
795 if len(self.applied) > 0:
795 if len(self.applied) > 0:
796 top = bin(self.applied[-1].rev)
796 top = bin(self.applied[-1].rev)
797 patch = self.applied[-1].name
797 patch = self.applied[-1].name
798 pp = repo.dirstate.parents()
798 pp = repo.dirstate.parents()
799 if top not in pp:
799 if top not in pp:
800 raise util.Abort(_("working directory revision is not qtip"))
800 raise util.Abort(_("working directory revision is not qtip"))
801 return top, patch
801 return top, patch
802 return None, None
802 return None, None
803
803
804 def check_localchanges(self, repo, force=False, refresh=True):
804 def check_localchanges(self, repo, force=False, refresh=True):
805 m, a, r, d = repo.status()[:4]
805 m, a, r, d = repo.status()[:4]
806 if (m or a or r or d) and not force:
806 if (m or a or r or d) and not force:
807 if refresh:
807 if refresh:
808 raise util.Abort(_("local changes found, refresh first"))
808 raise util.Abort(_("local changes found, refresh first"))
809 else:
809 else:
810 raise util.Abort(_("local changes found"))
810 raise util.Abort(_("local changes found"))
811 return m, a, r, d
811 return m, a, r, d
812
812
813 _reserved = ('series', 'status', 'guards')
813 _reserved = ('series', 'status', 'guards')
814 def check_reserved_name(self, name):
814 def check_reserved_name(self, name):
815 if (name in self._reserved or name.startswith('.hg')
815 if (name in self._reserved or name.startswith('.hg')
816 or name.startswith('.mq')):
816 or name.startswith('.mq')):
817 raise util.Abort(_('"%s" cannot be used as the name of a patch')
817 raise util.Abort(_('"%s" cannot be used as the name of a patch')
818 % name)
818 % name)
819
819
820 def new(self, repo, patchfn, *pats, **opts):
820 def new(self, repo, patchfn, *pats, **opts):
821 """options:
821 """options:
822 msg: a string or a no-argument function returning a string
822 msg: a string or a no-argument function returning a string
823 """
823 """
824 msg = opts.get('msg')
824 msg = opts.get('msg')
825 user = opts.get('user')
825 user = opts.get('user')
826 date = opts.get('date')
826 date = opts.get('date')
827 if date:
827 if date:
828 date = util.parsedate(date)
828 date = util.parsedate(date)
829 diffopts = self.diffopts({'git': opts.get('git')})
829 diffopts = self.diffopts({'git': opts.get('git')})
830 self.check_reserved_name(patchfn)
830 self.check_reserved_name(patchfn)
831 if os.path.exists(self.join(patchfn)):
831 if os.path.exists(self.join(patchfn)):
832 raise util.Abort(_('patch "%s" already exists') % patchfn)
832 raise util.Abort(_('patch "%s" already exists') % patchfn)
833 if opts.get('include') or opts.get('exclude') or pats:
833 if opts.get('include') or opts.get('exclude') or pats:
834 match = cmdutil.match(repo, pats, opts)
834 match = cmdutil.match(repo, pats, opts)
835 # detect missing files in pats
835 # detect missing files in pats
836 def badfn(f, msg):
836 def badfn(f, msg):
837 raise util.Abort('%s: %s' % (f, msg))
837 raise util.Abort('%s: %s' % (f, msg))
838 match.bad = badfn
838 match.bad = badfn
839 m, a, r, d = repo.status(match=match)[:4]
839 m, a, r, d = repo.status(match=match)[:4]
840 else:
840 else:
841 m, a, r, d = self.check_localchanges(repo, force=True)
841 m, a, r, d = self.check_localchanges(repo, force=True)
842 match = cmdutil.matchfiles(repo, m + a + r)
842 match = cmdutil.matchfiles(repo, m + a + r)
843 if len(repo[None].parents()) > 1:
843 if len(repo[None].parents()) > 1:
844 raise util.Abort(_('cannot manage merge changesets'))
844 raise util.Abort(_('cannot manage merge changesets'))
845 commitfiles = m + a + r
845 commitfiles = m + a + r
846 self.check_toppatch(repo)
846 self.check_toppatch(repo)
847 insert = self.full_series_end()
847 insert = self.full_series_end()
848 wlock = repo.wlock()
848 wlock = repo.wlock()
849 try:
849 try:
850 # if patch file write fails, abort early
850 # if patch file write fails, abort early
851 p = self.opener(patchfn, "w")
851 p = self.opener(patchfn, "w")
852 try:
852 try:
853 if self.plainmode:
853 if self.plainmode:
854 if user:
854 if user:
855 p.write("From: " + user + "\n")
855 p.write("From: " + user + "\n")
856 if not date:
856 if not date:
857 p.write("\n")
857 p.write("\n")
858 if date:
858 if date:
859 p.write("Date: %d %d\n\n" % date)
859 p.write("Date: %d %d\n\n" % date)
860 else:
860 else:
861 p.write("# HG changeset patch\n")
861 p.write("# HG changeset patch\n")
862 p.write("# Parent "
862 p.write("# Parent "
863 + hex(repo[None].parents()[0].node()) + "\n")
863 + hex(repo[None].parents()[0].node()) + "\n")
864 if user:
864 if user:
865 p.write("# User " + user + "\n")
865 p.write("# User " + user + "\n")
866 if date:
866 if date:
867 p.write("# Date %s %s\n\n" % date)
867 p.write("# Date %s %s\n\n" % date)
868 if hasattr(msg, '__call__'):
868 if hasattr(msg, '__call__'):
869 msg = msg()
869 msg = msg()
870 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
870 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
871 n = repo.commit(commitmsg, user, date, match=match, force=True)
871 n = repo.commit(commitmsg, user, date, match=match, force=True)
872 if n is None:
872 if n is None:
873 raise util.Abort(_("repo commit failed"))
873 raise util.Abort(_("repo commit failed"))
874 try:
874 try:
875 self.full_series[insert:insert] = [patchfn]
875 self.full_series[insert:insert] = [patchfn]
876 self.applied.append(statusentry(hex(n), patchfn))
876 self.applied.append(statusentry(hex(n), patchfn))
877 self.parse_series()
877 self.parse_series()
878 self.series_dirty = 1
878 self.series_dirty = 1
879 self.applied_dirty = 1
879 self.applied_dirty = 1
880 if msg:
880 if msg:
881 msg = msg + "\n\n"
881 msg = msg + "\n\n"
882 p.write(msg)
882 p.write(msg)
883 if commitfiles:
883 if commitfiles:
884 parent = self.qparents(repo, n)
884 parent = self.qparents(repo, n)
885 chunks = patch.diff(repo, node1=parent, node2=n,
885 chunks = patch.diff(repo, node1=parent, node2=n,
886 match=match, opts=diffopts)
886 match=match, opts=diffopts)
887 for chunk in chunks:
887 for chunk in chunks:
888 p.write(chunk)
888 p.write(chunk)
889 p.close()
889 p.close()
890 wlock.release()
890 wlock.release()
891 wlock = None
891 wlock = None
892 r = self.qrepo()
892 r = self.qrepo()
893 if r:
893 if r:
894 r.add([patchfn])
894 r.add([patchfn])
895 except:
895 except:
896 repo.rollback()
896 repo.rollback()
897 raise
897 raise
898 except Exception:
898 except Exception:
899 patchpath = self.join(patchfn)
899 patchpath = self.join(patchfn)
900 try:
900 try:
901 os.unlink(patchpath)
901 os.unlink(patchpath)
902 except:
902 except:
903 self.ui.warn(_('error unlinking %s\n') % patchpath)
903 self.ui.warn(_('error unlinking %s\n') % patchpath)
904 raise
904 raise
905 self.removeundo(repo)
905 self.removeundo(repo)
906 finally:
906 finally:
907 release(wlock)
907 release(wlock)
908
908
909 def strip(self, repo, rev, update=True, backup="all", force=None):
909 def strip(self, repo, rev, update=True, backup="all", force=None):
910 wlock = lock = None
910 wlock = lock = None
911 try:
911 try:
912 wlock = repo.wlock()
912 wlock = repo.wlock()
913 lock = repo.lock()
913 lock = repo.lock()
914
914
915 if update:
915 if update:
916 self.check_localchanges(repo, force=force, refresh=False)
916 self.check_localchanges(repo, force=force, refresh=False)
917 urev = self.qparents(repo, rev)
917 urev = self.qparents(repo, rev)
918 hg.clean(repo, urev)
918 hg.clean(repo, urev)
919 repo.dirstate.write()
919 repo.dirstate.write()
920
920
921 self.removeundo(repo)
921 self.removeundo(repo)
922 repair.strip(self.ui, repo, rev, backup)
922 repair.strip(self.ui, repo, rev, backup)
923 # strip may have unbundled a set of backed up revisions after
923 # strip may have unbundled a set of backed up revisions after
924 # the actual strip
924 # the actual strip
925 self.removeundo(repo)
925 self.removeundo(repo)
926 finally:
926 finally:
927 release(lock, wlock)
927 release(lock, wlock)
928
928
929 def isapplied(self, patch):
929 def isapplied(self, patch):
930 """returns (index, rev, patch)"""
930 """returns (index, rev, patch)"""
931 for i, a in enumerate(self.applied):
931 for i, a in enumerate(self.applied):
932 if a.name == patch:
932 if a.name == patch:
933 return (i, a.rev, a.name)
933 return (i, a.rev, a.name)
934 return None
934 return None
935
935
936 # if the exact patch name does not exist, we try a few
936 # if the exact patch name does not exist, we try a few
937 # variations. If strict is passed, we try only #1
937 # variations. If strict is passed, we try only #1
938 #
938 #
939 # 1) a number to indicate an offset in the series file
939 # 1) a number to indicate an offset in the series file
940 # 2) a unique substring of the patch name was given
940 # 2) a unique substring of the patch name was given
941 # 3) patchname[-+]num to indicate an offset in the series file
941 # 3) patchname[-+]num to indicate an offset in the series file
942 def lookup(self, patch, strict=False):
942 def lookup(self, patch, strict=False):
943 patch = patch and str(patch)
943 patch = patch and str(patch)
944
944
945 def partial_name(s):
945 def partial_name(s):
946 if s in self.series:
946 if s in self.series:
947 return s
947 return s
948 matches = [x for x in self.series if s in x]
948 matches = [x for x in self.series if s in x]
949 if len(matches) > 1:
949 if len(matches) > 1:
950 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
950 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
951 for m in matches:
951 for m in matches:
952 self.ui.warn(' %s\n' % m)
952 self.ui.warn(' %s\n' % m)
953 return None
953 return None
954 if matches:
954 if matches:
955 return matches[0]
955 return matches[0]
956 if len(self.series) > 0 and len(self.applied) > 0:
956 if len(self.series) > 0 and len(self.applied) > 0:
957 if s == 'qtip':
957 if s == 'qtip':
958 return self.series[self.series_end(True)-1]
958 return self.series[self.series_end(True)-1]
959 if s == 'qbase':
959 if s == 'qbase':
960 return self.series[0]
960 return self.series[0]
961 return None
961 return None
962
962
963 if patch is None:
963 if patch is None:
964 return None
964 return None
965 if patch in self.series:
965 if patch in self.series:
966 return patch
966 return patch
967
967
968 if not os.path.isfile(self.join(patch)):
968 if not os.path.isfile(self.join(patch)):
969 try:
969 try:
970 sno = int(patch)
970 sno = int(patch)
971 except (ValueError, OverflowError):
971 except (ValueError, OverflowError):
972 pass
972 pass
973 else:
973 else:
974 if -len(self.series) <= sno < len(self.series):
974 if -len(self.series) <= sno < len(self.series):
975 return self.series[sno]
975 return self.series[sno]
976
976
977 if not strict:
977 if not strict:
978 res = partial_name(patch)
978 res = partial_name(patch)
979 if res:
979 if res:
980 return res
980 return res
981 minus = patch.rfind('-')
981 minus = patch.rfind('-')
982 if minus >= 0:
982 if minus >= 0:
983 res = partial_name(patch[:minus])
983 res = partial_name(patch[:minus])
984 if res:
984 if res:
985 i = self.series.index(res)
985 i = self.series.index(res)
986 try:
986 try:
987 off = int(patch[minus + 1:] or 1)
987 off = int(patch[minus + 1:] or 1)
988 except (ValueError, OverflowError):
988 except (ValueError, OverflowError):
989 pass
989 pass
990 else:
990 else:
991 if i - off >= 0:
991 if i - off >= 0:
992 return self.series[i - off]
992 return self.series[i - off]
993 plus = patch.rfind('+')
993 plus = patch.rfind('+')
994 if plus >= 0:
994 if plus >= 0:
995 res = partial_name(patch[:plus])
995 res = partial_name(patch[:plus])
996 if res:
996 if res:
997 i = self.series.index(res)
997 i = self.series.index(res)
998 try:
998 try:
999 off = int(patch[plus + 1:] or 1)
999 off = int(patch[plus + 1:] or 1)
1000 except (ValueError, OverflowError):
1000 except (ValueError, OverflowError):
1001 pass
1001 pass
1002 else:
1002 else:
1003 if i + off < len(self.series):
1003 if i + off < len(self.series):
1004 return self.series[i + off]
1004 return self.series[i + off]
1005 raise util.Abort(_("patch %s not in series") % patch)
1005 raise util.Abort(_("patch %s not in series") % patch)
1006
1006
1007 def push(self, repo, patch=None, force=False, list=False,
1007 def push(self, repo, patch=None, force=False, list=False,
1008 mergeq=None, all=False):
1008 mergeq=None, all=False):
1009 diffopts = self.diffopts()
1009 diffopts = self.diffopts()
1010 wlock = repo.wlock()
1010 wlock = repo.wlock()
1011 try:
1011 try:
1012 heads = []
1012 heads = []
1013 for b, ls in repo.branchmap().iteritems():
1013 for b, ls in repo.branchmap().iteritems():
1014 heads += ls
1014 heads += ls
1015 if not heads:
1015 if not heads:
1016 heads = [nullid]
1016 heads = [nullid]
1017 if repo.dirstate.parents()[0] not in heads:
1017 if repo.dirstate.parents()[0] not in heads:
1018 self.ui.status(_("(working directory not at a head)\n"))
1018 self.ui.status(_("(working directory not at a head)\n"))
1019
1019
1020 if not self.series:
1020 if not self.series:
1021 self.ui.warn(_('no patches in series\n'))
1021 self.ui.warn(_('no patches in series\n'))
1022 return 0
1022 return 0
1023
1023
1024 patch = self.lookup(patch)
1024 patch = self.lookup(patch)
1025 # Suppose our series file is: A B C and the current 'top'
1025 # Suppose our series file is: A B C and the current 'top'
1026 # patch is B. qpush C should be performed (moving forward)
1026 # patch is B. qpush C should be performed (moving forward)
1027 # qpush B is a NOP (no change) qpush A is an error (can't
1027 # qpush B is a NOP (no change) qpush A is an error (can't
1028 # go backwards with qpush)
1028 # go backwards with qpush)
1029 if patch:
1029 if patch:
1030 info = self.isapplied(patch)
1030 info = self.isapplied(patch)
1031 if info:
1031 if info:
1032 if info[0] < len(self.applied) - 1:
1032 if info[0] < len(self.applied) - 1:
1033 raise util.Abort(
1033 raise util.Abort(
1034 _("cannot push to a previous patch: %s") % patch)
1034 _("cannot push to a previous patch: %s") % patch)
1035 self.ui.warn(
1035 self.ui.warn(
1036 _('qpush: %s is already at the top\n') % patch)
1036 _('qpush: %s is already at the top\n') % patch)
1037 return
1037 return
1038 pushable, reason = self.pushable(patch)
1038 pushable, reason = self.pushable(patch)
1039 if not pushable:
1039 if not pushable:
1040 if reason:
1040 if reason:
1041 reason = _('guarded by %r') % reason
1041 reason = _('guarded by %r') % reason
1042 else:
1042 else:
1043 reason = _('no matching guards')
1043 reason = _('no matching guards')
1044 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1044 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1045 return 1
1045 return 1
1046 elif all:
1046 elif all:
1047 patch = self.series[-1]
1047 patch = self.series[-1]
1048 if self.isapplied(patch):
1048 if self.isapplied(patch):
1049 self.ui.warn(_('all patches are currently applied\n'))
1049 self.ui.warn(_('all patches are currently applied\n'))
1050 return 0
1050 return 0
1051
1051
1052 # Following the above example, starting at 'top' of B:
1052 # Following the above example, starting at 'top' of B:
1053 # qpush should be performed (pushes C), but a subsequent
1053 # qpush should be performed (pushes C), but a subsequent
1054 # qpush without an argument is an error (nothing to
1054 # qpush without an argument is an error (nothing to
1055 # apply). This allows a loop of "...while hg qpush..." to
1055 # apply). This allows a loop of "...while hg qpush..." to
1056 # work as it detects an error when done
1056 # work as it detects an error when done
1057 start = self.series_end()
1057 start = self.series_end()
1058 if start == len(self.series):
1058 if start == len(self.series):
1059 self.ui.warn(_('patch series already fully applied\n'))
1059 self.ui.warn(_('patch series already fully applied\n'))
1060 return 1
1060 return 1
1061 if not force:
1061 if not force:
1062 self.check_localchanges(repo)
1062 self.check_localchanges(repo)
1063
1063
1064 self.applied_dirty = 1
1064 self.applied_dirty = 1
1065 if start > 0:
1065 if start > 0:
1066 self.check_toppatch(repo)
1066 self.check_toppatch(repo)
1067 if not patch:
1067 if not patch:
1068 patch = self.series[start]
1068 patch = self.series[start]
1069 end = start + 1
1069 end = start + 1
1070 else:
1070 else:
1071 end = self.series.index(patch, start) + 1
1071 end = self.series.index(patch, start) + 1
1072
1072
1073 s = self.series[start:end]
1073 s = self.series[start:end]
1074 all_files = {}
1074 all_files = {}
1075 try:
1075 try:
1076 if mergeq:
1076 if mergeq:
1077 ret = self.mergepatch(repo, mergeq, s, diffopts)
1077 ret = self.mergepatch(repo, mergeq, s, diffopts)
1078 else:
1078 else:
1079 ret = self.apply(repo, s, list, all_files=all_files)
1079 ret = self.apply(repo, s, list, all_files=all_files)
1080 except:
1080 except:
1081 self.ui.warn(_('cleaning up working directory...'))
1081 self.ui.warn(_('cleaning up working directory...'))
1082 node = repo.dirstate.parents()[0]
1082 node = repo.dirstate.parents()[0]
1083 hg.revert(repo, node, None)
1083 hg.revert(repo, node, None)
1084 unknown = repo.status(unknown=True)[4]
1084 unknown = repo.status(unknown=True)[4]
1085 # only remove unknown files that we know we touched or
1085 # only remove unknown files that we know we touched or
1086 # created while patching
1086 # created while patching
1087 for f in unknown:
1087 for f in unknown:
1088 if f in all_files:
1088 if f in all_files:
1089 util.unlink(repo.wjoin(f))
1089 util.unlink(repo.wjoin(f))
1090 self.ui.warn(_('done\n'))
1090 self.ui.warn(_('done\n'))
1091 raise
1091 raise
1092
1092
1093 if not self.applied:
1093 if not self.applied:
1094 return ret[0]
1094 return ret[0]
1095 top = self.applied[-1].name
1095 top = self.applied[-1].name
1096 if ret[0] and ret[0] > 1:
1096 if ret[0] and ret[0] > 1:
1097 msg = _("errors during apply, please fix and refresh %s\n")
1097 msg = _("errors during apply, please fix and refresh %s\n")
1098 self.ui.write(msg % top)
1098 self.ui.write(msg % top)
1099 else:
1099 else:
1100 self.ui.write(_("now at: %s\n") % top)
1100 self.ui.write(_("now at: %s\n") % top)
1101 return ret[0]
1101 return ret[0]
1102
1102
1103 finally:
1103 finally:
1104 wlock.release()
1104 wlock.release()
1105
1105
1106 def pop(self, repo, patch=None, force=False, update=True, all=False):
1106 def pop(self, repo, patch=None, force=False, update=True, all=False):
1107 def getfile(f, rev, flags):
1107 def getfile(f, rev, flags):
1108 t = repo.file(f).read(rev)
1108 t = repo.file(f).read(rev)
1109 repo.wwrite(f, t, flags)
1109 repo.wwrite(f, t, flags)
1110
1110
1111 wlock = repo.wlock()
1111 wlock = repo.wlock()
1112 try:
1112 try:
1113 if patch:
1113 if patch:
1114 # index, rev, patch
1114 # index, rev, patch
1115 info = self.isapplied(patch)
1115 info = self.isapplied(patch)
1116 if not info:
1116 if not info:
1117 patch = self.lookup(patch)
1117 patch = self.lookup(patch)
1118 info = self.isapplied(patch)
1118 info = self.isapplied(patch)
1119 if not info:
1119 if not info:
1120 raise util.Abort(_("patch %s is not applied") % patch)
1120 raise util.Abort(_("patch %s is not applied") % patch)
1121
1121
1122 if len(self.applied) == 0:
1122 if len(self.applied) == 0:
1123 # Allow qpop -a to work repeatedly,
1123 # Allow qpop -a to work repeatedly,
1124 # but not qpop without an argument
1124 # but not qpop without an argument
1125 self.ui.warn(_("no patches applied\n"))
1125 self.ui.warn(_("no patches applied\n"))
1126 return not all
1126 return not all
1127
1127
1128 if all:
1128 if all:
1129 start = 0
1129 start = 0
1130 elif patch:
1130 elif patch:
1131 start = info[0] + 1
1131 start = info[0] + 1
1132 else:
1132 else:
1133 start = len(self.applied) - 1
1133 start = len(self.applied) - 1
1134
1134
1135 if start >= len(self.applied):
1135 if start >= len(self.applied):
1136 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1136 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1137 return
1137 return
1138
1138
1139 if not update:
1139 if not update:
1140 parents = repo.dirstate.parents()
1140 parents = repo.dirstate.parents()
1141 rr = [bin(x.rev) for x in self.applied]
1141 rr = [bin(x.rev) for x in self.applied]
1142 for p in parents:
1142 for p in parents:
1143 if p in rr:
1143 if p in rr:
1144 self.ui.warn(_("qpop: forcing dirstate update\n"))
1144 self.ui.warn(_("qpop: forcing dirstate update\n"))
1145 update = True
1145 update = True
1146 else:
1146 else:
1147 parents = [p.hex() for p in repo[None].parents()]
1147 parents = [p.hex() for p in repo[None].parents()]
1148 needupdate = False
1148 needupdate = False
1149 for entry in self.applied[start:]:
1149 for entry in self.applied[start:]:
1150 if entry.rev in parents:
1150 if entry.rev in parents:
1151 needupdate = True
1151 needupdate = True
1152 break
1152 break
1153 update = needupdate
1153 update = needupdate
1154
1154
1155 if not force and update:
1155 if not force and update:
1156 self.check_localchanges(repo)
1156 self.check_localchanges(repo)
1157
1157
1158 self.applied_dirty = 1
1158 self.applied_dirty = 1
1159 end = len(self.applied)
1159 end = len(self.applied)
1160 rev = bin(self.applied[start].rev)
1160 rev = bin(self.applied[start].rev)
1161 if update:
1161 if update:
1162 top = self.check_toppatch(repo)[0]
1162 top = self.check_toppatch(repo)[0]
1163
1163
1164 try:
1164 try:
1165 heads = repo.changelog.heads(rev)
1165 heads = repo.changelog.heads(rev)
1166 except error.LookupError:
1166 except error.LookupError:
1167 node = short(rev)
1167 node = short(rev)
1168 raise util.Abort(_('trying to pop unknown node %s') % node)
1168 raise util.Abort(_('trying to pop unknown node %s') % node)
1169
1169
1170 if heads != [bin(self.applied[-1].rev)]:
1170 if heads != [bin(self.applied[-1].rev)]:
1171 raise util.Abort(_("popping would remove a revision not "
1171 raise util.Abort(_("popping would remove a revision not "
1172 "managed by this patch queue"))
1172 "managed by this patch queue"))
1173
1173
1174 # we know there are no local changes, so we can make a simplified
1174 # we know there are no local changes, so we can make a simplified
1175 # form of hg.update.
1175 # form of hg.update.
1176 if update:
1176 if update:
1177 qp = self.qparents(repo, rev)
1177 qp = self.qparents(repo, rev)
1178 changes = repo.changelog.read(qp)
1178 changes = repo.changelog.read(qp)
1179 mmap = repo.manifest.read(changes[0])
1179 mmap = repo.manifest.read(changes[0])
1180 m, a, r, d = repo.status(qp, top)[:4]
1180 m, a, r, d = repo.status(qp, top)[:4]
1181 if d:
1181 if d:
1182 raise util.Abort(_("deletions found between repo revs"))
1182 raise util.Abort(_("deletions found between repo revs"))
1183 for f in a:
1183 for f in a:
1184 try:
1184 try:
1185 os.unlink(repo.wjoin(f))
1185 os.unlink(repo.wjoin(f))
1186 except OSError, e:
1186 except OSError, e:
1187 if e.errno != errno.ENOENT:
1187 if e.errno != errno.ENOENT:
1188 raise
1188 raise
1189 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
1189 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
1190 except: pass
1190 except: pass
1191 repo.dirstate.forget(f)
1191 repo.dirstate.forget(f)
1192 for f in m:
1192 for f in m:
1193 getfile(f, mmap[f], mmap.flags(f))
1193 getfile(f, mmap[f], mmap.flags(f))
1194 for f in r:
1194 for f in r:
1195 getfile(f, mmap[f], mmap.flags(f))
1195 getfile(f, mmap[f], mmap.flags(f))
1196 for f in m + r:
1196 for f in m + r:
1197 repo.dirstate.normal(f)
1197 repo.dirstate.normal(f)
1198 repo.dirstate.setparents(qp, nullid)
1198 repo.dirstate.setparents(qp, nullid)
1199 for patch in reversed(self.applied[start:end]):
1199 for patch in reversed(self.applied[start:end]):
1200 self.ui.status(_("popping %s\n") % patch.name)
1200 self.ui.status(_("popping %s\n") % patch.name)
1201 del self.applied[start:end]
1201 del self.applied[start:end]
1202 self.strip(repo, rev, update=False, backup='strip')
1202 self.strip(repo, rev, update=False, backup='strip')
1203 if len(self.applied):
1203 if len(self.applied):
1204 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1204 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1205 else:
1205 else:
1206 self.ui.write(_("patch queue now empty\n"))
1206 self.ui.write(_("patch queue now empty\n"))
1207 finally:
1207 finally:
1208 wlock.release()
1208 wlock.release()
1209
1209
1210 def diff(self, repo, pats, opts):
1210 def diff(self, repo, pats, opts):
1211 top, patch = self.check_toppatch(repo)
1211 top, patch = self.check_toppatch(repo)
1212 if not top:
1212 if not top:
1213 self.ui.write(_("no patches applied\n"))
1213 self.ui.write(_("no patches applied\n"))
1214 return
1214 return
1215 qp = self.qparents(repo, top)
1215 qp = self.qparents(repo, top)
1216 if opts.get('reverse'):
1216 if opts.get('reverse'):
1217 node1, node2 = None, qp
1217 node1, node2 = None, qp
1218 else:
1218 else:
1219 node1, node2 = qp, None
1219 node1, node2 = qp, None
1220 diffopts = self.diffopts(opts, patch)
1220 diffopts = self.diffopts(opts, patch)
1221 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1221 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1222
1222
1223 def refresh(self, repo, pats=None, **opts):
1223 def refresh(self, repo, pats=None, **opts):
1224 if len(self.applied) == 0:
1224 if len(self.applied) == 0:
1225 self.ui.write(_("no patches applied\n"))
1225 self.ui.write(_("no patches applied\n"))
1226 return 1
1226 return 1
1227 msg = opts.get('msg', '').rstrip()
1227 msg = opts.get('msg', '').rstrip()
1228 newuser = opts.get('user')
1228 newuser = opts.get('user')
1229 newdate = opts.get('date')
1229 newdate = opts.get('date')
1230 if newdate:
1230 if newdate:
1231 newdate = '%d %d' % util.parsedate(newdate)
1231 newdate = '%d %d' % util.parsedate(newdate)
1232 wlock = repo.wlock()
1232 wlock = repo.wlock()
1233
1233
1234 try:
1234 try:
1235 self.check_toppatch(repo)
1235 self.check_toppatch(repo)
1236 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
1236 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
1237 top = bin(top)
1237 top = bin(top)
1238 if repo.changelog.heads(top) != [top]:
1238 if repo.changelog.heads(top) != [top]:
1239 raise util.Abort(_("cannot refresh a revision with children"))
1239 raise util.Abort(_("cannot refresh a revision with children"))
1240
1240
1241 cparents = repo.changelog.parents(top)
1241 cparents = repo.changelog.parents(top)
1242 patchparent = self.qparents(repo, top)
1242 patchparent = self.qparents(repo, top)
1243 ph = patchheader(self.join(patchfn), self.plainmode)
1243 ph = patchheader(self.join(patchfn), self.plainmode)
1244 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1244 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1245 if msg:
1245 if msg:
1246 ph.setmessage(msg)
1246 ph.setmessage(msg)
1247 if newuser:
1247 if newuser:
1248 ph.setuser(newuser)
1248 ph.setuser(newuser)
1249 if newdate:
1249 if newdate:
1250 ph.setdate(newdate)
1250 ph.setdate(newdate)
1251 ph.setparent(hex(patchparent))
1251 ph.setparent(hex(patchparent))
1252
1252
1253 # only commit new patch when write is complete
1253 # only commit new patch when write is complete
1254 patchf = self.opener(patchfn, 'w', atomictemp=True)
1254 patchf = self.opener(patchfn, 'w', atomictemp=True)
1255
1255
1256 comments = str(ph)
1256 comments = str(ph)
1257 if comments:
1257 if comments:
1258 patchf.write(comments)
1258 patchf.write(comments)
1259
1259
1260 # update the dirstate in place, strip off the qtip commit
1260 # update the dirstate in place, strip off the qtip commit
1261 # and then commit.
1261 # and then commit.
1262 #
1262 #
1263 # this should really read:
1263 # this should really read:
1264 # mm, dd, aa, aa2 = repo.status(tip, patchparent)[:4]
1264 # mm, dd, aa, aa2 = repo.status(tip, patchparent)[:4]
1265 # but we do it backwards to take advantage of manifest/chlog
1265 # but we do it backwards to take advantage of manifest/chlog
1266 # caching against the next repo.status call
1266 # caching against the next repo.status call
1267 mm, aa, dd, aa2 = repo.status(patchparent, top)[:4]
1267 mm, aa, dd, aa2 = repo.status(patchparent, top)[:4]
1268 changes = repo.changelog.read(top)
1268 changes = repo.changelog.read(top)
1269 man = repo.manifest.read(changes[0])
1269 man = repo.manifest.read(changes[0])
1270 aaa = aa[:]
1270 aaa = aa[:]
1271 matchfn = cmdutil.match(repo, pats, opts)
1271 matchfn = cmdutil.match(repo, pats, opts)
1272 # in short mode, we only diff the files included in the
1272 # in short mode, we only diff the files included in the
1273 # patch already plus specified files
1273 # patch already plus specified files
1274 if opts.get('short'):
1274 if opts.get('short'):
1275 # if amending a patch, we start with existing
1275 # if amending a patch, we start with existing
1276 # files plus specified files - unfiltered
1276 # files plus specified files - unfiltered
1277 match = cmdutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1277 match = cmdutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1278 # filter with inc/exl options
1278 # filter with inc/exl options
1279 matchfn = cmdutil.match(repo, opts=opts)
1279 matchfn = cmdutil.match(repo, opts=opts)
1280 else:
1280 else:
1281 match = cmdutil.matchall(repo)
1281 match = cmdutil.matchall(repo)
1282 m, a, r, d = repo.status(match=match)[:4]
1282 m, a, r, d = repo.status(match=match)[:4]
1283
1283
1284 # we might end up with files that were added between
1284 # we might end up with files that were added between
1285 # qtip and the dirstate parent, but then changed in the
1285 # qtip and the dirstate parent, but then changed in the
1286 # local dirstate. in this case, we want them to only
1286 # local dirstate. in this case, we want them to only
1287 # show up in the added section
1287 # show up in the added section
1288 for x in m:
1288 for x in m:
1289 if x not in aa:
1289 if x not in aa:
1290 mm.append(x)
1290 mm.append(x)
1291 # we might end up with files added by the local dirstate that
1291 # we might end up with files added by the local dirstate that
1292 # were deleted by the patch. In this case, they should only
1292 # were deleted by the patch. In this case, they should only
1293 # show up in the changed section.
1293 # show up in the changed section.
1294 for x in a:
1294 for x in a:
1295 if x in dd:
1295 if x in dd:
1296 del dd[dd.index(x)]
1296 del dd[dd.index(x)]
1297 mm.append(x)
1297 mm.append(x)
1298 else:
1298 else:
1299 aa.append(x)
1299 aa.append(x)
1300 # make sure any files deleted in the local dirstate
1300 # make sure any files deleted in the local dirstate
1301 # are not in the add or change column of the patch
1301 # are not in the add or change column of the patch
1302 forget = []
1302 forget = []
1303 for x in d + r:
1303 for x in d + r:
1304 if x in aa:
1304 if x in aa:
1305 del aa[aa.index(x)]
1305 del aa[aa.index(x)]
1306 forget.append(x)
1306 forget.append(x)
1307 continue
1307 continue
1308 elif x in mm:
1308 elif x in mm:
1309 del mm[mm.index(x)]
1309 del mm[mm.index(x)]
1310 dd.append(x)
1310 dd.append(x)
1311
1311
1312 m = list(set(mm))
1312 m = list(set(mm))
1313 r = list(set(dd))
1313 r = list(set(dd))
1314 a = list(set(aa))
1314 a = list(set(aa))
1315 c = [filter(matchfn, l) for l in (m, a, r)]
1315 c = [filter(matchfn, l) for l in (m, a, r)]
1316 match = cmdutil.matchfiles(repo, set(c[0] + c[1] + c[2]))
1316 match = cmdutil.matchfiles(repo, set(c[0] + c[1] + c[2]))
1317 chunks = patch.diff(repo, patchparent, match=match,
1317 chunks = patch.diff(repo, patchparent, match=match,
1318 changes=c, opts=diffopts)
1318 changes=c, opts=diffopts)
1319 for chunk in chunks:
1319 for chunk in chunks:
1320 patchf.write(chunk)
1320 patchf.write(chunk)
1321
1321
1322 try:
1322 try:
1323 if diffopts.git or diffopts.upgrade:
1323 if diffopts.git or diffopts.upgrade:
1324 copies = {}
1324 copies = {}
1325 for dst in a:
1325 for dst in a:
1326 src = repo.dirstate.copied(dst)
1326 src = repo.dirstate.copied(dst)
1327 # during qfold, the source file for copies may
1327 # during qfold, the source file for copies may
1328 # be removed. Treat this as a simple add.
1328 # be removed. Treat this as a simple add.
1329 if src is not None and src in repo.dirstate:
1329 if src is not None and src in repo.dirstate:
1330 copies.setdefault(src, []).append(dst)
1330 copies.setdefault(src, []).append(dst)
1331 repo.dirstate.add(dst)
1331 repo.dirstate.add(dst)
1332 # remember the copies between patchparent and qtip
1332 # remember the copies between patchparent and qtip
1333 for dst in aaa:
1333 for dst in aaa:
1334 f = repo.file(dst)
1334 f = repo.file(dst)
1335 src = f.renamed(man[dst])
1335 src = f.renamed(man[dst])
1336 if src:
1336 if src:
1337 copies.setdefault(src[0], []).extend(
1337 copies.setdefault(src[0], []).extend(
1338 copies.get(dst, []))
1338 copies.get(dst, []))
1339 if dst in a:
1339 if dst in a:
1340 copies[src[0]].append(dst)
1340 copies[src[0]].append(dst)
1341 # we can't copy a file created by the patch itself
1341 # we can't copy a file created by the patch itself
1342 if dst in copies:
1342 if dst in copies:
1343 del copies[dst]
1343 del copies[dst]
1344 for src, dsts in copies.iteritems():
1344 for src, dsts in copies.iteritems():
1345 for dst in dsts:
1345 for dst in dsts:
1346 repo.dirstate.copy(src, dst)
1346 repo.dirstate.copy(src, dst)
1347 else:
1347 else:
1348 for dst in a:
1348 for dst in a:
1349 repo.dirstate.add(dst)
1349 repo.dirstate.add(dst)
1350 # Drop useless copy information
1350 # Drop useless copy information
1351 for f in list(repo.dirstate.copies()):
1351 for f in list(repo.dirstate.copies()):
1352 repo.dirstate.copy(None, f)
1352 repo.dirstate.copy(None, f)
1353 for f in r:
1353 for f in r:
1354 repo.dirstate.remove(f)
1354 repo.dirstate.remove(f)
1355 # if the patch excludes a modified file, mark that
1355 # if the patch excludes a modified file, mark that
1356 # file with mtime=0 so status can see it.
1356 # file with mtime=0 so status can see it.
1357 mm = []
1357 mm = []
1358 for i in xrange(len(m)-1, -1, -1):
1358 for i in xrange(len(m)-1, -1, -1):
1359 if not matchfn(m[i]):
1359 if not matchfn(m[i]):
1360 mm.append(m[i])
1360 mm.append(m[i])
1361 del m[i]
1361 del m[i]
1362 for f in m:
1362 for f in m:
1363 repo.dirstate.normal(f)
1363 repo.dirstate.normal(f)
1364 for f in mm:
1364 for f in mm:
1365 repo.dirstate.normallookup(f)
1365 repo.dirstate.normallookup(f)
1366 for f in forget:
1366 for f in forget:
1367 repo.dirstate.forget(f)
1367 repo.dirstate.forget(f)
1368
1368
1369 if not msg:
1369 if not msg:
1370 if not ph.message:
1370 if not ph.message:
1371 message = "[mq]: %s\n" % patchfn
1371 message = "[mq]: %s\n" % patchfn
1372 else:
1372 else:
1373 message = "\n".join(ph.message)
1373 message = "\n".join(ph.message)
1374 else:
1374 else:
1375 message = msg
1375 message = msg
1376
1376
1377 user = ph.user or changes[1]
1377 user = ph.user or changes[1]
1378
1378
1379 # assumes strip can roll itself back if interrupted
1379 # assumes strip can roll itself back if interrupted
1380 repo.dirstate.setparents(*cparents)
1380 repo.dirstate.setparents(*cparents)
1381 self.applied.pop()
1381 self.applied.pop()
1382 self.applied_dirty = 1
1382 self.applied_dirty = 1
1383 self.strip(repo, top, update=False,
1383 self.strip(repo, top, update=False,
1384 backup='strip')
1384 backup='strip')
1385 except:
1385 except:
1386 repo.dirstate.invalidate()
1386 repo.dirstate.invalidate()
1387 raise
1387 raise
1388
1388
1389 try:
1389 try:
1390 # might be nice to attempt to roll back strip after this
1390 # might be nice to attempt to roll back strip after this
1391 patchf.rename()
1391 patchf.rename()
1392 n = repo.commit(message, user, ph.date, match=match,
1392 n = repo.commit(message, user, ph.date, match=match,
1393 force=True)
1393 force=True)
1394 self.applied.append(statusentry(hex(n), patchfn))
1394 self.applied.append(statusentry(hex(n), patchfn))
1395 except:
1395 except:
1396 ctx = repo[cparents[0]]
1396 ctx = repo[cparents[0]]
1397 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1397 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1398 self.save_dirty()
1398 self.save_dirty()
1399 self.ui.warn(_('refresh interrupted while patch was popped! '
1399 self.ui.warn(_('refresh interrupted while patch was popped! '
1400 '(revert --all, qpush to recover)\n'))
1400 '(revert --all, qpush to recover)\n'))
1401 raise
1401 raise
1402 finally:
1402 finally:
1403 wlock.release()
1403 wlock.release()
1404 self.removeundo(repo)
1404 self.removeundo(repo)
1405
1405
1406 def init(self, repo, create=False):
1406 def init(self, repo, create=False):
1407 if not create and os.path.isdir(self.path):
1407 if not create and os.path.isdir(self.path):
1408 raise util.Abort(_("patch queue directory already exists"))
1408 raise util.Abort(_("patch queue directory already exists"))
1409 try:
1409 try:
1410 os.mkdir(self.path)
1410 os.mkdir(self.path)
1411 except OSError, inst:
1411 except OSError, inst:
1412 if inst.errno != errno.EEXIST or not create:
1412 if inst.errno != errno.EEXIST or not create:
1413 raise
1413 raise
1414 if create:
1414 if create:
1415 return self.qrepo(create=True)
1415 return self.qrepo(create=True)
1416
1416
1417 def unapplied(self, repo, patch=None):
1417 def unapplied(self, repo, patch=None):
1418 if patch and patch not in self.series:
1418 if patch and patch not in self.series:
1419 raise util.Abort(_("patch %s is not in series file") % patch)
1419 raise util.Abort(_("patch %s is not in series file") % patch)
1420 if not patch:
1420 if not patch:
1421 start = self.series_end()
1421 start = self.series_end()
1422 else:
1422 else:
1423 start = self.series.index(patch) + 1
1423 start = self.series.index(patch) + 1
1424 unapplied = []
1424 unapplied = []
1425 for i in xrange(start, len(self.series)):
1425 for i in xrange(start, len(self.series)):
1426 pushable, reason = self.pushable(i)
1426 pushable, reason = self.pushable(i)
1427 if pushable:
1427 if pushable:
1428 unapplied.append((i, self.series[i]))
1428 unapplied.append((i, self.series[i]))
1429 self.explain_pushable(i)
1429 self.explain_pushable(i)
1430 return unapplied
1430 return unapplied
1431
1431
1432 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1432 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1433 summary=False):
1433 summary=False):
1434 def displayname(pfx, patchname):
1434 def displayname(pfx, patchname):
1435 if summary:
1435 if summary:
1436 ph = patchheader(self.join(patchname), self.plainmode)
1436 ph = patchheader(self.join(patchname), self.plainmode)
1437 msg = ph.message and ph.message[0] or ''
1437 msg = ph.message and ph.message[0] or ''
1438 if self.ui.interactive():
1438 if self.ui.interactive():
1439 width = util.termwidth() - len(pfx) - len(patchname) - 2
1439 width = util.termwidth() - len(pfx) - len(patchname) - 2
1440 if width > 0:
1440 if width > 0:
1441 msg = util.ellipsis(msg, width)
1441 msg = util.ellipsis(msg, width)
1442 else:
1442 else:
1443 msg = ''
1443 msg = ''
1444 msg = "%s%s: %s" % (pfx, patchname, msg)
1444 msg = "%s%s: %s" % (pfx, patchname, msg)
1445 else:
1445 else:
1446 msg = pfx + patchname
1446 msg = pfx + patchname
1447 self.ui.write(msg + '\n')
1447 self.ui.write(msg + '\n')
1448
1448
1449 applied = set([p.name for p in self.applied])
1449 applied = set([p.name for p in self.applied])
1450 if length is None:
1450 if length is None:
1451 length = len(self.series) - start
1451 length = len(self.series) - start
1452 if not missing:
1452 if not missing:
1453 if self.ui.verbose:
1453 if self.ui.verbose:
1454 idxwidth = len(str(start + length - 1))
1454 idxwidth = len(str(start + length - 1))
1455 for i in xrange(start, start + length):
1455 for i in xrange(start, start + length):
1456 patch = self.series[i]
1456 patch = self.series[i]
1457 if patch in applied:
1457 if patch in applied:
1458 stat = 'A'
1458 stat = 'A'
1459 elif self.pushable(i)[0]:
1459 elif self.pushable(i)[0]:
1460 stat = 'U'
1460 stat = 'U'
1461 else:
1461 else:
1462 stat = 'G'
1462 stat = 'G'
1463 pfx = ''
1463 pfx = ''
1464 if self.ui.verbose:
1464 if self.ui.verbose:
1465 pfx = '%*d %s ' % (idxwidth, i, stat)
1465 pfx = '%*d %s ' % (idxwidth, i, stat)
1466 elif status and status != stat:
1466 elif status and status != stat:
1467 continue
1467 continue
1468 displayname(pfx, patch)
1468 displayname(pfx, patch)
1469 else:
1469 else:
1470 msng_list = []
1470 msng_list = []
1471 for root, dirs, files in os.walk(self.path):
1471 for root, dirs, files in os.walk(self.path):
1472 d = root[len(self.path) + 1:]
1472 d = root[len(self.path) + 1:]
1473 for f in files:
1473 for f in files:
1474 fl = os.path.join(d, f)
1474 fl = os.path.join(d, f)
1475 if (fl not in self.series and
1475 if (fl not in self.series and
1476 fl not in (self.status_path, self.series_path,
1476 fl not in (self.status_path, self.series_path,
1477 self.guards_path)
1477 self.guards_path)
1478 and not fl.startswith('.')):
1478 and not fl.startswith('.')):
1479 msng_list.append(fl)
1479 msng_list.append(fl)
1480 for x in sorted(msng_list):
1480 for x in sorted(msng_list):
1481 pfx = self.ui.verbose and ('D ') or ''
1481 pfx = self.ui.verbose and ('D ') or ''
1482 displayname(pfx, x)
1482 displayname(pfx, x)
1483
1483
1484 def issaveline(self, l):
1484 def issaveline(self, l):
1485 if l.name == '.hg.patches.save.line':
1485 if l.name == '.hg.patches.save.line':
1486 return True
1486 return True
1487
1487
1488 def qrepo(self, create=False):
1488 def qrepo(self, create=False):
1489 if create or os.path.isdir(self.join(".hg")):
1489 if create or os.path.isdir(self.join(".hg")):
1490 return hg.repository(self.ui, path=self.path, create=create)
1490 return hg.repository(self.ui, path=self.path, create=create)
1491
1491
1492 def restore(self, repo, rev, delete=None, qupdate=None):
1492 def restore(self, repo, rev, delete=None, qupdate=None):
1493 c = repo.changelog.read(rev)
1493 c = repo.changelog.read(rev)
1494 desc = c[4].strip()
1494 desc = c[4].strip()
1495 lines = desc.splitlines()
1495 lines = desc.splitlines()
1496 i = 0
1496 i = 0
1497 datastart = None
1497 datastart = None
1498 series = []
1498 series = []
1499 applied = []
1499 applied = []
1500 qpp = None
1500 qpp = None
1501 for i, line in enumerate(lines):
1501 for i, line in enumerate(lines):
1502 if line == 'Patch Data:':
1502 if line == 'Patch Data:':
1503 datastart = i + 1
1503 datastart = i + 1
1504 elif line.startswith('Dirstate:'):
1504 elif line.startswith('Dirstate:'):
1505 l = line.rstrip()
1505 l = line.rstrip()
1506 l = l[10:].split(' ')
1506 l = l[10:].split(' ')
1507 qpp = [bin(x) for x in l]
1507 qpp = [bin(x) for x in l]
1508 elif datastart != None:
1508 elif datastart != None:
1509 l = line.rstrip()
1509 l = line.rstrip()
1510 se = statusentry(l)
1510 se = statusentry(l)
1511 file_ = se.name
1511 file_ = se.name
1512 if se.rev:
1512 if se.rev:
1513 applied.append(se)
1513 applied.append(se)
1514 else:
1514 else:
1515 series.append(file_)
1515 series.append(file_)
1516 if datastart is None:
1516 if datastart is None:
1517 self.ui.warn(_("No saved patch data found\n"))
1517 self.ui.warn(_("No saved patch data found\n"))
1518 return 1
1518 return 1
1519 self.ui.warn(_("restoring status: %s\n") % lines[0])
1519 self.ui.warn(_("restoring status: %s\n") % lines[0])
1520 self.full_series = series
1520 self.full_series = series
1521 self.applied = applied
1521 self.applied = applied
1522 self.parse_series()
1522 self.parse_series()
1523 self.series_dirty = 1
1523 self.series_dirty = 1
1524 self.applied_dirty = 1
1524 self.applied_dirty = 1
1525 heads = repo.changelog.heads()
1525 heads = repo.changelog.heads()
1526 if delete:
1526 if delete:
1527 if rev not in heads:
1527 if rev not in heads:
1528 self.ui.warn(_("save entry has children, leaving it alone\n"))
1528 self.ui.warn(_("save entry has children, leaving it alone\n"))
1529 else:
1529 else:
1530 self.ui.warn(_("removing save entry %s\n") % short(rev))
1530 self.ui.warn(_("removing save entry %s\n") % short(rev))
1531 pp = repo.dirstate.parents()
1531 pp = repo.dirstate.parents()
1532 if rev in pp:
1532 if rev in pp:
1533 update = True
1533 update = True
1534 else:
1534 else:
1535 update = False
1535 update = False
1536 self.strip(repo, rev, update=update, backup='strip')
1536 self.strip(repo, rev, update=update, backup='strip')
1537 if qpp:
1537 if qpp:
1538 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1538 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1539 (short(qpp[0]), short(qpp[1])))
1539 (short(qpp[0]), short(qpp[1])))
1540 if qupdate:
1540 if qupdate:
1541 self.ui.status(_("queue directory updating\n"))
1541 self.ui.status(_("queue directory updating\n"))
1542 r = self.qrepo()
1542 r = self.qrepo()
1543 if not r:
1543 if not r:
1544 self.ui.warn(_("Unable to load queue repository\n"))
1544 self.ui.warn(_("Unable to load queue repository\n"))
1545 return 1
1545 return 1
1546 hg.clean(r, qpp[0])
1546 hg.clean(r, qpp[0])
1547
1547
1548 def save(self, repo, msg=None):
1548 def save(self, repo, msg=None):
1549 if len(self.applied) == 0:
1549 if len(self.applied) == 0:
1550 self.ui.warn(_("save: no patches applied, exiting\n"))
1550 self.ui.warn(_("save: no patches applied, exiting\n"))
1551 return 1
1551 return 1
1552 if self.issaveline(self.applied[-1]):
1552 if self.issaveline(self.applied[-1]):
1553 self.ui.warn(_("status is already saved\n"))
1553 self.ui.warn(_("status is already saved\n"))
1554 return 1
1554 return 1
1555
1555
1556 ar = [':' + x for x in self.full_series]
1556 ar = [':' + x for x in self.full_series]
1557 if not msg:
1557 if not msg:
1558 msg = _("hg patches saved state")
1558 msg = _("hg patches saved state")
1559 else:
1559 else:
1560 msg = "hg patches: " + msg.rstrip('\r\n')
1560 msg = "hg patches: " + msg.rstrip('\r\n')
1561 r = self.qrepo()
1561 r = self.qrepo()
1562 if r:
1562 if r:
1563 pp = r.dirstate.parents()
1563 pp = r.dirstate.parents()
1564 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1564 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1565 msg += "\n\nPatch Data:\n"
1565 msg += "\n\nPatch Data:\n"
1566 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1566 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1567 "\n".join(ar) + '\n' or "")
1567 "\n".join(ar) + '\n' or "")
1568 n = repo.commit(text, force=True)
1568 n = repo.commit(text, force=True)
1569 if not n:
1569 if not n:
1570 self.ui.warn(_("repo commit failed\n"))
1570 self.ui.warn(_("repo commit failed\n"))
1571 return 1
1571 return 1
1572 self.applied.append(statusentry(hex(n),'.hg.patches.save.line'))
1572 self.applied.append(statusentry(hex(n),'.hg.patches.save.line'))
1573 self.applied_dirty = 1
1573 self.applied_dirty = 1
1574 self.removeundo(repo)
1574 self.removeundo(repo)
1575
1575
1576 def full_series_end(self):
1576 def full_series_end(self):
1577 if len(self.applied) > 0:
1577 if len(self.applied) > 0:
1578 p = self.applied[-1].name
1578 p = self.applied[-1].name
1579 end = self.find_series(p)
1579 end = self.find_series(p)
1580 if end is None:
1580 if end is None:
1581 return len(self.full_series)
1581 return len(self.full_series)
1582 return end + 1
1582 return end + 1
1583 return 0
1583 return 0
1584
1584
1585 def series_end(self, all_patches=False):
1585 def series_end(self, all_patches=False):
1586 """If all_patches is False, return the index of the next pushable patch
1586 """If all_patches is False, return the index of the next pushable patch
1587 in the series, or the series length. If all_patches is True, return the
1587 in the series, or the series length. If all_patches is True, return the
1588 index of the first patch past the last applied one.
1588 index of the first patch past the last applied one.
1589 """
1589 """
1590 end = 0
1590 end = 0
1591 def next(start):
1591 def next(start):
1592 if all_patches:
1592 if all_patches:
1593 return start
1593 return start
1594 i = start
1594 i = start
1595 while i < len(self.series):
1595 while i < len(self.series):
1596 p, reason = self.pushable(i)
1596 p, reason = self.pushable(i)
1597 if p:
1597 if p:
1598 break
1598 break
1599 self.explain_pushable(i)
1599 self.explain_pushable(i)
1600 i += 1
1600 i += 1
1601 return i
1601 return i
1602 if len(self.applied) > 0:
1602 if len(self.applied) > 0:
1603 p = self.applied[-1].name
1603 p = self.applied[-1].name
1604 try:
1604 try:
1605 end = self.series.index(p)
1605 end = self.series.index(p)
1606 except ValueError:
1606 except ValueError:
1607 return 0
1607 return 0
1608 return next(end + 1)
1608 return next(end + 1)
1609 return next(end)
1609 return next(end)
1610
1610
1611 def appliedname(self, index):
1611 def appliedname(self, index):
1612 pname = self.applied[index].name
1612 pname = self.applied[index].name
1613 if not self.ui.verbose:
1613 if not self.ui.verbose:
1614 p = pname
1614 p = pname
1615 else:
1615 else:
1616 p = str(self.series.index(pname)) + " " + pname
1616 p = str(self.series.index(pname)) + " " + pname
1617 return p
1617 return p
1618
1618
1619 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1619 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1620 force=None, git=False):
1620 force=None, git=False):
1621 def checkseries(patchname):
1621 def checkseries(patchname):
1622 if patchname in self.series:
1622 if patchname in self.series:
1623 raise util.Abort(_('patch %s is already in the series file')
1623 raise util.Abort(_('patch %s is already in the series file')
1624 % patchname)
1624 % patchname)
1625 def checkfile(patchname):
1625 def checkfile(patchname):
1626 if not force and os.path.exists(self.join(patchname)):
1626 if not force and os.path.exists(self.join(patchname)):
1627 raise util.Abort(_('patch "%s" already exists')
1627 raise util.Abort(_('patch "%s" already exists')
1628 % patchname)
1628 % patchname)
1629
1629
1630 if rev:
1630 if rev:
1631 if files:
1631 if files:
1632 raise util.Abort(_('option "-r" not valid when importing '
1632 raise util.Abort(_('option "-r" not valid when importing '
1633 'files'))
1633 'files'))
1634 rev = cmdutil.revrange(repo, rev)
1634 rev = cmdutil.revrange(repo, rev)
1635 rev.sort(reverse=True)
1635 rev.sort(reverse=True)
1636 if (len(files) > 1 or len(rev) > 1) and patchname:
1636 if (len(files) > 1 or len(rev) > 1) and patchname:
1637 raise util.Abort(_('option "-n" not valid when importing multiple '
1637 raise util.Abort(_('option "-n" not valid when importing multiple '
1638 'patches'))
1638 'patches'))
1639 i = 0
1639 i = 0
1640 added = []
1640 added = []
1641 if rev:
1641 if rev:
1642 # If mq patches are applied, we can only import revisions
1642 # If mq patches are applied, we can only import revisions
1643 # that form a linear path to qbase.
1643 # that form a linear path to qbase.
1644 # Otherwise, they should form a linear path to a head.
1644 # Otherwise, they should form a linear path to a head.
1645 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1645 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1646 if len(heads) > 1:
1646 if len(heads) > 1:
1647 raise util.Abort(_('revision %d is the root of more than one '
1647 raise util.Abort(_('revision %d is the root of more than one '
1648 'branch') % rev[-1])
1648 'branch') % rev[-1])
1649 if self.applied:
1649 if self.applied:
1650 base = hex(repo.changelog.node(rev[0]))
1650 base = hex(repo.changelog.node(rev[0]))
1651 if base in [n.rev for n in self.applied]:
1651 if base in [n.rev for n in self.applied]:
1652 raise util.Abort(_('revision %d is already managed')
1652 raise util.Abort(_('revision %d is already managed')
1653 % rev[0])
1653 % rev[0])
1654 if heads != [bin(self.applied[-1].rev)]:
1654 if heads != [bin(self.applied[-1].rev)]:
1655 raise util.Abort(_('revision %d is not the parent of '
1655 raise util.Abort(_('revision %d is not the parent of '
1656 'the queue') % rev[0])
1656 'the queue') % rev[0])
1657 base = repo.changelog.rev(bin(self.applied[0].rev))
1657 base = repo.changelog.rev(bin(self.applied[0].rev))
1658 lastparent = repo.changelog.parentrevs(base)[0]
1658 lastparent = repo.changelog.parentrevs(base)[0]
1659 else:
1659 else:
1660 if heads != [repo.changelog.node(rev[0])]:
1660 if heads != [repo.changelog.node(rev[0])]:
1661 raise util.Abort(_('revision %d has unmanaged children')
1661 raise util.Abort(_('revision %d has unmanaged children')
1662 % rev[0])
1662 % rev[0])
1663 lastparent = None
1663 lastparent = None
1664
1664
1665 diffopts = self.diffopts({'git': git})
1665 diffopts = self.diffopts({'git': git})
1666 for r in rev:
1666 for r in rev:
1667 p1, p2 = repo.changelog.parentrevs(r)
1667 p1, p2 = repo.changelog.parentrevs(r)
1668 n = repo.changelog.node(r)
1668 n = repo.changelog.node(r)
1669 if p2 != nullrev:
1669 if p2 != nullrev:
1670 raise util.Abort(_('cannot import merge revision %d') % r)
1670 raise util.Abort(_('cannot import merge revision %d') % r)
1671 if lastparent and lastparent != r:
1671 if lastparent and lastparent != r:
1672 raise util.Abort(_('revision %d is not the parent of %d')
1672 raise util.Abort(_('revision %d is not the parent of %d')
1673 % (r, lastparent))
1673 % (r, lastparent))
1674 lastparent = p1
1674 lastparent = p1
1675
1675
1676 if not patchname:
1676 if not patchname:
1677 patchname = normname('%d.diff' % r)
1677 patchname = normname('%d.diff' % r)
1678 self.check_reserved_name(patchname)
1678 self.check_reserved_name(patchname)
1679 checkseries(patchname)
1679 checkseries(patchname)
1680 checkfile(patchname)
1680 checkfile(patchname)
1681 self.full_series.insert(0, patchname)
1681 self.full_series.insert(0, patchname)
1682
1682
1683 patchf = self.opener(patchname, "w")
1683 patchf = self.opener(patchname, "w")
1684 patch.export(repo, [n], fp=patchf, opts=diffopts)
1684 patch.export(repo, [n], fp=patchf, opts=diffopts)
1685 patchf.close()
1685 patchf.close()
1686
1686
1687 se = statusentry(hex(n), patchname)
1687 se = statusentry(hex(n), patchname)
1688 self.applied.insert(0, se)
1688 self.applied.insert(0, se)
1689
1689
1690 added.append(patchname)
1690 added.append(patchname)
1691 patchname = None
1691 patchname = None
1692 self.parse_series()
1692 self.parse_series()
1693 self.applied_dirty = 1
1693 self.applied_dirty = 1
1694
1694
1695 for filename in files:
1695 for filename in files:
1696 if existing:
1696 if existing:
1697 if filename == '-':
1697 if filename == '-':
1698 raise util.Abort(_('-e is incompatible with import from -'))
1698 raise util.Abort(_('-e is incompatible with import from -'))
1699 if not patchname:
1699 if not patchname:
1700 patchname = normname(filename)
1700 patchname = normname(filename)
1701 self.check_reserved_name(patchname)
1701 self.check_reserved_name(patchname)
1702 if not os.path.isfile(self.join(patchname)):
1702 if not os.path.isfile(self.join(patchname)):
1703 raise util.Abort(_("patch %s does not exist") % patchname)
1703 raise util.Abort(_("patch %s does not exist") % patchname)
1704 else:
1704 else:
1705 try:
1705 try:
1706 if filename == '-':
1706 if filename == '-':
1707 if not patchname:
1707 if not patchname:
1708 raise util.Abort(
1708 raise util.Abort(
1709 _('need --name to import a patch from -'))
1709 _('need --name to import a patch from -'))
1710 text = sys.stdin.read()
1710 text = sys.stdin.read()
1711 else:
1711 else:
1712 text = url.open(self.ui, filename).read()
1712 text = url.open(self.ui, filename).read()
1713 except (OSError, IOError):
1713 except (OSError, IOError):
1714 raise util.Abort(_("unable to read %s") % filename)
1714 raise util.Abort(_("unable to read %s") % filename)
1715 if not patchname:
1715 if not patchname:
1716 patchname = normname(os.path.basename(filename))
1716 patchname = normname(os.path.basename(filename))
1717 self.check_reserved_name(patchname)
1717 self.check_reserved_name(patchname)
1718 checkfile(patchname)
1718 checkfile(patchname)
1719 patchf = self.opener(patchname, "w")
1719 patchf = self.opener(patchname, "w")
1720 patchf.write(text)
1720 patchf.write(text)
1721 if not force:
1721 if not force:
1722 checkseries(patchname)
1722 checkseries(patchname)
1723 if patchname not in self.series:
1723 if patchname not in self.series:
1724 index = self.full_series_end() + i
1724 index = self.full_series_end() + i
1725 self.full_series[index:index] = [patchname]
1725 self.full_series[index:index] = [patchname]
1726 self.parse_series()
1726 self.parse_series()
1727 self.ui.warn(_("adding %s to series file\n") % patchname)
1727 self.ui.warn(_("adding %s to series file\n") % patchname)
1728 i += 1
1728 i += 1
1729 added.append(patchname)
1729 added.append(patchname)
1730 patchname = None
1730 patchname = None
1731 self.series_dirty = 1
1731 self.series_dirty = 1
1732 qrepo = self.qrepo()
1732 qrepo = self.qrepo()
1733 if qrepo:
1733 if qrepo:
1734 qrepo.add(added)
1734 qrepo.add(added)
1735
1735
1736 def delete(ui, repo, *patches, **opts):
1736 def delete(ui, repo, *patches, **opts):
1737 """remove patches from queue
1737 """remove patches from queue
1738
1738
1739 The patches must not be applied, and at least one patch is required. With
1739 The patches must not be applied, and at least one patch is required. With
1740 -k/--keep, the patch files are preserved in the patch directory.
1740 -k/--keep, the patch files are preserved in the patch directory.
1741
1741
1742 To stop managing a patch and move it into permanent history,
1742 To stop managing a patch and move it into permanent history,
1743 use the qfinish command."""
1743 use the qfinish command."""
1744 q = repo.mq
1744 q = repo.mq
1745 q.delete(repo, patches, opts)
1745 q.delete(repo, patches, opts)
1746 q.save_dirty()
1746 q.save_dirty()
1747 return 0
1747 return 0
1748
1748
1749 def applied(ui, repo, patch=None, **opts):
1749 def applied(ui, repo, patch=None, **opts):
1750 """print the patches already applied"""
1750 """print the patches already applied"""
1751
1751
1752 q = repo.mq
1752 q = repo.mq
1753 l = len(q.applied)
1753 l = len(q.applied)
1754
1754
1755 if patch:
1755 if patch:
1756 if patch not in q.series:
1756 if patch not in q.series:
1757 raise util.Abort(_("patch %s is not in series file") % patch)
1757 raise util.Abort(_("patch %s is not in series file") % patch)
1758 end = q.series.index(patch) + 1
1758 end = q.series.index(patch) + 1
1759 else:
1759 else:
1760 end = q.series_end(True)
1760 end = q.series_end(True)
1761
1761
1762 if opts.get('last') and not end:
1762 if opts.get('last') and not end:
1763 ui.write(_("no patches applied\n"))
1763 ui.write(_("no patches applied\n"))
1764 return 1
1764 return 1
1765 elif opts.get('last') and end == 1:
1765 elif opts.get('last') and end == 1:
1766 ui.write(_("only one patch applied\n"))
1766 ui.write(_("only one patch applied\n"))
1767 return 1
1767 return 1
1768 elif opts.get('last'):
1768 elif opts.get('last'):
1769 start = end - 2
1769 start = end - 2
1770 end = 1
1770 end = 1
1771 else:
1771 else:
1772 start = 0
1772 start = 0
1773
1773
1774 return q.qseries(repo, length=end, start=start, status='A',
1774 return q.qseries(repo, length=end, start=start, status='A',
1775 summary=opts.get('summary'))
1775 summary=opts.get('summary'))
1776
1776
1777 def unapplied(ui, repo, patch=None, **opts):
1777 def unapplied(ui, repo, patch=None, **opts):
1778 """print the patches not yet applied"""
1778 """print the patches not yet applied"""
1779
1779
1780 q = repo.mq
1780 q = repo.mq
1781 if patch:
1781 if patch:
1782 if patch not in q.series:
1782 if patch not in q.series:
1783 raise util.Abort(_("patch %s is not in series file") % patch)
1783 raise util.Abort(_("patch %s is not in series file") % patch)
1784 start = q.series.index(patch) + 1
1784 start = q.series.index(patch) + 1
1785 else:
1785 else:
1786 start = q.series_end(True)
1786 start = q.series_end(True)
1787
1787
1788 if start == len(q.series) and opts.get('first'):
1788 if start == len(q.series) and opts.get('first'):
1789 ui.write(_("all patches applied\n"))
1789 ui.write(_("all patches applied\n"))
1790 return 1
1790 return 1
1791
1791
1792 length = opts.get('first') and 1 or None
1792 length = opts.get('first') and 1 or None
1793 return q.qseries(repo, start=start, length=length, status='U',
1793 return q.qseries(repo, start=start, length=length, status='U',
1794 summary=opts.get('summary'))
1794 summary=opts.get('summary'))
1795
1795
1796 def qimport(ui, repo, *filename, **opts):
1796 def qimport(ui, repo, *filename, **opts):
1797 """import a patch
1797 """import a patch
1798
1798
1799 The patch is inserted into the series after the last applied
1799 The patch is inserted into the series after the last applied
1800 patch. If no patches have been applied, qimport prepends the patch
1800 patch. If no patches have been applied, qimport prepends the patch
1801 to the series.
1801 to the series.
1802
1802
1803 The patch will have the same name as its source file unless you
1803 The patch will have the same name as its source file unless you
1804 give it a new one with -n/--name.
1804 give it a new one with -n/--name.
1805
1805
1806 You can register an existing patch inside the patch directory with
1806 You can register an existing patch inside the patch directory with
1807 the -e/--existing flag.
1807 the -e/--existing flag.
1808
1808
1809 With -f/--force, an existing patch of the same name will be
1809 With -f/--force, an existing patch of the same name will be
1810 overwritten.
1810 overwritten.
1811
1811
1812 An existing changeset may be placed under mq control with -r/--rev
1812 An existing changeset may be placed under mq control with -r/--rev
1813 (e.g. qimport --rev tip -n patch will place tip under mq control).
1813 (e.g. qimport --rev tip -n patch will place tip under mq control).
1814 With -g/--git, patches imported with --rev will use the git diff
1814 With -g/--git, patches imported with --rev will use the git diff
1815 format. See the diffs help topic for information on why this is
1815 format. See the diffs help topic for information on why this is
1816 important for preserving rename/copy information and permission
1816 important for preserving rename/copy information and permission
1817 changes.
1817 changes.
1818
1818
1819 To import a patch from standard input, pass - as the patch file.
1819 To import a patch from standard input, pass - as the patch file.
1820 When importing from standard input, a patch name must be specified
1820 When importing from standard input, a patch name must be specified
1821 using the --name flag.
1821 using the --name flag.
1822 """
1822 """
1823 q = repo.mq
1823 q = repo.mq
1824 q.qimport(repo, filename, patchname=opts['name'],
1824 q.qimport(repo, filename, patchname=opts['name'],
1825 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1825 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1826 git=opts['git'])
1826 git=opts['git'])
1827 q.save_dirty()
1827 q.save_dirty()
1828
1828
1829 if opts.get('push') and not opts.get('rev'):
1829 if opts.get('push') and not opts.get('rev'):
1830 return q.push(repo, None)
1830 return q.push(repo, None)
1831 return 0
1831 return 0
1832
1832
1833 def qinit(ui, repo, create):
1833 def qinit(ui, repo, create):
1834 """initialize a new queue repository
1834 """initialize a new queue repository
1835
1835
1836 This command also creates a series file for ordering patches, and
1836 This command also creates a series file for ordering patches, and
1837 an mq-specific .hgignore file in the queue repository, to exclude
1837 an mq-specific .hgignore file in the queue repository, to exclude
1838 the status and guards files (these contain mostly transient state)."""
1838 the status and guards files (these contain mostly transient state)."""
1839 q = repo.mq
1839 q = repo.mq
1840 r = q.init(repo, create)
1840 r = q.init(repo, create)
1841 q.save_dirty()
1841 q.save_dirty()
1842 if r:
1842 if r:
1843 if not os.path.exists(r.wjoin('.hgignore')):
1843 if not os.path.exists(r.wjoin('.hgignore')):
1844 fp = r.wopener('.hgignore', 'w')
1844 fp = r.wopener('.hgignore', 'w')
1845 fp.write('^\\.hg\n')
1845 fp.write('^\\.hg\n')
1846 fp.write('^\\.mq\n')
1846 fp.write('^\\.mq\n')
1847 fp.write('syntax: glob\n')
1847 fp.write('syntax: glob\n')
1848 fp.write('status\n')
1848 fp.write('status\n')
1849 fp.write('guards\n')
1849 fp.write('guards\n')
1850 fp.close()
1850 fp.close()
1851 if not os.path.exists(r.wjoin('series')):
1851 if not os.path.exists(r.wjoin('series')):
1852 r.wopener('series', 'w').close()
1852 r.wopener('series', 'w').close()
1853 r.add(['.hgignore', 'series'])
1853 r.add(['.hgignore', 'series'])
1854 commands.add(ui, r)
1854 commands.add(ui, r)
1855 return 0
1855 return 0
1856
1856
1857 def init(ui, repo, **opts):
1857 def init(ui, repo, **opts):
1858 """init a new queue repository (DEPRECATED)
1858 """init a new queue repository (DEPRECATED)
1859
1859
1860 The queue repository is unversioned by default. If
1860 The queue repository is unversioned by default. If
1861 -c/--create-repo is specified, qinit will create a separate nested
1861 -c/--create-repo is specified, qinit will create a separate nested
1862 repository for patches (qinit -c may also be run later to convert
1862 repository for patches (qinit -c may also be run later to convert
1863 an unversioned patch repository into a versioned one). You can use
1863 an unversioned patch repository into a versioned one). You can use
1864 qcommit to commit changes to this queue repository.
1864 qcommit to commit changes to this queue repository.
1865
1865
1866 This command is deprecated. Without -c, it's implied by other relevant
1866 This command is deprecated. Without -c, it's implied by other relevant
1867 commands. With -c, use hg init -Q instead."""
1867 commands. With -c, use hg init -Q instead."""
1868 return qinit(ui, repo, create=opts['create_repo'])
1868 return qinit(ui, repo, create=opts['create_repo'])
1869
1869
1870 def clone(ui, source, dest=None, **opts):
1870 def clone(ui, source, dest=None, **opts):
1871 '''clone main and patch repository at same time
1871 '''clone main and patch repository at same time
1872
1872
1873 If source is local, destination will have no patches applied. If
1873 If source is local, destination will have no patches applied. If
1874 source is remote, this command can not check if patches are
1874 source is remote, this command can not check if patches are
1875 applied in source, so cannot guarantee that patches are not
1875 applied in source, so cannot guarantee that patches are not
1876 applied in destination. If you clone remote repository, be sure
1876 applied in destination. If you clone remote repository, be sure
1877 before that it has no patches applied.
1877 before that it has no patches applied.
1878
1878
1879 Source patch repository is looked for in <src>/.hg/patches by
1879 Source patch repository is looked for in <src>/.hg/patches by
1880 default. Use -p <url> to change.
1880 default. Use -p <url> to change.
1881
1881
1882 The patch directory must be a nested Mercurial repository, as
1882 The patch directory must be a nested Mercurial repository, as
1883 would be created by qinit -c.
1883 would be created by qinit -c.
1884 '''
1884 '''
1885 def patchdir(repo):
1885 def patchdir(repo):
1886 url = repo.url()
1886 url = repo.url()
1887 if url.endswith('/'):
1887 if url.endswith('/'):
1888 url = url[:-1]
1888 url = url[:-1]
1889 return url + '/.hg/patches'
1889 return url + '/.hg/patches'
1890 if dest is None:
1890 if dest is None:
1891 dest = hg.defaultdest(source)
1891 dest = hg.defaultdest(source)
1892 sr = hg.repository(cmdutil.remoteui(ui, opts), ui.expandpath(source))
1892 sr = hg.repository(cmdutil.remoteui(ui, opts), ui.expandpath(source))
1893 if opts['patches']:
1893 if opts['patches']:
1894 patchespath = ui.expandpath(opts['patches'])
1894 patchespath = ui.expandpath(opts['patches'])
1895 else:
1895 else:
1896 patchespath = patchdir(sr)
1896 patchespath = patchdir(sr)
1897 try:
1897 try:
1898 hg.repository(ui, patchespath)
1898 hg.repository(ui, patchespath)
1899 except error.RepoError:
1899 except error.RepoError:
1900 raise util.Abort(_('versioned patch repository not found'
1900 raise util.Abort(_('versioned patch repository not found'
1901 ' (see qinit -c)'))
1901 ' (see qinit -c)'))
1902 qbase, destrev = None, None
1902 qbase, destrev = None, None
1903 if sr.local():
1903 if sr.local():
1904 if sr.mq.applied:
1904 if sr.mq.applied:
1905 qbase = bin(sr.mq.applied[0].rev)
1905 qbase = bin(sr.mq.applied[0].rev)
1906 if not hg.islocal(dest):
1906 if not hg.islocal(dest):
1907 heads = set(sr.heads())
1907 heads = set(sr.heads())
1908 destrev = list(heads.difference(sr.heads(qbase)))
1908 destrev = list(heads.difference(sr.heads(qbase)))
1909 destrev.append(sr.changelog.parents(qbase)[0])
1909 destrev.append(sr.changelog.parents(qbase)[0])
1910 elif sr.capable('lookup'):
1910 elif sr.capable('lookup'):
1911 try:
1911 try:
1912 qbase = sr.lookup('qbase')
1912 qbase = sr.lookup('qbase')
1913 except error.RepoError:
1913 except error.RepoError:
1914 pass
1914 pass
1915 ui.note(_('cloning main repository\n'))
1915 ui.note(_('cloning main repository\n'))
1916 sr, dr = hg.clone(ui, sr.url(), dest,
1916 sr, dr = hg.clone(ui, sr.url(), dest,
1917 pull=opts['pull'],
1917 pull=opts['pull'],
1918 rev=destrev,
1918 rev=destrev,
1919 update=False,
1919 update=False,
1920 stream=opts['uncompressed'])
1920 stream=opts['uncompressed'])
1921 ui.note(_('cloning patch repository\n'))
1921 ui.note(_('cloning patch repository\n'))
1922 hg.clone(ui, opts['patches'] or patchdir(sr), patchdir(dr),
1922 hg.clone(ui, opts['patches'] or patchdir(sr), patchdir(dr),
1923 pull=opts['pull'], update=not opts['noupdate'],
1923 pull=opts['pull'], update=not opts['noupdate'],
1924 stream=opts['uncompressed'])
1924 stream=opts['uncompressed'])
1925 if dr.local():
1925 if dr.local():
1926 if qbase:
1926 if qbase:
1927 ui.note(_('stripping applied patches from destination '
1927 ui.note(_('stripping applied patches from destination '
1928 'repository\n'))
1928 'repository\n'))
1929 dr.mq.strip(dr, qbase, update=False, backup=None)
1929 dr.mq.strip(dr, qbase, update=False, backup=None)
1930 if not opts['noupdate']:
1930 if not opts['noupdate']:
1931 ui.note(_('updating destination repository\n'))
1931 ui.note(_('updating destination repository\n'))
1932 hg.update(dr, dr.changelog.tip())
1932 hg.update(dr, dr.changelog.tip())
1933
1933
1934 def commit(ui, repo, *pats, **opts):
1934 def commit(ui, repo, *pats, **opts):
1935 """commit changes in the queue repository (DEPRECATED)
1935 """commit changes in the queue repository (DEPRECATED)
1936
1936
1937 This command is deprecated; use hg -Q commit instead."""
1937 This command is deprecated; use hg -Q commit instead."""
1938 q = repo.mq
1938 q = repo.mq
1939 r = q.qrepo()
1939 r = q.qrepo()
1940 if not r:
1940 if not r:
1941 raise util.Abort('no queue repository')
1941 raise util.Abort('no queue repository')
1942 commands.commit(r.ui, r, *pats, **opts)
1942 commands.commit(r.ui, r, *pats, **opts)
1943
1943
1944 def series(ui, repo, **opts):
1944 def series(ui, repo, **opts):
1945 """print the entire series file"""
1945 """print the entire series file"""
1946 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1946 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1947 return 0
1947 return 0
1948
1948
1949 def top(ui, repo, **opts):
1949 def top(ui, repo, **opts):
1950 """print the name of the current patch"""
1950 """print the name of the current patch"""
1951 q = repo.mq
1951 q = repo.mq
1952 t = q.applied and q.series_end(True) or 0
1952 t = q.applied and q.series_end(True) or 0
1953 if t:
1953 if t:
1954 return q.qseries(repo, start=t - 1, length=1, status='A',
1954 return q.qseries(repo, start=t - 1, length=1, status='A',
1955 summary=opts.get('summary'))
1955 summary=opts.get('summary'))
1956 else:
1956 else:
1957 ui.write(_("no patches applied\n"))
1957 ui.write(_("no patches applied\n"))
1958 return 1
1958 return 1
1959
1959
1960 def next(ui, repo, **opts):
1960 def next(ui, repo, **opts):
1961 """print the name of the next patch"""
1961 """print the name of the next patch"""
1962 q = repo.mq
1962 q = repo.mq
1963 end = q.series_end()
1963 end = q.series_end()
1964 if end == len(q.series):
1964 if end == len(q.series):
1965 ui.write(_("all patches applied\n"))
1965 ui.write(_("all patches applied\n"))
1966 return 1
1966 return 1
1967 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1967 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1968
1968
1969 def prev(ui, repo, **opts):
1969 def prev(ui, repo, **opts):
1970 """print the name of the previous patch"""
1970 """print the name of the previous patch"""
1971 q = repo.mq
1971 q = repo.mq
1972 l = len(q.applied)
1972 l = len(q.applied)
1973 if l == 1:
1973 if l == 1:
1974 ui.write(_("only one patch applied\n"))
1974 ui.write(_("only one patch applied\n"))
1975 return 1
1975 return 1
1976 if not l:
1976 if not l:
1977 ui.write(_("no patches applied\n"))
1977 ui.write(_("no patches applied\n"))
1978 return 1
1978 return 1
1979 return q.qseries(repo, start=l - 2, length=1, status='A',
1979 return q.qseries(repo, start=l - 2, length=1, status='A',
1980 summary=opts.get('summary'))
1980 summary=opts.get('summary'))
1981
1981
1982 def setupheaderopts(ui, opts):
1982 def setupheaderopts(ui, opts):
1983 if not opts.get('user') and opts.get('currentuser'):
1983 if not opts.get('user') and opts.get('currentuser'):
1984 opts['user'] = ui.username()
1984 opts['user'] = ui.username()
1985 if not opts.get('date') and opts.get('currentdate'):
1985 if not opts.get('date') and opts.get('currentdate'):
1986 opts['date'] = "%d %d" % util.makedate()
1986 opts['date'] = "%d %d" % util.makedate()
1987
1987
1988 def new(ui, repo, patch, *args, **opts):
1988 def new(ui, repo, patch, *args, **opts):
1989 """create a new patch
1989 """create a new patch
1990
1990
1991 qnew creates a new patch on top of the currently-applied patch (if
1991 qnew creates a new patch on top of the currently-applied patch (if
1992 any). It will refuse to run if there are any outstanding changes
1992 any). It will refuse to run if there are any outstanding changes
1993 unless -f/--force is specified, in which case the patch will be
1993 unless -f/--force is specified, in which case the patch will be
1994 initialized with them. You may also use -I/--include,
1994 initialized with them. You may also use -I/--include,
1995 -X/--exclude, and/or a list of files after the patch name to add
1995 -X/--exclude, and/or a list of files after the patch name to add
1996 only changes to matching files to the new patch, leaving the rest
1996 only changes to matching files to the new patch, leaving the rest
1997 as uncommitted modifications.
1997 as uncommitted modifications.
1998
1998
1999 -u/--user and -d/--date can be used to set the (given) user and
1999 -u/--user and -d/--date can be used to set the (given) user and
2000 date, respectively. -U/--currentuser and -D/--currentdate set user
2000 date, respectively. -U/--currentuser and -D/--currentdate set user
2001 to current user and date to current date.
2001 to current user and date to current date.
2002
2002
2003 -e/--edit, -m/--message or -l/--logfile set the patch header as
2003 -e/--edit, -m/--message or -l/--logfile set the patch header as
2004 well as the commit message. If none is specified, the header is
2004 well as the commit message. If none is specified, the header is
2005 empty and the commit message is '[mq]: PATCH'.
2005 empty and the commit message is '[mq]: PATCH'.
2006
2006
2007 Use the -g/--git option to keep the patch in the git extended diff
2007 Use the -g/--git option to keep the patch in the git extended diff
2008 format. Read the diffs help topic for more information on why this
2008 format. Read the diffs help topic for more information on why this
2009 is important for preserving permission changes and copy/rename
2009 is important for preserving permission changes and copy/rename
2010 information.
2010 information.
2011 """
2011 """
2012 msg = cmdutil.logmessage(opts)
2012 msg = cmdutil.logmessage(opts)
2013 def getmsg():
2013 def getmsg():
2014 return ui.edit(msg, ui.username())
2014 return ui.edit(msg, ui.username())
2015 q = repo.mq
2015 q = repo.mq
2016 opts['msg'] = msg
2016 opts['msg'] = msg
2017 if opts.get('edit'):
2017 if opts.get('edit'):
2018 opts['msg'] = getmsg
2018 opts['msg'] = getmsg
2019 else:
2019 else:
2020 opts['msg'] = msg
2020 opts['msg'] = msg
2021 setupheaderopts(ui, opts)
2021 setupheaderopts(ui, opts)
2022 q.new(repo, patch, *args, **opts)
2022 q.new(repo, patch, *args, **opts)
2023 q.save_dirty()
2023 q.save_dirty()
2024 return 0
2024 return 0
2025
2025
2026 def refresh(ui, repo, *pats, **opts):
2026 def refresh(ui, repo, *pats, **opts):
2027 """update the current patch
2027 """update the current patch
2028
2028
2029 If any file patterns are provided, the refreshed patch will
2029 If any file patterns are provided, the refreshed patch will
2030 contain only the modifications that match those patterns; the
2030 contain only the modifications that match those patterns; the
2031 remaining modifications will remain in the working directory.
2031 remaining modifications will remain in the working directory.
2032
2032
2033 If -s/--short is specified, files currently included in the patch
2033 If -s/--short is specified, files currently included in the patch
2034 will be refreshed just like matched files and remain in the patch.
2034 will be refreshed just like matched files and remain in the patch.
2035
2035
2036 hg add/remove/copy/rename work as usual, though you might want to
2036 hg add/remove/copy/rename work as usual, though you might want to
2037 use git-style patches (-g/--git or [diff] git=1) to track copies
2037 use git-style patches (-g/--git or [diff] git=1) to track copies
2038 and renames. See the diffs help topic for more information on the
2038 and renames. See the diffs help topic for more information on the
2039 git diff format.
2039 git diff format.
2040 """
2040 """
2041 q = repo.mq
2041 q = repo.mq
2042 message = cmdutil.logmessage(opts)
2042 message = cmdutil.logmessage(opts)
2043 if opts['edit']:
2043 if opts['edit']:
2044 if not q.applied:
2044 if not q.applied:
2045 ui.write(_("no patches applied\n"))
2045 ui.write(_("no patches applied\n"))
2046 return 1
2046 return 1
2047 if message:
2047 if message:
2048 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2048 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2049 patch = q.applied[-1].name
2049 patch = q.applied[-1].name
2050 ph = patchheader(q.join(patch), q.plainmode)
2050 ph = patchheader(q.join(patch), q.plainmode)
2051 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
2051 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
2052 setupheaderopts(ui, opts)
2052 setupheaderopts(ui, opts)
2053 ret = q.refresh(repo, pats, msg=message, **opts)
2053 ret = q.refresh(repo, pats, msg=message, **opts)
2054 q.save_dirty()
2054 q.save_dirty()
2055 return ret
2055 return ret
2056
2056
2057 def diff(ui, repo, *pats, **opts):
2057 def diff(ui, repo, *pats, **opts):
2058 """diff of the current patch and subsequent modifications
2058 """diff of the current patch and subsequent modifications
2059
2059
2060 Shows a diff which includes the current patch as well as any
2060 Shows a diff which includes the current patch as well as any
2061 changes which have been made in the working directory since the
2061 changes which have been made in the working directory since the
2062 last refresh (thus showing what the current patch would become
2062 last refresh (thus showing what the current patch would become
2063 after a qrefresh).
2063 after a qrefresh).
2064
2064
2065 Use 'hg diff' if you only want to see the changes made since the
2065 Use 'hg diff' if you only want to see the changes made since the
2066 last qrefresh, or 'hg export qtip' if you want to see changes made
2066 last qrefresh, or 'hg export qtip' if you want to see changes made
2067 by the current patch without including changes made since the
2067 by the current patch without including changes made since the
2068 qrefresh.
2068 qrefresh.
2069 """
2069 """
2070 repo.mq.diff(repo, pats, opts)
2070 repo.mq.diff(repo, pats, opts)
2071 return 0
2071 return 0
2072
2072
2073 def fold(ui, repo, *files, **opts):
2073 def fold(ui, repo, *files, **opts):
2074 """fold the named patches into the current patch
2074 """fold the named patches into the current patch
2075
2075
2076 Patches must not yet be applied. Each patch will be successively
2076 Patches must not yet be applied. Each patch will be successively
2077 applied to the current patch in the order given. If all the
2077 applied to the current patch in the order given. If all the
2078 patches apply successfully, the current patch will be refreshed
2078 patches apply successfully, the current patch will be refreshed
2079 with the new cumulative patch, and the folded patches will be
2079 with the new cumulative patch, and the folded patches will be
2080 deleted. With -k/--keep, the folded patch files will not be
2080 deleted. With -k/--keep, the folded patch files will not be
2081 removed afterwards.
2081 removed afterwards.
2082
2082
2083 The header for each folded patch will be concatenated with the
2083 The header for each folded patch will be concatenated with the
2084 current patch header, separated by a line of '* * *'."""
2084 current patch header, separated by a line of '* * *'."""
2085
2085
2086 q = repo.mq
2086 q = repo.mq
2087
2087
2088 if not files:
2088 if not files:
2089 raise util.Abort(_('qfold requires at least one patch name'))
2089 raise util.Abort(_('qfold requires at least one patch name'))
2090 if not q.check_toppatch(repo)[0]:
2090 if not q.check_toppatch(repo)[0]:
2091 raise util.Abort(_('No patches applied'))
2091 raise util.Abort(_('No patches applied'))
2092 q.check_localchanges(repo)
2092 q.check_localchanges(repo)
2093
2093
2094 message = cmdutil.logmessage(opts)
2094 message = cmdutil.logmessage(opts)
2095 if opts['edit']:
2095 if opts['edit']:
2096 if message:
2096 if message:
2097 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2097 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2098
2098
2099 parent = q.lookup('qtip')
2099 parent = q.lookup('qtip')
2100 patches = []
2100 patches = []
2101 messages = []
2101 messages = []
2102 for f in files:
2102 for f in files:
2103 p = q.lookup(f)
2103 p = q.lookup(f)
2104 if p in patches or p == parent:
2104 if p in patches or p == parent:
2105 ui.warn(_('Skipping already folded patch %s') % p)
2105 ui.warn(_('Skipping already folded patch %s') % p)
2106 if q.isapplied(p):
2106 if q.isapplied(p):
2107 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
2107 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
2108 patches.append(p)
2108 patches.append(p)
2109
2109
2110 for p in patches:
2110 for p in patches:
2111 if not message:
2111 if not message:
2112 ph = patchheader(q.join(p), q.plainmode)
2112 ph = patchheader(q.join(p), q.plainmode)
2113 if ph.message:
2113 if ph.message:
2114 messages.append(ph.message)
2114 messages.append(ph.message)
2115 pf = q.join(p)
2115 pf = q.join(p)
2116 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2116 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2117 if not patchsuccess:
2117 if not patchsuccess:
2118 raise util.Abort(_('Error folding patch %s') % p)
2118 raise util.Abort(_('Error folding patch %s') % p)
2119 patch.updatedir(ui, repo, files)
2119 patch.updatedir(ui, repo, files)
2120
2120
2121 if not message:
2121 if not message:
2122 ph = patchheader(q.join(parent), q.plainmode)
2122 ph = patchheader(q.join(parent), q.plainmode)
2123 message, user = ph.message, ph.user
2123 message, user = ph.message, ph.user
2124 for msg in messages:
2124 for msg in messages:
2125 message.append('* * *')
2125 message.append('* * *')
2126 message.extend(msg)
2126 message.extend(msg)
2127 message = '\n'.join(message)
2127 message = '\n'.join(message)
2128
2128
2129 if opts['edit']:
2129 if opts['edit']:
2130 message = ui.edit(message, user or ui.username())
2130 message = ui.edit(message, user or ui.username())
2131
2131
2132 diffopts = q.patchopts(q.diffopts(), *patches)
2132 diffopts = q.patchopts(q.diffopts(), *patches)
2133 q.refresh(repo, msg=message, git=diffopts.git)
2133 q.refresh(repo, msg=message, git=diffopts.git)
2134 q.delete(repo, patches, opts)
2134 q.delete(repo, patches, opts)
2135 q.save_dirty()
2135 q.save_dirty()
2136
2136
2137 def goto(ui, repo, patch, **opts):
2137 def goto(ui, repo, patch, **opts):
2138 '''push or pop patches until named patch is at top of stack'''
2138 '''push or pop patches until named patch is at top of stack'''
2139 q = repo.mq
2139 q = repo.mq
2140 patch = q.lookup(patch)
2140 patch = q.lookup(patch)
2141 if q.isapplied(patch):
2141 if q.isapplied(patch):
2142 ret = q.pop(repo, patch, force=opts['force'])
2142 ret = q.pop(repo, patch, force=opts['force'])
2143 else:
2143 else:
2144 ret = q.push(repo, patch, force=opts['force'])
2144 ret = q.push(repo, patch, force=opts['force'])
2145 q.save_dirty()
2145 q.save_dirty()
2146 return ret
2146 return ret
2147
2147
2148 def guard(ui, repo, *args, **opts):
2148 def guard(ui, repo, *args, **opts):
2149 '''set or print guards for a patch
2149 '''set or print guards for a patch
2150
2150
2151 Guards control whether a patch can be pushed. A patch with no
2151 Guards control whether a patch can be pushed. A patch with no
2152 guards is always pushed. A patch with a positive guard ("+foo") is
2152 guards is always pushed. A patch with a positive guard ("+foo") is
2153 pushed only if the qselect command has activated it. A patch with
2153 pushed only if the qselect command has activated it. A patch with
2154 a negative guard ("-foo") is never pushed if the qselect command
2154 a negative guard ("-foo") is never pushed if the qselect command
2155 has activated it.
2155 has activated it.
2156
2156
2157 With no arguments, print the currently active guards.
2157 With no arguments, print the currently active guards.
2158 With arguments, set guards for the named patch.
2158 With arguments, set guards for the named patch.
2159 NOTE: Specifying negative guards now requires '--'.
2159 NOTE: Specifying negative guards now requires '--'.
2160
2160
2161 To set guards on another patch::
2161 To set guards on another patch::
2162
2162
2163 hg qguard other.patch -- +2.6.17 -stable
2163 hg qguard other.patch -- +2.6.17 -stable
2164 '''
2164 '''
2165 def status(idx):
2165 def status(idx):
2166 guards = q.series_guards[idx] or ['unguarded']
2166 guards = q.series_guards[idx] or ['unguarded']
2167 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
2167 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
2168 q = repo.mq
2168 q = repo.mq
2169 patch = None
2169 patch = None
2170 args = list(args)
2170 args = list(args)
2171 if opts['list']:
2171 if opts['list']:
2172 if args or opts['none']:
2172 if args or opts['none']:
2173 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
2173 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
2174 for i in xrange(len(q.series)):
2174 for i in xrange(len(q.series)):
2175 status(i)
2175 status(i)
2176 return
2176 return
2177 if not args or args[0][0:1] in '-+':
2177 if not args or args[0][0:1] in '-+':
2178 if not q.applied:
2178 if not q.applied:
2179 raise util.Abort(_('no patches applied'))
2179 raise util.Abort(_('no patches applied'))
2180 patch = q.applied[-1].name
2180 patch = q.applied[-1].name
2181 if patch is None and args[0][0:1] not in '-+':
2181 if patch is None and args[0][0:1] not in '-+':
2182 patch = args.pop(0)
2182 patch = args.pop(0)
2183 if patch is None:
2183 if patch is None:
2184 raise util.Abort(_('no patch to work with'))
2184 raise util.Abort(_('no patch to work with'))
2185 if args or opts['none']:
2185 if args or opts['none']:
2186 idx = q.find_series(patch)
2186 idx = q.find_series(patch)
2187 if idx is None:
2187 if idx is None:
2188 raise util.Abort(_('no patch named %s') % patch)
2188 raise util.Abort(_('no patch named %s') % patch)
2189 q.set_guards(idx, args)
2189 q.set_guards(idx, args)
2190 q.save_dirty()
2190 q.save_dirty()
2191 else:
2191 else:
2192 status(q.series.index(q.lookup(patch)))
2192 status(q.series.index(q.lookup(patch)))
2193
2193
2194 def header(ui, repo, patch=None):
2194 def header(ui, repo, patch=None):
2195 """print the header of the topmost or specified patch"""
2195 """print the header of the topmost or specified patch"""
2196 q = repo.mq
2196 q = repo.mq
2197
2197
2198 if patch:
2198 if patch:
2199 patch = q.lookup(patch)
2199 patch = q.lookup(patch)
2200 else:
2200 else:
2201 if not q.applied:
2201 if not q.applied:
2202 ui.write('no patches applied\n')
2202 ui.write(_('no patches applied\n'))
2203 return 1
2203 return 1
2204 patch = q.lookup('qtip')
2204 patch = q.lookup('qtip')
2205 ph = patchheader(q.join(patch), q.plainmode)
2205 ph = patchheader(q.join(patch), q.plainmode)
2206
2206
2207 ui.write('\n'.join(ph.message) + '\n')
2207 ui.write('\n'.join(ph.message) + '\n')
2208
2208
2209 def lastsavename(path):
2209 def lastsavename(path):
2210 (directory, base) = os.path.split(path)
2210 (directory, base) = os.path.split(path)
2211 names = os.listdir(directory)
2211 names = os.listdir(directory)
2212 namere = re.compile("%s.([0-9]+)" % base)
2212 namere = re.compile("%s.([0-9]+)" % base)
2213 maxindex = None
2213 maxindex = None
2214 maxname = None
2214 maxname = None
2215 for f in names:
2215 for f in names:
2216 m = namere.match(f)
2216 m = namere.match(f)
2217 if m:
2217 if m:
2218 index = int(m.group(1))
2218 index = int(m.group(1))
2219 if maxindex is None or index > maxindex:
2219 if maxindex is None or index > maxindex:
2220 maxindex = index
2220 maxindex = index
2221 maxname = f
2221 maxname = f
2222 if maxname:
2222 if maxname:
2223 return (os.path.join(directory, maxname), maxindex)
2223 return (os.path.join(directory, maxname), maxindex)
2224 return (None, None)
2224 return (None, None)
2225
2225
2226 def savename(path):
2226 def savename(path):
2227 (last, index) = lastsavename(path)
2227 (last, index) = lastsavename(path)
2228 if last is None:
2228 if last is None:
2229 index = 0
2229 index = 0
2230 newpath = path + ".%d" % (index + 1)
2230 newpath = path + ".%d" % (index + 1)
2231 return newpath
2231 return newpath
2232
2232
2233 def push(ui, repo, patch=None, **opts):
2233 def push(ui, repo, patch=None, **opts):
2234 """push the next patch onto the stack
2234 """push the next patch onto the stack
2235
2235
2236 When -f/--force is applied, all local changes in patched files
2236 When -f/--force is applied, all local changes in patched files
2237 will be lost.
2237 will be lost.
2238 """
2238 """
2239 q = repo.mq
2239 q = repo.mq
2240 mergeq = None
2240 mergeq = None
2241
2241
2242 if opts['merge']:
2242 if opts['merge']:
2243 if opts['name']:
2243 if opts['name']:
2244 newpath = repo.join(opts['name'])
2244 newpath = repo.join(opts['name'])
2245 else:
2245 else:
2246 newpath, i = lastsavename(q.path)
2246 newpath, i = lastsavename(q.path)
2247 if not newpath:
2247 if not newpath:
2248 ui.warn(_("no saved queues found, please use -n\n"))
2248 ui.warn(_("no saved queues found, please use -n\n"))
2249 return 1
2249 return 1
2250 mergeq = queue(ui, repo.join(""), newpath)
2250 mergeq = queue(ui, repo.join(""), newpath)
2251 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2251 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2252 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
2252 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
2253 mergeq=mergeq, all=opts.get('all'))
2253 mergeq=mergeq, all=opts.get('all'))
2254 return ret
2254 return ret
2255
2255
2256 def pop(ui, repo, patch=None, **opts):
2256 def pop(ui, repo, patch=None, **opts):
2257 """pop the current patch off the stack
2257 """pop the current patch off the stack
2258
2258
2259 By default, pops off the top of the patch stack. If given a patch
2259 By default, pops off the top of the patch stack. If given a patch
2260 name, keeps popping off patches until the named patch is at the
2260 name, keeps popping off patches until the named patch is at the
2261 top of the stack.
2261 top of the stack.
2262 """
2262 """
2263 localupdate = True
2263 localupdate = True
2264 if opts['name']:
2264 if opts['name']:
2265 q = queue(ui, repo.join(""), repo.join(opts['name']))
2265 q = queue(ui, repo.join(""), repo.join(opts['name']))
2266 ui.warn(_('using patch queue: %s\n') % q.path)
2266 ui.warn(_('using patch queue: %s\n') % q.path)
2267 localupdate = False
2267 localupdate = False
2268 else:
2268 else:
2269 q = repo.mq
2269 q = repo.mq
2270 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
2270 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
2271 all=opts['all'])
2271 all=opts['all'])
2272 q.save_dirty()
2272 q.save_dirty()
2273 return ret
2273 return ret
2274
2274
2275 def rename(ui, repo, patch, name=None, **opts):
2275 def rename(ui, repo, patch, name=None, **opts):
2276 """rename a patch
2276 """rename a patch
2277
2277
2278 With one argument, renames the current patch to PATCH1.
2278 With one argument, renames the current patch to PATCH1.
2279 With two arguments, renames PATCH1 to PATCH2."""
2279 With two arguments, renames PATCH1 to PATCH2."""
2280
2280
2281 q = repo.mq
2281 q = repo.mq
2282
2282
2283 if not name:
2283 if not name:
2284 name = patch
2284 name = patch
2285 patch = None
2285 patch = None
2286
2286
2287 if patch:
2287 if patch:
2288 patch = q.lookup(patch)
2288 patch = q.lookup(patch)
2289 else:
2289 else:
2290 if not q.applied:
2290 if not q.applied:
2291 ui.write(_('no patches applied\n'))
2291 ui.write(_('no patches applied\n'))
2292 return
2292 return
2293 patch = q.lookup('qtip')
2293 patch = q.lookup('qtip')
2294 absdest = q.join(name)
2294 absdest = q.join(name)
2295 if os.path.isdir(absdest):
2295 if os.path.isdir(absdest):
2296 name = normname(os.path.join(name, os.path.basename(patch)))
2296 name = normname(os.path.join(name, os.path.basename(patch)))
2297 absdest = q.join(name)
2297 absdest = q.join(name)
2298 if os.path.exists(absdest):
2298 if os.path.exists(absdest):
2299 raise util.Abort(_('%s already exists') % absdest)
2299 raise util.Abort(_('%s already exists') % absdest)
2300
2300
2301 if name in q.series:
2301 if name in q.series:
2302 raise util.Abort(
2302 raise util.Abort(
2303 _('A patch named %s already exists in the series file') % name)
2303 _('A patch named %s already exists in the series file') % name)
2304
2304
2305 if ui.verbose:
2305 ui.note(_('renaming %s to %s\n') % (patch, name))
2306 ui.write('renaming %s to %s\n' % (patch, name))
2307 i = q.find_series(patch)
2306 i = q.find_series(patch)
2308 guards = q.guard_re.findall(q.full_series[i])
2307 guards = q.guard_re.findall(q.full_series[i])
2309 q.full_series[i] = name + ''.join([' #' + g for g in guards])
2308 q.full_series[i] = name + ''.join([' #' + g for g in guards])
2310 q.parse_series()
2309 q.parse_series()
2311 q.series_dirty = 1
2310 q.series_dirty = 1
2312
2311
2313 info = q.isapplied(patch)
2312 info = q.isapplied(patch)
2314 if info:
2313 if info:
2315 q.applied[info[0]] = statusentry(info[1], name)
2314 q.applied[info[0]] = statusentry(info[1], name)
2316 q.applied_dirty = 1
2315 q.applied_dirty = 1
2317
2316
2318 util.rename(q.join(patch), absdest)
2317 util.rename(q.join(patch), absdest)
2319 r = q.qrepo()
2318 r = q.qrepo()
2320 if r:
2319 if r:
2321 wlock = r.wlock()
2320 wlock = r.wlock()
2322 try:
2321 try:
2323 if r.dirstate[patch] == 'a':
2322 if r.dirstate[patch] == 'a':
2324 r.dirstate.forget(patch)
2323 r.dirstate.forget(patch)
2325 r.dirstate.add(name)
2324 r.dirstate.add(name)
2326 else:
2325 else:
2327 if r.dirstate[name] == 'r':
2326 if r.dirstate[name] == 'r':
2328 r.undelete([name])
2327 r.undelete([name])
2329 r.copy(patch, name)
2328 r.copy(patch, name)
2330 r.remove([patch], False)
2329 r.remove([patch], False)
2331 finally:
2330 finally:
2332 wlock.release()
2331 wlock.release()
2333
2332
2334 q.save_dirty()
2333 q.save_dirty()
2335
2334
2336 def restore(ui, repo, rev, **opts):
2335 def restore(ui, repo, rev, **opts):
2337 """restore the queue state saved by a revision (DEPRECATED)
2336 """restore the queue state saved by a revision (DEPRECATED)
2338
2337
2339 This command is deprecated, use rebase --mq instead."""
2338 This command is deprecated, use rebase --mq instead."""
2340 rev = repo.lookup(rev)
2339 rev = repo.lookup(rev)
2341 q = repo.mq
2340 q = repo.mq
2342 q.restore(repo, rev, delete=opts['delete'],
2341 q.restore(repo, rev, delete=opts['delete'],
2343 qupdate=opts['update'])
2342 qupdate=opts['update'])
2344 q.save_dirty()
2343 q.save_dirty()
2345 return 0
2344 return 0
2346
2345
2347 def save(ui, repo, **opts):
2346 def save(ui, repo, **opts):
2348 """save current queue state (DEPRECATED)
2347 """save current queue state (DEPRECATED)
2349
2348
2350 This command is deprecated, use rebase --mq instead."""
2349 This command is deprecated, use rebase --mq instead."""
2351 q = repo.mq
2350 q = repo.mq
2352 message = cmdutil.logmessage(opts)
2351 message = cmdutil.logmessage(opts)
2353 ret = q.save(repo, msg=message)
2352 ret = q.save(repo, msg=message)
2354 if ret:
2353 if ret:
2355 return ret
2354 return ret
2356 q.save_dirty()
2355 q.save_dirty()
2357 if opts['copy']:
2356 if opts['copy']:
2358 path = q.path
2357 path = q.path
2359 if opts['name']:
2358 if opts['name']:
2360 newpath = os.path.join(q.basepath, opts['name'])
2359 newpath = os.path.join(q.basepath, opts['name'])
2361 if os.path.exists(newpath):
2360 if os.path.exists(newpath):
2362 if not os.path.isdir(newpath):
2361 if not os.path.isdir(newpath):
2363 raise util.Abort(_('destination %s exists and is not '
2362 raise util.Abort(_('destination %s exists and is not '
2364 'a directory') % newpath)
2363 'a directory') % newpath)
2365 if not opts['force']:
2364 if not opts['force']:
2366 raise util.Abort(_('destination %s exists, '
2365 raise util.Abort(_('destination %s exists, '
2367 'use -f to force') % newpath)
2366 'use -f to force') % newpath)
2368 else:
2367 else:
2369 newpath = savename(path)
2368 newpath = savename(path)
2370 ui.warn(_("copy %s to %s\n") % (path, newpath))
2369 ui.warn(_("copy %s to %s\n") % (path, newpath))
2371 util.copyfiles(path, newpath)
2370 util.copyfiles(path, newpath)
2372 if opts['empty']:
2371 if opts['empty']:
2373 try:
2372 try:
2374 os.unlink(q.join(q.status_path))
2373 os.unlink(q.join(q.status_path))
2375 except:
2374 except:
2376 pass
2375 pass
2377 return 0
2376 return 0
2378
2377
2379 def strip(ui, repo, rev, **opts):
2378 def strip(ui, repo, rev, **opts):
2380 """strip a revision and all its descendants from the repository
2379 """strip a revision and all its descendants from the repository
2381
2380
2382 If one of the working directory's parent revisions is stripped, the
2381 If one of the working directory's parent revisions is stripped, the
2383 working directory will be updated to the parent of the stripped
2382 working directory will be updated to the parent of the stripped
2384 revision.
2383 revision.
2385 """
2384 """
2386 backup = 'all'
2385 backup = 'all'
2387 if opts['backup']:
2386 if opts['backup']:
2388 backup = 'strip'
2387 backup = 'strip'
2389 elif opts['nobackup']:
2388 elif opts['nobackup']:
2390 backup = 'none'
2389 backup = 'none'
2391
2390
2392 rev = repo.lookup(rev)
2391 rev = repo.lookup(rev)
2393 p = repo.dirstate.parents()
2392 p = repo.dirstate.parents()
2394 cl = repo.changelog
2393 cl = repo.changelog
2395 update = True
2394 update = True
2396 if p[0] == nullid:
2395 if p[0] == nullid:
2397 update = False
2396 update = False
2398 elif p[1] == nullid and rev != cl.ancestor(p[0], rev):
2397 elif p[1] == nullid and rev != cl.ancestor(p[0], rev):
2399 update = False
2398 update = False
2400 elif rev not in (cl.ancestor(p[0], rev), cl.ancestor(p[1], rev)):
2399 elif rev not in (cl.ancestor(p[0], rev), cl.ancestor(p[1], rev)):
2401 update = False
2400 update = False
2402
2401
2403 repo.mq.strip(repo, rev, backup=backup, update=update, force=opts['force'])
2402 repo.mq.strip(repo, rev, backup=backup, update=update, force=opts['force'])
2404 return 0
2403 return 0
2405
2404
2406 def select(ui, repo, *args, **opts):
2405 def select(ui, repo, *args, **opts):
2407 '''set or print guarded patches to push
2406 '''set or print guarded patches to push
2408
2407
2409 Use the qguard command to set or print guards on patch, then use
2408 Use the qguard command to set or print guards on patch, then use
2410 qselect to tell mq which guards to use. A patch will be pushed if
2409 qselect to tell mq which guards to use. A patch will be pushed if
2411 it has no guards or any positive guards match the currently
2410 it has no guards or any positive guards match the currently
2412 selected guard, but will not be pushed if any negative guards
2411 selected guard, but will not be pushed if any negative guards
2413 match the current guard. For example::
2412 match the current guard. For example::
2414
2413
2415 qguard foo.patch -stable (negative guard)
2414 qguard foo.patch -stable (negative guard)
2416 qguard bar.patch +stable (positive guard)
2415 qguard bar.patch +stable (positive guard)
2417 qselect stable
2416 qselect stable
2418
2417
2419 This activates the "stable" guard. mq will skip foo.patch (because
2418 This activates the "stable" guard. mq will skip foo.patch (because
2420 it has a negative match) but push bar.patch (because it has a
2419 it has a negative match) but push bar.patch (because it has a
2421 positive match).
2420 positive match).
2422
2421
2423 With no arguments, prints the currently active guards.
2422 With no arguments, prints the currently active guards.
2424 With one argument, sets the active guard.
2423 With one argument, sets the active guard.
2425
2424
2426 Use -n/--none to deactivate guards (no other arguments needed).
2425 Use -n/--none to deactivate guards (no other arguments needed).
2427 When no guards are active, patches with positive guards are
2426 When no guards are active, patches with positive guards are
2428 skipped and patches with negative guards are pushed.
2427 skipped and patches with negative guards are pushed.
2429
2428
2430 qselect can change the guards on applied patches. It does not pop
2429 qselect can change the guards on applied patches. It does not pop
2431 guarded patches by default. Use --pop to pop back to the last
2430 guarded patches by default. Use --pop to pop back to the last
2432 applied patch that is not guarded. Use --reapply (which implies
2431 applied patch that is not guarded. Use --reapply (which implies
2433 --pop) to push back to the current patch afterwards, but skip
2432 --pop) to push back to the current patch afterwards, but skip
2434 guarded patches.
2433 guarded patches.
2435
2434
2436 Use -s/--series to print a list of all guards in the series file
2435 Use -s/--series to print a list of all guards in the series file
2437 (no other arguments needed). Use -v for more information.'''
2436 (no other arguments needed). Use -v for more information.'''
2438
2437
2439 q = repo.mq
2438 q = repo.mq
2440 guards = q.active()
2439 guards = q.active()
2441 if args or opts['none']:
2440 if args or opts['none']:
2442 old_unapplied = q.unapplied(repo)
2441 old_unapplied = q.unapplied(repo)
2443 old_guarded = [i for i in xrange(len(q.applied)) if
2442 old_guarded = [i for i in xrange(len(q.applied)) if
2444 not q.pushable(i)[0]]
2443 not q.pushable(i)[0]]
2445 q.set_active(args)
2444 q.set_active(args)
2446 q.save_dirty()
2445 q.save_dirty()
2447 if not args:
2446 if not args:
2448 ui.status(_('guards deactivated\n'))
2447 ui.status(_('guards deactivated\n'))
2449 if not opts['pop'] and not opts['reapply']:
2448 if not opts['pop'] and not opts['reapply']:
2450 unapplied = q.unapplied(repo)
2449 unapplied = q.unapplied(repo)
2451 guarded = [i for i in xrange(len(q.applied))
2450 guarded = [i for i in xrange(len(q.applied))
2452 if not q.pushable(i)[0]]
2451 if not q.pushable(i)[0]]
2453 if len(unapplied) != len(old_unapplied):
2452 if len(unapplied) != len(old_unapplied):
2454 ui.status(_('number of unguarded, unapplied patches has '
2453 ui.status(_('number of unguarded, unapplied patches has '
2455 'changed from %d to %d\n') %
2454 'changed from %d to %d\n') %
2456 (len(old_unapplied), len(unapplied)))
2455 (len(old_unapplied), len(unapplied)))
2457 if len(guarded) != len(old_guarded):
2456 if len(guarded) != len(old_guarded):
2458 ui.status(_('number of guarded, applied patches has changed '
2457 ui.status(_('number of guarded, applied patches has changed '
2459 'from %d to %d\n') %
2458 'from %d to %d\n') %
2460 (len(old_guarded), len(guarded)))
2459 (len(old_guarded), len(guarded)))
2461 elif opts['series']:
2460 elif opts['series']:
2462 guards = {}
2461 guards = {}
2463 noguards = 0
2462 noguards = 0
2464 for gs in q.series_guards:
2463 for gs in q.series_guards:
2465 if not gs:
2464 if not gs:
2466 noguards += 1
2465 noguards += 1
2467 for g in gs:
2466 for g in gs:
2468 guards.setdefault(g, 0)
2467 guards.setdefault(g, 0)
2469 guards[g] += 1
2468 guards[g] += 1
2470 if ui.verbose:
2469 if ui.verbose:
2471 guards['NONE'] = noguards
2470 guards['NONE'] = noguards
2472 guards = guards.items()
2471 guards = guards.items()
2473 guards.sort(key=lambda x: x[0][1:])
2472 guards.sort(key=lambda x: x[0][1:])
2474 if guards:
2473 if guards:
2475 ui.note(_('guards in series file:\n'))
2474 ui.note(_('guards in series file:\n'))
2476 for guard, count in guards:
2475 for guard, count in guards:
2477 ui.note('%2d ' % count)
2476 ui.note('%2d ' % count)
2478 ui.write(guard, '\n')
2477 ui.write(guard, '\n')
2479 else:
2478 else:
2480 ui.note(_('no guards in series file\n'))
2479 ui.note(_('no guards in series file\n'))
2481 else:
2480 else:
2482 if guards:
2481 if guards:
2483 ui.note(_('active guards:\n'))
2482 ui.note(_('active guards:\n'))
2484 for g in guards:
2483 for g in guards:
2485 ui.write(g, '\n')
2484 ui.write(g, '\n')
2486 else:
2485 else:
2487 ui.write(_('no active guards\n'))
2486 ui.write(_('no active guards\n'))
2488 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2487 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2489 popped = False
2488 popped = False
2490 if opts['pop'] or opts['reapply']:
2489 if opts['pop'] or opts['reapply']:
2491 for i in xrange(len(q.applied)):
2490 for i in xrange(len(q.applied)):
2492 pushable, reason = q.pushable(i)
2491 pushable, reason = q.pushable(i)
2493 if not pushable:
2492 if not pushable:
2494 ui.status(_('popping guarded patches\n'))
2493 ui.status(_('popping guarded patches\n'))
2495 popped = True
2494 popped = True
2496 if i == 0:
2495 if i == 0:
2497 q.pop(repo, all=True)
2496 q.pop(repo, all=True)
2498 else:
2497 else:
2499 q.pop(repo, i - 1)
2498 q.pop(repo, i - 1)
2500 break
2499 break
2501 if popped:
2500 if popped:
2502 try:
2501 try:
2503 if reapply:
2502 if reapply:
2504 ui.status(_('reapplying unguarded patches\n'))
2503 ui.status(_('reapplying unguarded patches\n'))
2505 q.push(repo, reapply)
2504 q.push(repo, reapply)
2506 finally:
2505 finally:
2507 q.save_dirty()
2506 q.save_dirty()
2508
2507
2509 def finish(ui, repo, *revrange, **opts):
2508 def finish(ui, repo, *revrange, **opts):
2510 """move applied patches into repository history
2509 """move applied patches into repository history
2511
2510
2512 Finishes the specified revisions (corresponding to applied
2511 Finishes the specified revisions (corresponding to applied
2513 patches) by moving them out of mq control into regular repository
2512 patches) by moving them out of mq control into regular repository
2514 history.
2513 history.
2515
2514
2516 Accepts a revision range or the -a/--applied option. If --applied
2515 Accepts a revision range or the -a/--applied option. If --applied
2517 is specified, all applied mq revisions are removed from mq
2516 is specified, all applied mq revisions are removed from mq
2518 control. Otherwise, the given revisions must be at the base of the
2517 control. Otherwise, the given revisions must be at the base of the
2519 stack of applied patches.
2518 stack of applied patches.
2520
2519
2521 This can be especially useful if your changes have been applied to
2520 This can be especially useful if your changes have been applied to
2522 an upstream repository, or if you are about to push your changes
2521 an upstream repository, or if you are about to push your changes
2523 to upstream.
2522 to upstream.
2524 """
2523 """
2525 if not opts['applied'] and not revrange:
2524 if not opts['applied'] and not revrange:
2526 raise util.Abort(_('no revisions specified'))
2525 raise util.Abort(_('no revisions specified'))
2527 elif opts['applied']:
2526 elif opts['applied']:
2528 revrange = ('qbase:qtip',) + revrange
2527 revrange = ('qbase:qtip',) + revrange
2529
2528
2530 q = repo.mq
2529 q = repo.mq
2531 if not q.applied:
2530 if not q.applied:
2532 ui.status(_('no patches applied\n'))
2531 ui.status(_('no patches applied\n'))
2533 return 0
2532 return 0
2534
2533
2535 revs = cmdutil.revrange(repo, revrange)
2534 revs = cmdutil.revrange(repo, revrange)
2536 q.finish(repo, revs)
2535 q.finish(repo, revs)
2537 q.save_dirty()
2536 q.save_dirty()
2538 return 0
2537 return 0
2539
2538
2540 def reposetup(ui, repo):
2539 def reposetup(ui, repo):
2541 class mqrepo(repo.__class__):
2540 class mqrepo(repo.__class__):
2542 @util.propertycache
2541 @util.propertycache
2543 def mq(self):
2542 def mq(self):
2544 return queue(self.ui, self.join(""))
2543 return queue(self.ui, self.join(""))
2545
2544
2546 def abort_if_wdir_patched(self, errmsg, force=False):
2545 def abort_if_wdir_patched(self, errmsg, force=False):
2547 if self.mq.applied and not force:
2546 if self.mq.applied and not force:
2548 parent = hex(self.dirstate.parents()[0])
2547 parent = hex(self.dirstate.parents()[0])
2549 if parent in [s.rev for s in self.mq.applied]:
2548 if parent in [s.rev for s in self.mq.applied]:
2550 raise util.Abort(errmsg)
2549 raise util.Abort(errmsg)
2551
2550
2552 def commit(self, text="", user=None, date=None, match=None,
2551 def commit(self, text="", user=None, date=None, match=None,
2553 force=False, editor=False, extra={}):
2552 force=False, editor=False, extra={}):
2554 self.abort_if_wdir_patched(
2553 self.abort_if_wdir_patched(
2555 _('cannot commit over an applied mq patch'),
2554 _('cannot commit over an applied mq patch'),
2556 force)
2555 force)
2557
2556
2558 return super(mqrepo, self).commit(text, user, date, match, force,
2557 return super(mqrepo, self).commit(text, user, date, match, force,
2559 editor, extra)
2558 editor, extra)
2560
2559
2561 def push(self, remote, force=False, revs=None):
2560 def push(self, remote, force=False, revs=None):
2562 if self.mq.applied and not force and not revs:
2561 if self.mq.applied and not force and not revs:
2563 raise util.Abort(_('source has mq patches applied'))
2562 raise util.Abort(_('source has mq patches applied'))
2564 return super(mqrepo, self).push(remote, force, revs)
2563 return super(mqrepo, self).push(remote, force, revs)
2565
2564
2566 def _findtags(self):
2565 def _findtags(self):
2567 '''augment tags from base class with patch tags'''
2566 '''augment tags from base class with patch tags'''
2568 result = super(mqrepo, self)._findtags()
2567 result = super(mqrepo, self)._findtags()
2569
2568
2570 q = self.mq
2569 q = self.mq
2571 if not q.applied:
2570 if not q.applied:
2572 return result
2571 return result
2573
2572
2574 mqtags = [(bin(patch.rev), patch.name) for patch in q.applied]
2573 mqtags = [(bin(patch.rev), patch.name) for patch in q.applied]
2575
2574
2576 if mqtags[-1][0] not in self.changelog.nodemap:
2575 if mqtags[-1][0] not in self.changelog.nodemap:
2577 self.ui.warn(_('mq status file refers to unknown node %s\n')
2576 self.ui.warn(_('mq status file refers to unknown node %s\n')
2578 % short(mqtags[-1][0]))
2577 % short(mqtags[-1][0]))
2579 return result
2578 return result
2580
2579
2581 mqtags.append((mqtags[-1][0], 'qtip'))
2580 mqtags.append((mqtags[-1][0], 'qtip'))
2582 mqtags.append((mqtags[0][0], 'qbase'))
2581 mqtags.append((mqtags[0][0], 'qbase'))
2583 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2582 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2584 tags = result[0]
2583 tags = result[0]
2585 for patch in mqtags:
2584 for patch in mqtags:
2586 if patch[1] in tags:
2585 if patch[1] in tags:
2587 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
2586 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
2588 % patch[1])
2587 % patch[1])
2589 else:
2588 else:
2590 tags[patch[1]] = patch[0]
2589 tags[patch[1]] = patch[0]
2591
2590
2592 return result
2591 return result
2593
2592
2594 def _branchtags(self, partial, lrev):
2593 def _branchtags(self, partial, lrev):
2595 q = self.mq
2594 q = self.mq
2596 if not q.applied:
2595 if not q.applied:
2597 return super(mqrepo, self)._branchtags(partial, lrev)
2596 return super(mqrepo, self)._branchtags(partial, lrev)
2598
2597
2599 cl = self.changelog
2598 cl = self.changelog
2600 qbasenode = bin(q.applied[0].rev)
2599 qbasenode = bin(q.applied[0].rev)
2601 if qbasenode not in cl.nodemap:
2600 if qbasenode not in cl.nodemap:
2602 self.ui.warn(_('mq status file refers to unknown node %s\n')
2601 self.ui.warn(_('mq status file refers to unknown node %s\n')
2603 % short(qbasenode))
2602 % short(qbasenode))
2604 return super(mqrepo, self)._branchtags(partial, lrev)
2603 return super(mqrepo, self)._branchtags(partial, lrev)
2605
2604
2606 qbase = cl.rev(qbasenode)
2605 qbase = cl.rev(qbasenode)
2607 start = lrev + 1
2606 start = lrev + 1
2608 if start < qbase:
2607 if start < qbase:
2609 # update the cache (excluding the patches) and save it
2608 # update the cache (excluding the patches) and save it
2610 self._updatebranchcache(partial, lrev + 1, qbase)
2609 self._updatebranchcache(partial, lrev + 1, qbase)
2611 self._writebranchcache(partial, cl.node(qbase - 1), qbase - 1)
2610 self._writebranchcache(partial, cl.node(qbase - 1), qbase - 1)
2612 start = qbase
2611 start = qbase
2613 # if start = qbase, the cache is as updated as it should be.
2612 # if start = qbase, the cache is as updated as it should be.
2614 # if start > qbase, the cache includes (part of) the patches.
2613 # if start > qbase, the cache includes (part of) the patches.
2615 # we might as well use it, but we won't save it.
2614 # we might as well use it, but we won't save it.
2616
2615
2617 # update the cache up to the tip
2616 # update the cache up to the tip
2618 self._updatebranchcache(partial, start, len(cl))
2617 self._updatebranchcache(partial, start, len(cl))
2619
2618
2620 return partial
2619 return partial
2621
2620
2622 if repo.local():
2621 if repo.local():
2623 repo.__class__ = mqrepo
2622 repo.__class__ = mqrepo
2624
2623
2625 def mqimport(orig, ui, repo, *args, **kwargs):
2624 def mqimport(orig, ui, repo, *args, **kwargs):
2626 if (hasattr(repo, 'abort_if_wdir_patched')
2625 if (hasattr(repo, 'abort_if_wdir_patched')
2627 and not kwargs.get('no_commit', False)):
2626 and not kwargs.get('no_commit', False)):
2628 repo.abort_if_wdir_patched(_('cannot import over an applied patch'),
2627 repo.abort_if_wdir_patched(_('cannot import over an applied patch'),
2629 kwargs.get('force'))
2628 kwargs.get('force'))
2630 return orig(ui, repo, *args, **kwargs)
2629 return orig(ui, repo, *args, **kwargs)
2631
2630
2632 def mqinit(orig, ui, *args, **kwargs):
2631 def mqinit(orig, ui, *args, **kwargs):
2633 mq = kwargs['mq']
2632 mq = kwargs['mq']
2634 del kwargs['mq']
2633 del kwargs['mq']
2635
2634
2636 if not mq:
2635 if not mq:
2637 return orig(ui, *args, **kwargs)
2636 return orig(ui, *args, **kwargs)
2638
2637
2639 repopath = cmdutil.findrepo(os.getcwd())
2638 repopath = cmdutil.findrepo(os.getcwd())
2640 repo = hg.repository(ui, repopath)
2639 repo = hg.repository(ui, repopath)
2641 return qinit(ui, repo, True)
2640 return qinit(ui, repo, True)
2642
2641
2643 def mqcommand(orig, ui, repo, *args, **kwargs):
2642 def mqcommand(orig, ui, repo, *args, **kwargs):
2644 """Add --mq option to operate on patch repository instead of main"""
2643 """Add --mq option to operate on patch repository instead of main"""
2645
2644
2646 # some commands do not like getting unknown options
2645 # some commands do not like getting unknown options
2647 mq = kwargs['mq']
2646 mq = kwargs['mq']
2648 del kwargs['mq']
2647 del kwargs['mq']
2649
2648
2650 if not mq:
2649 if not mq:
2651 return orig(ui, repo, *args, **kwargs)
2650 return orig(ui, repo, *args, **kwargs)
2652
2651
2653 q = repo.mq
2652 q = repo.mq
2654 r = q.qrepo()
2653 r = q.qrepo()
2655 if not r:
2654 if not r:
2656 raise util.Abort('no queue repository')
2655 raise util.Abort('no queue repository')
2657 return orig(r.ui, r, *args, **kwargs)
2656 return orig(r.ui, r, *args, **kwargs)
2658
2657
2659 def uisetup(ui):
2658 def uisetup(ui):
2660 mqopt = [('Q', 'mq', None, _("operate on patch repository"))]
2659 mqopt = [('Q', 'mq', None, _("operate on patch repository"))]
2661
2660
2662 extensions.wrapcommand(commands.table, 'import', mqimport)
2661 extensions.wrapcommand(commands.table, 'import', mqimport)
2663
2662
2664 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
2663 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
2665 entry[1].extend(mqopt)
2664 entry[1].extend(mqopt)
2666
2665
2667 for cmd in commands.table:
2666 for cmd in commands.table.keys():
2668 cmd = cmdutil.parsealiases(cmd)[0]
2667 cmd = cmdutil.parsealiases(cmd)[0]
2669 if cmd in commands.norepo:
2668 if cmd in commands.norepo:
2670 continue
2669 continue
2671 entry = extensions.wrapcommand(commands.table, cmd, mqcommand)
2670 entry = extensions.wrapcommand(commands.table, cmd, mqcommand)
2672 entry[1].extend(mqopt)
2671 entry[1].extend(mqopt)
2673
2672
2674 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2673 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2675
2674
2676 cmdtable = {
2675 cmdtable = {
2677 "qapplied":
2676 "qapplied":
2678 (applied,
2677 (applied,
2679 [('1', 'last', None, _('show only the last patch'))] + seriesopts,
2678 [('1', 'last', None, _('show only the last patch'))] + seriesopts,
2680 _('hg qapplied [-1] [-s] [PATCH]')),
2679 _('hg qapplied [-1] [-s] [PATCH]')),
2681 "qclone":
2680 "qclone":
2682 (clone,
2681 (clone,
2683 [('', 'pull', None, _('use pull protocol to copy metadata')),
2682 [('', 'pull', None, _('use pull protocol to copy metadata')),
2684 ('U', 'noupdate', None, _('do not update the new working directories')),
2683 ('U', 'noupdate', None, _('do not update the new working directories')),
2685 ('', 'uncompressed', None,
2684 ('', 'uncompressed', None,
2686 _('use uncompressed transfer (fast over LAN)')),
2685 _('use uncompressed transfer (fast over LAN)')),
2687 ('p', 'patches', '', _('location of source patch repository')),
2686 ('p', 'patches', '', _('location of source patch repository')),
2688 ] + commands.remoteopts,
2687 ] + commands.remoteopts,
2689 _('hg qclone [OPTION]... SOURCE [DEST]')),
2688 _('hg qclone [OPTION]... SOURCE [DEST]')),
2690 "qcommit|qci":
2689 "qcommit|qci":
2691 (commit,
2690 (commit,
2692 commands.table["^commit|ci"][1],
2691 commands.table["^commit|ci"][1],
2693 _('hg qcommit [OPTION]... [FILE]...')),
2692 _('hg qcommit [OPTION]... [FILE]...')),
2694 "^qdiff":
2693 "^qdiff":
2695 (diff,
2694 (diff,
2696 commands.diffopts + commands.diffopts2 + commands.walkopts,
2695 commands.diffopts + commands.diffopts2 + commands.walkopts,
2697 _('hg qdiff [OPTION]... [FILE]...')),
2696 _('hg qdiff [OPTION]... [FILE]...')),
2698 "qdelete|qremove|qrm":
2697 "qdelete|qremove|qrm":
2699 (delete,
2698 (delete,
2700 [('k', 'keep', None, _('keep patch file')),
2699 [('k', 'keep', None, _('keep patch file')),
2701 ('r', 'rev', [], _('stop managing a revision (DEPRECATED)'))],
2700 ('r', 'rev', [], _('stop managing a revision (DEPRECATED)'))],
2702 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2701 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2703 'qfold':
2702 'qfold':
2704 (fold,
2703 (fold,
2705 [('e', 'edit', None, _('edit patch header')),
2704 [('e', 'edit', None, _('edit patch header')),
2706 ('k', 'keep', None, _('keep folded patch files')),
2705 ('k', 'keep', None, _('keep folded patch files')),
2707 ] + commands.commitopts,
2706 ] + commands.commitopts,
2708 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2707 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2709 'qgoto':
2708 'qgoto':
2710 (goto,
2709 (goto,
2711 [('f', 'force', None, _('overwrite any local changes'))],
2710 [('f', 'force', None, _('overwrite any local changes'))],
2712 _('hg qgoto [OPTION]... PATCH')),
2711 _('hg qgoto [OPTION]... PATCH')),
2713 'qguard':
2712 'qguard':
2714 (guard,
2713 (guard,
2715 [('l', 'list', None, _('list all patches and guards')),
2714 [('l', 'list', None, _('list all patches and guards')),
2716 ('n', 'none', None, _('drop all guards'))],
2715 ('n', 'none', None, _('drop all guards'))],
2717 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]')),
2716 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]')),
2718 'qheader': (header, [], _('hg qheader [PATCH]')),
2717 'qheader': (header, [], _('hg qheader [PATCH]')),
2719 "^qimport":
2718 "^qimport":
2720 (qimport,
2719 (qimport,
2721 [('e', 'existing', None, _('import file in patch directory')),
2720 [('e', 'existing', None, _('import file in patch directory')),
2722 ('n', 'name', '', _('name of patch file')),
2721 ('n', 'name', '', _('name of patch file')),
2723 ('f', 'force', None, _('overwrite existing files')),
2722 ('f', 'force', None, _('overwrite existing files')),
2724 ('r', 'rev', [], _('place existing revisions under mq control')),
2723 ('r', 'rev', [], _('place existing revisions under mq control')),
2725 ('g', 'git', None, _('use git extended diff format')),
2724 ('g', 'git', None, _('use git extended diff format')),
2726 ('P', 'push', None, _('qpush after importing'))],
2725 ('P', 'push', None, _('qpush after importing'))],
2727 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...')),
2726 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...')),
2728 "^qinit":
2727 "^qinit":
2729 (init,
2728 (init,
2730 [('c', 'create-repo', None, _('create queue repository'))],
2729 [('c', 'create-repo', None, _('create queue repository'))],
2731 _('hg qinit [-c]')),
2730 _('hg qinit [-c]')),
2732 "qnew":
2731 "qnew":
2733 (new,
2732 (new,
2734 [('e', 'edit', None, _('edit commit message')),
2733 [('e', 'edit', None, _('edit commit message')),
2735 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
2734 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
2736 ('g', 'git', None, _('use git extended diff format')),
2735 ('g', 'git', None, _('use git extended diff format')),
2737 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2736 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2738 ('u', 'user', '', _('add "From: <given user>" to patch')),
2737 ('u', 'user', '', _('add "From: <given user>" to patch')),
2739 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2738 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2740 ('d', 'date', '', _('add "Date: <given date>" to patch'))
2739 ('d', 'date', '', _('add "Date: <given date>" to patch'))
2741 ] + commands.walkopts + commands.commitopts,
2740 ] + commands.walkopts + commands.commitopts,
2742 _('hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH [FILE]...')),
2741 _('hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH [FILE]...')),
2743 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2742 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2744 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2743 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2745 "^qpop":
2744 "^qpop":
2746 (pop,
2745 (pop,
2747 [('a', 'all', None, _('pop all patches')),
2746 [('a', 'all', None, _('pop all patches')),
2748 ('n', 'name', '', _('queue name to pop (DEPRECATED)')),
2747 ('n', 'name', '', _('queue name to pop (DEPRECATED)')),
2749 ('f', 'force', None, _('forget any local changes to patched files'))],
2748 ('f', 'force', None, _('forget any local changes to patched files'))],
2750 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2749 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2751 "^qpush":
2750 "^qpush":
2752 (push,
2751 (push,
2753 [('f', 'force', None, _('apply if the patch has rejects')),
2752 [('f', 'force', None, _('apply if the patch has rejects')),
2754 ('l', 'list', None, _('list patch name in commit text')),
2753 ('l', 'list', None, _('list patch name in commit text')),
2755 ('a', 'all', None, _('apply all patches')),
2754 ('a', 'all', None, _('apply all patches')),
2756 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2755 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2757 ('n', 'name', '', _('merge queue name (DEPRECATED)'))],
2756 ('n', 'name', '', _('merge queue name (DEPRECATED)'))],
2758 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]')),
2757 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]')),
2759 "^qrefresh":
2758 "^qrefresh":
2760 (refresh,
2759 (refresh,
2761 [('e', 'edit', None, _('edit commit message')),
2760 [('e', 'edit', None, _('edit commit message')),
2762 ('g', 'git', None, _('use git extended diff format')),
2761 ('g', 'git', None, _('use git extended diff format')),
2763 ('s', 'short', None,
2762 ('s', 'short', None,
2764 _('refresh only files already in the patch and specified files')),
2763 _('refresh only files already in the patch and specified files')),
2765 ('U', 'currentuser', None,
2764 ('U', 'currentuser', None,
2766 _('add/update author field in patch with current user')),
2765 _('add/update author field in patch with current user')),
2767 ('u', 'user', '',
2766 ('u', 'user', '',
2768 _('add/update author field in patch with given user')),
2767 _('add/update author field in patch with given user')),
2769 ('D', 'currentdate', None,
2768 ('D', 'currentdate', None,
2770 _('add/update date field in patch with current date')),
2769 _('add/update date field in patch with current date')),
2771 ('d', 'date', '',
2770 ('d', 'date', '',
2772 _('add/update date field in patch with given date'))
2771 _('add/update date field in patch with given date'))
2773 ] + commands.walkopts + commands.commitopts,
2772 ] + commands.walkopts + commands.commitopts,
2774 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2773 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2775 'qrename|qmv':
2774 'qrename|qmv':
2776 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2775 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2777 "qrestore":
2776 "qrestore":
2778 (restore,
2777 (restore,
2779 [('d', 'delete', None, _('delete save entry')),
2778 [('d', 'delete', None, _('delete save entry')),
2780 ('u', 'update', None, _('update queue working directory'))],
2779 ('u', 'update', None, _('update queue working directory'))],
2781 _('hg qrestore [-d] [-u] REV')),
2780 _('hg qrestore [-d] [-u] REV')),
2782 "qsave":
2781 "qsave":
2783 (save,
2782 (save,
2784 [('c', 'copy', None, _('copy patch directory')),
2783 [('c', 'copy', None, _('copy patch directory')),
2785 ('n', 'name', '', _('copy directory name')),
2784 ('n', 'name', '', _('copy directory name')),
2786 ('e', 'empty', None, _('clear queue status file')),
2785 ('e', 'empty', None, _('clear queue status file')),
2787 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2786 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2788 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2787 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2789 "qselect":
2788 "qselect":
2790 (select,
2789 (select,
2791 [('n', 'none', None, _('disable all guards')),
2790 [('n', 'none', None, _('disable all guards')),
2792 ('s', 'series', None, _('list all guards in series file')),
2791 ('s', 'series', None, _('list all guards in series file')),
2793 ('', 'pop', None, _('pop to before first guarded applied patch')),
2792 ('', 'pop', None, _('pop to before first guarded applied patch')),
2794 ('', 'reapply', None, _('pop, then reapply patches'))],
2793 ('', 'reapply', None, _('pop, then reapply patches'))],
2795 _('hg qselect [OPTION]... [GUARD]...')),
2794 _('hg qselect [OPTION]... [GUARD]...')),
2796 "qseries":
2795 "qseries":
2797 (series,
2796 (series,
2798 [('m', 'missing', None, _('print patches not in series')),
2797 [('m', 'missing', None, _('print patches not in series')),
2799 ] + seriesopts,
2798 ] + seriesopts,
2800 _('hg qseries [-ms]')),
2799 _('hg qseries [-ms]')),
2801 "^strip":
2800 "^strip":
2802 (strip,
2801 (strip,
2803 [('f', 'force', None, _('force removal with local changes')),
2802 [('f', 'force', None, _('force removal with local changes')),
2804 ('b', 'backup', None, _('bundle unrelated changesets')),
2803 ('b', 'backup', None, _('bundle unrelated changesets')),
2805 ('n', 'nobackup', None, _('no backups'))],
2804 ('n', 'nobackup', None, _('no backups'))],
2806 _('hg strip [-f] [-b] [-n] REV')),
2805 _('hg strip [-f] [-b] [-n] REV')),
2807 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
2806 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
2808 "qunapplied":
2807 "qunapplied":
2809 (unapplied,
2808 (unapplied,
2810 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
2809 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
2811 _('hg qunapplied [-1] [-s] [PATCH]')),
2810 _('hg qunapplied [-1] [-s] [PATCH]')),
2812 "qfinish":
2811 "qfinish":
2813 (finish,
2812 (finish,
2814 [('a', 'applied', None, _('finish all applied changesets'))],
2813 [('a', 'applied', None, _('finish all applied changesets'))],
2815 _('hg qfinish [-a] [REV]...')),
2814 _('hg qfinish [-a] [REV]...')),
2816 }
2815 }
@@ -1,605 +1,605 b''
1 # Patch transplanting extension for Mercurial
1 # Patch transplanting extension for Mercurial
2 #
2 #
3 # Copyright 2006, 2007 Brendan Cully <brendan@kublai.com>
3 # Copyright 2006, 2007 Brendan Cully <brendan@kublai.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 '''command to transplant changesets from another branch
8 '''command to transplant changesets from another branch
9
9
10 This extension allows you to transplant patches from another branch.
10 This extension allows you to transplant patches from another branch.
11
11
12 Transplanted patches are recorded in .hg/transplant/transplants, as a
12 Transplanted patches are recorded in .hg/transplant/transplants, as a
13 map from a changeset hash to its hash in the source repository.
13 map from a changeset hash to its hash in the source repository.
14 '''
14 '''
15
15
16 from mercurial.i18n import _
16 from mercurial.i18n import _
17 import os, tempfile
17 import os, tempfile
18 from mercurial import bundlerepo, changegroup, cmdutil, hg, merge, match
18 from mercurial import bundlerepo, changegroup, cmdutil, hg, merge, match
19 from mercurial import patch, revlog, util, error
19 from mercurial import patch, revlog, util, error
20
20
21 class transplantentry(object):
21 class transplantentry(object):
22 def __init__(self, lnode, rnode):
22 def __init__(self, lnode, rnode):
23 self.lnode = lnode
23 self.lnode = lnode
24 self.rnode = rnode
24 self.rnode = rnode
25
25
26 class transplants(object):
26 class transplants(object):
27 def __init__(self, path=None, transplantfile=None, opener=None):
27 def __init__(self, path=None, transplantfile=None, opener=None):
28 self.path = path
28 self.path = path
29 self.transplantfile = transplantfile
29 self.transplantfile = transplantfile
30 self.opener = opener
30 self.opener = opener
31
31
32 if not opener:
32 if not opener:
33 self.opener = util.opener(self.path)
33 self.opener = util.opener(self.path)
34 self.transplants = []
34 self.transplants = []
35 self.dirty = False
35 self.dirty = False
36 self.read()
36 self.read()
37
37
38 def read(self):
38 def read(self):
39 abspath = os.path.join(self.path, self.transplantfile)
39 abspath = os.path.join(self.path, self.transplantfile)
40 if self.transplantfile and os.path.exists(abspath):
40 if self.transplantfile and os.path.exists(abspath):
41 for line in self.opener(self.transplantfile).read().splitlines():
41 for line in self.opener(self.transplantfile).read().splitlines():
42 lnode, rnode = map(revlog.bin, line.split(':'))
42 lnode, rnode = map(revlog.bin, line.split(':'))
43 self.transplants.append(transplantentry(lnode, rnode))
43 self.transplants.append(transplantentry(lnode, rnode))
44
44
45 def write(self):
45 def write(self):
46 if self.dirty and self.transplantfile:
46 if self.dirty and self.transplantfile:
47 if not os.path.isdir(self.path):
47 if not os.path.isdir(self.path):
48 os.mkdir(self.path)
48 os.mkdir(self.path)
49 fp = self.opener(self.transplantfile, 'w')
49 fp = self.opener(self.transplantfile, 'w')
50 for c in self.transplants:
50 for c in self.transplants:
51 l, r = map(revlog.hex, (c.lnode, c.rnode))
51 l, r = map(revlog.hex, (c.lnode, c.rnode))
52 fp.write(l + ':' + r + '\n')
52 fp.write(l + ':' + r + '\n')
53 fp.close()
53 fp.close()
54 self.dirty = False
54 self.dirty = False
55
55
56 def get(self, rnode):
56 def get(self, rnode):
57 return [t for t in self.transplants if t.rnode == rnode]
57 return [t for t in self.transplants if t.rnode == rnode]
58
58
59 def set(self, lnode, rnode):
59 def set(self, lnode, rnode):
60 self.transplants.append(transplantentry(lnode, rnode))
60 self.transplants.append(transplantentry(lnode, rnode))
61 self.dirty = True
61 self.dirty = True
62
62
63 def remove(self, transplant):
63 def remove(self, transplant):
64 del self.transplants[self.transplants.index(transplant)]
64 del self.transplants[self.transplants.index(transplant)]
65 self.dirty = True
65 self.dirty = True
66
66
67 class transplanter(object):
67 class transplanter(object):
68 def __init__(self, ui, repo):
68 def __init__(self, ui, repo):
69 self.ui = ui
69 self.ui = ui
70 self.path = repo.join('transplant')
70 self.path = repo.join('transplant')
71 self.opener = util.opener(self.path)
71 self.opener = util.opener(self.path)
72 self.transplants = transplants(self.path, 'transplants',
72 self.transplants = transplants(self.path, 'transplants',
73 opener=self.opener)
73 opener=self.opener)
74
74
75 def applied(self, repo, node, parent):
75 def applied(self, repo, node, parent):
76 '''returns True if a node is already an ancestor of parent
76 '''returns True if a node is already an ancestor of parent
77 or has already been transplanted'''
77 or has already been transplanted'''
78 if hasnode(repo, node):
78 if hasnode(repo, node):
79 if node in repo.changelog.reachable(parent, stop=node):
79 if node in repo.changelog.reachable(parent, stop=node):
80 return True
80 return True
81 for t in self.transplants.get(node):
81 for t in self.transplants.get(node):
82 # it might have been stripped
82 # it might have been stripped
83 if not hasnode(repo, t.lnode):
83 if not hasnode(repo, t.lnode):
84 self.transplants.remove(t)
84 self.transplants.remove(t)
85 return False
85 return False
86 if t.lnode in repo.changelog.reachable(parent, stop=t.lnode):
86 if t.lnode in repo.changelog.reachable(parent, stop=t.lnode):
87 return True
87 return True
88 return False
88 return False
89
89
90 def apply(self, repo, source, revmap, merges, opts={}):
90 def apply(self, repo, source, revmap, merges, opts={}):
91 '''apply the revisions in revmap one by one in revision order'''
91 '''apply the revisions in revmap one by one in revision order'''
92 revs = sorted(revmap)
92 revs = sorted(revmap)
93 p1, p2 = repo.dirstate.parents()
93 p1, p2 = repo.dirstate.parents()
94 pulls = []
94 pulls = []
95 diffopts = patch.diffopts(self.ui, opts)
95 diffopts = patch.diffopts(self.ui, opts)
96 diffopts.git = True
96 diffopts.git = True
97
97
98 lock = wlock = None
98 lock = wlock = None
99 try:
99 try:
100 wlock = repo.wlock()
100 wlock = repo.wlock()
101 lock = repo.lock()
101 lock = repo.lock()
102 for rev in revs:
102 for rev in revs:
103 node = revmap[rev]
103 node = revmap[rev]
104 revstr = '%s:%s' % (rev, revlog.short(node))
104 revstr = '%s:%s' % (rev, revlog.short(node))
105
105
106 if self.applied(repo, node, p1):
106 if self.applied(repo, node, p1):
107 self.ui.warn(_('skipping already applied revision %s\n') %
107 self.ui.warn(_('skipping already applied revision %s\n') %
108 revstr)
108 revstr)
109 continue
109 continue
110
110
111 parents = source.changelog.parents(node)
111 parents = source.changelog.parents(node)
112 if not opts.get('filter'):
112 if not opts.get('filter'):
113 # If the changeset parent is the same as the
113 # If the changeset parent is the same as the
114 # wdir's parent, just pull it.
114 # wdir's parent, just pull it.
115 if parents[0] == p1:
115 if parents[0] == p1:
116 pulls.append(node)
116 pulls.append(node)
117 p1 = node
117 p1 = node
118 continue
118 continue
119 if pulls:
119 if pulls:
120 if source != repo:
120 if source != repo:
121 repo.pull(source, heads=pulls)
121 repo.pull(source, heads=pulls)
122 merge.update(repo, pulls[-1], False, False, None)
122 merge.update(repo, pulls[-1], False, False, None)
123 p1, p2 = repo.dirstate.parents()
123 p1, p2 = repo.dirstate.parents()
124 pulls = []
124 pulls = []
125
125
126 domerge = False
126 domerge = False
127 if node in merges:
127 if node in merges:
128 # pulling all the merge revs at once would mean we
128 # pulling all the merge revs at once would mean we
129 # couldn't transplant after the latest even if
129 # couldn't transplant after the latest even if
130 # transplants before them fail.
130 # transplants before them fail.
131 domerge = True
131 domerge = True
132 if not hasnode(repo, node):
132 if not hasnode(repo, node):
133 repo.pull(source, heads=[node])
133 repo.pull(source, heads=[node])
134
134
135 if parents[1] != revlog.nullid:
135 if parents[1] != revlog.nullid:
136 self.ui.note(_('skipping merge changeset %s:%s\n')
136 self.ui.note(_('skipping merge changeset %s:%s\n')
137 % (rev, revlog.short(node)))
137 % (rev, revlog.short(node)))
138 patchfile = None
138 patchfile = None
139 else:
139 else:
140 fd, patchfile = tempfile.mkstemp(prefix='hg-transplant-')
140 fd, patchfile = tempfile.mkstemp(prefix='hg-transplant-')
141 fp = os.fdopen(fd, 'w')
141 fp = os.fdopen(fd, 'w')
142 gen = patch.diff(source, parents[0], node, opts=diffopts)
142 gen = patch.diff(source, parents[0], node, opts=diffopts)
143 for chunk in gen:
143 for chunk in gen:
144 fp.write(chunk)
144 fp.write(chunk)
145 fp.close()
145 fp.close()
146
146
147 del revmap[rev]
147 del revmap[rev]
148 if patchfile or domerge:
148 if patchfile or domerge:
149 try:
149 try:
150 n = self.applyone(repo, node,
150 n = self.applyone(repo, node,
151 source.changelog.read(node),
151 source.changelog.read(node),
152 patchfile, merge=domerge,
152 patchfile, merge=domerge,
153 log=opts.get('log'),
153 log=opts.get('log'),
154 filter=opts.get('filter'))
154 filter=opts.get('filter'))
155 if n and domerge:
155 if n and domerge:
156 self.ui.status(_('%s merged at %s\n') % (revstr,
156 self.ui.status(_('%s merged at %s\n') % (revstr,
157 revlog.short(n)))
157 revlog.short(n)))
158 elif n:
158 elif n:
159 self.ui.status(_('%s transplanted to %s\n')
159 self.ui.status(_('%s transplanted to %s\n')
160 % (revlog.short(node),
160 % (revlog.short(node),
161 revlog.short(n)))
161 revlog.short(n)))
162 finally:
162 finally:
163 if patchfile:
163 if patchfile:
164 os.unlink(patchfile)
164 os.unlink(patchfile)
165 if pulls:
165 if pulls:
166 repo.pull(source, heads=pulls)
166 repo.pull(source, heads=pulls)
167 merge.update(repo, pulls[-1], False, False, None)
167 merge.update(repo, pulls[-1], False, False, None)
168 finally:
168 finally:
169 self.saveseries(revmap, merges)
169 self.saveseries(revmap, merges)
170 self.transplants.write()
170 self.transplants.write()
171 lock.release()
171 lock.release()
172 wlock.release()
172 wlock.release()
173
173
174 def filter(self, filter, changelog, patchfile):
174 def filter(self, filter, changelog, patchfile):
175 '''arbitrarily rewrite changeset before applying it'''
175 '''arbitrarily rewrite changeset before applying it'''
176
176
177 self.ui.status(_('filtering %s\n') % patchfile)
177 self.ui.status(_('filtering %s\n') % patchfile)
178 user, date, msg = (changelog[1], changelog[2], changelog[4])
178 user, date, msg = (changelog[1], changelog[2], changelog[4])
179
179
180 fd, headerfile = tempfile.mkstemp(prefix='hg-transplant-')
180 fd, headerfile = tempfile.mkstemp(prefix='hg-transplant-')
181 fp = os.fdopen(fd, 'w')
181 fp = os.fdopen(fd, 'w')
182 fp.write("# HG changeset patch\n")
182 fp.write("# HG changeset patch\n")
183 fp.write("# User %s\n" % user)
183 fp.write("# User %s\n" % user)
184 fp.write("# Date %d %d\n" % date)
184 fp.write("# Date %d %d\n" % date)
185 fp.write(msg + '\n')
185 fp.write(msg + '\n')
186 fp.close()
186 fp.close()
187
187
188 try:
188 try:
189 util.system('%s %s %s' % (filter, util.shellquote(headerfile),
189 util.system('%s %s %s' % (filter, util.shellquote(headerfile),
190 util.shellquote(patchfile)),
190 util.shellquote(patchfile)),
191 environ={'HGUSER': changelog[1]},
191 environ={'HGUSER': changelog[1]},
192 onerr=util.Abort, errprefix=_('filter failed'))
192 onerr=util.Abort, errprefix=_('filter failed'))
193 user, date, msg = self.parselog(file(headerfile))[1:4]
193 user, date, msg = self.parselog(file(headerfile))[1:4]
194 finally:
194 finally:
195 os.unlink(headerfile)
195 os.unlink(headerfile)
196
196
197 return (user, date, msg)
197 return (user, date, msg)
198
198
199 def applyone(self, repo, node, cl, patchfile, merge=False, log=False,
199 def applyone(self, repo, node, cl, patchfile, merge=False, log=False,
200 filter=None):
200 filter=None):
201 '''apply the patch in patchfile to the repository as a transplant'''
201 '''apply the patch in patchfile to the repository as a transplant'''
202 (manifest, user, (time, timezone), files, message) = cl[:5]
202 (manifest, user, (time, timezone), files, message) = cl[:5]
203 date = "%d %d" % (time, timezone)
203 date = "%d %d" % (time, timezone)
204 extra = {'transplant_source': node}
204 extra = {'transplant_source': node}
205 if filter:
205 if filter:
206 (user, date, message) = self.filter(filter, cl, patchfile)
206 (user, date, message) = self.filter(filter, cl, patchfile)
207
207
208 if log:
208 if log:
209 # we don't translate messages inserted into commits
209 # we don't translate messages inserted into commits
210 message += '\n(transplanted from %s)' % revlog.hex(node)
210 message += '\n(transplanted from %s)' % revlog.hex(node)
211
211
212 self.ui.status(_('applying %s\n') % revlog.short(node))
212 self.ui.status(_('applying %s\n') % revlog.short(node))
213 self.ui.note('%s %s\n%s\n' % (user, date, message))
213 self.ui.note('%s %s\n%s\n' % (user, date, message))
214
214
215 if not patchfile and not merge:
215 if not patchfile and not merge:
216 raise util.Abort(_('can only omit patchfile if merging'))
216 raise util.Abort(_('can only omit patchfile if merging'))
217 if patchfile:
217 if patchfile:
218 try:
218 try:
219 files = {}
219 files = {}
220 try:
220 try:
221 patch.patch(patchfile, self.ui, cwd=repo.root,
221 patch.patch(patchfile, self.ui, cwd=repo.root,
222 files=files, eolmode=None)
222 files=files, eolmode=None)
223 if not files:
223 if not files:
224 self.ui.warn(_('%s: empty changeset')
224 self.ui.warn(_('%s: empty changeset')
225 % revlog.hex(node))
225 % revlog.hex(node))
226 return None
226 return None
227 finally:
227 finally:
228 files = patch.updatedir(self.ui, repo, files)
228 files = patch.updatedir(self.ui, repo, files)
229 except Exception, inst:
229 except Exception, inst:
230 seriespath = os.path.join(self.path, 'series')
230 seriespath = os.path.join(self.path, 'series')
231 if os.path.exists(seriespath):
231 if os.path.exists(seriespath):
232 os.unlink(seriespath)
232 os.unlink(seriespath)
233 p1 = repo.dirstate.parents()[0]
233 p1 = repo.dirstate.parents()[0]
234 p2 = node
234 p2 = node
235 self.log(user, date, message, p1, p2, merge=merge)
235 self.log(user, date, message, p1, p2, merge=merge)
236 self.ui.write(str(inst) + '\n')
236 self.ui.write(str(inst) + '\n')
237 raise util.Abort(_('Fix up the merge and run '
237 raise util.Abort(_('Fix up the merge and run '
238 'hg transplant --continue'))
238 'hg transplant --continue'))
239 else:
239 else:
240 files = None
240 files = None
241 if merge:
241 if merge:
242 p1, p2 = repo.dirstate.parents()
242 p1, p2 = repo.dirstate.parents()
243 repo.dirstate.setparents(p1, node)
243 repo.dirstate.setparents(p1, node)
244 m = match.always(repo.root, '')
244 m = match.always(repo.root, '')
245 else:
245 else:
246 m = match.exact(repo.root, '', files)
246 m = match.exact(repo.root, '', files)
247
247
248 n = repo.commit(message, user, date, extra=extra, match=m)
248 n = repo.commit(message, user, date, extra=extra, match=m)
249 if not merge:
249 if not merge:
250 self.transplants.set(n, node)
250 self.transplants.set(n, node)
251
251
252 return n
252 return n
253
253
254 def resume(self, repo, source, opts=None):
254 def resume(self, repo, source, opts=None):
255 '''recover last transaction and apply remaining changesets'''
255 '''recover last transaction and apply remaining changesets'''
256 if os.path.exists(os.path.join(self.path, 'journal')):
256 if os.path.exists(os.path.join(self.path, 'journal')):
257 n, node = self.recover(repo)
257 n, node = self.recover(repo)
258 self.ui.status(_('%s transplanted as %s\n') % (revlog.short(node),
258 self.ui.status(_('%s transplanted as %s\n') % (revlog.short(node),
259 revlog.short(n)))
259 revlog.short(n)))
260 seriespath = os.path.join(self.path, 'series')
260 seriespath = os.path.join(self.path, 'series')
261 if not os.path.exists(seriespath):
261 if not os.path.exists(seriespath):
262 self.transplants.write()
262 self.transplants.write()
263 return
263 return
264 nodes, merges = self.readseries()
264 nodes, merges = self.readseries()
265 revmap = {}
265 revmap = {}
266 for n in nodes:
266 for n in nodes:
267 revmap[source.changelog.rev(n)] = n
267 revmap[source.changelog.rev(n)] = n
268 os.unlink(seriespath)
268 os.unlink(seriespath)
269
269
270 self.apply(repo, source, revmap, merges, opts)
270 self.apply(repo, source, revmap, merges, opts)
271
271
272 def recover(self, repo):
272 def recover(self, repo):
273 '''commit working directory using journal metadata'''
273 '''commit working directory using journal metadata'''
274 node, user, date, message, parents = self.readlog()
274 node, user, date, message, parents = self.readlog()
275 merge = len(parents) == 2
275 merge = len(parents) == 2
276
276
277 if not user or not date or not message or not parents[0]:
277 if not user or not date or not message or not parents[0]:
278 raise util.Abort(_('transplant log file is corrupt'))
278 raise util.Abort(_('transplant log file is corrupt'))
279
279
280 extra = {'transplant_source': node}
280 extra = {'transplant_source': node}
281 wlock = repo.wlock()
281 wlock = repo.wlock()
282 try:
282 try:
283 p1, p2 = repo.dirstate.parents()
283 p1, p2 = repo.dirstate.parents()
284 if p1 != parents[0]:
284 if p1 != parents[0]:
285 raise util.Abort(
285 raise util.Abort(
286 _('working dir not at transplant parent %s') %
286 _('working dir not at transplant parent %s') %
287 revlog.hex(parents[0]))
287 revlog.hex(parents[0]))
288 if merge:
288 if merge:
289 repo.dirstate.setparents(p1, parents[1])
289 repo.dirstate.setparents(p1, parents[1])
290 n = repo.commit(message, user, date, extra=extra)
290 n = repo.commit(message, user, date, extra=extra)
291 if not n:
291 if not n:
292 raise util.Abort(_('commit failed'))
292 raise util.Abort(_('commit failed'))
293 if not merge:
293 if not merge:
294 self.transplants.set(n, node)
294 self.transplants.set(n, node)
295 self.unlog()
295 self.unlog()
296
296
297 return n, node
297 return n, node
298 finally:
298 finally:
299 wlock.release()
299 wlock.release()
300
300
301 def readseries(self):
301 def readseries(self):
302 nodes = []
302 nodes = []
303 merges = []
303 merges = []
304 cur = nodes
304 cur = nodes
305 for line in self.opener('series').read().splitlines():
305 for line in self.opener('series').read().splitlines():
306 if line.startswith('# Merges'):
306 if line.startswith('# Merges'):
307 cur = merges
307 cur = merges
308 continue
308 continue
309 cur.append(revlog.bin(line))
309 cur.append(revlog.bin(line))
310
310
311 return (nodes, merges)
311 return (nodes, merges)
312
312
313 def saveseries(self, revmap, merges):
313 def saveseries(self, revmap, merges):
314 if not revmap:
314 if not revmap:
315 return
315 return
316
316
317 if not os.path.isdir(self.path):
317 if not os.path.isdir(self.path):
318 os.mkdir(self.path)
318 os.mkdir(self.path)
319 series = self.opener('series', 'w')
319 series = self.opener('series', 'w')
320 for rev in sorted(revmap):
320 for rev in sorted(revmap):
321 series.write(revlog.hex(revmap[rev]) + '\n')
321 series.write(revlog.hex(revmap[rev]) + '\n')
322 if merges:
322 if merges:
323 series.write('# Merges\n')
323 series.write('# Merges\n')
324 for m in merges:
324 for m in merges:
325 series.write(revlog.hex(m) + '\n')
325 series.write(revlog.hex(m) + '\n')
326 series.close()
326 series.close()
327
327
328 def parselog(self, fp):
328 def parselog(self, fp):
329 parents = []
329 parents = []
330 message = []
330 message = []
331 node = revlog.nullid
331 node = revlog.nullid
332 inmsg = False
332 inmsg = False
333 for line in fp.read().splitlines():
333 for line in fp.read().splitlines():
334 if inmsg:
334 if inmsg:
335 message.append(line)
335 message.append(line)
336 elif line.startswith('# User '):
336 elif line.startswith('# User '):
337 user = line[7:]
337 user = line[7:]
338 elif line.startswith('# Date '):
338 elif line.startswith('# Date '):
339 date = line[7:]
339 date = line[7:]
340 elif line.startswith('# Node ID '):
340 elif line.startswith('# Node ID '):
341 node = revlog.bin(line[10:])
341 node = revlog.bin(line[10:])
342 elif line.startswith('# Parent '):
342 elif line.startswith('# Parent '):
343 parents.append(revlog.bin(line[9:]))
343 parents.append(revlog.bin(line[9:]))
344 elif not line.startswith('#'):
344 elif not line.startswith('#'):
345 inmsg = True
345 inmsg = True
346 message.append(line)
346 message.append(line)
347 return (node, user, date, '\n'.join(message), parents)
347 return (node, user, date, '\n'.join(message), parents)
348
348
349 def log(self, user, date, message, p1, p2, merge=False):
349 def log(self, user, date, message, p1, p2, merge=False):
350 '''journal changelog metadata for later recover'''
350 '''journal changelog metadata for later recover'''
351
351
352 if not os.path.isdir(self.path):
352 if not os.path.isdir(self.path):
353 os.mkdir(self.path)
353 os.mkdir(self.path)
354 fp = self.opener('journal', 'w')
354 fp = self.opener('journal', 'w')
355 fp.write('# User %s\n' % user)
355 fp.write('# User %s\n' % user)
356 fp.write('# Date %s\n' % date)
356 fp.write('# Date %s\n' % date)
357 fp.write('# Node ID %s\n' % revlog.hex(p2))
357 fp.write('# Node ID %s\n' % revlog.hex(p2))
358 fp.write('# Parent ' + revlog.hex(p1) + '\n')
358 fp.write('# Parent ' + revlog.hex(p1) + '\n')
359 if merge:
359 if merge:
360 fp.write('# Parent ' + revlog.hex(p2) + '\n')
360 fp.write('# Parent ' + revlog.hex(p2) + '\n')
361 fp.write(message.rstrip() + '\n')
361 fp.write(message.rstrip() + '\n')
362 fp.close()
362 fp.close()
363
363
364 def readlog(self):
364 def readlog(self):
365 return self.parselog(self.opener('journal'))
365 return self.parselog(self.opener('journal'))
366
366
367 def unlog(self):
367 def unlog(self):
368 '''remove changelog journal'''
368 '''remove changelog journal'''
369 absdst = os.path.join(self.path, 'journal')
369 absdst = os.path.join(self.path, 'journal')
370 if os.path.exists(absdst):
370 if os.path.exists(absdst):
371 os.unlink(absdst)
371 os.unlink(absdst)
372
372
373 def transplantfilter(self, repo, source, root):
373 def transplantfilter(self, repo, source, root):
374 def matchfn(node):
374 def matchfn(node):
375 if self.applied(repo, node, root):
375 if self.applied(repo, node, root):
376 return False
376 return False
377 if source.changelog.parents(node)[1] != revlog.nullid:
377 if source.changelog.parents(node)[1] != revlog.nullid:
378 return False
378 return False
379 extra = source.changelog.read(node)[5]
379 extra = source.changelog.read(node)[5]
380 cnode = extra.get('transplant_source')
380 cnode = extra.get('transplant_source')
381 if cnode and self.applied(repo, cnode, root):
381 if cnode and self.applied(repo, cnode, root):
382 return False
382 return False
383 return True
383 return True
384
384
385 return matchfn
385 return matchfn
386
386
387 def hasnode(repo, node):
387 def hasnode(repo, node):
388 try:
388 try:
389 return repo.changelog.rev(node) != None
389 return repo.changelog.rev(node) != None
390 except error.RevlogError:
390 except error.RevlogError:
391 return False
391 return False
392
392
393 def browserevs(ui, repo, nodes, opts):
393 def browserevs(ui, repo, nodes, opts):
394 '''interactively transplant changesets'''
394 '''interactively transplant changesets'''
395 def browsehelp(ui):
395 def browsehelp(ui):
396 ui.write('y: transplant this changeset\n'
396 ui.write(_('y: transplant this changeset\n'
397 'n: skip this changeset\n'
397 'n: skip this changeset\n'
398 'm: merge at this changeset\n'
398 'm: merge at this changeset\n'
399 'p: show patch\n'
399 'p: show patch\n'
400 'c: commit selected changesets\n'
400 'c: commit selected changesets\n'
401 'q: cancel transplant\n'
401 'q: cancel transplant\n'
402 '?: show this help\n')
402 '?: show this help\n'))
403
403
404 displayer = cmdutil.show_changeset(ui, repo, opts)
404 displayer = cmdutil.show_changeset(ui, repo, opts)
405 transplants = []
405 transplants = []
406 merges = []
406 merges = []
407 for node in nodes:
407 for node in nodes:
408 displayer.show(repo[node])
408 displayer.show(repo[node])
409 action = None
409 action = None
410 while not action:
410 while not action:
411 action = ui.prompt(_('apply changeset? [ynmpcq?]:'))
411 action = ui.prompt(_('apply changeset? [ynmpcq?]:'))
412 if action == '?':
412 if action == '?':
413 browsehelp(ui)
413 browsehelp(ui)
414 action = None
414 action = None
415 elif action == 'p':
415 elif action == 'p':
416 parent = repo.changelog.parents(node)[0]
416 parent = repo.changelog.parents(node)[0]
417 for chunk in patch.diff(repo, parent, node):
417 for chunk in patch.diff(repo, parent, node):
418 ui.write(chunk)
418 ui.write(chunk)
419 action = None
419 action = None
420 elif action not in ('y', 'n', 'm', 'c', 'q'):
420 elif action not in ('y', 'n', 'm', 'c', 'q'):
421 ui.write('no such option\n')
421 ui.write(_('no such option\n'))
422 action = None
422 action = None
423 if action == 'y':
423 if action == 'y':
424 transplants.append(node)
424 transplants.append(node)
425 elif action == 'm':
425 elif action == 'm':
426 merges.append(node)
426 merges.append(node)
427 elif action == 'c':
427 elif action == 'c':
428 break
428 break
429 elif action == 'q':
429 elif action == 'q':
430 transplants = ()
430 transplants = ()
431 merges = ()
431 merges = ()
432 break
432 break
433 displayer.close()
433 displayer.close()
434 return (transplants, merges)
434 return (transplants, merges)
435
435
436 def transplant(ui, repo, *revs, **opts):
436 def transplant(ui, repo, *revs, **opts):
437 '''transplant changesets from another branch
437 '''transplant changesets from another branch
438
438
439 Selected changesets will be applied on top of the current working
439 Selected changesets will be applied on top of the current working
440 directory with the log of the original changeset. If --log is
440 directory with the log of the original changeset. If --log is
441 specified, log messages will have a comment appended of the form::
441 specified, log messages will have a comment appended of the form::
442
442
443 (transplanted from CHANGESETHASH)
443 (transplanted from CHANGESETHASH)
444
444
445 You can rewrite the changelog message with the --filter option.
445 You can rewrite the changelog message with the --filter option.
446 Its argument will be invoked with the current changelog message as
446 Its argument will be invoked with the current changelog message as
447 $1 and the patch as $2.
447 $1 and the patch as $2.
448
448
449 If --source/-s is specified, selects changesets from the named
449 If --source/-s is specified, selects changesets from the named
450 repository. If --branch/-b is specified, selects changesets from
450 repository. If --branch/-b is specified, selects changesets from
451 the branch holding the named revision, up to that revision. If
451 the branch holding the named revision, up to that revision. If
452 --all/-a is specified, all changesets on the branch will be
452 --all/-a is specified, all changesets on the branch will be
453 transplanted, otherwise you will be prompted to select the
453 transplanted, otherwise you will be prompted to select the
454 changesets you want.
454 changesets you want.
455
455
456 hg transplant --branch REVISION --all will rebase the selected
456 hg transplant --branch REVISION --all will rebase the selected
457 branch (up to the named revision) onto your current working
457 branch (up to the named revision) onto your current working
458 directory.
458 directory.
459
459
460 You can optionally mark selected transplanted changesets as merge
460 You can optionally mark selected transplanted changesets as merge
461 changesets. You will not be prompted to transplant any ancestors
461 changesets. You will not be prompted to transplant any ancestors
462 of a merged transplant, and you can merge descendants of them
462 of a merged transplant, and you can merge descendants of them
463 normally instead of transplanting them.
463 normally instead of transplanting them.
464
464
465 If no merges or revisions are provided, hg transplant will start
465 If no merges or revisions are provided, hg transplant will start
466 an interactive changeset browser.
466 an interactive changeset browser.
467
467
468 If a changeset application fails, you can fix the merge by hand
468 If a changeset application fails, you can fix the merge by hand
469 and then resume where you left off by calling hg transplant
469 and then resume where you left off by calling hg transplant
470 --continue/-c.
470 --continue/-c.
471 '''
471 '''
472 def getremotechanges(repo, url):
472 def getremotechanges(repo, url):
473 sourcerepo = ui.expandpath(url)
473 sourcerepo = ui.expandpath(url)
474 source = hg.repository(ui, sourcerepo)
474 source = hg.repository(ui, sourcerepo)
475 common, incoming, rheads = repo.findcommonincoming(source, force=True)
475 common, incoming, rheads = repo.findcommonincoming(source, force=True)
476 if not incoming:
476 if not incoming:
477 return (source, None, None)
477 return (source, None, None)
478
478
479 bundle = None
479 bundle = None
480 if not source.local():
480 if not source.local():
481 if source.capable('changegroupsubset'):
481 if source.capable('changegroupsubset'):
482 cg = source.changegroupsubset(incoming, rheads, 'incoming')
482 cg = source.changegroupsubset(incoming, rheads, 'incoming')
483 else:
483 else:
484 cg = source.changegroup(incoming, 'incoming')
484 cg = source.changegroup(incoming, 'incoming')
485 bundle = changegroup.writebundle(cg, None, 'HG10UN')
485 bundle = changegroup.writebundle(cg, None, 'HG10UN')
486 source = bundlerepo.bundlerepository(ui, repo.root, bundle)
486 source = bundlerepo.bundlerepository(ui, repo.root, bundle)
487
487
488 return (source, incoming, bundle)
488 return (source, incoming, bundle)
489
489
490 def incwalk(repo, incoming, branches, match=util.always):
490 def incwalk(repo, incoming, branches, match=util.always):
491 if not branches:
491 if not branches:
492 branches = None
492 branches = None
493 for node in repo.changelog.nodesbetween(incoming, branches)[0]:
493 for node in repo.changelog.nodesbetween(incoming, branches)[0]:
494 if match(node):
494 if match(node):
495 yield node
495 yield node
496
496
497 def transplantwalk(repo, root, branches, match=util.always):
497 def transplantwalk(repo, root, branches, match=util.always):
498 if not branches:
498 if not branches:
499 branches = repo.heads()
499 branches = repo.heads()
500 ancestors = []
500 ancestors = []
501 for branch in branches:
501 for branch in branches:
502 ancestors.append(repo.changelog.ancestor(root, branch))
502 ancestors.append(repo.changelog.ancestor(root, branch))
503 for node in repo.changelog.nodesbetween(ancestors, branches)[0]:
503 for node in repo.changelog.nodesbetween(ancestors, branches)[0]:
504 if match(node):
504 if match(node):
505 yield node
505 yield node
506
506
507 def checkopts(opts, revs):
507 def checkopts(opts, revs):
508 if opts.get('continue'):
508 if opts.get('continue'):
509 if opts.get('branch') or opts.get('all') or opts.get('merge'):
509 if opts.get('branch') or opts.get('all') or opts.get('merge'):
510 raise util.Abort(_('--continue is incompatible with '
510 raise util.Abort(_('--continue is incompatible with '
511 'branch, all or merge'))
511 'branch, all or merge'))
512 return
512 return
513 if not (opts.get('source') or revs or
513 if not (opts.get('source') or revs or
514 opts.get('merge') or opts.get('branch')):
514 opts.get('merge') or opts.get('branch')):
515 raise util.Abort(_('no source URL, branch tag or revision '
515 raise util.Abort(_('no source URL, branch tag or revision '
516 'list provided'))
516 'list provided'))
517 if opts.get('all'):
517 if opts.get('all'):
518 if not opts.get('branch'):
518 if not opts.get('branch'):
519 raise util.Abort(_('--all requires a branch revision'))
519 raise util.Abort(_('--all requires a branch revision'))
520 if revs:
520 if revs:
521 raise util.Abort(_('--all is incompatible with a '
521 raise util.Abort(_('--all is incompatible with a '
522 'revision list'))
522 'revision list'))
523
523
524 checkopts(opts, revs)
524 checkopts(opts, revs)
525
525
526 if not opts.get('log'):
526 if not opts.get('log'):
527 opts['log'] = ui.config('transplant', 'log')
527 opts['log'] = ui.config('transplant', 'log')
528 if not opts.get('filter'):
528 if not opts.get('filter'):
529 opts['filter'] = ui.config('transplant', 'filter')
529 opts['filter'] = ui.config('transplant', 'filter')
530
530
531 tp = transplanter(ui, repo)
531 tp = transplanter(ui, repo)
532
532
533 p1, p2 = repo.dirstate.parents()
533 p1, p2 = repo.dirstate.parents()
534 if len(repo) > 0 and p1 == revlog.nullid:
534 if len(repo) > 0 and p1 == revlog.nullid:
535 raise util.Abort(_('no revision checked out'))
535 raise util.Abort(_('no revision checked out'))
536 if not opts.get('continue'):
536 if not opts.get('continue'):
537 if p2 != revlog.nullid:
537 if p2 != revlog.nullid:
538 raise util.Abort(_('outstanding uncommitted merges'))
538 raise util.Abort(_('outstanding uncommitted merges'))
539 m, a, r, d = repo.status()[:4]
539 m, a, r, d = repo.status()[:4]
540 if m or a or r or d:
540 if m or a or r or d:
541 raise util.Abort(_('outstanding local changes'))
541 raise util.Abort(_('outstanding local changes'))
542
542
543 bundle = None
543 bundle = None
544 source = opts.get('source')
544 source = opts.get('source')
545 if source:
545 if source:
546 (source, incoming, bundle) = getremotechanges(repo, source)
546 (source, incoming, bundle) = getremotechanges(repo, source)
547 else:
547 else:
548 source = repo
548 source = repo
549
549
550 try:
550 try:
551 if opts.get('continue'):
551 if opts.get('continue'):
552 tp.resume(repo, source, opts)
552 tp.resume(repo, source, opts)
553 return
553 return
554
554
555 tf = tp.transplantfilter(repo, source, p1)
555 tf = tp.transplantfilter(repo, source, p1)
556 if opts.get('prune'):
556 if opts.get('prune'):
557 prune = [source.lookup(r)
557 prune = [source.lookup(r)
558 for r in cmdutil.revrange(source, opts.get('prune'))]
558 for r in cmdutil.revrange(source, opts.get('prune'))]
559 matchfn = lambda x: tf(x) and x not in prune
559 matchfn = lambda x: tf(x) and x not in prune
560 else:
560 else:
561 matchfn = tf
561 matchfn = tf
562 branches = map(source.lookup, opts.get('branch', ()))
562 branches = map(source.lookup, opts.get('branch', ()))
563 merges = map(source.lookup, opts.get('merge', ()))
563 merges = map(source.lookup, opts.get('merge', ()))
564 revmap = {}
564 revmap = {}
565 if revs:
565 if revs:
566 for r in cmdutil.revrange(source, revs):
566 for r in cmdutil.revrange(source, revs):
567 revmap[int(r)] = source.lookup(r)
567 revmap[int(r)] = source.lookup(r)
568 elif opts.get('all') or not merges:
568 elif opts.get('all') or not merges:
569 if source != repo:
569 if source != repo:
570 alltransplants = incwalk(source, incoming, branches,
570 alltransplants = incwalk(source, incoming, branches,
571 match=matchfn)
571 match=matchfn)
572 else:
572 else:
573 alltransplants = transplantwalk(source, p1, branches,
573 alltransplants = transplantwalk(source, p1, branches,
574 match=matchfn)
574 match=matchfn)
575 if opts.get('all'):
575 if opts.get('all'):
576 revs = alltransplants
576 revs = alltransplants
577 else:
577 else:
578 revs, newmerges = browserevs(ui, source, alltransplants, opts)
578 revs, newmerges = browserevs(ui, source, alltransplants, opts)
579 merges.extend(newmerges)
579 merges.extend(newmerges)
580 for r in revs:
580 for r in revs:
581 revmap[source.changelog.rev(r)] = r
581 revmap[source.changelog.rev(r)] = r
582 for r in merges:
582 for r in merges:
583 revmap[source.changelog.rev(r)] = r
583 revmap[source.changelog.rev(r)] = r
584
584
585 tp.apply(repo, source, revmap, merges, opts)
585 tp.apply(repo, source, revmap, merges, opts)
586 finally:
586 finally:
587 if bundle:
587 if bundle:
588 source.close()
588 source.close()
589 os.unlink(bundle)
589 os.unlink(bundle)
590
590
591 cmdtable = {
591 cmdtable = {
592 "transplant":
592 "transplant":
593 (transplant,
593 (transplant,
594 [('s', 'source', '', _('pull patches from REPOSITORY')),
594 [('s', 'source', '', _('pull patches from REPOSITORY')),
595 ('b', 'branch', [], _('pull patches from branch BRANCH')),
595 ('b', 'branch', [], _('pull patches from branch BRANCH')),
596 ('a', 'all', None, _('pull all changesets up to BRANCH')),
596 ('a', 'all', None, _('pull all changesets up to BRANCH')),
597 ('p', 'prune', [], _('skip over REV')),
597 ('p', 'prune', [], _('skip over REV')),
598 ('m', 'merge', [], _('merge at REV')),
598 ('m', 'merge', [], _('merge at REV')),
599 ('', 'log', None, _('append transplant info to log message')),
599 ('', 'log', None, _('append transplant info to log message')),
600 ('c', 'continue', None, _('continue last transplant session '
600 ('c', 'continue', None, _('continue last transplant session '
601 'after repair')),
601 'after repair')),
602 ('', 'filter', '', _('filter changesets through FILTER'))],
602 ('', 'filter', '', _('filter changesets through FILTER'))],
603 _('hg transplant [-s REPOSITORY] [-b BRANCH [-a]] [-p REV] '
603 _('hg transplant [-s REPOSITORY] [-b BRANCH [-a]] [-p REV] '
604 '[-m REV] [REV]...'))
604 '[-m REV] [REV]...'))
605 }
605 }
@@ -1,403 +1,407 b''
1 /*
1 /*
2 bdiff.c - efficient binary diff extension for Mercurial
2 bdiff.c - efficient binary diff extension for Mercurial
3
3
4 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
5
5
6 This software may be used and distributed according to the terms of
6 This software may be used and distributed according to the terms of
7 the GNU General Public License, incorporated herein by reference.
7 the GNU General Public License, incorporated herein by reference.
8
8
9 Based roughly on Python difflib
9 Based roughly on Python difflib
10 */
10 */
11
11
12 #include <Python.h>
12 #include <Python.h>
13 #include <stdlib.h>
13 #include <stdlib.h>
14 #include <string.h>
14 #include <string.h>
15 #include <limits.h>
15 #include <limits.h>
16
16
17 #if defined __hpux || defined __SUNPRO_C || defined _AIX
17 #if defined __hpux || defined __SUNPRO_C || defined _AIX
18 #define inline
18 #define inline
19 #endif
19 #endif
20
20
21 #ifdef __linux
21 #ifdef __linux
22 #define inline __inline
22 #define inline __inline
23 #endif
23 #endif
24
24
25 #ifdef _WIN32
25 #ifdef _WIN32
26 #ifdef _MSC_VER
26 #ifdef _MSC_VER
27 #define inline __inline
27 #define inline __inline
28 typedef unsigned long uint32_t;
28 typedef unsigned long uint32_t;
29 #else
29 #else
30 #include <stdint.h>
30 #include <stdint.h>
31 #endif
31 #endif
32 static uint32_t htonl(uint32_t x)
32 static uint32_t htonl(uint32_t x)
33 {
33 {
34 return ((x & 0x000000ffUL) << 24) |
34 return ((x & 0x000000ffUL) << 24) |
35 ((x & 0x0000ff00UL) << 8) |
35 ((x & 0x0000ff00UL) << 8) |
36 ((x & 0x00ff0000UL) >> 8) |
36 ((x & 0x00ff0000UL) >> 8) |
37 ((x & 0xff000000UL) >> 24);
37 ((x & 0xff000000UL) >> 24);
38 }
38 }
39 #else
39 #else
40 #include <sys/types.h>
40 #include <sys/types.h>
41 #if defined __BEOS__ && !defined __HAIKU__
41 #if defined __BEOS__ && !defined __HAIKU__
42 #include <ByteOrder.h>
42 #include <ByteOrder.h>
43 #else
43 #else
44 #include <arpa/inet.h>
44 #include <arpa/inet.h>
45 #endif
45 #endif
46 #include <inttypes.h>
46 #include <inttypes.h>
47 #endif
47 #endif
48
48
49 struct line {
49 struct line {
50 int h, len, n, e;
50 int h, len, n, e;
51 const char *l;
51 const char *l;
52 };
52 };
53
53
54 struct pos {
54 struct pos {
55 int pos, len;
55 int pos, len;
56 };
56 };
57
57
58 struct hunk {
58 struct hunk {
59 int a1, a2, b1, b2;
59 int a1, a2, b1, b2;
60 };
60 };
61
61
62 struct hunklist {
62 struct hunklist {
63 struct hunk *base, *head;
63 struct hunk *base, *head;
64 };
64 };
65
65
66 int splitlines(const char *a, int len, struct line **lr)
66 int splitlines(const char *a, int len, struct line **lr)
67 {
67 {
68 int h, i;
68 int h, i;
69 const char *p, *b = a;
69 const char *p, *b = a;
70 const char * const plast = a + len - 1;
70 const char * const plast = a + len - 1;
71 struct line *l;
71 struct line *l;
72
72
73 /* count the lines */
73 /* count the lines */
74 i = 1; /* extra line for sentinel */
74 i = 1; /* extra line for sentinel */
75 for (p = a; p < a + len; p++)
75 for (p = a; p < a + len; p++)
76 if (*p == '\n' || p == plast)
76 if (*p == '\n' || p == plast)
77 i++;
77 i++;
78
78
79 *lr = l = (struct line *)malloc(sizeof(struct line) * i);
79 *lr = l = (struct line *)malloc(sizeof(struct line) * i);
80 if (!l)
80 if (!l)
81 return -1;
81 return -1;
82
82
83 /* build the line array and calculate hashes */
83 /* build the line array and calculate hashes */
84 h = 0;
84 h = 0;
85 for (p = a; p < a + len; p++) {
85 for (p = a; p < a + len; p++) {
86 /* Leonid Yuriev's hash */
86 /* Leonid Yuriev's hash */
87 h = (h * 1664525) + *p + 1013904223;
87 h = (h * 1664525) + *p + 1013904223;
88
88
89 if (*p == '\n' || p == plast) {
89 if (*p == '\n' || p == plast) {
90 l->h = h;
90 l->h = h;
91 h = 0;
91 h = 0;
92 l->len = p - b + 1;
92 l->len = p - b + 1;
93 l->l = b;
93 l->l = b;
94 l->n = INT_MAX;
94 l->n = INT_MAX;
95 l++;
95 l++;
96 b = p + 1;
96 b = p + 1;
97 }
97 }
98 }
98 }
99
99
100 /* set up a sentinel */
100 /* set up a sentinel */
101 l->h = l->len = 0;
101 l->h = l->len = 0;
102 l->l = a + len;
102 l->l = a + len;
103 return i - 1;
103 return i - 1;
104 }
104 }
105
105
106 int inline cmp(struct line *a, struct line *b)
106 int inline cmp(struct line *a, struct line *b)
107 {
107 {
108 return a->h != b->h || a->len != b->len || memcmp(a->l, b->l, a->len);
108 return a->h != b->h || a->len != b->len || memcmp(a->l, b->l, a->len);
109 }
109 }
110
110
111 static int equatelines(struct line *a, int an, struct line *b, int bn)
111 static int equatelines(struct line *a, int an, struct line *b, int bn)
112 {
112 {
113 int i, j, buckets = 1, t, scale;
113 int i, j, buckets = 1, t, scale;
114 struct pos *h = NULL;
114 struct pos *h = NULL;
115
115
116 /* build a hash table of the next highest power of 2 */
116 /* build a hash table of the next highest power of 2 */
117 while (buckets < bn + 1)
117 while (buckets < bn + 1)
118 buckets *= 2;
118 buckets *= 2;
119
119
120 /* try to allocate a large hash table to avoid collisions */
120 /* try to allocate a large hash table to avoid collisions */
121 for (scale = 4; scale; scale /= 2) {
121 for (scale = 4; scale; scale /= 2) {
122 h = (struct pos *)malloc(scale * buckets * sizeof(struct pos));
122 h = (struct pos *)malloc(scale * buckets * sizeof(struct pos));
123 if (h)
123 if (h)
124 break;
124 break;
125 }
125 }
126
126
127 if (!h)
127 if (!h)
128 return 0;
128 return 0;
129
129
130 buckets = buckets * scale - 1;
130 buckets = buckets * scale - 1;
131
131
132 /* clear the hash table */
132 /* clear the hash table */
133 for (i = 0; i <= buckets; i++) {
133 for (i = 0; i <= buckets; i++) {
134 h[i].pos = INT_MAX;
134 h[i].pos = INT_MAX;
135 h[i].len = 0;
135 h[i].len = 0;
136 }
136 }
137
137
138 /* add lines to the hash table chains */
138 /* add lines to the hash table chains */
139 for (i = bn - 1; i >= 0; i--) {
139 for (i = bn - 1; i >= 0; i--) {
140 /* find the equivalence class */
140 /* find the equivalence class */
141 for (j = b[i].h & buckets; h[j].pos != INT_MAX;
141 for (j = b[i].h & buckets; h[j].pos != INT_MAX;
142 j = (j + 1) & buckets)
142 j = (j + 1) & buckets)
143 if (!cmp(b + i, b + h[j].pos))
143 if (!cmp(b + i, b + h[j].pos))
144 break;
144 break;
145
145
146 /* add to the head of the equivalence class */
146 /* add to the head of the equivalence class */
147 b[i].n = h[j].pos;
147 b[i].n = h[j].pos;
148 b[i].e = j;
148 b[i].e = j;
149 h[j].pos = i;
149 h[j].pos = i;
150 h[j].len++; /* keep track of popularity */
150 h[j].len++; /* keep track of popularity */
151 }
151 }
152
152
153 /* compute popularity threshold */
153 /* compute popularity threshold */
154 t = (bn >= 31000) ? bn / 1000 : 1000000 / (bn + 1);
154 t = (bn >= 31000) ? bn / 1000 : 1000000 / (bn + 1);
155
155
156 /* match items in a to their equivalence class in b */
156 /* match items in a to their equivalence class in b */
157 for (i = 0; i < an; i++) {
157 for (i = 0; i < an; i++) {
158 /* find the equivalence class */
158 /* find the equivalence class */
159 for (j = a[i].h & buckets; h[j].pos != INT_MAX;
159 for (j = a[i].h & buckets; h[j].pos != INT_MAX;
160 j = (j + 1) & buckets)
160 j = (j + 1) & buckets)
161 if (!cmp(a + i, b + h[j].pos))
161 if (!cmp(a + i, b + h[j].pos))
162 break;
162 break;
163
163
164 a[i].e = j; /* use equivalence class for quick compare */
164 a[i].e = j; /* use equivalence class for quick compare */
165 if (h[j].len <= t)
165 if (h[j].len <= t)
166 a[i].n = h[j].pos; /* point to head of match list */
166 a[i].n = h[j].pos; /* point to head of match list */
167 else
167 else
168 a[i].n = INT_MAX; /* too popular */
168 a[i].n = INT_MAX; /* too popular */
169 }
169 }
170
170
171 /* discard hash tables */
171 /* discard hash tables */
172 free(h);
172 free(h);
173 return 1;
173 return 1;
174 }
174 }
175
175
176 static int longest_match(struct line *a, struct line *b, struct pos *pos,
176 static int longest_match(struct line *a, struct line *b, struct pos *pos,
177 int a1, int a2, int b1, int b2, int *omi, int *omj)
177 int a1, int a2, int b1, int b2, int *omi, int *omj)
178 {
178 {
179 int mi = a1, mj = b1, mk = 0, mb = 0, i, j, k;
179 int mi = a1, mj = b1, mk = 0, mb = 0, i, j, k;
180
180
181 for (i = a1; i < a2; i++) {
181 for (i = a1; i < a2; i++) {
182 /* skip things before the current block */
182 /* skip things before the current block */
183 for (j = a[i].n; j < b1; j = b[j].n)
183 for (j = a[i].n; j < b1; j = b[j].n)
184 ;
184 ;
185
185
186 /* loop through all lines match a[i] in b */
186 /* loop through all lines match a[i] in b */
187 for (; j < b2; j = b[j].n) {
187 for (; j < b2; j = b[j].n) {
188 /* does this extend an earlier match? */
188 /* does this extend an earlier match? */
189 if (i > a1 && j > b1 && pos[j - 1].pos == i - 1)
189 if (i > a1 && j > b1 && pos[j - 1].pos == i - 1)
190 k = pos[j - 1].len + 1;
190 k = pos[j - 1].len + 1;
191 else
191 else
192 k = 1;
192 k = 1;
193 pos[j].pos = i;
193 pos[j].pos = i;
194 pos[j].len = k;
194 pos[j].len = k;
195
195
196 /* best match so far? */
196 /* best match so far? */
197 if (k > mk) {
197 if (k > mk) {
198 mi = i;
198 mi = i;
199 mj = j;
199 mj = j;
200 mk = k;
200 mk = k;
201 }
201 }
202 }
202 }
203 }
203 }
204
204
205 if (mk) {
205 if (mk) {
206 mi = mi - mk + 1;
206 mi = mi - mk + 1;
207 mj = mj - mk + 1;
207 mj = mj - mk + 1;
208 }
208 }
209
209
210 /* expand match to include neighboring popular lines */
210 /* expand match to include neighboring popular lines */
211 while (mi - mb > a1 && mj - mb > b1 &&
211 while (mi - mb > a1 && mj - mb > b1 &&
212 a[mi - mb - 1].e == b[mj - mb - 1].e)
212 a[mi - mb - 1].e == b[mj - mb - 1].e)
213 mb++;
213 mb++;
214 while (mi + mk < a2 && mj + mk < b2 &&
214 while (mi + mk < a2 && mj + mk < b2 &&
215 a[mi + mk].e == b[mj + mk].e)
215 a[mi + mk].e == b[mj + mk].e)
216 mk++;
216 mk++;
217
217
218 *omi = mi - mb;
218 *omi = mi - mb;
219 *omj = mj - mb;
219 *omj = mj - mb;
220
220
221 return mk + mb;
221 return mk + mb;
222 }
222 }
223
223
224 static void recurse(struct line *a, struct line *b, struct pos *pos,
224 static void recurse(struct line *a, struct line *b, struct pos *pos,
225 int a1, int a2, int b1, int b2, struct hunklist *l)
225 int a1, int a2, int b1, int b2, struct hunklist *l)
226 {
226 {
227 int i, j, k;
227 int i, j, k;
228
228
229 /* find the longest match in this chunk */
229 while (1) {
230 k = longest_match(a, b, pos, a1, a2, b1, b2, &i, &j);
230 /* find the longest match in this chunk */
231 if (!k)
231 k = longest_match(a, b, pos, a1, a2, b1, b2, &i, &j);
232 return;
232 if (!k)
233 return;
233
234
234 /* and recurse on the remaining chunks on either side */
235 /* and recurse on the remaining chunks on either side */
235 recurse(a, b, pos, a1, i, b1, j, l);
236 recurse(a, b, pos, a1, i, b1, j, l);
236 l->head->a1 = i;
237 l->head->a1 = i;
237 l->head->a2 = i + k;
238 l->head->a2 = i + k;
238 l->head->b1 = j;
239 l->head->b1 = j;
239 l->head->b2 = j + k;
240 l->head->b2 = j + k;
240 l->head++;
241 l->head++;
241 recurse(a, b, pos, i + k, a2, j + k, b2, l);
242 /* tail-recursion didn't happen, so doing equivalent iteration */
243 a1 = i + k;
244 b1 = j + k;
245 }
242 }
246 }
243
247
244 static struct hunklist diff(struct line *a, int an, struct line *b, int bn)
248 static struct hunklist diff(struct line *a, int an, struct line *b, int bn)
245 {
249 {
246 struct hunklist l;
250 struct hunklist l;
247 struct hunk *curr;
251 struct hunk *curr;
248 struct pos *pos;
252 struct pos *pos;
249 int t;
253 int t;
250
254
251 /* allocate and fill arrays */
255 /* allocate and fill arrays */
252 t = equatelines(a, an, b, bn);
256 t = equatelines(a, an, b, bn);
253 pos = (struct pos *)calloc(bn ? bn : 1, sizeof(struct pos));
257 pos = (struct pos *)calloc(bn ? bn : 1, sizeof(struct pos));
254 /* we can't have more matches than lines in the shorter file */
258 /* we can't have more matches than lines in the shorter file */
255 l.head = l.base = (struct hunk *)malloc(sizeof(struct hunk) *
259 l.head = l.base = (struct hunk *)malloc(sizeof(struct hunk) *
256 ((an<bn ? an:bn) + 1));
260 ((an<bn ? an:bn) + 1));
257
261
258 if (pos && l.base && t) {
262 if (pos && l.base && t) {
259 /* generate the matching block list */
263 /* generate the matching block list */
260 recurse(a, b, pos, 0, an, 0, bn, &l);
264 recurse(a, b, pos, 0, an, 0, bn, &l);
261 l.head->a1 = l.head->a2 = an;
265 l.head->a1 = l.head->a2 = an;
262 l.head->b1 = l.head->b2 = bn;
266 l.head->b1 = l.head->b2 = bn;
263 l.head++;
267 l.head++;
264 }
268 }
265
269
266 free(pos);
270 free(pos);
267
271
268 /* normalize the hunk list, try to push each hunk towards the end */
272 /* normalize the hunk list, try to push each hunk towards the end */
269 for (curr = l.base; curr != l.head; curr++) {
273 for (curr = l.base; curr != l.head; curr++) {
270 struct hunk *next = curr + 1;
274 struct hunk *next = curr + 1;
271 int shift = 0;
275 int shift = 0;
272
276
273 if (next == l.head)
277 if (next == l.head)
274 break;
278 break;
275
279
276 if (curr->a2 == next->a1)
280 if (curr->a2 == next->a1)
277 while (curr->a2 + shift < an && curr->b2 + shift < bn
281 while (curr->a2 + shift < an && curr->b2 + shift < bn
278 && !cmp(a + curr->a2 + shift,
282 && !cmp(a + curr->a2 + shift,
279 b + curr->b2 + shift))
283 b + curr->b2 + shift))
280 shift++;
284 shift++;
281 else if (curr->b2 == next->b1)
285 else if (curr->b2 == next->b1)
282 while (curr->b2 + shift < bn && curr->a2 + shift < an
286 while (curr->b2 + shift < bn && curr->a2 + shift < an
283 && !cmp(b + curr->b2 + shift,
287 && !cmp(b + curr->b2 + shift,
284 a + curr->a2 + shift))
288 a + curr->a2 + shift))
285 shift++;
289 shift++;
286 if (!shift)
290 if (!shift)
287 continue;
291 continue;
288 curr->b2 += shift;
292 curr->b2 += shift;
289 next->b1 += shift;
293 next->b1 += shift;
290 curr->a2 += shift;
294 curr->a2 += shift;
291 next->a1 += shift;
295 next->a1 += shift;
292 }
296 }
293
297
294 return l;
298 return l;
295 }
299 }
296
300
297 static PyObject *blocks(PyObject *self, PyObject *args)
301 static PyObject *blocks(PyObject *self, PyObject *args)
298 {
302 {
299 PyObject *sa, *sb, *rl = NULL, *m;
303 PyObject *sa, *sb, *rl = NULL, *m;
300 struct line *a, *b;
304 struct line *a, *b;
301 struct hunklist l = {NULL, NULL};
305 struct hunklist l = {NULL, NULL};
302 struct hunk *h;
306 struct hunk *h;
303 int an, bn, pos = 0;
307 int an, bn, pos = 0;
304
308
305 if (!PyArg_ParseTuple(args, "SS:bdiff", &sa, &sb))
309 if (!PyArg_ParseTuple(args, "SS:bdiff", &sa, &sb))
306 return NULL;
310 return NULL;
307
311
308 an = splitlines(PyString_AsString(sa), PyString_Size(sa), &a);
312 an = splitlines(PyString_AsString(sa), PyString_Size(sa), &a);
309 bn = splitlines(PyString_AsString(sb), PyString_Size(sb), &b);
313 bn = splitlines(PyString_AsString(sb), PyString_Size(sb), &b);
310 if (!a || !b)
314 if (!a || !b)
311 goto nomem;
315 goto nomem;
312
316
313 l = diff(a, an, b, bn);
317 l = diff(a, an, b, bn);
314 rl = PyList_New(l.head - l.base);
318 rl = PyList_New(l.head - l.base);
315 if (!l.head || !rl)
319 if (!l.head || !rl)
316 goto nomem;
320 goto nomem;
317
321
318 for (h = l.base; h != l.head; h++) {
322 for (h = l.base; h != l.head; h++) {
319 m = Py_BuildValue("iiii", h->a1, h->a2, h->b1, h->b2);
323 m = Py_BuildValue("iiii", h->a1, h->a2, h->b1, h->b2);
320 PyList_SetItem(rl, pos, m);
324 PyList_SetItem(rl, pos, m);
321 pos++;
325 pos++;
322 }
326 }
323
327
324 nomem:
328 nomem:
325 free(a);
329 free(a);
326 free(b);
330 free(b);
327 free(l.base);
331 free(l.base);
328 return rl ? rl : PyErr_NoMemory();
332 return rl ? rl : PyErr_NoMemory();
329 }
333 }
330
334
331 static PyObject *bdiff(PyObject *self, PyObject *args)
335 static PyObject *bdiff(PyObject *self, PyObject *args)
332 {
336 {
333 char *sa, *sb;
337 char *sa, *sb;
334 PyObject *result = NULL;
338 PyObject *result = NULL;
335 struct line *al, *bl;
339 struct line *al, *bl;
336 struct hunklist l = {NULL, NULL};
340 struct hunklist l = {NULL, NULL};
337 struct hunk *h;
341 struct hunk *h;
338 char encode[12], *rb;
342 char encode[12], *rb;
339 int an, bn, len = 0, la, lb;
343 int an, bn, len = 0, la, lb;
340
344
341 if (!PyArg_ParseTuple(args, "s#s#:bdiff", &sa, &la, &sb, &lb))
345 if (!PyArg_ParseTuple(args, "s#s#:bdiff", &sa, &la, &sb, &lb))
342 return NULL;
346 return NULL;
343
347
344 an = splitlines(sa, la, &al);
348 an = splitlines(sa, la, &al);
345 bn = splitlines(sb, lb, &bl);
349 bn = splitlines(sb, lb, &bl);
346 if (!al || !bl)
350 if (!al || !bl)
347 goto nomem;
351 goto nomem;
348
352
349 l = diff(al, an, bl, bn);
353 l = diff(al, an, bl, bn);
350 if (!l.head)
354 if (!l.head)
351 goto nomem;
355 goto nomem;
352
356
353 /* calculate length of output */
357 /* calculate length of output */
354 la = lb = 0;
358 la = lb = 0;
355 for (h = l.base; h != l.head; h++) {
359 for (h = l.base; h != l.head; h++) {
356 if (h->a1 != la || h->b1 != lb)
360 if (h->a1 != la || h->b1 != lb)
357 len += 12 + bl[h->b1].l - bl[lb].l;
361 len += 12 + bl[h->b1].l - bl[lb].l;
358 la = h->a2;
362 la = h->a2;
359 lb = h->b2;
363 lb = h->b2;
360 }
364 }
361
365
362 result = PyString_FromStringAndSize(NULL, len);
366 result = PyString_FromStringAndSize(NULL, len);
363 if (!result)
367 if (!result)
364 goto nomem;
368 goto nomem;
365
369
366 /* build binary patch */
370 /* build binary patch */
367 rb = PyString_AsString(result);
371 rb = PyString_AsString(result);
368 la = lb = 0;
372 la = lb = 0;
369
373
370 for (h = l.base; h != l.head; h++) {
374 for (h = l.base; h != l.head; h++) {
371 if (h->a1 != la || h->b1 != lb) {
375 if (h->a1 != la || h->b1 != lb) {
372 len = bl[h->b1].l - bl[lb].l;
376 len = bl[h->b1].l - bl[lb].l;
373 *(uint32_t *)(encode) = htonl(al[la].l - al->l);
377 *(uint32_t *)(encode) = htonl(al[la].l - al->l);
374 *(uint32_t *)(encode + 4) = htonl(al[h->a1].l - al->l);
378 *(uint32_t *)(encode + 4) = htonl(al[h->a1].l - al->l);
375 *(uint32_t *)(encode + 8) = htonl(len);
379 *(uint32_t *)(encode + 8) = htonl(len);
376 memcpy(rb, encode, 12);
380 memcpy(rb, encode, 12);
377 memcpy(rb + 12, bl[lb].l, len);
381 memcpy(rb + 12, bl[lb].l, len);
378 rb += 12 + len;
382 rb += 12 + len;
379 }
383 }
380 la = h->a2;
384 la = h->a2;
381 lb = h->b2;
385 lb = h->b2;
382 }
386 }
383
387
384 nomem:
388 nomem:
385 free(al);
389 free(al);
386 free(bl);
390 free(bl);
387 free(l.base);
391 free(l.base);
388 return result ? result : PyErr_NoMemory();
392 return result ? result : PyErr_NoMemory();
389 }
393 }
390
394
391 static char mdiff_doc[] = "Efficient binary diff.";
395 static char mdiff_doc[] = "Efficient binary diff.";
392
396
393 static PyMethodDef methods[] = {
397 static PyMethodDef methods[] = {
394 {"bdiff", bdiff, METH_VARARGS, "calculate a binary diff\n"},
398 {"bdiff", bdiff, METH_VARARGS, "calculate a binary diff\n"},
395 {"blocks", blocks, METH_VARARGS, "find a list of matching lines\n"},
399 {"blocks", blocks, METH_VARARGS, "find a list of matching lines\n"},
396 {NULL, NULL}
400 {NULL, NULL}
397 };
401 };
398
402
399 PyMODINIT_FUNC initbdiff(void)
403 PyMODINIT_FUNC initbdiff(void)
400 {
404 {
401 Py_InitModule3("bdiff", methods, mdiff_doc);
405 Py_InitModule3("bdiff", methods, mdiff_doc);
402 }
406 }
403
407
@@ -1,3829 +1,3829 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from node import hex, nullid, nullrev, short
8 from node import hex, nullid, nullrev, short
9 from lock import release
9 from lock import release
10 from i18n import _, gettext
10 from i18n import _, gettext
11 import os, re, sys, difflib, time, tempfile
11 import os, re, sys, difflib, time, tempfile
12 import hg, util, revlog, bundlerepo, extensions, copies, error
12 import hg, util, revlog, bundlerepo, extensions, copies, error
13 import patch, help, mdiff, url, encoding, templatekw
13 import patch, help, mdiff, url, encoding, templatekw
14 import archival, changegroup, cmdutil, sshserver, hbisect
14 import archival, changegroup, cmdutil, sshserver, hbisect
15 from hgweb import server
15 from hgweb import server
16 import merge as merge_
16 import merge as merge_
17 import minirst
17 import minirst
18
18
19 # Commands start here, listed alphabetically
19 # Commands start here, listed alphabetically
20
20
21 def add(ui, repo, *pats, **opts):
21 def add(ui, repo, *pats, **opts):
22 """add the specified files on the next commit
22 """add the specified files on the next commit
23
23
24 Schedule files to be version controlled and added to the
24 Schedule files to be version controlled and added to the
25 repository.
25 repository.
26
26
27 The files will be added to the repository at the next commit. To
27 The files will be added to the repository at the next commit. To
28 undo an add before that, see hg forget.
28 undo an add before that, see hg forget.
29
29
30 If no names are given, add all files to the repository.
30 If no names are given, add all files to the repository.
31
31
32 .. container:: verbose
32 .. container:: verbose
33
33
34 An example showing how new (unknown) files are added
34 An example showing how new (unknown) files are added
35 automatically by ``hg add``::
35 automatically by ``hg add``::
36
36
37 $ ls
37 $ ls
38 foo.c
38 foo.c
39 $ hg status
39 $ hg status
40 ? foo.c
40 ? foo.c
41 $ hg add
41 $ hg add
42 adding foo.c
42 adding foo.c
43 $ hg status
43 $ hg status
44 A foo.c
44 A foo.c
45 """
45 """
46
46
47 bad = []
47 bad = []
48 names = []
48 names = []
49 m = cmdutil.match(repo, pats, opts)
49 m = cmdutil.match(repo, pats, opts)
50 oldbad = m.bad
50 oldbad = m.bad
51 m.bad = lambda x, y: bad.append(x) or oldbad(x, y)
51 m.bad = lambda x, y: bad.append(x) or oldbad(x, y)
52
52
53 for f in repo.walk(m):
53 for f in repo.walk(m):
54 exact = m.exact(f)
54 exact = m.exact(f)
55 if exact or f not in repo.dirstate:
55 if exact or f not in repo.dirstate:
56 names.append(f)
56 names.append(f)
57 if ui.verbose or not exact:
57 if ui.verbose or not exact:
58 ui.status(_('adding %s\n') % m.rel(f))
58 ui.status(_('adding %s\n') % m.rel(f))
59 if not opts.get('dry_run'):
59 if not opts.get('dry_run'):
60 bad += [f for f in repo.add(names) if f in m.files()]
60 bad += [f for f in repo.add(names) if f in m.files()]
61 return bad and 1 or 0
61 return bad and 1 or 0
62
62
63 def addremove(ui, repo, *pats, **opts):
63 def addremove(ui, repo, *pats, **opts):
64 """add all new files, delete all missing files
64 """add all new files, delete all missing files
65
65
66 Add all new files and remove all missing files from the
66 Add all new files and remove all missing files from the
67 repository.
67 repository.
68
68
69 New files are ignored if they match any of the patterns in
69 New files are ignored if they match any of the patterns in
70 .hgignore. As with add, these changes take effect at the next
70 .hgignore. As with add, these changes take effect at the next
71 commit.
71 commit.
72
72
73 Use the -s/--similarity option to detect renamed files. With a
73 Use the -s/--similarity option to detect renamed files. With a
74 parameter greater than 0, this compares every removed file with
74 parameter greater than 0, this compares every removed file with
75 every added file and records those similar enough as renames. This
75 every added file and records those similar enough as renames. This
76 option takes a percentage between 0 (disabled) and 100 (files must
76 option takes a percentage between 0 (disabled) and 100 (files must
77 be identical) as its parameter. Detecting renamed files this way
77 be identical) as its parameter. Detecting renamed files this way
78 can be expensive.
78 can be expensive.
79 """
79 """
80 try:
80 try:
81 sim = float(opts.get('similarity') or 0)
81 sim = float(opts.get('similarity') or 0)
82 except ValueError:
82 except ValueError:
83 raise util.Abort(_('similarity must be a number'))
83 raise util.Abort(_('similarity must be a number'))
84 if sim < 0 or sim > 100:
84 if sim < 0 or sim > 100:
85 raise util.Abort(_('similarity must be between 0 and 100'))
85 raise util.Abort(_('similarity must be between 0 and 100'))
86 return cmdutil.addremove(repo, pats, opts, similarity=sim / 100.0)
86 return cmdutil.addremove(repo, pats, opts, similarity=sim / 100.0)
87
87
88 def annotate(ui, repo, *pats, **opts):
88 def annotate(ui, repo, *pats, **opts):
89 """show changeset information by line for each file
89 """show changeset information by line for each file
90
90
91 List changes in files, showing the revision id responsible for
91 List changes in files, showing the revision id responsible for
92 each line
92 each line
93
93
94 This command is useful for discovering when a change was made and
94 This command is useful for discovering when a change was made and
95 by whom.
95 by whom.
96
96
97 Without the -a/--text option, annotate will avoid processing files
97 Without the -a/--text option, annotate will avoid processing files
98 it detects as binary. With -a, annotate will annotate the file
98 it detects as binary. With -a, annotate will annotate the file
99 anyway, although the results will probably be neither useful
99 anyway, although the results will probably be neither useful
100 nor desirable.
100 nor desirable.
101 """
101 """
102 datefunc = ui.quiet and util.shortdate or util.datestr
102 datefunc = ui.quiet and util.shortdate or util.datestr
103 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
103 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
104
104
105 if not pats:
105 if not pats:
106 raise util.Abort(_('at least one filename or pattern is required'))
106 raise util.Abort(_('at least one filename or pattern is required'))
107
107
108 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
108 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
109 ('number', lambda x: str(x[0].rev())),
109 ('number', lambda x: str(x[0].rev())),
110 ('changeset', lambda x: short(x[0].node())),
110 ('changeset', lambda x: short(x[0].node())),
111 ('date', getdate),
111 ('date', getdate),
112 ('file', lambda x: x[0].path()),
112 ('file', lambda x: x[0].path()),
113 ]
113 ]
114
114
115 if (not opts.get('user') and not opts.get('changeset')
115 if (not opts.get('user') and not opts.get('changeset')
116 and not opts.get('date') and not opts.get('file')):
116 and not opts.get('date') and not opts.get('file')):
117 opts['number'] = 1
117 opts['number'] = 1
118
118
119 linenumber = opts.get('line_number') is not None
119 linenumber = opts.get('line_number') is not None
120 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
120 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
121 raise util.Abort(_('at least one of -n/-c is required for -l'))
121 raise util.Abort(_('at least one of -n/-c is required for -l'))
122
122
123 funcmap = [func for op, func in opmap if opts.get(op)]
123 funcmap = [func for op, func in opmap if opts.get(op)]
124 if linenumber:
124 if linenumber:
125 lastfunc = funcmap[-1]
125 lastfunc = funcmap[-1]
126 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
126 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
127
127
128 ctx = repo[opts.get('rev')]
128 ctx = repo[opts.get('rev')]
129 m = cmdutil.match(repo, pats, opts)
129 m = cmdutil.match(repo, pats, opts)
130 follow = not opts.get('no_follow')
130 follow = not opts.get('no_follow')
131 for abs in ctx.walk(m):
131 for abs in ctx.walk(m):
132 fctx = ctx[abs]
132 fctx = ctx[abs]
133 if not opts.get('text') and util.binary(fctx.data()):
133 if not opts.get('text') and util.binary(fctx.data()):
134 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
134 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
135 continue
135 continue
136
136
137 lines = fctx.annotate(follow=follow, linenumber=linenumber)
137 lines = fctx.annotate(follow=follow, linenumber=linenumber)
138 pieces = []
138 pieces = []
139
139
140 for f in funcmap:
140 for f in funcmap:
141 l = [f(n) for n, dummy in lines]
141 l = [f(n) for n, dummy in lines]
142 if l:
142 if l:
143 ml = max(map(len, l))
143 ml = max(map(len, l))
144 pieces.append(["%*s" % (ml, x) for x in l])
144 pieces.append(["%*s" % (ml, x) for x in l])
145
145
146 if pieces:
146 if pieces:
147 for p, l in zip(zip(*pieces), lines):
147 for p, l in zip(zip(*pieces), lines):
148 ui.write("%s: %s" % (" ".join(p), l[1]))
148 ui.write("%s: %s" % (" ".join(p), l[1]))
149
149
150 def archive(ui, repo, dest, **opts):
150 def archive(ui, repo, dest, **opts):
151 '''create an unversioned archive of a repository revision
151 '''create an unversioned archive of a repository revision
152
152
153 By default, the revision used is the parent of the working
153 By default, the revision used is the parent of the working
154 directory; use -r/--rev to specify a different revision.
154 directory; use -r/--rev to specify a different revision.
155
155
156 To specify the type of archive to create, use -t/--type. Valid
156 To specify the type of archive to create, use -t/--type. Valid
157 types are:
157 types are:
158
158
159 :``files``: a directory full of files (default)
159 :``files``: a directory full of files (default)
160 :``tar``: tar archive, uncompressed
160 :``tar``: tar archive, uncompressed
161 :``tbz2``: tar archive, compressed using bzip2
161 :``tbz2``: tar archive, compressed using bzip2
162 :``tgz``: tar archive, compressed using gzip
162 :``tgz``: tar archive, compressed using gzip
163 :``uzip``: zip archive, uncompressed
163 :``uzip``: zip archive, uncompressed
164 :``zip``: zip archive, compressed using deflate
164 :``zip``: zip archive, compressed using deflate
165
165
166 The exact name of the destination archive or directory is given
166 The exact name of the destination archive or directory is given
167 using a format string; see 'hg help export' for details.
167 using a format string; see 'hg help export' for details.
168
168
169 Each member added to an archive file has a directory prefix
169 Each member added to an archive file has a directory prefix
170 prepended. Use -p/--prefix to specify a format string for the
170 prepended. Use -p/--prefix to specify a format string for the
171 prefix. The default is the basename of the archive, with suffixes
171 prefix. The default is the basename of the archive, with suffixes
172 removed.
172 removed.
173 '''
173 '''
174
174
175 ctx = repo[opts.get('rev')]
175 ctx = repo[opts.get('rev')]
176 if not ctx:
176 if not ctx:
177 raise util.Abort(_('no working directory: please specify a revision'))
177 raise util.Abort(_('no working directory: please specify a revision'))
178 node = ctx.node()
178 node = ctx.node()
179 dest = cmdutil.make_filename(repo, dest, node)
179 dest = cmdutil.make_filename(repo, dest, node)
180 if os.path.realpath(dest) == repo.root:
180 if os.path.realpath(dest) == repo.root:
181 raise util.Abort(_('repository root cannot be destination'))
181 raise util.Abort(_('repository root cannot be destination'))
182 matchfn = cmdutil.match(repo, [], opts)
182 matchfn = cmdutil.match(repo, [], opts)
183 kind = opts.get('type') or 'files'
183 kind = opts.get('type') or 'files'
184 prefix = opts.get('prefix')
184 prefix = opts.get('prefix')
185 if dest == '-':
185 if dest == '-':
186 if kind == 'files':
186 if kind == 'files':
187 raise util.Abort(_('cannot archive plain files to stdout'))
187 raise util.Abort(_('cannot archive plain files to stdout'))
188 dest = sys.stdout
188 dest = sys.stdout
189 if not prefix:
189 if not prefix:
190 prefix = os.path.basename(repo.root) + '-%h'
190 prefix = os.path.basename(repo.root) + '-%h'
191 prefix = cmdutil.make_filename(repo, prefix, node)
191 prefix = cmdutil.make_filename(repo, prefix, node)
192 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
192 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
193 matchfn, prefix)
193 matchfn, prefix)
194
194
195 def backout(ui, repo, node=None, rev=None, **opts):
195 def backout(ui, repo, node=None, rev=None, **opts):
196 '''reverse effect of earlier changeset
196 '''reverse effect of earlier changeset
197
197
198 Commit the backed out changes as a new changeset. The new
198 Commit the backed out changes as a new changeset. The new
199 changeset is a child of the backed out changeset.
199 changeset is a child of the backed out changeset.
200
200
201 If you backout a changeset other than the tip, a new head is
201 If you backout a changeset other than the tip, a new head is
202 created. This head will be the new tip and you should merge this
202 created. This head will be the new tip and you should merge this
203 backout changeset with another head.
203 backout changeset with another head.
204
204
205 The --merge option remembers the parent of the working directory
205 The --merge option remembers the parent of the working directory
206 before starting the backout, then merges the new head with that
206 before starting the backout, then merges the new head with that
207 changeset afterwards. This saves you from doing the merge by hand.
207 changeset afterwards. This saves you from doing the merge by hand.
208 The result of this merge is not committed, as with a normal merge.
208 The result of this merge is not committed, as with a normal merge.
209
209
210 See 'hg help dates' for a list of formats valid for -d/--date.
210 See 'hg help dates' for a list of formats valid for -d/--date.
211 '''
211 '''
212 if rev and node:
212 if rev and node:
213 raise util.Abort(_("please specify just one revision"))
213 raise util.Abort(_("please specify just one revision"))
214
214
215 if not rev:
215 if not rev:
216 rev = node
216 rev = node
217
217
218 if not rev:
218 if not rev:
219 raise util.Abort(_("please specify a revision to backout"))
219 raise util.Abort(_("please specify a revision to backout"))
220
220
221 date = opts.get('date')
221 date = opts.get('date')
222 if date:
222 if date:
223 opts['date'] = util.parsedate(date)
223 opts['date'] = util.parsedate(date)
224
224
225 cmdutil.bail_if_changed(repo)
225 cmdutil.bail_if_changed(repo)
226 node = repo.lookup(rev)
226 node = repo.lookup(rev)
227
227
228 op1, op2 = repo.dirstate.parents()
228 op1, op2 = repo.dirstate.parents()
229 a = repo.changelog.ancestor(op1, node)
229 a = repo.changelog.ancestor(op1, node)
230 if a != node:
230 if a != node:
231 raise util.Abort(_('cannot backout change on a different branch'))
231 raise util.Abort(_('cannot backout change on a different branch'))
232
232
233 p1, p2 = repo.changelog.parents(node)
233 p1, p2 = repo.changelog.parents(node)
234 if p1 == nullid:
234 if p1 == nullid:
235 raise util.Abort(_('cannot backout a change with no parents'))
235 raise util.Abort(_('cannot backout a change with no parents'))
236 if p2 != nullid:
236 if p2 != nullid:
237 if not opts.get('parent'):
237 if not opts.get('parent'):
238 raise util.Abort(_('cannot backout a merge changeset without '
238 raise util.Abort(_('cannot backout a merge changeset without '
239 '--parent'))
239 '--parent'))
240 p = repo.lookup(opts['parent'])
240 p = repo.lookup(opts['parent'])
241 if p not in (p1, p2):
241 if p not in (p1, p2):
242 raise util.Abort(_('%s is not a parent of %s') %
242 raise util.Abort(_('%s is not a parent of %s') %
243 (short(p), short(node)))
243 (short(p), short(node)))
244 parent = p
244 parent = p
245 else:
245 else:
246 if opts.get('parent'):
246 if opts.get('parent'):
247 raise util.Abort(_('cannot use --parent on non-merge changeset'))
247 raise util.Abort(_('cannot use --parent on non-merge changeset'))
248 parent = p1
248 parent = p1
249
249
250 # the backout should appear on the same branch
250 # the backout should appear on the same branch
251 branch = repo.dirstate.branch()
251 branch = repo.dirstate.branch()
252 hg.clean(repo, node, show_stats=False)
252 hg.clean(repo, node, show_stats=False)
253 repo.dirstate.setbranch(branch)
253 repo.dirstate.setbranch(branch)
254 revert_opts = opts.copy()
254 revert_opts = opts.copy()
255 revert_opts['date'] = None
255 revert_opts['date'] = None
256 revert_opts['all'] = True
256 revert_opts['all'] = True
257 revert_opts['rev'] = hex(parent)
257 revert_opts['rev'] = hex(parent)
258 revert_opts['no_backup'] = None
258 revert_opts['no_backup'] = None
259 revert(ui, repo, **revert_opts)
259 revert(ui, repo, **revert_opts)
260 commit_opts = opts.copy()
260 commit_opts = opts.copy()
261 commit_opts['addremove'] = False
261 commit_opts['addremove'] = False
262 if not commit_opts['message'] and not commit_opts['logfile']:
262 if not commit_opts['message'] and not commit_opts['logfile']:
263 # we don't translate commit messages
263 # we don't translate commit messages
264 commit_opts['message'] = "Backed out changeset %s" % short(node)
264 commit_opts['message'] = "Backed out changeset %s" % short(node)
265 commit_opts['force_editor'] = True
265 commit_opts['force_editor'] = True
266 commit(ui, repo, **commit_opts)
266 commit(ui, repo, **commit_opts)
267 def nice(node):
267 def nice(node):
268 return '%d:%s' % (repo.changelog.rev(node), short(node))
268 return '%d:%s' % (repo.changelog.rev(node), short(node))
269 ui.status(_('changeset %s backs out changeset %s\n') %
269 ui.status(_('changeset %s backs out changeset %s\n') %
270 (nice(repo.changelog.tip()), nice(node)))
270 (nice(repo.changelog.tip()), nice(node)))
271 if op1 != node:
271 if op1 != node:
272 hg.clean(repo, op1, show_stats=False)
272 hg.clean(repo, op1, show_stats=False)
273 if opts.get('merge'):
273 if opts.get('merge'):
274 ui.status(_('merging with changeset %s\n')
274 ui.status(_('merging with changeset %s\n')
275 % nice(repo.changelog.tip()))
275 % nice(repo.changelog.tip()))
276 hg.merge(repo, hex(repo.changelog.tip()))
276 hg.merge(repo, hex(repo.changelog.tip()))
277 else:
277 else:
278 ui.status(_('the backout changeset is a new head - '
278 ui.status(_('the backout changeset is a new head - '
279 'do not forget to merge\n'))
279 'do not forget to merge\n'))
280 ui.status(_('(use "backout --merge" '
280 ui.status(_('(use "backout --merge" '
281 'if you want to auto-merge)\n'))
281 'if you want to auto-merge)\n'))
282
282
283 def bisect(ui, repo, rev=None, extra=None, command=None,
283 def bisect(ui, repo, rev=None, extra=None, command=None,
284 reset=None, good=None, bad=None, skip=None, noupdate=None):
284 reset=None, good=None, bad=None, skip=None, noupdate=None):
285 """subdivision search of changesets
285 """subdivision search of changesets
286
286
287 This command helps to find changesets which introduce problems. To
287 This command helps to find changesets which introduce problems. To
288 use, mark the earliest changeset you know exhibits the problem as
288 use, mark the earliest changeset you know exhibits the problem as
289 bad, then mark the latest changeset which is free from the problem
289 bad, then mark the latest changeset which is free from the problem
290 as good. Bisect will update your working directory to a revision
290 as good. Bisect will update your working directory to a revision
291 for testing (unless the -U/--noupdate option is specified). Once
291 for testing (unless the -U/--noupdate option is specified). Once
292 you have performed tests, mark the working directory as good or
292 you have performed tests, mark the working directory as good or
293 bad, and bisect will either update to another candidate changeset
293 bad, and bisect will either update to another candidate changeset
294 or announce that it has found the bad revision.
294 or announce that it has found the bad revision.
295
295
296 As a shortcut, you can also use the revision argument to mark a
296 As a shortcut, you can also use the revision argument to mark a
297 revision as good or bad without checking it out first.
297 revision as good or bad without checking it out first.
298
298
299 If you supply a command, it will be used for automatic bisection.
299 If you supply a command, it will be used for automatic bisection.
300 Its exit status will be used to mark revisions as good or bad:
300 Its exit status will be used to mark revisions as good or bad:
301 status 0 means good, 125 means to skip the revision, 127
301 status 0 means good, 125 means to skip the revision, 127
302 (command not found) will abort the bisection, and any other
302 (command not found) will abort the bisection, and any other
303 non-zero exit status means the revision is bad.
303 non-zero exit status means the revision is bad.
304 """
304 """
305 def print_result(nodes, good):
305 def print_result(nodes, good):
306 displayer = cmdutil.show_changeset(ui, repo, {})
306 displayer = cmdutil.show_changeset(ui, repo, {})
307 if len(nodes) == 1:
307 if len(nodes) == 1:
308 # narrowed it down to a single revision
308 # narrowed it down to a single revision
309 if good:
309 if good:
310 ui.write(_("The first good revision is:\n"))
310 ui.write(_("The first good revision is:\n"))
311 else:
311 else:
312 ui.write(_("The first bad revision is:\n"))
312 ui.write(_("The first bad revision is:\n"))
313 displayer.show(repo[nodes[0]])
313 displayer.show(repo[nodes[0]])
314 else:
314 else:
315 # multiple possible revisions
315 # multiple possible revisions
316 if good:
316 if good:
317 ui.write(_("Due to skipped revisions, the first "
317 ui.write(_("Due to skipped revisions, the first "
318 "good revision could be any of:\n"))
318 "good revision could be any of:\n"))
319 else:
319 else:
320 ui.write(_("Due to skipped revisions, the first "
320 ui.write(_("Due to skipped revisions, the first "
321 "bad revision could be any of:\n"))
321 "bad revision could be any of:\n"))
322 for n in nodes:
322 for n in nodes:
323 displayer.show(repo[n])
323 displayer.show(repo[n])
324 displayer.close()
324 displayer.close()
325
325
326 def check_state(state, interactive=True):
326 def check_state(state, interactive=True):
327 if not state['good'] or not state['bad']:
327 if not state['good'] or not state['bad']:
328 if (good or bad or skip or reset) and interactive:
328 if (good or bad or skip or reset) and interactive:
329 return
329 return
330 if not state['good']:
330 if not state['good']:
331 raise util.Abort(_('cannot bisect (no known good revisions)'))
331 raise util.Abort(_('cannot bisect (no known good revisions)'))
332 else:
332 else:
333 raise util.Abort(_('cannot bisect (no known bad revisions)'))
333 raise util.Abort(_('cannot bisect (no known bad revisions)'))
334 return True
334 return True
335
335
336 # backward compatibility
336 # backward compatibility
337 if rev in "good bad reset init".split():
337 if rev in "good bad reset init".split():
338 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
338 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
339 cmd, rev, extra = rev, extra, None
339 cmd, rev, extra = rev, extra, None
340 if cmd == "good":
340 if cmd == "good":
341 good = True
341 good = True
342 elif cmd == "bad":
342 elif cmd == "bad":
343 bad = True
343 bad = True
344 else:
344 else:
345 reset = True
345 reset = True
346 elif extra or good + bad + skip + reset + bool(command) > 1:
346 elif extra or good + bad + skip + reset + bool(command) > 1:
347 raise util.Abort(_('incompatible arguments'))
347 raise util.Abort(_('incompatible arguments'))
348
348
349 if reset:
349 if reset:
350 p = repo.join("bisect.state")
350 p = repo.join("bisect.state")
351 if os.path.exists(p):
351 if os.path.exists(p):
352 os.unlink(p)
352 os.unlink(p)
353 return
353 return
354
354
355 state = hbisect.load_state(repo)
355 state = hbisect.load_state(repo)
356
356
357 if command:
357 if command:
358 changesets = 1
358 changesets = 1
359 try:
359 try:
360 while changesets:
360 while changesets:
361 # update state
361 # update state
362 status = util.system(command)
362 status = util.system(command)
363 if status == 125:
363 if status == 125:
364 transition = "skip"
364 transition = "skip"
365 elif status == 0:
365 elif status == 0:
366 transition = "good"
366 transition = "good"
367 # status < 0 means process was killed
367 # status < 0 means process was killed
368 elif status == 127:
368 elif status == 127:
369 raise util.Abort(_("failed to execute %s") % command)
369 raise util.Abort(_("failed to execute %s") % command)
370 elif status < 0:
370 elif status < 0:
371 raise util.Abort(_("%s killed") % command)
371 raise util.Abort(_("%s killed") % command)
372 else:
372 else:
373 transition = "bad"
373 transition = "bad"
374 ctx = repo[rev or '.']
374 ctx = repo[rev or '.']
375 state[transition].append(ctx.node())
375 state[transition].append(ctx.node())
376 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
376 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
377 check_state(state, interactive=False)
377 check_state(state, interactive=False)
378 # bisect
378 # bisect
379 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
379 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
380 # update to next check
380 # update to next check
381 cmdutil.bail_if_changed(repo)
381 cmdutil.bail_if_changed(repo)
382 hg.clean(repo, nodes[0], show_stats=False)
382 hg.clean(repo, nodes[0], show_stats=False)
383 finally:
383 finally:
384 hbisect.save_state(repo, state)
384 hbisect.save_state(repo, state)
385 return print_result(nodes, good)
385 return print_result(nodes, good)
386
386
387 # update state
387 # update state
388 node = repo.lookup(rev or '.')
388 node = repo.lookup(rev or '.')
389 if good or bad or skip:
389 if good or bad or skip:
390 if good:
390 if good:
391 state['good'].append(node)
391 state['good'].append(node)
392 elif bad:
392 elif bad:
393 state['bad'].append(node)
393 state['bad'].append(node)
394 elif skip:
394 elif skip:
395 state['skip'].append(node)
395 state['skip'].append(node)
396 hbisect.save_state(repo, state)
396 hbisect.save_state(repo, state)
397
397
398 if not check_state(state):
398 if not check_state(state):
399 return
399 return
400
400
401 # actually bisect
401 # actually bisect
402 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
402 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
403 if changesets == 0:
403 if changesets == 0:
404 print_result(nodes, good)
404 print_result(nodes, good)
405 else:
405 else:
406 assert len(nodes) == 1 # only a single node can be tested next
406 assert len(nodes) == 1 # only a single node can be tested next
407 node = nodes[0]
407 node = nodes[0]
408 # compute the approximate number of remaining tests
408 # compute the approximate number of remaining tests
409 tests, size = 0, 2
409 tests, size = 0, 2
410 while size <= changesets:
410 while size <= changesets:
411 tests, size = tests + 1, size * 2
411 tests, size = tests + 1, size * 2
412 rev = repo.changelog.rev(node)
412 rev = repo.changelog.rev(node)
413 ui.write(_("Testing changeset %d:%s "
413 ui.write(_("Testing changeset %d:%s "
414 "(%d changesets remaining, ~%d tests)\n")
414 "(%d changesets remaining, ~%d tests)\n")
415 % (rev, short(node), changesets, tests))
415 % (rev, short(node), changesets, tests))
416 if not noupdate:
416 if not noupdate:
417 cmdutil.bail_if_changed(repo)
417 cmdutil.bail_if_changed(repo)
418 return hg.clean(repo, node)
418 return hg.clean(repo, node)
419
419
420 def branch(ui, repo, label=None, **opts):
420 def branch(ui, repo, label=None, **opts):
421 """set or show the current branch name
421 """set or show the current branch name
422
422
423 With no argument, show the current branch name. With one argument,
423 With no argument, show the current branch name. With one argument,
424 set the working directory branch name (the branch will not exist
424 set the working directory branch name (the branch will not exist
425 in the repository until the next commit). Standard practice
425 in the repository until the next commit). Standard practice
426 recommends that primary development take place on the 'default'
426 recommends that primary development take place on the 'default'
427 branch.
427 branch.
428
428
429 Unless -f/--force is specified, branch will not let you set a
429 Unless -f/--force is specified, branch will not let you set a
430 branch name that already exists, even if it's inactive.
430 branch name that already exists, even if it's inactive.
431
431
432 Use -C/--clean to reset the working directory branch to that of
432 Use -C/--clean to reset the working directory branch to that of
433 the parent of the working directory, negating a previous branch
433 the parent of the working directory, negating a previous branch
434 change.
434 change.
435
435
436 Use the command 'hg update' to switch to an existing branch. Use
436 Use the command 'hg update' to switch to an existing branch. Use
437 'hg commit --close-branch' to mark this branch as closed.
437 'hg commit --close-branch' to mark this branch as closed.
438 """
438 """
439
439
440 if opts.get('clean'):
440 if opts.get('clean'):
441 label = repo[None].parents()[0].branch()
441 label = repo[None].parents()[0].branch()
442 repo.dirstate.setbranch(label)
442 repo.dirstate.setbranch(label)
443 ui.status(_('reset working directory to branch %s\n') % label)
443 ui.status(_('reset working directory to branch %s\n') % label)
444 elif label:
444 elif label:
445 utflabel = encoding.fromlocal(label)
445 utflabel = encoding.fromlocal(label)
446 if not opts.get('force') and utflabel in repo.branchtags():
446 if not opts.get('force') and utflabel in repo.branchtags():
447 if label not in [p.branch() for p in repo.parents()]:
447 if label not in [p.branch() for p in repo.parents()]:
448 raise util.Abort(_('a branch of the same name already exists'
448 raise util.Abort(_('a branch of the same name already exists'
449 ' (use --force to override)'))
449 ' (use --force to override)'))
450 repo.dirstate.setbranch(utflabel)
450 repo.dirstate.setbranch(utflabel)
451 ui.status(_('marked working directory as branch %s\n') % label)
451 ui.status(_('marked working directory as branch %s\n') % label)
452 else:
452 else:
453 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
453 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
454
454
455 def branches(ui, repo, active=False, closed=False):
455 def branches(ui, repo, active=False, closed=False):
456 """list repository named branches
456 """list repository named branches
457
457
458 List the repository's named branches, indicating which ones are
458 List the repository's named branches, indicating which ones are
459 inactive. If -c/--closed is specified, also list branches which have
459 inactive. If -c/--closed is specified, also list branches which have
460 been marked closed (see hg commit --close-branch).
460 been marked closed (see hg commit --close-branch).
461
461
462 If -a/--active is specified, only show active branches. A branch
462 If -a/--active is specified, only show active branches. A branch
463 is considered active if it contains repository heads.
463 is considered active if it contains repository heads.
464
464
465 Use the command 'hg update' to switch to an existing branch.
465 Use the command 'hg update' to switch to an existing branch.
466 """
466 """
467
467
468 hexfunc = ui.debugflag and hex or short
468 hexfunc = ui.debugflag and hex or short
469 activebranches = [repo[n].branch() for n in repo.heads()]
469 activebranches = [repo[n].branch() for n in repo.heads()]
470 def testactive(tag, node):
470 def testactive(tag, node):
471 realhead = tag in activebranches
471 realhead = tag in activebranches
472 open = node in repo.branchheads(tag, closed=False)
472 open = node in repo.branchheads(tag, closed=False)
473 return realhead and open
473 return realhead and open
474 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
474 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
475 for tag, node in repo.branchtags().items()],
475 for tag, node in repo.branchtags().items()],
476 reverse=True)
476 reverse=True)
477
477
478 for isactive, node, tag in branches:
478 for isactive, node, tag in branches:
479 if (not active) or isactive:
479 if (not active) or isactive:
480 encodedtag = encoding.tolocal(tag)
480 encodedtag = encoding.tolocal(tag)
481 if ui.quiet:
481 if ui.quiet:
482 ui.write("%s\n" % encodedtag)
482 ui.write("%s\n" % encodedtag)
483 else:
483 else:
484 hn = repo.lookup(node)
484 hn = repo.lookup(node)
485 if isactive:
485 if isactive:
486 notice = ''
486 notice = ''
487 elif hn not in repo.branchheads(tag, closed=False):
487 elif hn not in repo.branchheads(tag, closed=False):
488 if not closed:
488 if not closed:
489 continue
489 continue
490 notice = _(' (closed)')
490 notice = _(' (closed)')
491 else:
491 else:
492 notice = _(' (inactive)')
492 notice = _(' (inactive)')
493 rev = str(node).rjust(31 - encoding.colwidth(encodedtag))
493 rev = str(node).rjust(31 - encoding.colwidth(encodedtag))
494 data = encodedtag, rev, hexfunc(hn), notice
494 data = encodedtag, rev, hexfunc(hn), notice
495 ui.write("%s %s:%s%s\n" % data)
495 ui.write("%s %s:%s%s\n" % data)
496
496
497 def bundle(ui, repo, fname, dest=None, **opts):
497 def bundle(ui, repo, fname, dest=None, **opts):
498 """create a changegroup file
498 """create a changegroup file
499
499
500 Generate a compressed changegroup file collecting changesets not
500 Generate a compressed changegroup file collecting changesets not
501 known to be in another repository.
501 known to be in another repository.
502
502
503 If you omit the destination repository, then hg assumes the
503 If you omit the destination repository, then hg assumes the
504 destination will have all the nodes you specify with --base
504 destination will have all the nodes you specify with --base
505 parameters. To create a bundle containing all changesets, use
505 parameters. To create a bundle containing all changesets, use
506 -a/--all (or --base null).
506 -a/--all (or --base null).
507
507
508 You can change compression method with the -t/--type option.
508 You can change compression method with the -t/--type option.
509 The available compression methods are: none, bzip2, and
509 The available compression methods are: none, bzip2, and
510 gzip (by default, bundles are compressed using bzip2).
510 gzip (by default, bundles are compressed using bzip2).
511
511
512 The bundle file can then be transferred using conventional means
512 The bundle file can then be transferred using conventional means
513 and applied to another repository with the unbundle or pull
513 and applied to another repository with the unbundle or pull
514 command. This is useful when direct push and pull are not
514 command. This is useful when direct push and pull are not
515 available or when exporting an entire repository is undesirable.
515 available or when exporting an entire repository is undesirable.
516
516
517 Applying bundles preserves all changeset contents including
517 Applying bundles preserves all changeset contents including
518 permissions, copy/rename information, and revision history.
518 permissions, copy/rename information, and revision history.
519 """
519 """
520 revs = opts.get('rev') or None
520 revs = opts.get('rev') or None
521 if revs:
521 if revs:
522 revs = [repo.lookup(rev) for rev in revs]
522 revs = [repo.lookup(rev) for rev in revs]
523 if opts.get('all'):
523 if opts.get('all'):
524 base = ['null']
524 base = ['null']
525 else:
525 else:
526 base = opts.get('base')
526 base = opts.get('base')
527 if base:
527 if base:
528 if dest:
528 if dest:
529 raise util.Abort(_("--base is incompatible with specifying "
529 raise util.Abort(_("--base is incompatible with specifying "
530 "a destination"))
530 "a destination"))
531 base = [repo.lookup(rev) for rev in base]
531 base = [repo.lookup(rev) for rev in base]
532 # create the right base
532 # create the right base
533 # XXX: nodesbetween / changegroup* should be "fixed" instead
533 # XXX: nodesbetween / changegroup* should be "fixed" instead
534 o = []
534 o = []
535 has = set((nullid,))
535 has = set((nullid,))
536 for n in base:
536 for n in base:
537 has.update(repo.changelog.reachable(n))
537 has.update(repo.changelog.reachable(n))
538 if revs:
538 if revs:
539 visit = list(revs)
539 visit = list(revs)
540 has.difference_update(revs)
540 has.difference_update(revs)
541 else:
541 else:
542 visit = repo.changelog.heads()
542 visit = repo.changelog.heads()
543 seen = {}
543 seen = {}
544 while visit:
544 while visit:
545 n = visit.pop(0)
545 n = visit.pop(0)
546 parents = [p for p in repo.changelog.parents(n) if p not in has]
546 parents = [p for p in repo.changelog.parents(n) if p not in has]
547 if len(parents) == 0:
547 if len(parents) == 0:
548 if n not in has:
548 if n not in has:
549 o.append(n)
549 o.append(n)
550 else:
550 else:
551 for p in parents:
551 for p in parents:
552 if p not in seen:
552 if p not in seen:
553 seen[p] = 1
553 seen[p] = 1
554 visit.append(p)
554 visit.append(p)
555 else:
555 else:
556 dest = ui.expandpath(dest or 'default-push', dest or 'default')
556 dest = ui.expandpath(dest or 'default-push', dest or 'default')
557 dest, branches = hg.parseurl(dest, opts.get('branch'))
557 dest, branches = hg.parseurl(dest, opts.get('branch'))
558 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
558 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
559 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
559 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
560 o = repo.findoutgoing(other, force=opts.get('force'))
560 o = repo.findoutgoing(other, force=opts.get('force'))
561
561
562 if revs:
562 if revs:
563 cg = repo.changegroupsubset(o, revs, 'bundle')
563 cg = repo.changegroupsubset(o, revs, 'bundle')
564 else:
564 else:
565 cg = repo.changegroup(o, 'bundle')
565 cg = repo.changegroup(o, 'bundle')
566
566
567 bundletype = opts.get('type', 'bzip2').lower()
567 bundletype = opts.get('type', 'bzip2').lower()
568 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
568 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
569 bundletype = btypes.get(bundletype)
569 bundletype = btypes.get(bundletype)
570 if bundletype not in changegroup.bundletypes:
570 if bundletype not in changegroup.bundletypes:
571 raise util.Abort(_('unknown bundle type specified with --type'))
571 raise util.Abort(_('unknown bundle type specified with --type'))
572
572
573 changegroup.writebundle(cg, fname, bundletype)
573 changegroup.writebundle(cg, fname, bundletype)
574
574
575 def cat(ui, repo, file1, *pats, **opts):
575 def cat(ui, repo, file1, *pats, **opts):
576 """output the current or given revision of files
576 """output the current or given revision of files
577
577
578 Print the specified files as they were at the given revision. If
578 Print the specified files as they were at the given revision. If
579 no revision is given, the parent of the working directory is used,
579 no revision is given, the parent of the working directory is used,
580 or tip if no revision is checked out.
580 or tip if no revision is checked out.
581
581
582 Output may be to a file, in which case the name of the file is
582 Output may be to a file, in which case the name of the file is
583 given using a format string. The formatting rules are the same as
583 given using a format string. The formatting rules are the same as
584 for the export command, with the following additions:
584 for the export command, with the following additions:
585
585
586 :``%s``: basename of file being printed
586 :``%s``: basename of file being printed
587 :``%d``: dirname of file being printed, or '.' if in repository root
587 :``%d``: dirname of file being printed, or '.' if in repository root
588 :``%p``: root-relative path name of file being printed
588 :``%p``: root-relative path name of file being printed
589 """
589 """
590 ctx = repo[opts.get('rev')]
590 ctx = repo[opts.get('rev')]
591 err = 1
591 err = 1
592 m = cmdutil.match(repo, (file1,) + pats, opts)
592 m = cmdutil.match(repo, (file1,) + pats, opts)
593 for abs in ctx.walk(m):
593 for abs in ctx.walk(m):
594 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
594 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
595 data = ctx[abs].data()
595 data = ctx[abs].data()
596 if opts.get('decode'):
596 if opts.get('decode'):
597 data = repo.wwritedata(abs, data)
597 data = repo.wwritedata(abs, data)
598 fp.write(data)
598 fp.write(data)
599 err = 0
599 err = 0
600 return err
600 return err
601
601
602 def clone(ui, source, dest=None, **opts):
602 def clone(ui, source, dest=None, **opts):
603 """make a copy of an existing repository
603 """make a copy of an existing repository
604
604
605 Create a copy of an existing repository in a new directory.
605 Create a copy of an existing repository in a new directory.
606
606
607 If no destination directory name is specified, it defaults to the
607 If no destination directory name is specified, it defaults to the
608 basename of the source.
608 basename of the source.
609
609
610 The location of the source is added to the new repository's
610 The location of the source is added to the new repository's
611 .hg/hgrc file, as the default to be used for future pulls.
611 .hg/hgrc file, as the default to be used for future pulls.
612
612
613 See 'hg help urls' for valid source format details.
613 See 'hg help urls' for valid source format details.
614
614
615 It is possible to specify an ``ssh://`` URL as the destination, but no
615 It is possible to specify an ``ssh://`` URL as the destination, but no
616 .hg/hgrc and working directory will be created on the remote side.
616 .hg/hgrc and working directory will be created on the remote side.
617 Please see 'hg help urls' for important details about ``ssh://`` URLs.
617 Please see 'hg help urls' for important details about ``ssh://`` URLs.
618
618
619 If the -U/--noupdate option is specified, the new clone will contain
619 If the -U/--noupdate option is specified, the new clone will contain
620 only a repository (.hg) and no working copy (the working copy parent
620 only a repository (.hg) and no working copy (the working copy parent
621 will be the null changeset). Otherwise, clone will initially check
621 will be the null changeset). Otherwise, clone will initially check
622 out (in order of precedence):
622 out (in order of precedence):
623
623
624 a) the changeset, tag or branch specified with -u/--updaterev
624 a) the changeset, tag or branch specified with -u/--updaterev
625 b) the changeset, tag or branch given with the first -r/--rev
625 b) the changeset, tag or branch given with the first -r/--rev
626 c) the branch given with the first -b/--branch
626 c) the branch given with the first -b/--branch
627 d) the branch given with the url#branch source syntax
627 d) the branch given with the url#branch source syntax
628 e) the head of the default branch
628 e) the head of the default branch
629
629
630 Use 'hg clone -u . src dst' to checkout the source repository's
630 Use 'hg clone -u . src dst' to checkout the source repository's
631 parent changeset (applicable for local source repositories only).
631 parent changeset (applicable for local source repositories only).
632
632
633 A set of changesets (tags, or branch names) to pull may be specified
633 A set of changesets (tags, or branch names) to pull may be specified
634 by listing each changeset (tag, or branch name) with -r/--rev.
634 by listing each changeset (tag, or branch name) with -r/--rev.
635 If -r/--rev is used, the cloned repository will contain only a subset
635 If -r/--rev is used, the cloned repository will contain only a subset
636 of the changesets of the source repository. Only the set of changesets
636 of the changesets of the source repository. Only the set of changesets
637 defined by all -r/--rev options (including all their ancestors)
637 defined by all -r/--rev options (including all their ancestors)
638 will be pulled into the destination repository.
638 will be pulled into the destination repository.
639 No subsequent changesets (including subsequent tags) will be present
639 No subsequent changesets (including subsequent tags) will be present
640 in the destination.
640 in the destination.
641
641
642 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
642 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
643 local source repositories.
643 local source repositories.
644
644
645 For efficiency, hardlinks are used for cloning whenever the source
645 For efficiency, hardlinks are used for cloning whenever the source
646 and destination are on the same filesystem (note this applies only
646 and destination are on the same filesystem (note this applies only
647 to the repository data, not to the checked out files). Some
647 to the repository data, not to the checked out files). Some
648 filesystems, such as AFS, implement hardlinking incorrectly, but
648 filesystems, such as AFS, implement hardlinking incorrectly, but
649 do not report errors. In these cases, use the --pull option to
649 do not report errors. In these cases, use the --pull option to
650 avoid hardlinking.
650 avoid hardlinking.
651
651
652 In some cases, you can clone repositories and checked out files
652 In some cases, you can clone repositories and checked out files
653 using full hardlinks with ::
653 using full hardlinks with ::
654
654
655 $ cp -al REPO REPOCLONE
655 $ cp -al REPO REPOCLONE
656
656
657 This is the fastest way to clone, but it is not always safe. The
657 This is the fastest way to clone, but it is not always safe. The
658 operation is not atomic (making sure REPO is not modified during
658 operation is not atomic (making sure REPO is not modified during
659 the operation is up to you) and you have to make sure your editor
659 the operation is up to you) and you have to make sure your editor
660 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
660 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
661 this is not compatible with certain extensions that place their
661 this is not compatible with certain extensions that place their
662 metadata under the .hg directory, such as mq.
662 metadata under the .hg directory, such as mq.
663 """
663 """
664 if opts.get('noupdate') and opts.get('updaterev'):
664 if opts.get('noupdate') and opts.get('updaterev'):
665 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
665 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
666
666
667 hg.clone(cmdutil.remoteui(ui, opts), source, dest,
667 hg.clone(cmdutil.remoteui(ui, opts), source, dest,
668 pull=opts.get('pull'),
668 pull=opts.get('pull'),
669 stream=opts.get('uncompressed'),
669 stream=opts.get('uncompressed'),
670 rev=opts.get('rev'),
670 rev=opts.get('rev'),
671 update=opts.get('updaterev') or not opts.get('noupdate'),
671 update=opts.get('updaterev') or not opts.get('noupdate'),
672 branch=opts.get('branch'))
672 branch=opts.get('branch'))
673
673
674 def commit(ui, repo, *pats, **opts):
674 def commit(ui, repo, *pats, **opts):
675 """commit the specified files or all outstanding changes
675 """commit the specified files or all outstanding changes
676
676
677 Commit changes to the given files into the repository. Unlike a
677 Commit changes to the given files into the repository. Unlike a
678 centralized RCS, this operation is a local operation. See hg push
678 centralized RCS, this operation is a local operation. See hg push
679 for a way to actively distribute your changes.
679 for a way to actively distribute your changes.
680
680
681 If a list of files is omitted, all changes reported by "hg status"
681 If a list of files is omitted, all changes reported by "hg status"
682 will be committed.
682 will be committed.
683
683
684 If you are committing the result of a merge, do not provide any
684 If you are committing the result of a merge, do not provide any
685 filenames or -I/-X filters.
685 filenames or -I/-X filters.
686
686
687 If no commit message is specified, the configured editor is
687 If no commit message is specified, the configured editor is
688 started to prompt you for a message.
688 started to prompt you for a message.
689
689
690 See 'hg help dates' for a list of formats valid for -d/--date.
690 See 'hg help dates' for a list of formats valid for -d/--date.
691 """
691 """
692 extra = {}
692 extra = {}
693 if opts.get('close_branch'):
693 if opts.get('close_branch'):
694 extra['close'] = 1
694 extra['close'] = 1
695 e = cmdutil.commiteditor
695 e = cmdutil.commiteditor
696 if opts.get('force_editor'):
696 if opts.get('force_editor'):
697 e = cmdutil.commitforceeditor
697 e = cmdutil.commitforceeditor
698
698
699 def commitfunc(ui, repo, message, match, opts):
699 def commitfunc(ui, repo, message, match, opts):
700 return repo.commit(message, opts.get('user'), opts.get('date'), match,
700 return repo.commit(message, opts.get('user'), opts.get('date'), match,
701 editor=e, extra=extra)
701 editor=e, extra=extra)
702
702
703 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
703 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
704 if not node:
704 if not node:
705 ui.status(_("nothing changed\n"))
705 ui.status(_("nothing changed\n"))
706 return
706 return
707 cl = repo.changelog
707 cl = repo.changelog
708 rev = cl.rev(node)
708 rev = cl.rev(node)
709 parents = cl.parentrevs(rev)
709 parents = cl.parentrevs(rev)
710 if rev - 1 in parents:
710 if rev - 1 in parents:
711 # one of the parents was the old tip
711 # one of the parents was the old tip
712 pass
712 pass
713 elif (parents == (nullrev, nullrev) or
713 elif (parents == (nullrev, nullrev) or
714 len(cl.heads(cl.node(parents[0]))) > 1 and
714 len(cl.heads(cl.node(parents[0]))) > 1 and
715 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
715 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
716 ui.status(_('created new head\n'))
716 ui.status(_('created new head\n'))
717
717
718 if ui.debugflag:
718 if ui.debugflag:
719 ui.write(_('committed changeset %d:%s\n') % (rev, hex(node)))
719 ui.write(_('committed changeset %d:%s\n') % (rev, hex(node)))
720 elif ui.verbose:
720 elif ui.verbose:
721 ui.write(_('committed changeset %d:%s\n') % (rev, short(node)))
721 ui.write(_('committed changeset %d:%s\n') % (rev, short(node)))
722
722
723 def copy(ui, repo, *pats, **opts):
723 def copy(ui, repo, *pats, **opts):
724 """mark files as copied for the next commit
724 """mark files as copied for the next commit
725
725
726 Mark dest as having copies of source files. If dest is a
726 Mark dest as having copies of source files. If dest is a
727 directory, copies are put in that directory. If dest is a file,
727 directory, copies are put in that directory. If dest is a file,
728 the source must be a single file.
728 the source must be a single file.
729
729
730 By default, this command copies the contents of files as they
730 By default, this command copies the contents of files as they
731 exist in the working directory. If invoked with -A/--after, the
731 exist in the working directory. If invoked with -A/--after, the
732 operation is recorded, but no copying is performed.
732 operation is recorded, but no copying is performed.
733
733
734 This command takes effect with the next commit. To undo a copy
734 This command takes effect with the next commit. To undo a copy
735 before that, see hg revert.
735 before that, see hg revert.
736 """
736 """
737 wlock = repo.wlock(False)
737 wlock = repo.wlock(False)
738 try:
738 try:
739 return cmdutil.copy(ui, repo, pats, opts)
739 return cmdutil.copy(ui, repo, pats, opts)
740 finally:
740 finally:
741 wlock.release()
741 wlock.release()
742
742
743 def debugancestor(ui, repo, *args):
743 def debugancestor(ui, repo, *args):
744 """find the ancestor revision of two revisions in a given index"""
744 """find the ancestor revision of two revisions in a given index"""
745 if len(args) == 3:
745 if len(args) == 3:
746 index, rev1, rev2 = args
746 index, rev1, rev2 = args
747 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
747 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
748 lookup = r.lookup
748 lookup = r.lookup
749 elif len(args) == 2:
749 elif len(args) == 2:
750 if not repo:
750 if not repo:
751 raise util.Abort(_("There is no Mercurial repository here "
751 raise util.Abort(_("There is no Mercurial repository here "
752 "(.hg not found)"))
752 "(.hg not found)"))
753 rev1, rev2 = args
753 rev1, rev2 = args
754 r = repo.changelog
754 r = repo.changelog
755 lookup = repo.lookup
755 lookup = repo.lookup
756 else:
756 else:
757 raise util.Abort(_('either two or three arguments required'))
757 raise util.Abort(_('either two or three arguments required'))
758 a = r.ancestor(lookup(rev1), lookup(rev2))
758 a = r.ancestor(lookup(rev1), lookup(rev2))
759 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
759 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
760
760
761 def debugcommands(ui, cmd='', *args):
761 def debugcommands(ui, cmd='', *args):
762 for cmd, vals in sorted(table.iteritems()):
762 for cmd, vals in sorted(table.iteritems()):
763 cmd = cmd.split('|')[0].strip('^')
763 cmd = cmd.split('|')[0].strip('^')
764 opts = ', '.join([i[1] for i in vals[1]])
764 opts = ', '.join([i[1] for i in vals[1]])
765 ui.write('%s: %s\n' % (cmd, opts))
765 ui.write('%s: %s\n' % (cmd, opts))
766
766
767 def debugcomplete(ui, cmd='', **opts):
767 def debugcomplete(ui, cmd='', **opts):
768 """returns the completion list associated with the given command"""
768 """returns the completion list associated with the given command"""
769
769
770 if opts.get('options'):
770 if opts.get('options'):
771 options = []
771 options = []
772 otables = [globalopts]
772 otables = [globalopts]
773 if cmd:
773 if cmd:
774 aliases, entry = cmdutil.findcmd(cmd, table, False)
774 aliases, entry = cmdutil.findcmd(cmd, table, False)
775 otables.append(entry[1])
775 otables.append(entry[1])
776 for t in otables:
776 for t in otables:
777 for o in t:
777 for o in t:
778 if o[0]:
778 if o[0]:
779 options.append('-%s' % o[0])
779 options.append('-%s' % o[0])
780 options.append('--%s' % o[1])
780 options.append('--%s' % o[1])
781 ui.write("%s\n" % "\n".join(options))
781 ui.write("%s\n" % "\n".join(options))
782 return
782 return
783
783
784 cmdlist = cmdutil.findpossible(cmd, table)
784 cmdlist = cmdutil.findpossible(cmd, table)
785 if ui.verbose:
785 if ui.verbose:
786 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
786 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
787 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
787 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
788
788
789 def debugfsinfo(ui, path = "."):
789 def debugfsinfo(ui, path = "."):
790 open('.debugfsinfo', 'w').write('')
790 open('.debugfsinfo', 'w').write('')
791 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
791 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
792 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
792 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
793 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
793 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
794 and 'yes' or 'no'))
794 and 'yes' or 'no'))
795 os.unlink('.debugfsinfo')
795 os.unlink('.debugfsinfo')
796
796
797 def debugrebuildstate(ui, repo, rev="tip"):
797 def debugrebuildstate(ui, repo, rev="tip"):
798 """rebuild the dirstate as it would look like for the given revision"""
798 """rebuild the dirstate as it would look like for the given revision"""
799 ctx = repo[rev]
799 ctx = repo[rev]
800 wlock = repo.wlock()
800 wlock = repo.wlock()
801 try:
801 try:
802 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
802 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
803 finally:
803 finally:
804 wlock.release()
804 wlock.release()
805
805
806 def debugcheckstate(ui, repo):
806 def debugcheckstate(ui, repo):
807 """validate the correctness of the current dirstate"""
807 """validate the correctness of the current dirstate"""
808 parent1, parent2 = repo.dirstate.parents()
808 parent1, parent2 = repo.dirstate.parents()
809 m1 = repo[parent1].manifest()
809 m1 = repo[parent1].manifest()
810 m2 = repo[parent2].manifest()
810 m2 = repo[parent2].manifest()
811 errors = 0
811 errors = 0
812 for f in repo.dirstate:
812 for f in repo.dirstate:
813 state = repo.dirstate[f]
813 state = repo.dirstate[f]
814 if state in "nr" and f not in m1:
814 if state in "nr" and f not in m1:
815 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
815 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
816 errors += 1
816 errors += 1
817 if state in "a" and f in m1:
817 if state in "a" and f in m1:
818 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
818 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
819 errors += 1
819 errors += 1
820 if state in "m" and f not in m1 and f not in m2:
820 if state in "m" and f not in m1 and f not in m2:
821 ui.warn(_("%s in state %s, but not in either manifest\n") %
821 ui.warn(_("%s in state %s, but not in either manifest\n") %
822 (f, state))
822 (f, state))
823 errors += 1
823 errors += 1
824 for f in m1:
824 for f in m1:
825 state = repo.dirstate[f]
825 state = repo.dirstate[f]
826 if state not in "nrm":
826 if state not in "nrm":
827 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
827 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
828 errors += 1
828 errors += 1
829 if errors:
829 if errors:
830 error = _(".hg/dirstate inconsistent with current parent's manifest")
830 error = _(".hg/dirstate inconsistent with current parent's manifest")
831 raise util.Abort(error)
831 raise util.Abort(error)
832
832
833 def showconfig(ui, repo, *values, **opts):
833 def showconfig(ui, repo, *values, **opts):
834 """show combined config settings from all hgrc files
834 """show combined config settings from all hgrc files
835
835
836 With no arguments, print names and values of all config items.
836 With no arguments, print names and values of all config items.
837
837
838 With one argument of the form section.name, print just the value
838 With one argument of the form section.name, print just the value
839 of that config item.
839 of that config item.
840
840
841 With multiple arguments, print names and values of all config
841 With multiple arguments, print names and values of all config
842 items with matching section names.
842 items with matching section names.
843
843
844 With --debug, the source (filename and line number) is printed
844 With --debug, the source (filename and line number) is printed
845 for each config item.
845 for each config item.
846 """
846 """
847
847
848 untrusted = bool(opts.get('untrusted'))
848 untrusted = bool(opts.get('untrusted'))
849 if values:
849 if values:
850 if len([v for v in values if '.' in v]) > 1:
850 if len([v for v in values if '.' in v]) > 1:
851 raise util.Abort(_('only one config item permitted'))
851 raise util.Abort(_('only one config item permitted'))
852 for section, name, value in ui.walkconfig(untrusted=untrusted):
852 for section, name, value in ui.walkconfig(untrusted=untrusted):
853 sectname = section + '.' + name
853 sectname = section + '.' + name
854 if values:
854 if values:
855 for v in values:
855 for v in values:
856 if v == section:
856 if v == section:
857 ui.debug('%s: ' %
857 ui.debug('%s: ' %
858 ui.configsource(section, name, untrusted))
858 ui.configsource(section, name, untrusted))
859 ui.write('%s=%s\n' % (sectname, value))
859 ui.write('%s=%s\n' % (sectname, value))
860 elif v == sectname:
860 elif v == sectname:
861 ui.debug('%s: ' %
861 ui.debug('%s: ' %
862 ui.configsource(section, name, untrusted))
862 ui.configsource(section, name, untrusted))
863 ui.write(value, '\n')
863 ui.write(value, '\n')
864 else:
864 else:
865 ui.debug('%s: ' %
865 ui.debug('%s: ' %
866 ui.configsource(section, name, untrusted))
866 ui.configsource(section, name, untrusted))
867 ui.write('%s=%s\n' % (sectname, value))
867 ui.write('%s=%s\n' % (sectname, value))
868
868
869 def debugsetparents(ui, repo, rev1, rev2=None):
869 def debugsetparents(ui, repo, rev1, rev2=None):
870 """manually set the parents of the current working directory
870 """manually set the parents of the current working directory
871
871
872 This is useful for writing repository conversion tools, but should
872 This is useful for writing repository conversion tools, but should
873 be used with care.
873 be used with care.
874 """
874 """
875
875
876 if not rev2:
876 if not rev2:
877 rev2 = hex(nullid)
877 rev2 = hex(nullid)
878
878
879 wlock = repo.wlock()
879 wlock = repo.wlock()
880 try:
880 try:
881 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
881 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
882 finally:
882 finally:
883 wlock.release()
883 wlock.release()
884
884
885 def debugstate(ui, repo, nodates=None):
885 def debugstate(ui, repo, nodates=None):
886 """show the contents of the current dirstate"""
886 """show the contents of the current dirstate"""
887 timestr = ""
887 timestr = ""
888 showdate = not nodates
888 showdate = not nodates
889 for file_, ent in sorted(repo.dirstate._map.iteritems()):
889 for file_, ent in sorted(repo.dirstate._map.iteritems()):
890 if showdate:
890 if showdate:
891 if ent[3] == -1:
891 if ent[3] == -1:
892 # Pad or slice to locale representation
892 # Pad or slice to locale representation
893 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
893 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
894 time.localtime(0)))
894 time.localtime(0)))
895 timestr = 'unset'
895 timestr = 'unset'
896 timestr = (timestr[:locale_len] +
896 timestr = (timestr[:locale_len] +
897 ' ' * (locale_len - len(timestr)))
897 ' ' * (locale_len - len(timestr)))
898 else:
898 else:
899 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
899 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
900 time.localtime(ent[3]))
900 time.localtime(ent[3]))
901 if ent[1] & 020000:
901 if ent[1] & 020000:
902 mode = 'lnk'
902 mode = 'lnk'
903 else:
903 else:
904 mode = '%3o' % (ent[1] & 0777)
904 mode = '%3o' % (ent[1] & 0777)
905 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
905 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
906 for f in repo.dirstate.copies():
906 for f in repo.dirstate.copies():
907 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
907 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
908
908
909 def debugsub(ui, repo, rev=None):
909 def debugsub(ui, repo, rev=None):
910 if rev == '':
910 if rev == '':
911 rev = None
911 rev = None
912 for k, v in sorted(repo[rev].substate.items()):
912 for k, v in sorted(repo[rev].substate.items()):
913 ui.write('path %s\n' % k)
913 ui.write('path %s\n' % k)
914 ui.write(' source %s\n' % v[0])
914 ui.write(' source %s\n' % v[0])
915 ui.write(' revision %s\n' % v[1])
915 ui.write(' revision %s\n' % v[1])
916
916
917 def debugdata(ui, file_, rev):
917 def debugdata(ui, file_, rev):
918 """dump the contents of a data file revision"""
918 """dump the contents of a data file revision"""
919 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
919 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
920 try:
920 try:
921 ui.write(r.revision(r.lookup(rev)))
921 ui.write(r.revision(r.lookup(rev)))
922 except KeyError:
922 except KeyError:
923 raise util.Abort(_('invalid revision identifier %s') % rev)
923 raise util.Abort(_('invalid revision identifier %s') % rev)
924
924
925 def debugdate(ui, date, range=None, **opts):
925 def debugdate(ui, date, range=None, **opts):
926 """parse and display a date"""
926 """parse and display a date"""
927 if opts["extended"]:
927 if opts["extended"]:
928 d = util.parsedate(date, util.extendeddateformats)
928 d = util.parsedate(date, util.extendeddateformats)
929 else:
929 else:
930 d = util.parsedate(date)
930 d = util.parsedate(date)
931 ui.write("internal: %s %s\n" % d)
931 ui.write("internal: %s %s\n" % d)
932 ui.write("standard: %s\n" % util.datestr(d))
932 ui.write("standard: %s\n" % util.datestr(d))
933 if range:
933 if range:
934 m = util.matchdate(range)
934 m = util.matchdate(range)
935 ui.write("match: %s\n" % m(d[0]))
935 ui.write("match: %s\n" % m(d[0]))
936
936
937 def debugindex(ui, file_):
937 def debugindex(ui, file_):
938 """dump the contents of an index file"""
938 """dump the contents of an index file"""
939 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
939 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
940 ui.write(" rev offset length base linkrev"
940 ui.write(" rev offset length base linkrev"
941 " nodeid p1 p2\n")
941 " nodeid p1 p2\n")
942 for i in r:
942 for i in r:
943 node = r.node(i)
943 node = r.node(i)
944 try:
944 try:
945 pp = r.parents(node)
945 pp = r.parents(node)
946 except:
946 except:
947 pp = [nullid, nullid]
947 pp = [nullid, nullid]
948 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
948 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
949 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
949 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
950 short(node), short(pp[0]), short(pp[1])))
950 short(node), short(pp[0]), short(pp[1])))
951
951
952 def debugindexdot(ui, file_):
952 def debugindexdot(ui, file_):
953 """dump an index DAG as a graphviz dot file"""
953 """dump an index DAG as a graphviz dot file"""
954 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
954 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
955 ui.write("digraph G {\n")
955 ui.write("digraph G {\n")
956 for i in r:
956 for i in r:
957 node = r.node(i)
957 node = r.node(i)
958 pp = r.parents(node)
958 pp = r.parents(node)
959 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
959 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
960 if pp[1] != nullid:
960 if pp[1] != nullid:
961 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
961 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
962 ui.write("}\n")
962 ui.write("}\n")
963
963
964 def debuginstall(ui):
964 def debuginstall(ui):
965 '''test Mercurial installation'''
965 '''test Mercurial installation'''
966
966
967 def writetemp(contents):
967 def writetemp(contents):
968 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
968 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
969 f = os.fdopen(fd, "wb")
969 f = os.fdopen(fd, "wb")
970 f.write(contents)
970 f.write(contents)
971 f.close()
971 f.close()
972 return name
972 return name
973
973
974 problems = 0
974 problems = 0
975
975
976 # encoding
976 # encoding
977 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
977 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
978 try:
978 try:
979 encoding.fromlocal("test")
979 encoding.fromlocal("test")
980 except util.Abort, inst:
980 except util.Abort, inst:
981 ui.write(" %s\n" % inst)
981 ui.write(" %s\n" % inst)
982 ui.write(_(" (check that your locale is properly set)\n"))
982 ui.write(_(" (check that your locale is properly set)\n"))
983 problems += 1
983 problems += 1
984
984
985 # compiled modules
985 # compiled modules
986 ui.status(_("Checking extensions...\n"))
986 ui.status(_("Checking extensions...\n"))
987 try:
987 try:
988 import bdiff, mpatch, base85
988 import bdiff, mpatch, base85
989 except Exception, inst:
989 except Exception, inst:
990 ui.write(" %s\n" % inst)
990 ui.write(" %s\n" % inst)
991 ui.write(_(" One or more extensions could not be found"))
991 ui.write(_(" One or more extensions could not be found"))
992 ui.write(_(" (check that you compiled the extensions)\n"))
992 ui.write(_(" (check that you compiled the extensions)\n"))
993 problems += 1
993 problems += 1
994
994
995 # templates
995 # templates
996 ui.status(_("Checking templates...\n"))
996 ui.status(_("Checking templates...\n"))
997 try:
997 try:
998 import templater
998 import templater
999 templater.templater(templater.templatepath("map-cmdline.default"))
999 templater.templater(templater.templatepath("map-cmdline.default"))
1000 except Exception, inst:
1000 except Exception, inst:
1001 ui.write(" %s\n" % inst)
1001 ui.write(" %s\n" % inst)
1002 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1002 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1003 problems += 1
1003 problems += 1
1004
1004
1005 # patch
1005 # patch
1006 ui.status(_("Checking patch...\n"))
1006 ui.status(_("Checking patch...\n"))
1007 patchproblems = 0
1007 patchproblems = 0
1008 a = "1\n2\n3\n4\n"
1008 a = "1\n2\n3\n4\n"
1009 b = "1\n2\n3\ninsert\n4\n"
1009 b = "1\n2\n3\ninsert\n4\n"
1010 fa = writetemp(a)
1010 fa = writetemp(a)
1011 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
1011 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
1012 os.path.basename(fa))
1012 os.path.basename(fa))
1013 fd = writetemp(d)
1013 fd = writetemp(d)
1014
1014
1015 files = {}
1015 files = {}
1016 try:
1016 try:
1017 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
1017 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
1018 except util.Abort, e:
1018 except util.Abort, e:
1019 ui.write(_(" patch call failed:\n"))
1019 ui.write(_(" patch call failed:\n"))
1020 ui.write(" " + str(e) + "\n")
1020 ui.write(" " + str(e) + "\n")
1021 patchproblems += 1
1021 patchproblems += 1
1022 else:
1022 else:
1023 if list(files) != [os.path.basename(fa)]:
1023 if list(files) != [os.path.basename(fa)]:
1024 ui.write(_(" unexpected patch output!\n"))
1024 ui.write(_(" unexpected patch output!\n"))
1025 patchproblems += 1
1025 patchproblems += 1
1026 a = open(fa).read()
1026 a = open(fa).read()
1027 if a != b:
1027 if a != b:
1028 ui.write(_(" patch test failed!\n"))
1028 ui.write(_(" patch test failed!\n"))
1029 patchproblems += 1
1029 patchproblems += 1
1030
1030
1031 if patchproblems:
1031 if patchproblems:
1032 if ui.config('ui', 'patch'):
1032 if ui.config('ui', 'patch'):
1033 ui.write(_(" (Current patch tool may be incompatible with patch,"
1033 ui.write(_(" (Current patch tool may be incompatible with patch,"
1034 " or misconfigured. Please check your .hgrc file)\n"))
1034 " or misconfigured. Please check your .hgrc file)\n"))
1035 else:
1035 else:
1036 ui.write(_(" Internal patcher failure, please report this error"
1036 ui.write(_(" Internal patcher failure, please report this error"
1037 " to http://mercurial.selenic.com/bts/\n"))
1037 " to http://mercurial.selenic.com/bts/\n"))
1038 problems += patchproblems
1038 problems += patchproblems
1039
1039
1040 os.unlink(fa)
1040 os.unlink(fa)
1041 os.unlink(fd)
1041 os.unlink(fd)
1042
1042
1043 # editor
1043 # editor
1044 ui.status(_("Checking commit editor...\n"))
1044 ui.status(_("Checking commit editor...\n"))
1045 editor = ui.geteditor()
1045 editor = ui.geteditor()
1046 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1046 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1047 if not cmdpath:
1047 if not cmdpath:
1048 if editor == 'vi':
1048 if editor == 'vi':
1049 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1049 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1050 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1050 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1051 else:
1051 else:
1052 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1052 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1053 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1053 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1054 problems += 1
1054 problems += 1
1055
1055
1056 # check username
1056 # check username
1057 ui.status(_("Checking username...\n"))
1057 ui.status(_("Checking username...\n"))
1058 try:
1058 try:
1059 user = ui.username()
1059 user = ui.username()
1060 except util.Abort, e:
1060 except util.Abort, e:
1061 ui.write(" %s\n" % e)
1061 ui.write(" %s\n" % e)
1062 ui.write(_(" (specify a username in your .hgrc file)\n"))
1062 ui.write(_(" (specify a username in your .hgrc file)\n"))
1063 problems += 1
1063 problems += 1
1064
1064
1065 if not problems:
1065 if not problems:
1066 ui.status(_("No problems detected\n"))
1066 ui.status(_("No problems detected\n"))
1067 else:
1067 else:
1068 ui.write(_("%s problems detected,"
1068 ui.write(_("%s problems detected,"
1069 " please check your install!\n") % problems)
1069 " please check your install!\n") % problems)
1070
1070
1071 return problems
1071 return problems
1072
1072
1073 def debugrename(ui, repo, file1, *pats, **opts):
1073 def debugrename(ui, repo, file1, *pats, **opts):
1074 """dump rename information"""
1074 """dump rename information"""
1075
1075
1076 ctx = repo[opts.get('rev')]
1076 ctx = repo[opts.get('rev')]
1077 m = cmdutil.match(repo, (file1,) + pats, opts)
1077 m = cmdutil.match(repo, (file1,) + pats, opts)
1078 for abs in ctx.walk(m):
1078 for abs in ctx.walk(m):
1079 fctx = ctx[abs]
1079 fctx = ctx[abs]
1080 o = fctx.filelog().renamed(fctx.filenode())
1080 o = fctx.filelog().renamed(fctx.filenode())
1081 rel = m.rel(abs)
1081 rel = m.rel(abs)
1082 if o:
1082 if o:
1083 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1083 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1084 else:
1084 else:
1085 ui.write(_("%s not renamed\n") % rel)
1085 ui.write(_("%s not renamed\n") % rel)
1086
1086
1087 def debugwalk(ui, repo, *pats, **opts):
1087 def debugwalk(ui, repo, *pats, **opts):
1088 """show how files match on given patterns"""
1088 """show how files match on given patterns"""
1089 m = cmdutil.match(repo, pats, opts)
1089 m = cmdutil.match(repo, pats, opts)
1090 items = list(repo.walk(m))
1090 items = list(repo.walk(m))
1091 if not items:
1091 if not items:
1092 return
1092 return
1093 fmt = 'f %%-%ds %%-%ds %%s' % (
1093 fmt = 'f %%-%ds %%-%ds %%s' % (
1094 max([len(abs) for abs in items]),
1094 max([len(abs) for abs in items]),
1095 max([len(m.rel(abs)) for abs in items]))
1095 max([len(m.rel(abs)) for abs in items]))
1096 for abs in items:
1096 for abs in items:
1097 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1097 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1098 ui.write("%s\n" % line.rstrip())
1098 ui.write("%s\n" % line.rstrip())
1099
1099
1100 def diff(ui, repo, *pats, **opts):
1100 def diff(ui, repo, *pats, **opts):
1101 """diff repository (or selected files)
1101 """diff repository (or selected files)
1102
1102
1103 Show differences between revisions for the specified files.
1103 Show differences between revisions for the specified files.
1104
1104
1105 Differences between files are shown using the unified diff format.
1105 Differences between files are shown using the unified diff format.
1106
1106
1107 NOTE: diff may generate unexpected results for merges, as it will
1107 NOTE: diff may generate unexpected results for merges, as it will
1108 default to comparing against the working directory's first parent
1108 default to comparing against the working directory's first parent
1109 changeset if no revisions are specified.
1109 changeset if no revisions are specified.
1110
1110
1111 When two revision arguments are given, then changes are shown
1111 When two revision arguments are given, then changes are shown
1112 between those revisions. If only one revision is specified then
1112 between those revisions. If only one revision is specified then
1113 that revision is compared to the working directory, and, when no
1113 that revision is compared to the working directory, and, when no
1114 revisions are specified, the working directory files are compared
1114 revisions are specified, the working directory files are compared
1115 to its parent.
1115 to its parent.
1116
1116
1117 Without the -a/--text option, diff will avoid generating diffs of
1117 Without the -a/--text option, diff will avoid generating diffs of
1118 files it detects as binary. With -a, diff will generate a diff
1118 files it detects as binary. With -a, diff will generate a diff
1119 anyway, probably with undesirable results.
1119 anyway, probably with undesirable results.
1120
1120
1121 Use the -g/--git option to generate diffs in the git extended diff
1121 Use the -g/--git option to generate diffs in the git extended diff
1122 format. For more information, read 'hg help diffs'.
1122 format. For more information, read 'hg help diffs'.
1123 """
1123 """
1124
1124
1125 revs = opts.get('rev')
1125 revs = opts.get('rev')
1126 change = opts.get('change')
1126 change = opts.get('change')
1127 stat = opts.get('stat')
1127 stat = opts.get('stat')
1128 reverse = opts.get('reverse')
1128 reverse = opts.get('reverse')
1129
1129
1130 if revs and change:
1130 if revs and change:
1131 msg = _('cannot specify --rev and --change at the same time')
1131 msg = _('cannot specify --rev and --change at the same time')
1132 raise util.Abort(msg)
1132 raise util.Abort(msg)
1133 elif change:
1133 elif change:
1134 node2 = repo.lookup(change)
1134 node2 = repo.lookup(change)
1135 node1 = repo[node2].parents()[0].node()
1135 node1 = repo[node2].parents()[0].node()
1136 else:
1136 else:
1137 node1, node2 = cmdutil.revpair(repo, revs)
1137 node1, node2 = cmdutil.revpair(repo, revs)
1138
1138
1139 if reverse:
1139 if reverse:
1140 node1, node2 = node2, node1
1140 node1, node2 = node2, node1
1141
1141
1142 if stat:
1142 if stat:
1143 opts['unified'] = '0'
1143 opts['unified'] = '0'
1144 diffopts = patch.diffopts(ui, opts)
1144 diffopts = patch.diffopts(ui, opts)
1145
1145
1146 m = cmdutil.match(repo, pats, opts)
1146 m = cmdutil.match(repo, pats, opts)
1147 it = patch.diff(repo, node1, node2, match=m, opts=diffopts)
1147 it = patch.diff(repo, node1, node2, match=m, opts=diffopts)
1148 if stat:
1148 if stat:
1149 width = ui.interactive() and util.termwidth() or 80
1149 width = ui.interactive() and util.termwidth() or 80
1150 ui.write(patch.diffstat(util.iterlines(it), width=width,
1150 ui.write(patch.diffstat(util.iterlines(it), width=width,
1151 git=diffopts.git))
1151 git=diffopts.git))
1152 else:
1152 else:
1153 for chunk in it:
1153 for chunk in it:
1154 ui.write(chunk)
1154 ui.write(chunk)
1155
1155
1156 def export(ui, repo, *changesets, **opts):
1156 def export(ui, repo, *changesets, **opts):
1157 """dump the header and diffs for one or more changesets
1157 """dump the header and diffs for one or more changesets
1158
1158
1159 Print the changeset header and diffs for one or more revisions.
1159 Print the changeset header and diffs for one or more revisions.
1160
1160
1161 The information shown in the changeset header is: author, date,
1161 The information shown in the changeset header is: author, date,
1162 branch name (if non-default), changeset hash, parent(s) and commit
1162 branch name (if non-default), changeset hash, parent(s) and commit
1163 comment.
1163 comment.
1164
1164
1165 NOTE: export may generate unexpected diff output for merge
1165 NOTE: export may generate unexpected diff output for merge
1166 changesets, as it will compare the merge changeset against its
1166 changesets, as it will compare the merge changeset against its
1167 first parent only.
1167 first parent only.
1168
1168
1169 Output may be to a file, in which case the name of the file is
1169 Output may be to a file, in which case the name of the file is
1170 given using a format string. The formatting rules are as follows:
1170 given using a format string. The formatting rules are as follows:
1171
1171
1172 :``%%``: literal "%" character
1172 :``%%``: literal "%" character
1173 :``%H``: changeset hash (40 bytes of hexadecimal)
1173 :``%H``: changeset hash (40 bytes of hexadecimal)
1174 :``%N``: number of patches being generated
1174 :``%N``: number of patches being generated
1175 :``%R``: changeset revision number
1175 :``%R``: changeset revision number
1176 :``%b``: basename of the exporting repository
1176 :``%b``: basename of the exporting repository
1177 :``%h``: short-form changeset hash (12 bytes of hexadecimal)
1177 :``%h``: short-form changeset hash (12 bytes of hexadecimal)
1178 :``%n``: zero-padded sequence number, starting at 1
1178 :``%n``: zero-padded sequence number, starting at 1
1179 :``%r``: zero-padded changeset revision number
1179 :``%r``: zero-padded changeset revision number
1180
1180
1181 Without the -a/--text option, export will avoid generating diffs
1181 Without the -a/--text option, export will avoid generating diffs
1182 of files it detects as binary. With -a, export will generate a
1182 of files it detects as binary. With -a, export will generate a
1183 diff anyway, probably with undesirable results.
1183 diff anyway, probably with undesirable results.
1184
1184
1185 Use the -g/--git option to generate diffs in the git extended diff
1185 Use the -g/--git option to generate diffs in the git extended diff
1186 format. See 'hg help diffs' for more information.
1186 format. See 'hg help diffs' for more information.
1187
1187
1188 With the --switch-parent option, the diff will be against the
1188 With the --switch-parent option, the diff will be against the
1189 second parent. It can be useful to review a merge.
1189 second parent. It can be useful to review a merge.
1190 """
1190 """
1191 changesets += tuple(opts.get('rev', []))
1191 changesets += tuple(opts.get('rev', []))
1192 if not changesets:
1192 if not changesets:
1193 raise util.Abort(_("export requires at least one changeset"))
1193 raise util.Abort(_("export requires at least one changeset"))
1194 revs = cmdutil.revrange(repo, changesets)
1194 revs = cmdutil.revrange(repo, changesets)
1195 if len(revs) > 1:
1195 if len(revs) > 1:
1196 ui.note(_('exporting patches:\n'))
1196 ui.note(_('exporting patches:\n'))
1197 else:
1197 else:
1198 ui.note(_('exporting patch:\n'))
1198 ui.note(_('exporting patch:\n'))
1199 patch.export(repo, revs, template=opts.get('output'),
1199 patch.export(repo, revs, template=opts.get('output'),
1200 switch_parent=opts.get('switch_parent'),
1200 switch_parent=opts.get('switch_parent'),
1201 opts=patch.diffopts(ui, opts))
1201 opts=patch.diffopts(ui, opts))
1202
1202
1203 def forget(ui, repo, *pats, **opts):
1203 def forget(ui, repo, *pats, **opts):
1204 """forget the specified files on the next commit
1204 """forget the specified files on the next commit
1205
1205
1206 Mark the specified files so they will no longer be tracked
1206 Mark the specified files so they will no longer be tracked
1207 after the next commit.
1207 after the next commit.
1208
1208
1209 This only removes files from the current branch, not from the
1209 This only removes files from the current branch, not from the
1210 entire project history, and it does not delete them from the
1210 entire project history, and it does not delete them from the
1211 working directory.
1211 working directory.
1212
1212
1213 To undo a forget before the next commit, see hg add.
1213 To undo a forget before the next commit, see hg add.
1214 """
1214 """
1215
1215
1216 if not pats:
1216 if not pats:
1217 raise util.Abort(_('no files specified'))
1217 raise util.Abort(_('no files specified'))
1218
1218
1219 m = cmdutil.match(repo, pats, opts)
1219 m = cmdutil.match(repo, pats, opts)
1220 s = repo.status(match=m, clean=True)
1220 s = repo.status(match=m, clean=True)
1221 forget = sorted(s[0] + s[1] + s[3] + s[6])
1221 forget = sorted(s[0] + s[1] + s[3] + s[6])
1222
1222
1223 for f in m.files():
1223 for f in m.files():
1224 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1224 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1225 ui.warn(_('not removing %s: file is already untracked\n')
1225 ui.warn(_('not removing %s: file is already untracked\n')
1226 % m.rel(f))
1226 % m.rel(f))
1227
1227
1228 for f in forget:
1228 for f in forget:
1229 if ui.verbose or not m.exact(f):
1229 if ui.verbose or not m.exact(f):
1230 ui.status(_('removing %s\n') % m.rel(f))
1230 ui.status(_('removing %s\n') % m.rel(f))
1231
1231
1232 repo.remove(forget, unlink=False)
1232 repo.remove(forget, unlink=False)
1233
1233
1234 def grep(ui, repo, pattern, *pats, **opts):
1234 def grep(ui, repo, pattern, *pats, **opts):
1235 """search for a pattern in specified files and revisions
1235 """search for a pattern in specified files and revisions
1236
1236
1237 Search revisions of files for a regular expression.
1237 Search revisions of files for a regular expression.
1238
1238
1239 This command behaves differently than Unix grep. It only accepts
1239 This command behaves differently than Unix grep. It only accepts
1240 Python/Perl regexps. It searches repository history, not the
1240 Python/Perl regexps. It searches repository history, not the
1241 working directory. It always prints the revision number in which a
1241 working directory. It always prints the revision number in which a
1242 match appears.
1242 match appears.
1243
1243
1244 By default, grep only prints output for the first revision of a
1244 By default, grep only prints output for the first revision of a
1245 file in which it finds a match. To get it to print every revision
1245 file in which it finds a match. To get it to print every revision
1246 that contains a change in match status ("-" for a match that
1246 that contains a change in match status ("-" for a match that
1247 becomes a non-match, or "+" for a non-match that becomes a match),
1247 becomes a non-match, or "+" for a non-match that becomes a match),
1248 use the --all flag.
1248 use the --all flag.
1249 """
1249 """
1250 reflags = 0
1250 reflags = 0
1251 if opts.get('ignore_case'):
1251 if opts.get('ignore_case'):
1252 reflags |= re.I
1252 reflags |= re.I
1253 try:
1253 try:
1254 regexp = re.compile(pattern, reflags)
1254 regexp = re.compile(pattern, reflags)
1255 except Exception, inst:
1255 except Exception, inst:
1256 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1256 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1257 return None
1257 return None
1258 sep, eol = ':', '\n'
1258 sep, eol = ':', '\n'
1259 if opts.get('print0'):
1259 if opts.get('print0'):
1260 sep = eol = '\0'
1260 sep = eol = '\0'
1261
1261
1262 getfile = util.lrucachefunc(repo.file)
1262 getfile = util.lrucachefunc(repo.file)
1263
1263
1264 def matchlines(body):
1264 def matchlines(body):
1265 begin = 0
1265 begin = 0
1266 linenum = 0
1266 linenum = 0
1267 while True:
1267 while True:
1268 match = regexp.search(body, begin)
1268 match = regexp.search(body, begin)
1269 if not match:
1269 if not match:
1270 break
1270 break
1271 mstart, mend = match.span()
1271 mstart, mend = match.span()
1272 linenum += body.count('\n', begin, mstart) + 1
1272 linenum += body.count('\n', begin, mstart) + 1
1273 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1273 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1274 begin = body.find('\n', mend) + 1 or len(body)
1274 begin = body.find('\n', mend) + 1 or len(body)
1275 lend = begin - 1
1275 lend = begin - 1
1276 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1276 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1277
1277
1278 class linestate(object):
1278 class linestate(object):
1279 def __init__(self, line, linenum, colstart, colend):
1279 def __init__(self, line, linenum, colstart, colend):
1280 self.line = line
1280 self.line = line
1281 self.linenum = linenum
1281 self.linenum = linenum
1282 self.colstart = colstart
1282 self.colstart = colstart
1283 self.colend = colend
1283 self.colend = colend
1284
1284
1285 def __hash__(self):
1285 def __hash__(self):
1286 return hash((self.linenum, self.line))
1286 return hash((self.linenum, self.line))
1287
1287
1288 def __eq__(self, other):
1288 def __eq__(self, other):
1289 return self.line == other.line
1289 return self.line == other.line
1290
1290
1291 matches = {}
1291 matches = {}
1292 copies = {}
1292 copies = {}
1293 def grepbody(fn, rev, body):
1293 def grepbody(fn, rev, body):
1294 matches[rev].setdefault(fn, [])
1294 matches[rev].setdefault(fn, [])
1295 m = matches[rev][fn]
1295 m = matches[rev][fn]
1296 for lnum, cstart, cend, line in matchlines(body):
1296 for lnum, cstart, cend, line in matchlines(body):
1297 s = linestate(line, lnum, cstart, cend)
1297 s = linestate(line, lnum, cstart, cend)
1298 m.append(s)
1298 m.append(s)
1299
1299
1300 def difflinestates(a, b):
1300 def difflinestates(a, b):
1301 sm = difflib.SequenceMatcher(None, a, b)
1301 sm = difflib.SequenceMatcher(None, a, b)
1302 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1302 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1303 if tag == 'insert':
1303 if tag == 'insert':
1304 for i in xrange(blo, bhi):
1304 for i in xrange(blo, bhi):
1305 yield ('+', b[i])
1305 yield ('+', b[i])
1306 elif tag == 'delete':
1306 elif tag == 'delete':
1307 for i in xrange(alo, ahi):
1307 for i in xrange(alo, ahi):
1308 yield ('-', a[i])
1308 yield ('-', a[i])
1309 elif tag == 'replace':
1309 elif tag == 'replace':
1310 for i in xrange(alo, ahi):
1310 for i in xrange(alo, ahi):
1311 yield ('-', a[i])
1311 yield ('-', a[i])
1312 for i in xrange(blo, bhi):
1312 for i in xrange(blo, bhi):
1313 yield ('+', b[i])
1313 yield ('+', b[i])
1314
1314
1315 def display(fn, ctx, pstates, states):
1315 def display(fn, ctx, pstates, states):
1316 rev = ctx.rev()
1316 rev = ctx.rev()
1317 datefunc = ui.quiet and util.shortdate or util.datestr
1317 datefunc = ui.quiet and util.shortdate or util.datestr
1318 found = False
1318 found = False
1319 filerevmatches = {}
1319 filerevmatches = {}
1320 if opts.get('all'):
1320 if opts.get('all'):
1321 iter = difflinestates(pstates, states)
1321 iter = difflinestates(pstates, states)
1322 else:
1322 else:
1323 iter = [('', l) for l in states]
1323 iter = [('', l) for l in states]
1324 for change, l in iter:
1324 for change, l in iter:
1325 cols = [fn, str(rev)]
1325 cols = [fn, str(rev)]
1326 if opts.get('line_number'):
1326 if opts.get('line_number'):
1327 cols.append(str(l.linenum))
1327 cols.append(str(l.linenum))
1328 if opts.get('all'):
1328 if opts.get('all'):
1329 cols.append(change)
1329 cols.append(change)
1330 if opts.get('user'):
1330 if opts.get('user'):
1331 cols.append(ui.shortuser(ctx.user()))
1331 cols.append(ui.shortuser(ctx.user()))
1332 if opts.get('date'):
1332 if opts.get('date'):
1333 cols.append(datefunc(ctx.date()))
1333 cols.append(datefunc(ctx.date()))
1334 if opts.get('files_with_matches'):
1334 if opts.get('files_with_matches'):
1335 c = (fn, rev)
1335 c = (fn, rev)
1336 if c in filerevmatches:
1336 if c in filerevmatches:
1337 continue
1337 continue
1338 filerevmatches[c] = 1
1338 filerevmatches[c] = 1
1339 else:
1339 else:
1340 cols.append(l.line)
1340 cols.append(l.line)
1341 ui.write(sep.join(cols), eol)
1341 ui.write(sep.join(cols), eol)
1342 found = True
1342 found = True
1343 return found
1343 return found
1344
1344
1345 skip = {}
1345 skip = {}
1346 revfiles = {}
1346 revfiles = {}
1347 matchfn = cmdutil.match(repo, pats, opts)
1347 matchfn = cmdutil.match(repo, pats, opts)
1348 found = False
1348 found = False
1349 follow = opts.get('follow')
1349 follow = opts.get('follow')
1350
1350
1351 def prep(ctx, fns):
1351 def prep(ctx, fns):
1352 rev = ctx.rev()
1352 rev = ctx.rev()
1353 pctx = ctx.parents()[0]
1353 pctx = ctx.parents()[0]
1354 parent = pctx.rev()
1354 parent = pctx.rev()
1355 matches.setdefault(rev, {})
1355 matches.setdefault(rev, {})
1356 matches.setdefault(parent, {})
1356 matches.setdefault(parent, {})
1357 files = revfiles.setdefault(rev, [])
1357 files = revfiles.setdefault(rev, [])
1358 for fn in fns:
1358 for fn in fns:
1359 flog = getfile(fn)
1359 flog = getfile(fn)
1360 try:
1360 try:
1361 fnode = ctx.filenode(fn)
1361 fnode = ctx.filenode(fn)
1362 except error.LookupError:
1362 except error.LookupError:
1363 continue
1363 continue
1364
1364
1365 copied = flog.renamed(fnode)
1365 copied = flog.renamed(fnode)
1366 copy = follow and copied and copied[0]
1366 copy = follow and copied and copied[0]
1367 if copy:
1367 if copy:
1368 copies.setdefault(rev, {})[fn] = copy
1368 copies.setdefault(rev, {})[fn] = copy
1369 if fn in skip:
1369 if fn in skip:
1370 if copy:
1370 if copy:
1371 skip[copy] = True
1371 skip[copy] = True
1372 continue
1372 continue
1373 files.append(fn)
1373 files.append(fn)
1374
1374
1375 if fn not in matches[rev]:
1375 if fn not in matches[rev]:
1376 grepbody(fn, rev, flog.read(fnode))
1376 grepbody(fn, rev, flog.read(fnode))
1377
1377
1378 pfn = copy or fn
1378 pfn = copy or fn
1379 if pfn not in matches[parent]:
1379 if pfn not in matches[parent]:
1380 try:
1380 try:
1381 fnode = pctx.filenode(pfn)
1381 fnode = pctx.filenode(pfn)
1382 grepbody(pfn, parent, flog.read(fnode))
1382 grepbody(pfn, parent, flog.read(fnode))
1383 except error.LookupError:
1383 except error.LookupError:
1384 pass
1384 pass
1385
1385
1386 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
1386 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
1387 rev = ctx.rev()
1387 rev = ctx.rev()
1388 parent = ctx.parents()[0].rev()
1388 parent = ctx.parents()[0].rev()
1389 for fn in sorted(revfiles.get(rev, [])):
1389 for fn in sorted(revfiles.get(rev, [])):
1390 states = matches[rev][fn]
1390 states = matches[rev][fn]
1391 copy = copies.get(rev, {}).get(fn)
1391 copy = copies.get(rev, {}).get(fn)
1392 if fn in skip:
1392 if fn in skip:
1393 if copy:
1393 if copy:
1394 skip[copy] = True
1394 skip[copy] = True
1395 continue
1395 continue
1396 pstates = matches.get(parent, {}).get(copy or fn, [])
1396 pstates = matches.get(parent, {}).get(copy or fn, [])
1397 if pstates or states:
1397 if pstates or states:
1398 r = display(fn, ctx, pstates, states)
1398 r = display(fn, ctx, pstates, states)
1399 found = found or r
1399 found = found or r
1400 if r and not opts.get('all'):
1400 if r and not opts.get('all'):
1401 skip[fn] = True
1401 skip[fn] = True
1402 if copy:
1402 if copy:
1403 skip[copy] = True
1403 skip[copy] = True
1404 del matches[rev]
1404 del matches[rev]
1405 del revfiles[rev]
1405 del revfiles[rev]
1406
1406
1407 def heads(ui, repo, *branchrevs, **opts):
1407 def heads(ui, repo, *branchrevs, **opts):
1408 """show current repository heads or show branch heads
1408 """show current repository heads or show branch heads
1409
1409
1410 With no arguments, show all repository branch heads.
1410 With no arguments, show all repository branch heads.
1411
1411
1412 Repository "heads" are changesets with no child changesets. They are
1412 Repository "heads" are changesets with no child changesets. They are
1413 where development generally takes place and are the usual targets
1413 where development generally takes place and are the usual targets
1414 for update and merge operations. Branch heads are changesets that have
1414 for update and merge operations. Branch heads are changesets that have
1415 no child changeset on the same branch.
1415 no child changeset on the same branch.
1416
1416
1417 If one or more REVs are given, only branch heads on the branches
1417 If one or more REVs are given, only branch heads on the branches
1418 associated with the specified changesets are shown.
1418 associated with the specified changesets are shown.
1419
1419
1420 If -c/--closed is specified, also show branch heads marked closed
1420 If -c/--closed is specified, also show branch heads marked closed
1421 (see hg commit --close-branch).
1421 (see hg commit --close-branch).
1422
1422
1423 If STARTREV is specified, only those heads that are descendants of
1423 If STARTREV is specified, only those heads that are descendants of
1424 STARTREV will be displayed.
1424 STARTREV will be displayed.
1425
1425
1426 If -t/--topo is specified, named branch mechanics will be ignored and only
1426 If -t/--topo is specified, named branch mechanics will be ignored and only
1427 changesets without children will be shown.
1427 changesets without children will be shown.
1428 """
1428 """
1429
1429
1430 if opts.get('rev'):
1430 if opts.get('rev'):
1431 start = repo.lookup(opts['rev'])
1431 start = repo.lookup(opts['rev'])
1432 else:
1432 else:
1433 start = None
1433 start = None
1434
1434
1435 if opts.get('topo'):
1435 if opts.get('topo'):
1436 heads = [repo[h] for h in repo.heads(start)]
1436 heads = [repo[h] for h in repo.heads(start)]
1437 else:
1437 else:
1438 heads = []
1438 heads = []
1439 for b, ls in repo.branchmap().iteritems():
1439 for b, ls in repo.branchmap().iteritems():
1440 if start is None:
1440 if start is None:
1441 heads += [repo[h] for h in ls]
1441 heads += [repo[h] for h in ls]
1442 continue
1442 continue
1443 startrev = repo.changelog.rev(start)
1443 startrev = repo.changelog.rev(start)
1444 descendants = set(repo.changelog.descendants(startrev))
1444 descendants = set(repo.changelog.descendants(startrev))
1445 descendants.add(startrev)
1445 descendants.add(startrev)
1446 rev = repo.changelog.rev
1446 rev = repo.changelog.rev
1447 heads += [repo[h] for h in ls if rev(h) in descendants]
1447 heads += [repo[h] for h in ls if rev(h) in descendants]
1448
1448
1449 if branchrevs:
1449 if branchrevs:
1450 decode, encode = encoding.fromlocal, encoding.tolocal
1450 decode, encode = encoding.fromlocal, encoding.tolocal
1451 branches = set(repo[decode(br)].branch() for br in branchrevs)
1451 branches = set(repo[decode(br)].branch() for br in branchrevs)
1452 heads = [h for h in heads if h.branch() in branches]
1452 heads = [h for h in heads if h.branch() in branches]
1453
1453
1454 if not opts.get('closed'):
1454 if not opts.get('closed'):
1455 heads = [h for h in heads if not h.extra().get('close')]
1455 heads = [h for h in heads if not h.extra().get('close')]
1456
1456
1457 if opts.get('active') and branchrevs:
1457 if opts.get('active') and branchrevs:
1458 dagheads = repo.heads(start)
1458 dagheads = repo.heads(start)
1459 heads = [h for h in heads if h.node() in dagheads]
1459 heads = [h for h in heads if h.node() in dagheads]
1460
1460
1461 if branchrevs:
1461 if branchrevs:
1462 haveheads = set(h.branch() for h in heads)
1462 haveheads = set(h.branch() for h in heads)
1463 if branches - haveheads:
1463 if branches - haveheads:
1464 headless = ', '.join(encode(b) for b in branches - haveheads)
1464 headless = ', '.join(encode(b) for b in branches - haveheads)
1465 msg = _('no open branch heads found on branches %s')
1465 msg = _('no open branch heads found on branches %s')
1466 if opts.get('rev'):
1466 if opts.get('rev'):
1467 msg += _(' (started at %s)' % opts['rev'])
1467 msg += _(' (started at %s)' % opts['rev'])
1468 ui.warn((msg + '\n') % headless)
1468 ui.warn((msg + '\n') % headless)
1469
1469
1470 if not heads:
1470 if not heads:
1471 return 1
1471 return 1
1472
1472
1473 heads = sorted(heads, key=lambda x: -x.rev())
1473 heads = sorted(heads, key=lambda x: -x.rev())
1474 displayer = cmdutil.show_changeset(ui, repo, opts)
1474 displayer = cmdutil.show_changeset(ui, repo, opts)
1475 for ctx in heads:
1475 for ctx in heads:
1476 displayer.show(ctx)
1476 displayer.show(ctx)
1477 displayer.close()
1477 displayer.close()
1478
1478
1479 def help_(ui, name=None, with_version=False, unknowncmd=False):
1479 def help_(ui, name=None, with_version=False, unknowncmd=False):
1480 """show help for a given topic or a help overview
1480 """show help for a given topic or a help overview
1481
1481
1482 With no arguments, print a list of commands with short help messages.
1482 With no arguments, print a list of commands with short help messages.
1483
1483
1484 Given a topic, extension, or command name, print help for that
1484 Given a topic, extension, or command name, print help for that
1485 topic."""
1485 topic."""
1486 option_lists = []
1486 option_lists = []
1487 textwidth = util.termwidth() - 2
1487 textwidth = util.termwidth() - 2
1488
1488
1489 def addglobalopts(aliases):
1489 def addglobalopts(aliases):
1490 if ui.verbose:
1490 if ui.verbose:
1491 option_lists.append((_("global options:"), globalopts))
1491 option_lists.append((_("global options:"), globalopts))
1492 if name == 'shortlist':
1492 if name == 'shortlist':
1493 option_lists.append((_('use "hg help" for the full list '
1493 option_lists.append((_('use "hg help" for the full list '
1494 'of commands'), ()))
1494 'of commands'), ()))
1495 else:
1495 else:
1496 if name == 'shortlist':
1496 if name == 'shortlist':
1497 msg = _('use "hg help" for the full list of commands '
1497 msg = _('use "hg help" for the full list of commands '
1498 'or "hg -v" for details')
1498 'or "hg -v" for details')
1499 elif aliases:
1499 elif aliases:
1500 msg = _('use "hg -v help%s" to show aliases and '
1500 msg = _('use "hg -v help%s" to show aliases and '
1501 'global options') % (name and " " + name or "")
1501 'global options') % (name and " " + name or "")
1502 else:
1502 else:
1503 msg = _('use "hg -v help %s" to show global options') % name
1503 msg = _('use "hg -v help %s" to show global options') % name
1504 option_lists.append((msg, ()))
1504 option_lists.append((msg, ()))
1505
1505
1506 def helpcmd(name):
1506 def helpcmd(name):
1507 if with_version:
1507 if with_version:
1508 version_(ui)
1508 version_(ui)
1509 ui.write('\n')
1509 ui.write('\n')
1510
1510
1511 try:
1511 try:
1512 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
1512 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
1513 except error.AmbiguousCommand, inst:
1513 except error.AmbiguousCommand, inst:
1514 # py3k fix: except vars can't be used outside the scope of the
1514 # py3k fix: except vars can't be used outside the scope of the
1515 # except block, nor can be used inside a lambda. python issue4617
1515 # except block, nor can be used inside a lambda. python issue4617
1516 prefix = inst.args[0]
1516 prefix = inst.args[0]
1517 select = lambda c: c.lstrip('^').startswith(prefix)
1517 select = lambda c: c.lstrip('^').startswith(prefix)
1518 helplist(_('list of commands:\n\n'), select)
1518 helplist(_('list of commands:\n\n'), select)
1519 return
1519 return
1520
1520
1521 # check if it's an invalid alias and display its error if it is
1521 # check if it's an invalid alias and display its error if it is
1522 if getattr(entry[0], 'badalias', False):
1522 if getattr(entry[0], 'badalias', False):
1523 if not unknowncmd:
1523 if not unknowncmd:
1524 entry[0](ui)
1524 entry[0](ui)
1525 return
1525 return
1526
1526
1527 # synopsis
1527 # synopsis
1528 if len(entry) > 2:
1528 if len(entry) > 2:
1529 if entry[2].startswith('hg'):
1529 if entry[2].startswith('hg'):
1530 ui.write("%s\n" % entry[2])
1530 ui.write("%s\n" % entry[2])
1531 else:
1531 else:
1532 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
1532 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
1533 else:
1533 else:
1534 ui.write('hg %s\n' % aliases[0])
1534 ui.write('hg %s\n' % aliases[0])
1535
1535
1536 # aliases
1536 # aliases
1537 if not ui.quiet and len(aliases) > 1:
1537 if not ui.quiet and len(aliases) > 1:
1538 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1538 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1539
1539
1540 # description
1540 # description
1541 doc = gettext(entry[0].__doc__)
1541 doc = gettext(entry[0].__doc__)
1542 if not doc:
1542 if not doc:
1543 doc = _("(no help text available)")
1543 doc = _("(no help text available)")
1544 if ui.quiet:
1544 if ui.quiet:
1545 doc = doc.splitlines()[0]
1545 doc = doc.splitlines()[0]
1546 keep = ui.verbose and ['verbose'] or []
1546 keep = ui.verbose and ['verbose'] or []
1547 formatted, pruned = minirst.format(doc, textwidth, keep=keep)
1547 formatted, pruned = minirst.format(doc, textwidth, keep=keep)
1548 ui.write("\n%s\n" % formatted)
1548 ui.write("\n%s\n" % formatted)
1549 if pruned:
1549 if pruned:
1550 ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
1550 ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
1551
1551
1552 if not ui.quiet:
1552 if not ui.quiet:
1553 # options
1553 # options
1554 if entry[1]:
1554 if entry[1]:
1555 option_lists.append((_("options:\n"), entry[1]))
1555 option_lists.append((_("options:\n"), entry[1]))
1556
1556
1557 addglobalopts(False)
1557 addglobalopts(False)
1558
1558
1559 def helplist(header, select=None):
1559 def helplist(header, select=None):
1560 h = {}
1560 h = {}
1561 cmds = {}
1561 cmds = {}
1562 for c, e in table.iteritems():
1562 for c, e in table.iteritems():
1563 f = c.split("|", 1)[0]
1563 f = c.split("|", 1)[0]
1564 if select and not select(f):
1564 if select and not select(f):
1565 continue
1565 continue
1566 if (not select and name != 'shortlist' and
1566 if (not select and name != 'shortlist' and
1567 e[0].__module__ != __name__):
1567 e[0].__module__ != __name__):
1568 continue
1568 continue
1569 if name == "shortlist" and not f.startswith("^"):
1569 if name == "shortlist" and not f.startswith("^"):
1570 continue
1570 continue
1571 f = f.lstrip("^")
1571 f = f.lstrip("^")
1572 if not ui.debugflag and f.startswith("debug"):
1572 if not ui.debugflag and f.startswith("debug"):
1573 continue
1573 continue
1574 doc = e[0].__doc__
1574 doc = e[0].__doc__
1575 if doc and 'DEPRECATED' in doc and not ui.verbose:
1575 if doc and 'DEPRECATED' in doc and not ui.verbose:
1576 continue
1576 continue
1577 doc = gettext(doc)
1577 doc = gettext(doc)
1578 if not doc:
1578 if not doc:
1579 doc = _("(no help text available)")
1579 doc = _("(no help text available)")
1580 h[f] = doc.splitlines()[0].rstrip()
1580 h[f] = doc.splitlines()[0].rstrip()
1581 cmds[f] = c.lstrip("^")
1581 cmds[f] = c.lstrip("^")
1582
1582
1583 if not h:
1583 if not h:
1584 ui.status(_('no commands defined\n'))
1584 ui.status(_('no commands defined\n'))
1585 return
1585 return
1586
1586
1587 ui.status(header)
1587 ui.status(header)
1588 fns = sorted(h)
1588 fns = sorted(h)
1589 m = max(map(len, fns))
1589 m = max(map(len, fns))
1590 for f in fns:
1590 for f in fns:
1591 if ui.verbose:
1591 if ui.verbose:
1592 commands = cmds[f].replace("|",", ")
1592 commands = cmds[f].replace("|",", ")
1593 ui.write(" %s:\n %s\n"%(commands, h[f]))
1593 ui.write(" %s:\n %s\n"%(commands, h[f]))
1594 else:
1594 else:
1595 ui.write(' %-*s %s\n' % (m, f, util.wrap(h[f], m + 4)))
1595 ui.write(' %-*s %s\n' % (m, f, util.wrap(h[f], m + 4)))
1596
1596
1597 if not ui.quiet:
1597 if not ui.quiet:
1598 addglobalopts(True)
1598 addglobalopts(True)
1599
1599
1600 def helptopic(name):
1600 def helptopic(name):
1601 for names, header, doc in help.helptable:
1601 for names, header, doc in help.helptable:
1602 if name in names:
1602 if name in names:
1603 break
1603 break
1604 else:
1604 else:
1605 raise error.UnknownCommand(name)
1605 raise error.UnknownCommand(name)
1606
1606
1607 # description
1607 # description
1608 if not doc:
1608 if not doc:
1609 doc = _("(no help text available)")
1609 doc = _("(no help text available)")
1610 if hasattr(doc, '__call__'):
1610 if hasattr(doc, '__call__'):
1611 doc = doc()
1611 doc = doc()
1612
1612
1613 ui.write("%s\n\n" % header)
1613 ui.write("%s\n\n" % header)
1614 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
1614 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
1615
1615
1616 def helpext(name):
1616 def helpext(name):
1617 try:
1617 try:
1618 mod = extensions.find(name)
1618 mod = extensions.find(name)
1619 doc = gettext(mod.__doc__) or _('no help text available')
1619 doc = gettext(mod.__doc__) or _('no help text available')
1620 except KeyError:
1620 except KeyError:
1621 mod = None
1621 mod = None
1622 doc = extensions.disabledext(name)
1622 doc = extensions.disabledext(name)
1623 if not doc:
1623 if not doc:
1624 raise error.UnknownCommand(name)
1624 raise error.UnknownCommand(name)
1625
1625
1626 if '\n' not in doc:
1626 if '\n' not in doc:
1627 head, tail = doc, ""
1627 head, tail = doc, ""
1628 else:
1628 else:
1629 head, tail = doc.split('\n', 1)
1629 head, tail = doc.split('\n', 1)
1630 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
1630 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
1631 if tail:
1631 if tail:
1632 ui.write(minirst.format(tail, textwidth))
1632 ui.write(minirst.format(tail, textwidth))
1633 ui.status('\n\n')
1633 ui.status('\n\n')
1634
1634
1635 if mod:
1635 if mod:
1636 try:
1636 try:
1637 ct = mod.cmdtable
1637 ct = mod.cmdtable
1638 except AttributeError:
1638 except AttributeError:
1639 ct = {}
1639 ct = {}
1640 modcmds = set([c.split('|', 1)[0] for c in ct])
1640 modcmds = set([c.split('|', 1)[0] for c in ct])
1641 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1641 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1642 else:
1642 else:
1643 ui.write(_('use "hg help extensions" for information on enabling '
1643 ui.write(_('use "hg help extensions" for information on enabling '
1644 'extensions\n'))
1644 'extensions\n'))
1645
1645
1646 def helpextcmd(name):
1646 def helpextcmd(name):
1647 cmd, ext, mod = extensions.disabledcmd(name, ui.config('ui', 'strict'))
1647 cmd, ext, mod = extensions.disabledcmd(name, ui.config('ui', 'strict'))
1648 doc = gettext(mod.__doc__).splitlines()[0]
1648 doc = gettext(mod.__doc__).splitlines()[0]
1649
1649
1650 msg = help.listexts(_("'%s' is provided by the following "
1650 msg = help.listexts(_("'%s' is provided by the following "
1651 "extension:") % cmd, {ext: doc}, len(ext),
1651 "extension:") % cmd, {ext: doc}, len(ext),
1652 indent=4)
1652 indent=4)
1653 ui.write(minirst.format(msg, textwidth))
1653 ui.write(minirst.format(msg, textwidth))
1654 ui.write('\n\n')
1654 ui.write('\n\n')
1655 ui.write(_('use "hg help extensions" for information on enabling '
1655 ui.write(_('use "hg help extensions" for information on enabling '
1656 'extensions\n'))
1656 'extensions\n'))
1657
1657
1658 if name and name != 'shortlist':
1658 if name and name != 'shortlist':
1659 i = None
1659 i = None
1660 if unknowncmd:
1660 if unknowncmd:
1661 queries = (helpextcmd,)
1661 queries = (helpextcmd,)
1662 else:
1662 else:
1663 queries = (helptopic, helpcmd, helpext, helpextcmd)
1663 queries = (helptopic, helpcmd, helpext, helpextcmd)
1664 for f in queries:
1664 for f in queries:
1665 try:
1665 try:
1666 f(name)
1666 f(name)
1667 i = None
1667 i = None
1668 break
1668 break
1669 except error.UnknownCommand, inst:
1669 except error.UnknownCommand, inst:
1670 i = inst
1670 i = inst
1671 if i:
1671 if i:
1672 raise i
1672 raise i
1673
1673
1674 else:
1674 else:
1675 # program name
1675 # program name
1676 if ui.verbose or with_version:
1676 if ui.verbose or with_version:
1677 version_(ui)
1677 version_(ui)
1678 else:
1678 else:
1679 ui.status(_("Mercurial Distributed SCM\n"))
1679 ui.status(_("Mercurial Distributed SCM\n"))
1680 ui.status('\n')
1680 ui.status('\n')
1681
1681
1682 # list of commands
1682 # list of commands
1683 if name == "shortlist":
1683 if name == "shortlist":
1684 header = _('basic commands:\n\n')
1684 header = _('basic commands:\n\n')
1685 else:
1685 else:
1686 header = _('list of commands:\n\n')
1686 header = _('list of commands:\n\n')
1687
1687
1688 helplist(header)
1688 helplist(header)
1689 if name != 'shortlist':
1689 if name != 'shortlist':
1690 exts, maxlength = extensions.enabled()
1690 exts, maxlength = extensions.enabled()
1691 text = help.listexts(_('enabled extensions:'), exts, maxlength)
1691 text = help.listexts(_('enabled extensions:'), exts, maxlength)
1692 if text:
1692 if text:
1693 ui.write("\n%s\n" % minirst.format(text, textwidth))
1693 ui.write("\n%s\n" % minirst.format(text, textwidth))
1694
1694
1695 # list all option lists
1695 # list all option lists
1696 opt_output = []
1696 opt_output = []
1697 for title, options in option_lists:
1697 for title, options in option_lists:
1698 opt_output.append(("\n%s" % title, None))
1698 opt_output.append(("\n%s" % title, None))
1699 for shortopt, longopt, default, desc in options:
1699 for shortopt, longopt, default, desc in options:
1700 if _("DEPRECATED") in desc and not ui.verbose:
1700 if _("DEPRECATED") in desc and not ui.verbose:
1701 continue
1701 continue
1702 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1702 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1703 longopt and " --%s" % longopt),
1703 longopt and " --%s" % longopt),
1704 "%s%s" % (desc,
1704 "%s%s" % (desc,
1705 default
1705 default
1706 and _(" (default: %s)") % default
1706 and _(" (default: %s)") % default
1707 or "")))
1707 or "")))
1708
1708
1709 if not name:
1709 if not name:
1710 ui.write(_("\nadditional help topics:\n\n"))
1710 ui.write(_("\nadditional help topics:\n\n"))
1711 topics = []
1711 topics = []
1712 for names, header, doc in help.helptable:
1712 for names, header, doc in help.helptable:
1713 topics.append((sorted(names, key=len, reverse=True)[0], header))
1713 topics.append((sorted(names, key=len, reverse=True)[0], header))
1714 topics_len = max([len(s[0]) for s in topics])
1714 topics_len = max([len(s[0]) for s in topics])
1715 for t, desc in topics:
1715 for t, desc in topics:
1716 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1716 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1717
1717
1718 if opt_output:
1718 if opt_output:
1719 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1719 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1720 for first, second in opt_output:
1720 for first, second in opt_output:
1721 if second:
1721 if second:
1722 second = util.wrap(second, opts_len + 3)
1722 second = util.wrap(second, opts_len + 3)
1723 ui.write(" %-*s %s\n" % (opts_len, first, second))
1723 ui.write(" %-*s %s\n" % (opts_len, first, second))
1724 else:
1724 else:
1725 ui.write("%s\n" % first)
1725 ui.write("%s\n" % first)
1726
1726
1727 def identify(ui, repo, source=None,
1727 def identify(ui, repo, source=None,
1728 rev=None, num=None, id=None, branch=None, tags=None):
1728 rev=None, num=None, id=None, branch=None, tags=None):
1729 """identify the working copy or specified revision
1729 """identify the working copy or specified revision
1730
1730
1731 With no revision, print a summary of the current state of the
1731 With no revision, print a summary of the current state of the
1732 repository.
1732 repository.
1733
1733
1734 Specifying a path to a repository root or Mercurial bundle will
1734 Specifying a path to a repository root or Mercurial bundle will
1735 cause lookup to operate on that repository/bundle.
1735 cause lookup to operate on that repository/bundle.
1736
1736
1737 This summary identifies the repository state using one or two
1737 This summary identifies the repository state using one or two
1738 parent hash identifiers, followed by a "+" if there are
1738 parent hash identifiers, followed by a "+" if there are
1739 uncommitted changes in the working directory, a list of tags for
1739 uncommitted changes in the working directory, a list of tags for
1740 this revision and a branch name for non-default branches.
1740 this revision and a branch name for non-default branches.
1741 """
1741 """
1742
1742
1743 if not repo and not source:
1743 if not repo and not source:
1744 raise util.Abort(_("There is no Mercurial repository here "
1744 raise util.Abort(_("There is no Mercurial repository here "
1745 "(.hg not found)"))
1745 "(.hg not found)"))
1746
1746
1747 hexfunc = ui.debugflag and hex or short
1747 hexfunc = ui.debugflag and hex or short
1748 default = not (num or id or branch or tags)
1748 default = not (num or id or branch or tags)
1749 output = []
1749 output = []
1750
1750
1751 revs = []
1751 revs = []
1752 if source:
1752 if source:
1753 source, branches = hg.parseurl(ui.expandpath(source))
1753 source, branches = hg.parseurl(ui.expandpath(source))
1754 repo = hg.repository(ui, source)
1754 repo = hg.repository(ui, source)
1755 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
1755 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
1756
1756
1757 if not repo.local():
1757 if not repo.local():
1758 if not rev and revs:
1758 if not rev and revs:
1759 rev = revs[0]
1759 rev = revs[0]
1760 if not rev:
1760 if not rev:
1761 rev = "tip"
1761 rev = "tip"
1762 if num or branch or tags:
1762 if num or branch or tags:
1763 raise util.Abort(
1763 raise util.Abort(
1764 "can't query remote revision number, branch, or tags")
1764 "can't query remote revision number, branch, or tags")
1765 output = [hexfunc(repo.lookup(rev))]
1765 output = [hexfunc(repo.lookup(rev))]
1766 elif not rev:
1766 elif not rev:
1767 ctx = repo[None]
1767 ctx = repo[None]
1768 parents = ctx.parents()
1768 parents = ctx.parents()
1769 changed = False
1769 changed = False
1770 if default or id or num:
1770 if default or id or num:
1771 changed = ctx.files() + ctx.deleted()
1771 changed = ctx.files() + ctx.deleted()
1772 if default or id:
1772 if default or id:
1773 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1773 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1774 (changed) and "+" or "")]
1774 (changed) and "+" or "")]
1775 if num:
1775 if num:
1776 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1776 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1777 (changed) and "+" or ""))
1777 (changed) and "+" or ""))
1778 else:
1778 else:
1779 ctx = repo[rev]
1779 ctx = repo[rev]
1780 if default or id:
1780 if default or id:
1781 output = [hexfunc(ctx.node())]
1781 output = [hexfunc(ctx.node())]
1782 if num:
1782 if num:
1783 output.append(str(ctx.rev()))
1783 output.append(str(ctx.rev()))
1784
1784
1785 if repo.local() and default and not ui.quiet:
1785 if repo.local() and default and not ui.quiet:
1786 b = encoding.tolocal(ctx.branch())
1786 b = encoding.tolocal(ctx.branch())
1787 if b != 'default':
1787 if b != 'default':
1788 output.append("(%s)" % b)
1788 output.append("(%s)" % b)
1789
1789
1790 # multiple tags for a single parent separated by '/'
1790 # multiple tags for a single parent separated by '/'
1791 t = "/".join(ctx.tags())
1791 t = "/".join(ctx.tags())
1792 if t:
1792 if t:
1793 output.append(t)
1793 output.append(t)
1794
1794
1795 if branch:
1795 if branch:
1796 output.append(encoding.tolocal(ctx.branch()))
1796 output.append(encoding.tolocal(ctx.branch()))
1797
1797
1798 if tags:
1798 if tags:
1799 output.extend(ctx.tags())
1799 output.extend(ctx.tags())
1800
1800
1801 ui.write("%s\n" % ' '.join(output))
1801 ui.write("%s\n" % ' '.join(output))
1802
1802
1803 def import_(ui, repo, patch1, *patches, **opts):
1803 def import_(ui, repo, patch1, *patches, **opts):
1804 """import an ordered set of patches
1804 """import an ordered set of patches
1805
1805
1806 Import a list of patches and commit them individually (unless
1806 Import a list of patches and commit them individually (unless
1807 --no-commit is specified).
1807 --no-commit is specified).
1808
1808
1809 If there are outstanding changes in the working directory, import
1809 If there are outstanding changes in the working directory, import
1810 will abort unless given the -f/--force flag.
1810 will abort unless given the -f/--force flag.
1811
1811
1812 You can import a patch straight from a mail message. Even patches
1812 You can import a patch straight from a mail message. Even patches
1813 as attachments work (to use the body part, it must have type
1813 as attachments work (to use the body part, it must have type
1814 text/plain or text/x-patch). From and Subject headers of email
1814 text/plain or text/x-patch). From and Subject headers of email
1815 message are used as default committer and commit message. All
1815 message are used as default committer and commit message. All
1816 text/plain body parts before first diff are added to commit
1816 text/plain body parts before first diff are added to commit
1817 message.
1817 message.
1818
1818
1819 If the imported patch was generated by hg export, user and
1819 If the imported patch was generated by hg export, user and
1820 description from patch override values from message headers and
1820 description from patch override values from message headers and
1821 body. Values given on command line with -m/--message and -u/--user
1821 body. Values given on command line with -m/--message and -u/--user
1822 override these.
1822 override these.
1823
1823
1824 If --exact is specified, import will set the working directory to
1824 If --exact is specified, import will set the working directory to
1825 the parent of each patch before applying it, and will abort if the
1825 the parent of each patch before applying it, and will abort if the
1826 resulting changeset has a different ID than the one recorded in
1826 resulting changeset has a different ID than the one recorded in
1827 the patch. This may happen due to character set problems or other
1827 the patch. This may happen due to character set problems or other
1828 deficiencies in the text patch format.
1828 deficiencies in the text patch format.
1829
1829
1830 With -s/--similarity, hg will attempt to discover renames and
1830 With -s/--similarity, hg will attempt to discover renames and
1831 copies in the patch in the same way as 'addremove'.
1831 copies in the patch in the same way as 'addremove'.
1832
1832
1833 To read a patch from standard input, use "-" as the patch name. If
1833 To read a patch from standard input, use "-" as the patch name. If
1834 a URL is specified, the patch will be downloaded from it.
1834 a URL is specified, the patch will be downloaded from it.
1835 See 'hg help dates' for a list of formats valid for -d/--date.
1835 See 'hg help dates' for a list of formats valid for -d/--date.
1836 """
1836 """
1837 patches = (patch1,) + patches
1837 patches = (patch1,) + patches
1838
1838
1839 date = opts.get('date')
1839 date = opts.get('date')
1840 if date:
1840 if date:
1841 opts['date'] = util.parsedate(date)
1841 opts['date'] = util.parsedate(date)
1842
1842
1843 try:
1843 try:
1844 sim = float(opts.get('similarity') or 0)
1844 sim = float(opts.get('similarity') or 0)
1845 except ValueError:
1845 except ValueError:
1846 raise util.Abort(_('similarity must be a number'))
1846 raise util.Abort(_('similarity must be a number'))
1847 if sim < 0 or sim > 100:
1847 if sim < 0 or sim > 100:
1848 raise util.Abort(_('similarity must be between 0 and 100'))
1848 raise util.Abort(_('similarity must be between 0 and 100'))
1849
1849
1850 if opts.get('exact') or not opts.get('force'):
1850 if opts.get('exact') or not opts.get('force'):
1851 cmdutil.bail_if_changed(repo)
1851 cmdutil.bail_if_changed(repo)
1852
1852
1853 d = opts["base"]
1853 d = opts["base"]
1854 strip = opts["strip"]
1854 strip = opts["strip"]
1855 wlock = lock = None
1855 wlock = lock = None
1856
1856
1857 def tryone(ui, hunk):
1857 def tryone(ui, hunk):
1858 tmpname, message, user, date, branch, nodeid, p1, p2 = \
1858 tmpname, message, user, date, branch, nodeid, p1, p2 = \
1859 patch.extract(ui, hunk)
1859 patch.extract(ui, hunk)
1860
1860
1861 if not tmpname:
1861 if not tmpname:
1862 return None
1862 return None
1863 commitid = _('to working directory')
1863 commitid = _('to working directory')
1864
1864
1865 try:
1865 try:
1866 cmdline_message = cmdutil.logmessage(opts)
1866 cmdline_message = cmdutil.logmessage(opts)
1867 if cmdline_message:
1867 if cmdline_message:
1868 # pickup the cmdline msg
1868 # pickup the cmdline msg
1869 message = cmdline_message
1869 message = cmdline_message
1870 elif message:
1870 elif message:
1871 # pickup the patch msg
1871 # pickup the patch msg
1872 message = message.strip()
1872 message = message.strip()
1873 else:
1873 else:
1874 # launch the editor
1874 # launch the editor
1875 message = None
1875 message = None
1876 ui.debug('message:\n%s\n' % message)
1876 ui.debug('message:\n%s\n' % message)
1877
1877
1878 wp = repo.parents()
1878 wp = repo.parents()
1879 if opts.get('exact'):
1879 if opts.get('exact'):
1880 if not nodeid or not p1:
1880 if not nodeid or not p1:
1881 raise util.Abort(_('not a Mercurial patch'))
1881 raise util.Abort(_('not a Mercurial patch'))
1882 p1 = repo.lookup(p1)
1882 p1 = repo.lookup(p1)
1883 p2 = repo.lookup(p2 or hex(nullid))
1883 p2 = repo.lookup(p2 or hex(nullid))
1884
1884
1885 if p1 != wp[0].node():
1885 if p1 != wp[0].node():
1886 hg.clean(repo, p1)
1886 hg.clean(repo, p1)
1887 repo.dirstate.setparents(p1, p2)
1887 repo.dirstate.setparents(p1, p2)
1888 elif p2:
1888 elif p2:
1889 try:
1889 try:
1890 p1 = repo.lookup(p1)
1890 p1 = repo.lookup(p1)
1891 p2 = repo.lookup(p2)
1891 p2 = repo.lookup(p2)
1892 if p1 == wp[0].node():
1892 if p1 == wp[0].node():
1893 repo.dirstate.setparents(p1, p2)
1893 repo.dirstate.setparents(p1, p2)
1894 except error.RepoError:
1894 except error.RepoError:
1895 pass
1895 pass
1896 if opts.get('exact') or opts.get('import_branch'):
1896 if opts.get('exact') or opts.get('import_branch'):
1897 repo.dirstate.setbranch(branch or 'default')
1897 repo.dirstate.setbranch(branch or 'default')
1898
1898
1899 files = {}
1899 files = {}
1900 try:
1900 try:
1901 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1901 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1902 files=files, eolmode=None)
1902 files=files, eolmode=None)
1903 finally:
1903 finally:
1904 files = patch.updatedir(ui, repo, files,
1904 files = patch.updatedir(ui, repo, files,
1905 similarity=sim / 100.0)
1905 similarity=sim / 100.0)
1906 if not opts.get('no_commit'):
1906 if not opts.get('no_commit'):
1907 if opts.get('exact'):
1907 if opts.get('exact'):
1908 m = None
1908 m = None
1909 else:
1909 else:
1910 m = cmdutil.matchfiles(repo, files or [])
1910 m = cmdutil.matchfiles(repo, files or [])
1911 n = repo.commit(message, opts.get('user') or user,
1911 n = repo.commit(message, opts.get('user') or user,
1912 opts.get('date') or date, match=m,
1912 opts.get('date') or date, match=m,
1913 editor=cmdutil.commiteditor)
1913 editor=cmdutil.commiteditor)
1914 if opts.get('exact'):
1914 if opts.get('exact'):
1915 if hex(n) != nodeid:
1915 if hex(n) != nodeid:
1916 repo.rollback()
1916 repo.rollback()
1917 raise util.Abort(_('patch is damaged'
1917 raise util.Abort(_('patch is damaged'
1918 ' or loses information'))
1918 ' or loses information'))
1919 # Force a dirstate write so that the next transaction
1919 # Force a dirstate write so that the next transaction
1920 # backups an up-do-date file.
1920 # backups an up-do-date file.
1921 repo.dirstate.write()
1921 repo.dirstate.write()
1922 if n:
1922 if n:
1923 commitid = short(n)
1923 commitid = short(n)
1924
1924
1925 return commitid
1925 return commitid
1926 finally:
1926 finally:
1927 os.unlink(tmpname)
1927 os.unlink(tmpname)
1928
1928
1929 try:
1929 try:
1930 wlock = repo.wlock()
1930 wlock = repo.wlock()
1931 lock = repo.lock()
1931 lock = repo.lock()
1932 lastcommit = None
1932 lastcommit = None
1933 for p in patches:
1933 for p in patches:
1934 pf = os.path.join(d, p)
1934 pf = os.path.join(d, p)
1935
1935
1936 if pf == '-':
1936 if pf == '-':
1937 ui.status(_("applying patch from stdin\n"))
1937 ui.status(_("applying patch from stdin\n"))
1938 pf = sys.stdin
1938 pf = sys.stdin
1939 else:
1939 else:
1940 ui.status(_("applying %s\n") % p)
1940 ui.status(_("applying %s\n") % p)
1941 pf = url.open(ui, pf)
1941 pf = url.open(ui, pf)
1942
1942
1943 haspatch = False
1943 haspatch = False
1944 for hunk in patch.split(pf):
1944 for hunk in patch.split(pf):
1945 commitid = tryone(ui, hunk)
1945 commitid = tryone(ui, hunk)
1946 if commitid:
1946 if commitid:
1947 haspatch = True
1947 haspatch = True
1948 if lastcommit:
1948 if lastcommit:
1949 ui.status(_('applied %s\n') % lastcommit)
1949 ui.status(_('applied %s\n') % lastcommit)
1950 lastcommit = commitid
1950 lastcommit = commitid
1951
1951
1952 if not haspatch:
1952 if not haspatch:
1953 raise util.Abort(_('no diffs found'))
1953 raise util.Abort(_('no diffs found'))
1954
1954
1955 finally:
1955 finally:
1956 release(lock, wlock)
1956 release(lock, wlock)
1957
1957
1958 def incoming(ui, repo, source="default", **opts):
1958 def incoming(ui, repo, source="default", **opts):
1959 """show new changesets found in source
1959 """show new changesets found in source
1960
1960
1961 Show new changesets found in the specified path/URL or the default
1961 Show new changesets found in the specified path/URL or the default
1962 pull location. These are the changesets that would have been pulled
1962 pull location. These are the changesets that would have been pulled
1963 if a pull at the time you issued this command.
1963 if a pull at the time you issued this command.
1964
1964
1965 For remote repository, using --bundle avoids downloading the
1965 For remote repository, using --bundle avoids downloading the
1966 changesets twice if the incoming is followed by a pull.
1966 changesets twice if the incoming is followed by a pull.
1967
1967
1968 See pull for valid source format details.
1968 See pull for valid source format details.
1969 """
1969 """
1970 limit = cmdutil.loglimit(opts)
1970 limit = cmdutil.loglimit(opts)
1971 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
1971 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
1972 other = hg.repository(cmdutil.remoteui(repo, opts), source)
1972 other = hg.repository(cmdutil.remoteui(repo, opts), source)
1973 ui.status(_('comparing with %s\n') % url.hidepassword(source))
1973 ui.status(_('comparing with %s\n') % url.hidepassword(source))
1974 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
1974 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
1975 if revs:
1975 if revs:
1976 revs = [other.lookup(rev) for rev in revs]
1976 revs = [other.lookup(rev) for rev in revs]
1977 common, incoming, rheads = repo.findcommonincoming(other, heads=revs,
1977 common, incoming, rheads = repo.findcommonincoming(other, heads=revs,
1978 force=opts["force"])
1978 force=opts["force"])
1979 if not incoming:
1979 if not incoming:
1980 try:
1980 try:
1981 os.unlink(opts["bundle"])
1981 os.unlink(opts["bundle"])
1982 except:
1982 except:
1983 pass
1983 pass
1984 ui.status(_("no changes found\n"))
1984 ui.status(_("no changes found\n"))
1985 return 1
1985 return 1
1986
1986
1987 cleanup = None
1987 cleanup = None
1988 try:
1988 try:
1989 fname = opts["bundle"]
1989 fname = opts["bundle"]
1990 if fname or not other.local():
1990 if fname or not other.local():
1991 # create a bundle (uncompressed if other repo is not local)
1991 # create a bundle (uncompressed if other repo is not local)
1992
1992
1993 if revs is None and other.capable('changegroupsubset'):
1993 if revs is None and other.capable('changegroupsubset'):
1994 revs = rheads
1994 revs = rheads
1995
1995
1996 if revs is None:
1996 if revs is None:
1997 cg = other.changegroup(incoming, "incoming")
1997 cg = other.changegroup(incoming, "incoming")
1998 else:
1998 else:
1999 cg = other.changegroupsubset(incoming, revs, 'incoming')
1999 cg = other.changegroupsubset(incoming, revs, 'incoming')
2000 bundletype = other.local() and "HG10BZ" or "HG10UN"
2000 bundletype = other.local() and "HG10BZ" or "HG10UN"
2001 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
2001 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
2002 # keep written bundle?
2002 # keep written bundle?
2003 if opts["bundle"]:
2003 if opts["bundle"]:
2004 cleanup = None
2004 cleanup = None
2005 if not other.local():
2005 if not other.local():
2006 # use the created uncompressed bundlerepo
2006 # use the created uncompressed bundlerepo
2007 other = bundlerepo.bundlerepository(ui, repo.root, fname)
2007 other = bundlerepo.bundlerepository(ui, repo.root, fname)
2008
2008
2009 o = other.changelog.nodesbetween(incoming, revs)[0]
2009 o = other.changelog.nodesbetween(incoming, revs)[0]
2010 if opts.get('newest_first'):
2010 if opts.get('newest_first'):
2011 o.reverse()
2011 o.reverse()
2012 displayer = cmdutil.show_changeset(ui, other, opts)
2012 displayer = cmdutil.show_changeset(ui, other, opts)
2013 count = 0
2013 count = 0
2014 for n in o:
2014 for n in o:
2015 if limit is not None and count >= limit:
2015 if limit is not None and count >= limit:
2016 break
2016 break
2017 parents = [p for p in other.changelog.parents(n) if p != nullid]
2017 parents = [p for p in other.changelog.parents(n) if p != nullid]
2018 if opts.get('no_merges') and len(parents) == 2:
2018 if opts.get('no_merges') and len(parents) == 2:
2019 continue
2019 continue
2020 count += 1
2020 count += 1
2021 displayer.show(other[n])
2021 displayer.show(other[n])
2022 displayer.close()
2022 displayer.close()
2023 finally:
2023 finally:
2024 if hasattr(other, 'close'):
2024 if hasattr(other, 'close'):
2025 other.close()
2025 other.close()
2026 if cleanup:
2026 if cleanup:
2027 os.unlink(cleanup)
2027 os.unlink(cleanup)
2028
2028
2029 def init(ui, dest=".", **opts):
2029 def init(ui, dest=".", **opts):
2030 """create a new repository in the given directory
2030 """create a new repository in the given directory
2031
2031
2032 Initialize a new repository in the given directory. If the given
2032 Initialize a new repository in the given directory. If the given
2033 directory does not exist, it will be created.
2033 directory does not exist, it will be created.
2034
2034
2035 If no directory is given, the current directory is used.
2035 If no directory is given, the current directory is used.
2036
2036
2037 It is possible to specify an ``ssh://`` URL as the destination.
2037 It is possible to specify an ``ssh://`` URL as the destination.
2038 See 'hg help urls' for more information.
2038 See 'hg help urls' for more information.
2039 """
2039 """
2040 hg.repository(cmdutil.remoteui(ui, opts), dest, create=1)
2040 hg.repository(cmdutil.remoteui(ui, opts), dest, create=1)
2041
2041
2042 def locate(ui, repo, *pats, **opts):
2042 def locate(ui, repo, *pats, **opts):
2043 """locate files matching specific patterns
2043 """locate files matching specific patterns
2044
2044
2045 Print files under Mercurial control in the working directory whose
2045 Print files under Mercurial control in the working directory whose
2046 names match the given patterns.
2046 names match the given patterns.
2047
2047
2048 By default, this command searches all directories in the working
2048 By default, this command searches all directories in the working
2049 directory. To search just the current directory and its
2049 directory. To search just the current directory and its
2050 subdirectories, use "--include .".
2050 subdirectories, use "--include .".
2051
2051
2052 If no patterns are given to match, this command prints the names
2052 If no patterns are given to match, this command prints the names
2053 of all files under Mercurial control in the working directory.
2053 of all files under Mercurial control in the working directory.
2054
2054
2055 If you want to feed the output of this command into the "xargs"
2055 If you want to feed the output of this command into the "xargs"
2056 command, use the -0 option to both this command and "xargs". This
2056 command, use the -0 option to both this command and "xargs". This
2057 will avoid the problem of "xargs" treating single filenames that
2057 will avoid the problem of "xargs" treating single filenames that
2058 contain whitespace as multiple filenames.
2058 contain whitespace as multiple filenames.
2059 """
2059 """
2060 end = opts.get('print0') and '\0' or '\n'
2060 end = opts.get('print0') and '\0' or '\n'
2061 rev = opts.get('rev') or None
2061 rev = opts.get('rev') or None
2062
2062
2063 ret = 1
2063 ret = 1
2064 m = cmdutil.match(repo, pats, opts, default='relglob')
2064 m = cmdutil.match(repo, pats, opts, default='relglob')
2065 m.bad = lambda x, y: False
2065 m.bad = lambda x, y: False
2066 for abs in repo[rev].walk(m):
2066 for abs in repo[rev].walk(m):
2067 if not rev and abs not in repo.dirstate:
2067 if not rev and abs not in repo.dirstate:
2068 continue
2068 continue
2069 if opts.get('fullpath'):
2069 if opts.get('fullpath'):
2070 ui.write(repo.wjoin(abs), end)
2070 ui.write(repo.wjoin(abs), end)
2071 else:
2071 else:
2072 ui.write(((pats and m.rel(abs)) or abs), end)
2072 ui.write(((pats and m.rel(abs)) or abs), end)
2073 ret = 0
2073 ret = 0
2074
2074
2075 return ret
2075 return ret
2076
2076
2077 def log(ui, repo, *pats, **opts):
2077 def log(ui, repo, *pats, **opts):
2078 """show revision history of entire repository or files
2078 """show revision history of entire repository or files
2079
2079
2080 Print the revision history of the specified files or the entire
2080 Print the revision history of the specified files or the entire
2081 project.
2081 project.
2082
2082
2083 File history is shown without following rename or copy history of
2083 File history is shown without following rename or copy history of
2084 files. Use -f/--follow with a filename to follow history across
2084 files. Use -f/--follow with a filename to follow history across
2085 renames and copies. --follow without a filename will only show
2085 renames and copies. --follow without a filename will only show
2086 ancestors or descendants of the starting revision. --follow-first
2086 ancestors or descendants of the starting revision. --follow-first
2087 only follows the first parent of merge revisions.
2087 only follows the first parent of merge revisions.
2088
2088
2089 If no revision range is specified, the default is tip:0 unless
2089 If no revision range is specified, the default is tip:0 unless
2090 --follow is set, in which case the working directory parent is
2090 --follow is set, in which case the working directory parent is
2091 used as the starting revision.
2091 used as the starting revision.
2092
2092
2093 See 'hg help dates' for a list of formats valid for -d/--date.
2093 See 'hg help dates' for a list of formats valid for -d/--date.
2094
2094
2095 By default this command prints revision number and changeset id,
2095 By default this command prints revision number and changeset id,
2096 tags, non-trivial parents, user, date and time, and a summary for
2096 tags, non-trivial parents, user, date and time, and a summary for
2097 each commit. When the -v/--verbose switch is used, the list of
2097 each commit. When the -v/--verbose switch is used, the list of
2098 changed files and full commit message are shown.
2098 changed files and full commit message are shown.
2099
2099
2100 NOTE: log -p/--patch may generate unexpected diff output for merge
2100 NOTE: log -p/--patch may generate unexpected diff output for merge
2101 changesets, as it will only compare the merge changeset against
2101 changesets, as it will only compare the merge changeset against
2102 its first parent. Also, only files different from BOTH parents
2102 its first parent. Also, only files different from BOTH parents
2103 will appear in files:.
2103 will appear in files:.
2104 """
2104 """
2105
2105
2106 matchfn = cmdutil.match(repo, pats, opts)
2106 matchfn = cmdutil.match(repo, pats, opts)
2107 limit = cmdutil.loglimit(opts)
2107 limit = cmdutil.loglimit(opts)
2108 count = 0
2108 count = 0
2109
2109
2110 endrev = None
2110 endrev = None
2111 if opts.get('copies') and opts.get('rev'):
2111 if opts.get('copies') and opts.get('rev'):
2112 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
2112 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
2113
2113
2114 df = False
2114 df = False
2115 if opts["date"]:
2115 if opts["date"]:
2116 df = util.matchdate(opts["date"])
2116 df = util.matchdate(opts["date"])
2117
2117
2118 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
2118 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
2119 def prep(ctx, fns):
2119 def prep(ctx, fns):
2120 rev = ctx.rev()
2120 rev = ctx.rev()
2121 parents = [p for p in repo.changelog.parentrevs(rev)
2121 parents = [p for p in repo.changelog.parentrevs(rev)
2122 if p != nullrev]
2122 if p != nullrev]
2123 if opts.get('no_merges') and len(parents) == 2:
2123 if opts.get('no_merges') and len(parents) == 2:
2124 return
2124 return
2125 if opts.get('only_merges') and len(parents) != 2:
2125 if opts.get('only_merges') and len(parents) != 2:
2126 return
2126 return
2127 if opts.get('only_branch') and ctx.branch() not in opts['only_branch']:
2127 if opts.get('only_branch') and ctx.branch() not in opts['only_branch']:
2128 return
2128 return
2129 if df and not df(ctx.date()[0]):
2129 if df and not df(ctx.date()[0]):
2130 return
2130 return
2131 if opts['user'] and not [k for k in opts['user'] if k in ctx.user()]:
2131 if opts['user'] and not [k for k in opts['user'] if k in ctx.user()]:
2132 return
2132 return
2133 if opts.get('keyword'):
2133 if opts.get('keyword'):
2134 for k in [kw.lower() for kw in opts['keyword']]:
2134 for k in [kw.lower() for kw in opts['keyword']]:
2135 if (k in ctx.user().lower() or
2135 if (k in ctx.user().lower() or
2136 k in ctx.description().lower() or
2136 k in ctx.description().lower() or
2137 k in " ".join(ctx.files()).lower()):
2137 k in " ".join(ctx.files()).lower()):
2138 break
2138 break
2139 else:
2139 else:
2140 return
2140 return
2141
2141
2142 copies = None
2142 copies = None
2143 if opts.get('copies') and rev:
2143 if opts.get('copies') and rev:
2144 copies = []
2144 copies = []
2145 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2145 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2146 for fn in ctx.files():
2146 for fn in ctx.files():
2147 rename = getrenamed(fn, rev)
2147 rename = getrenamed(fn, rev)
2148 if rename:
2148 if rename:
2149 copies.append((fn, rename[0]))
2149 copies.append((fn, rename[0]))
2150
2150
2151 displayer.show(ctx, copies=copies)
2151 displayer.show(ctx, copies=copies)
2152
2152
2153 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2153 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2154 if count == limit:
2154 if count == limit:
2155 break
2155 break
2156 if displayer.flush(ctx.rev()):
2156 if displayer.flush(ctx.rev()):
2157 count += 1
2157 count += 1
2158 displayer.close()
2158 displayer.close()
2159
2159
2160 def manifest(ui, repo, node=None, rev=None):
2160 def manifest(ui, repo, node=None, rev=None):
2161 """output the current or given revision of the project manifest
2161 """output the current or given revision of the project manifest
2162
2162
2163 Print a list of version controlled files for the given revision.
2163 Print a list of version controlled files for the given revision.
2164 If no revision is given, the first parent of the working directory
2164 If no revision is given, the first parent of the working directory
2165 is used, or the null revision if no revision is checked out.
2165 is used, or the null revision if no revision is checked out.
2166
2166
2167 With -v, print file permissions, symlink and executable bits.
2167 With -v, print file permissions, symlink and executable bits.
2168 With --debug, print file revision hashes.
2168 With --debug, print file revision hashes.
2169 """
2169 """
2170
2170
2171 if rev and node:
2171 if rev and node:
2172 raise util.Abort(_("please specify just one revision"))
2172 raise util.Abort(_("please specify just one revision"))
2173
2173
2174 if not node:
2174 if not node:
2175 node = rev
2175 node = rev
2176
2176
2177 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2177 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2178 ctx = repo[node]
2178 ctx = repo[node]
2179 for f in ctx:
2179 for f in ctx:
2180 if ui.debugflag:
2180 if ui.debugflag:
2181 ui.write("%40s " % hex(ctx.manifest()[f]))
2181 ui.write("%40s " % hex(ctx.manifest()[f]))
2182 if ui.verbose:
2182 if ui.verbose:
2183 ui.write(decor[ctx.flags(f)])
2183 ui.write(decor[ctx.flags(f)])
2184 ui.write("%s\n" % f)
2184 ui.write("%s\n" % f)
2185
2185
2186 def merge(ui, repo, node=None, **opts):
2186 def merge(ui, repo, node=None, **opts):
2187 """merge working directory with another revision
2187 """merge working directory with another revision
2188
2188
2189 The current working directory is updated with all changes made in
2189 The current working directory is updated with all changes made in
2190 the requested revision since the last common predecessor revision.
2190 the requested revision since the last common predecessor revision.
2191
2191
2192 Files that changed between either parent are marked as changed for
2192 Files that changed between either parent are marked as changed for
2193 the next commit and a commit must be performed before any further
2193 the next commit and a commit must be performed before any further
2194 updates to the repository are allowed. The next commit will have
2194 updates to the repository are allowed. The next commit will have
2195 two parents.
2195 two parents.
2196
2196
2197 If no revision is specified, the working directory's parent is a
2197 If no revision is specified, the working directory's parent is a
2198 head revision, and the current branch contains exactly one other
2198 head revision, and the current branch contains exactly one other
2199 head, the other head is merged with by default. Otherwise, an
2199 head, the other head is merged with by default. Otherwise, an
2200 explicit revision with which to merge with must be provided.
2200 explicit revision with which to merge with must be provided.
2201 """
2201 """
2202
2202
2203 if opts.get('rev') and node:
2203 if opts.get('rev') and node:
2204 raise util.Abort(_("please specify just one revision"))
2204 raise util.Abort(_("please specify just one revision"))
2205 if not node:
2205 if not node:
2206 node = opts.get('rev')
2206 node = opts.get('rev')
2207
2207
2208 if not node:
2208 if not node:
2209 branch = repo.changectx(None).branch()
2209 branch = repo.changectx(None).branch()
2210 bheads = repo.branchheads(branch)
2210 bheads = repo.branchheads(branch)
2211 if len(bheads) > 2:
2211 if len(bheads) > 2:
2212 ui.warn(_("abort: branch '%s' has %d heads - "
2212 ui.warn(_("abort: branch '%s' has %d heads - "
2213 "please merge with an explicit rev\n")
2213 "please merge with an explicit rev\n")
2214 % (branch, len(bheads)))
2214 % (branch, len(bheads)))
2215 ui.status(_("(run 'hg heads .' to see heads)\n"))
2215 ui.status(_("(run 'hg heads .' to see heads)\n"))
2216 return False
2216 return False
2217
2217
2218 parent = repo.dirstate.parents()[0]
2218 parent = repo.dirstate.parents()[0]
2219 if len(bheads) == 1:
2219 if len(bheads) == 1:
2220 if len(repo.heads()) > 1:
2220 if len(repo.heads()) > 1:
2221 ui.warn(_("abort: branch '%s' has one head - "
2221 ui.warn(_("abort: branch '%s' has one head - "
2222 "please merge with an explicit rev\n" % branch))
2222 "please merge with an explicit rev\n" % branch))
2223 ui.status(_("(run 'hg heads' to see all heads)\n"))
2223 ui.status(_("(run 'hg heads' to see all heads)\n"))
2224 return False
2224 return False
2225 msg = _('there is nothing to merge')
2225 msg = _('there is nothing to merge')
2226 if parent != repo.lookup(repo[None].branch()):
2226 if parent != repo.lookup(repo[None].branch()):
2227 msg = _('%s - use "hg update" instead') % msg
2227 msg = _('%s - use "hg update" instead') % msg
2228 raise util.Abort(msg)
2228 raise util.Abort(msg)
2229
2229
2230 if parent not in bheads:
2230 if parent not in bheads:
2231 raise util.Abort(_('working dir not at a head rev - '
2231 raise util.Abort(_('working dir not at a head rev - '
2232 'use "hg update" or merge with an explicit rev'))
2232 'use "hg update" or merge with an explicit rev'))
2233 node = parent == bheads[0] and bheads[-1] or bheads[0]
2233 node = parent == bheads[0] and bheads[-1] or bheads[0]
2234
2234
2235 if opts.get('preview'):
2235 if opts.get('preview'):
2236 p1 = repo['.']
2236 # find nodes that are ancestors of p2 but not of p1
2237 p2 = repo[node]
2237 p1 = repo.lookup('.')
2238 common = p1.ancestor(p2)
2238 p2 = repo.lookup(node)
2239 roots, heads = [common.node()], [p2.node()]
2239 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
2240
2240 displayer = cmdutil.show_changeset(ui, repo, opts)
2241 displayer = cmdutil.show_changeset(ui, repo, opts)
2241 for node in repo.changelog.nodesbetween(roots=roots, heads=heads)[0]:
2242 for node in nodes:
2242 if node not in roots:
2243 displayer.show(repo[node])
2243 displayer.show(repo[node])
2244 displayer.close()
2244 displayer.close()
2245 return 0
2245 return 0
2246
2246
2247 return hg.merge(repo, node, force=opts.get('force'))
2247 return hg.merge(repo, node, force=opts.get('force'))
2248
2248
2249 def outgoing(ui, repo, dest=None, **opts):
2249 def outgoing(ui, repo, dest=None, **opts):
2250 """show changesets not found in the destination
2250 """show changesets not found in the destination
2251
2251
2252 Show changesets not found in the specified destination repository
2252 Show changesets not found in the specified destination repository
2253 or the default push location. These are the changesets that would
2253 or the default push location. These are the changesets that would
2254 be pushed if a push was requested.
2254 be pushed if a push was requested.
2255
2255
2256 See pull for details of valid destination formats.
2256 See pull for details of valid destination formats.
2257 """
2257 """
2258 limit = cmdutil.loglimit(opts)
2258 limit = cmdutil.loglimit(opts)
2259 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2259 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2260 dest, branches = hg.parseurl(dest, opts.get('branch'))
2260 dest, branches = hg.parseurl(dest, opts.get('branch'))
2261 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2261 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2262 if revs:
2262 if revs:
2263 revs = [repo.lookup(rev) for rev in revs]
2263 revs = [repo.lookup(rev) for rev in revs]
2264
2264
2265 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2265 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2266 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2266 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2267 o = repo.findoutgoing(other, force=opts.get('force'))
2267 o = repo.findoutgoing(other, force=opts.get('force'))
2268 if not o:
2268 if not o:
2269 ui.status(_("no changes found\n"))
2269 ui.status(_("no changes found\n"))
2270 return 1
2270 return 1
2271 o = repo.changelog.nodesbetween(o, revs)[0]
2271 o = repo.changelog.nodesbetween(o, revs)[0]
2272 if opts.get('newest_first'):
2272 if opts.get('newest_first'):
2273 o.reverse()
2273 o.reverse()
2274 displayer = cmdutil.show_changeset(ui, repo, opts)
2274 displayer = cmdutil.show_changeset(ui, repo, opts)
2275 count = 0
2275 count = 0
2276 for n in o:
2276 for n in o:
2277 if limit is not None and count >= limit:
2277 if limit is not None and count >= limit:
2278 break
2278 break
2279 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2279 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2280 if opts.get('no_merges') and len(parents) == 2:
2280 if opts.get('no_merges') and len(parents) == 2:
2281 continue
2281 continue
2282 count += 1
2282 count += 1
2283 displayer.show(repo[n])
2283 displayer.show(repo[n])
2284 displayer.close()
2284 displayer.close()
2285
2285
2286 def parents(ui, repo, file_=None, **opts):
2286 def parents(ui, repo, file_=None, **opts):
2287 """show the parents of the working directory or revision
2287 """show the parents of the working directory or revision
2288
2288
2289 Print the working directory's parent revisions. If a revision is
2289 Print the working directory's parent revisions. If a revision is
2290 given via -r/--rev, the parent of that revision will be printed.
2290 given via -r/--rev, the parent of that revision will be printed.
2291 If a file argument is given, the revision in which the file was
2291 If a file argument is given, the revision in which the file was
2292 last changed (before the working directory revision or the
2292 last changed (before the working directory revision or the
2293 argument to --rev if given) is printed.
2293 argument to --rev if given) is printed.
2294 """
2294 """
2295 rev = opts.get('rev')
2295 rev = opts.get('rev')
2296 if rev:
2296 if rev:
2297 ctx = repo[rev]
2297 ctx = repo[rev]
2298 else:
2298 else:
2299 ctx = repo[None]
2299 ctx = repo[None]
2300
2300
2301 if file_:
2301 if file_:
2302 m = cmdutil.match(repo, (file_,), opts)
2302 m = cmdutil.match(repo, (file_,), opts)
2303 if m.anypats() or len(m.files()) != 1:
2303 if m.anypats() or len(m.files()) != 1:
2304 raise util.Abort(_('can only specify an explicit filename'))
2304 raise util.Abort(_('can only specify an explicit filename'))
2305 file_ = m.files()[0]
2305 file_ = m.files()[0]
2306 filenodes = []
2306 filenodes = []
2307 for cp in ctx.parents():
2307 for cp in ctx.parents():
2308 if not cp:
2308 if not cp:
2309 continue
2309 continue
2310 try:
2310 try:
2311 filenodes.append(cp.filenode(file_))
2311 filenodes.append(cp.filenode(file_))
2312 except error.LookupError:
2312 except error.LookupError:
2313 pass
2313 pass
2314 if not filenodes:
2314 if not filenodes:
2315 raise util.Abort(_("'%s' not found in manifest!") % file_)
2315 raise util.Abort(_("'%s' not found in manifest!") % file_)
2316 fl = repo.file(file_)
2316 fl = repo.file(file_)
2317 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2317 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2318 else:
2318 else:
2319 p = [cp.node() for cp in ctx.parents()]
2319 p = [cp.node() for cp in ctx.parents()]
2320
2320
2321 displayer = cmdutil.show_changeset(ui, repo, opts)
2321 displayer = cmdutil.show_changeset(ui, repo, opts)
2322 for n in p:
2322 for n in p:
2323 if n != nullid:
2323 if n != nullid:
2324 displayer.show(repo[n])
2324 displayer.show(repo[n])
2325 displayer.close()
2325 displayer.close()
2326
2326
2327 def paths(ui, repo, search=None):
2327 def paths(ui, repo, search=None):
2328 """show aliases for remote repositories
2328 """show aliases for remote repositories
2329
2329
2330 Show definition of symbolic path name NAME. If no name is given,
2330 Show definition of symbolic path name NAME. If no name is given,
2331 show definition of all available names.
2331 show definition of all available names.
2332
2332
2333 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2333 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2334 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2334 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2335
2335
2336 See 'hg help urls' for more information.
2336 See 'hg help urls' for more information.
2337 """
2337 """
2338 if search:
2338 if search:
2339 for name, path in ui.configitems("paths"):
2339 for name, path in ui.configitems("paths"):
2340 if name == search:
2340 if name == search:
2341 ui.write("%s\n" % url.hidepassword(path))
2341 ui.write("%s\n" % url.hidepassword(path))
2342 return
2342 return
2343 ui.warn(_("not found!\n"))
2343 ui.warn(_("not found!\n"))
2344 return 1
2344 return 1
2345 else:
2345 else:
2346 for name, path in ui.configitems("paths"):
2346 for name, path in ui.configitems("paths"):
2347 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2347 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2348
2348
2349 def postincoming(ui, repo, modheads, optupdate, checkout):
2349 def postincoming(ui, repo, modheads, optupdate, checkout):
2350 if modheads == 0:
2350 if modheads == 0:
2351 return
2351 return
2352 if optupdate:
2352 if optupdate:
2353 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2353 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2354 return hg.update(repo, checkout)
2354 return hg.update(repo, checkout)
2355 else:
2355 else:
2356 ui.status(_("not updating, since new heads added\n"))
2356 ui.status(_("not updating, since new heads added\n"))
2357 if modheads > 1:
2357 if modheads > 1:
2358 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2358 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2359 else:
2359 else:
2360 ui.status(_("(run 'hg update' to get a working copy)\n"))
2360 ui.status(_("(run 'hg update' to get a working copy)\n"))
2361
2361
2362 def pull(ui, repo, source="default", **opts):
2362 def pull(ui, repo, source="default", **opts):
2363 """pull changes from the specified source
2363 """pull changes from the specified source
2364
2364
2365 Pull changes from a remote repository to a local one.
2365 Pull changes from a remote repository to a local one.
2366
2366
2367 This finds all changes from the repository at the specified path
2367 This finds all changes from the repository at the specified path
2368 or URL and adds them to a local repository (the current one unless
2368 or URL and adds them to a local repository (the current one unless
2369 -R is specified). By default, this does not update the copy of the
2369 -R is specified). By default, this does not update the copy of the
2370 project in the working directory.
2370 project in the working directory.
2371
2371
2372 Use hg incoming if you want to see what would have been added by a
2372 Use hg incoming if you want to see what would have been added by a
2373 pull at the time you issued this command. If you then decide to
2373 pull at the time you issued this command. If you then decide to
2374 added those changes to the repository, you should use pull -r X
2374 added those changes to the repository, you should use pull -r X
2375 where X is the last changeset listed by hg incoming.
2375 where X is the last changeset listed by hg incoming.
2376
2376
2377 If SOURCE is omitted, the 'default' path will be used.
2377 If SOURCE is omitted, the 'default' path will be used.
2378 See 'hg help urls' for more information.
2378 See 'hg help urls' for more information.
2379 """
2379 """
2380 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2380 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2381 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2381 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2382 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2382 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2383 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2383 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2384 if revs:
2384 if revs:
2385 try:
2385 try:
2386 revs = [other.lookup(rev) for rev in revs]
2386 revs = [other.lookup(rev) for rev in revs]
2387 except error.CapabilityError:
2387 except error.CapabilityError:
2388 err = _("Other repository doesn't support revision lookup, "
2388 err = _("Other repository doesn't support revision lookup, "
2389 "so a rev cannot be specified.")
2389 "so a rev cannot be specified.")
2390 raise util.Abort(err)
2390 raise util.Abort(err)
2391
2391
2392 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2392 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2393 if checkout:
2393 if checkout:
2394 checkout = str(repo.changelog.rev(other.lookup(checkout)))
2394 checkout = str(repo.changelog.rev(other.lookup(checkout)))
2395 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2395 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2396
2396
2397 def push(ui, repo, dest=None, **opts):
2397 def push(ui, repo, dest=None, **opts):
2398 """push changes to the specified destination
2398 """push changes to the specified destination
2399
2399
2400 Push changes from the local repository to the specified destination.
2400 Push changes from the local repository to the specified destination.
2401
2401
2402 This is the symmetrical operation for pull. It moves changes from
2402 This is the symmetrical operation for pull. It moves changes from
2403 the current repository to a different one. If the destination is
2403 the current repository to a different one. If the destination is
2404 local this is identical to a pull in that directory from the
2404 local this is identical to a pull in that directory from the
2405 current one.
2405 current one.
2406
2406
2407 By default, push will refuse to run if it detects the result would
2407 By default, push will refuse to run if it detects the result would
2408 increase the number of remote heads. This generally indicates the
2408 increase the number of remote heads. This generally indicates the
2409 user forgot to pull and merge before pushing.
2409 user forgot to pull and merge before pushing.
2410
2410
2411 If -r/--rev is used, the named revision and all its ancestors will
2411 If -r/--rev is used, the named revision and all its ancestors will
2412 be pushed to the remote repository.
2412 be pushed to the remote repository.
2413
2413
2414 Please see 'hg help urls' for important details about ``ssh://``
2414 Please see 'hg help urls' for important details about ``ssh://``
2415 URLs. If DESTINATION is omitted, a default path will be used.
2415 URLs. If DESTINATION is omitted, a default path will be used.
2416 """
2416 """
2417 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2417 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2418 dest, branches = hg.parseurl(dest, opts.get('branch'))
2418 dest, branches = hg.parseurl(dest, opts.get('branch'))
2419 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2419 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2420 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2420 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2421 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2421 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2422 if revs:
2422 if revs:
2423 revs = [repo.lookup(rev) for rev in revs]
2423 revs = [repo.lookup(rev) for rev in revs]
2424
2424
2425 # push subrepos depth-first for coherent ordering
2425 # push subrepos depth-first for coherent ordering
2426 c = repo['']
2426 c = repo['']
2427 subs = c.substate # only repos that are committed
2427 subs = c.substate # only repos that are committed
2428 for s in sorted(subs):
2428 for s in sorted(subs):
2429 c.sub(s).push(opts.get('force'))
2429 c.sub(s).push(opts.get('force'))
2430
2430
2431 r = repo.push(other, opts.get('force'), revs=revs)
2431 r = repo.push(other, opts.get('force'), revs=revs)
2432 return r == 0
2432 return r == 0
2433
2433
2434 def recover(ui, repo):
2434 def recover(ui, repo):
2435 """roll back an interrupted transaction
2435 """roll back an interrupted transaction
2436
2436
2437 Recover from an interrupted commit or pull.
2437 Recover from an interrupted commit or pull.
2438
2438
2439 This command tries to fix the repository status after an
2439 This command tries to fix the repository status after an
2440 interrupted operation. It should only be necessary when Mercurial
2440 interrupted operation. It should only be necessary when Mercurial
2441 suggests it.
2441 suggests it.
2442 """
2442 """
2443 if repo.recover():
2443 if repo.recover():
2444 return hg.verify(repo)
2444 return hg.verify(repo)
2445 return 1
2445 return 1
2446
2446
2447 def remove(ui, repo, *pats, **opts):
2447 def remove(ui, repo, *pats, **opts):
2448 """remove the specified files on the next commit
2448 """remove the specified files on the next commit
2449
2449
2450 Schedule the indicated files for removal from the repository.
2450 Schedule the indicated files for removal from the repository.
2451
2451
2452 This only removes files from the current branch, not from the
2452 This only removes files from the current branch, not from the
2453 entire project history. -A/--after can be used to remove only
2453 entire project history. -A/--after can be used to remove only
2454 files that have already been deleted, -f/--force can be used to
2454 files that have already been deleted, -f/--force can be used to
2455 force deletion, and -Af can be used to remove files from the next
2455 force deletion, and -Af can be used to remove files from the next
2456 revision without deleting them from the working directory.
2456 revision without deleting them from the working directory.
2457
2457
2458 The following table details the behavior of remove for different
2458 The following table details the behavior of remove for different
2459 file states (columns) and option combinations (rows). The file
2459 file states (columns) and option combinations (rows). The file
2460 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
2460 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
2461 reported by hg status). The actions are Warn, Remove (from branch)
2461 reported by hg status). The actions are Warn, Remove (from branch)
2462 and Delete (from disk)::
2462 and Delete (from disk)::
2463
2463
2464 A C M !
2464 A C M !
2465 none W RD W R
2465 none W RD W R
2466 -f R RD RD R
2466 -f R RD RD R
2467 -A W W W R
2467 -A W W W R
2468 -Af R R R R
2468 -Af R R R R
2469
2469
2470 This command schedules the files to be removed at the next commit.
2470 This command schedules the files to be removed at the next commit.
2471 To undo a remove before that, see hg revert.
2471 To undo a remove before that, see hg revert.
2472 """
2472 """
2473
2473
2474 after, force = opts.get('after'), opts.get('force')
2474 after, force = opts.get('after'), opts.get('force')
2475 if not pats and not after:
2475 if not pats and not after:
2476 raise util.Abort(_('no files specified'))
2476 raise util.Abort(_('no files specified'))
2477
2477
2478 m = cmdutil.match(repo, pats, opts)
2478 m = cmdutil.match(repo, pats, opts)
2479 s = repo.status(match=m, clean=True)
2479 s = repo.status(match=m, clean=True)
2480 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2480 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2481
2481
2482 for f in m.files():
2482 for f in m.files():
2483 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2483 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2484 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2484 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2485
2485
2486 def warn(files, reason):
2486 def warn(files, reason):
2487 for f in files:
2487 for f in files:
2488 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2488 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2489 % (m.rel(f), reason))
2489 % (m.rel(f), reason))
2490
2490
2491 if force:
2491 if force:
2492 remove, forget = modified + deleted + clean, added
2492 remove, forget = modified + deleted + clean, added
2493 elif after:
2493 elif after:
2494 remove, forget = deleted, []
2494 remove, forget = deleted, []
2495 warn(modified + added + clean, _('still exists'))
2495 warn(modified + added + clean, _('still exists'))
2496 else:
2496 else:
2497 remove, forget = deleted + clean, []
2497 remove, forget = deleted + clean, []
2498 warn(modified, _('is modified'))
2498 warn(modified, _('is modified'))
2499 warn(added, _('has been marked for add'))
2499 warn(added, _('has been marked for add'))
2500
2500
2501 for f in sorted(remove + forget):
2501 for f in sorted(remove + forget):
2502 if ui.verbose or not m.exact(f):
2502 if ui.verbose or not m.exact(f):
2503 ui.status(_('removing %s\n') % m.rel(f))
2503 ui.status(_('removing %s\n') % m.rel(f))
2504
2504
2505 repo.forget(forget)
2505 repo.forget(forget)
2506 repo.remove(remove, unlink=not after)
2506 repo.remove(remove, unlink=not after)
2507
2507
2508 def rename(ui, repo, *pats, **opts):
2508 def rename(ui, repo, *pats, **opts):
2509 """rename files; equivalent of copy + remove
2509 """rename files; equivalent of copy + remove
2510
2510
2511 Mark dest as copies of sources; mark sources for deletion. If dest
2511 Mark dest as copies of sources; mark sources for deletion. If dest
2512 is a directory, copies are put in that directory. If dest is a
2512 is a directory, copies are put in that directory. If dest is a
2513 file, there can only be one source.
2513 file, there can only be one source.
2514
2514
2515 By default, this command copies the contents of files as they
2515 By default, this command copies the contents of files as they
2516 exist in the working directory. If invoked with -A/--after, the
2516 exist in the working directory. If invoked with -A/--after, the
2517 operation is recorded, but no copying is performed.
2517 operation is recorded, but no copying is performed.
2518
2518
2519 This command takes effect at the next commit. To undo a rename
2519 This command takes effect at the next commit. To undo a rename
2520 before that, see hg revert.
2520 before that, see hg revert.
2521 """
2521 """
2522 wlock = repo.wlock(False)
2522 wlock = repo.wlock(False)
2523 try:
2523 try:
2524 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2524 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2525 finally:
2525 finally:
2526 wlock.release()
2526 wlock.release()
2527
2527
2528 def resolve(ui, repo, *pats, **opts):
2528 def resolve(ui, repo, *pats, **opts):
2529 """retry file merges from a merge or update
2529 """retry file merges from a merge or update
2530
2530
2531 This command can cleanly retry unresolved file merges using file
2531 This command can cleanly retry unresolved file merges using file
2532 revisions preserved from the last update or merge.
2532 revisions preserved from the last update or merge.
2533
2533
2534 If a conflict is resolved manually, please note that the changes
2534 If a conflict is resolved manually, please note that the changes
2535 will be overwritten if the merge is retried with resolve. The
2535 will be overwritten if the merge is retried with resolve. The
2536 -m/--mark switch should be used to mark the file as resolved.
2536 -m/--mark switch should be used to mark the file as resolved.
2537
2537
2538 You can specify a set of files to operate on, or use the -a/--all
2538 You can specify a set of files to operate on, or use the -a/--all
2539 switch to select all unresolved files.
2539 switch to select all unresolved files.
2540
2540
2541 This command also allows listing resolved files and manually
2541 This command also allows listing resolved files and manually
2542 indicating whether or not files are resolved. All files must be
2542 indicating whether or not files are resolved. All files must be
2543 marked as resolved before a commit is permitted.
2543 marked as resolved before a commit is permitted.
2544
2544
2545 The codes used to show the status of files are::
2545 The codes used to show the status of files are::
2546
2546
2547 U = unresolved
2547 U = unresolved
2548 R = resolved
2548 R = resolved
2549 """
2549 """
2550
2550
2551 all, mark, unmark, show, nostatus = \
2551 all, mark, unmark, show, nostatus = \
2552 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
2552 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
2553
2553
2554 if (show and (mark or unmark)) or (mark and unmark):
2554 if (show and (mark or unmark)) or (mark and unmark):
2555 raise util.Abort(_("too many options specified"))
2555 raise util.Abort(_("too many options specified"))
2556 if pats and all:
2556 if pats and all:
2557 raise util.Abort(_("can't specify --all and patterns"))
2557 raise util.Abort(_("can't specify --all and patterns"))
2558 if not (all or pats or show or mark or unmark):
2558 if not (all or pats or show or mark or unmark):
2559 raise util.Abort(_('no files or directories specified; '
2559 raise util.Abort(_('no files or directories specified; '
2560 'use --all to remerge all files'))
2560 'use --all to remerge all files'))
2561
2561
2562 ms = merge_.mergestate(repo)
2562 ms = merge_.mergestate(repo)
2563 m = cmdutil.match(repo, pats, opts)
2563 m = cmdutil.match(repo, pats, opts)
2564
2564
2565 for f in ms:
2565 for f in ms:
2566 if m(f):
2566 if m(f):
2567 if show:
2567 if show:
2568 if nostatus:
2568 if nostatus:
2569 ui.write("%s\n" % f)
2569 ui.write("%s\n" % f)
2570 else:
2570 else:
2571 ui.write("%s %s\n" % (ms[f].upper(), f))
2571 ui.write("%s %s\n" % (ms[f].upper(), f))
2572 elif mark:
2572 elif mark:
2573 ms.mark(f, "r")
2573 ms.mark(f, "r")
2574 elif unmark:
2574 elif unmark:
2575 ms.mark(f, "u")
2575 ms.mark(f, "u")
2576 else:
2576 else:
2577 wctx = repo[None]
2577 wctx = repo[None]
2578 mctx = wctx.parents()[-1]
2578 mctx = wctx.parents()[-1]
2579
2579
2580 # backup pre-resolve (merge uses .orig for its own purposes)
2580 # backup pre-resolve (merge uses .orig for its own purposes)
2581 a = repo.wjoin(f)
2581 a = repo.wjoin(f)
2582 util.copyfile(a, a + ".resolve")
2582 util.copyfile(a, a + ".resolve")
2583
2583
2584 # resolve file
2584 # resolve file
2585 ms.resolve(f, wctx, mctx)
2585 ms.resolve(f, wctx, mctx)
2586
2586
2587 # replace filemerge's .orig file with our resolve file
2587 # replace filemerge's .orig file with our resolve file
2588 util.rename(a + ".resolve", a + ".orig")
2588 util.rename(a + ".resolve", a + ".orig")
2589
2589
2590 def revert(ui, repo, *pats, **opts):
2590 def revert(ui, repo, *pats, **opts):
2591 """restore individual files or directories to an earlier state
2591 """restore individual files or directories to an earlier state
2592
2592
2593 (Use update -r to check out earlier revisions, revert does not
2593 (Use update -r to check out earlier revisions, revert does not
2594 change the working directory parents.)
2594 change the working directory parents.)
2595
2595
2596 With no revision specified, revert the named files or directories
2596 With no revision specified, revert the named files or directories
2597 to the contents they had in the parent of the working directory.
2597 to the contents they had in the parent of the working directory.
2598 This restores the contents of the affected files to an unmodified
2598 This restores the contents of the affected files to an unmodified
2599 state and unschedules adds, removes, copies, and renames. If the
2599 state and unschedules adds, removes, copies, and renames. If the
2600 working directory has two parents, you must explicitly specify a
2600 working directory has two parents, you must explicitly specify a
2601 revision.
2601 revision.
2602
2602
2603 Using the -r/--rev option, revert the given files or directories
2603 Using the -r/--rev option, revert the given files or directories
2604 to their contents as of a specific revision. This can be helpful
2604 to their contents as of a specific revision. This can be helpful
2605 to "roll back" some or all of an earlier change. See 'hg help
2605 to "roll back" some or all of an earlier change. See 'hg help
2606 dates' for a list of formats valid for -d/--date.
2606 dates' for a list of formats valid for -d/--date.
2607
2607
2608 Revert modifies the working directory. It does not commit any
2608 Revert modifies the working directory. It does not commit any
2609 changes, or change the parent of the working directory. If you
2609 changes, or change the parent of the working directory. If you
2610 revert to a revision other than the parent of the working
2610 revert to a revision other than the parent of the working
2611 directory, the reverted files will thus appear modified
2611 directory, the reverted files will thus appear modified
2612 afterwards.
2612 afterwards.
2613
2613
2614 If a file has been deleted, it is restored. If the executable mode
2614 If a file has been deleted, it is restored. If the executable mode
2615 of a file was changed, it is reset.
2615 of a file was changed, it is reset.
2616
2616
2617 If names are given, all files matching the names are reverted.
2617 If names are given, all files matching the names are reverted.
2618 If no arguments are given, no files are reverted.
2618 If no arguments are given, no files are reverted.
2619
2619
2620 Modified files are saved with a .orig suffix before reverting.
2620 Modified files are saved with a .orig suffix before reverting.
2621 To disable these backups, use --no-backup.
2621 To disable these backups, use --no-backup.
2622 """
2622 """
2623
2623
2624 if opts["date"]:
2624 if opts["date"]:
2625 if opts["rev"]:
2625 if opts["rev"]:
2626 raise util.Abort(_("you can't specify a revision and a date"))
2626 raise util.Abort(_("you can't specify a revision and a date"))
2627 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2627 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2628
2628
2629 if not pats and not opts.get('all'):
2629 if not pats and not opts.get('all'):
2630 raise util.Abort(_('no files or directories specified; '
2630 raise util.Abort(_('no files or directories specified; '
2631 'use --all to revert the whole repo'))
2631 'use --all to revert the whole repo'))
2632
2632
2633 parent, p2 = repo.dirstate.parents()
2633 parent, p2 = repo.dirstate.parents()
2634 if not opts.get('rev') and p2 != nullid:
2634 if not opts.get('rev') and p2 != nullid:
2635 raise util.Abort(_('uncommitted merge - please provide a '
2635 raise util.Abort(_('uncommitted merge - please provide a '
2636 'specific revision'))
2636 'specific revision'))
2637 ctx = repo[opts.get('rev')]
2637 ctx = repo[opts.get('rev')]
2638 node = ctx.node()
2638 node = ctx.node()
2639 mf = ctx.manifest()
2639 mf = ctx.manifest()
2640 if node == parent:
2640 if node == parent:
2641 pmf = mf
2641 pmf = mf
2642 else:
2642 else:
2643 pmf = None
2643 pmf = None
2644
2644
2645 # need all matching names in dirstate and manifest of target rev,
2645 # need all matching names in dirstate and manifest of target rev,
2646 # so have to walk both. do not print errors if files exist in one
2646 # so have to walk both. do not print errors if files exist in one
2647 # but not other.
2647 # but not other.
2648
2648
2649 names = {}
2649 names = {}
2650
2650
2651 wlock = repo.wlock()
2651 wlock = repo.wlock()
2652 try:
2652 try:
2653 # walk dirstate.
2653 # walk dirstate.
2654
2654
2655 m = cmdutil.match(repo, pats, opts)
2655 m = cmdutil.match(repo, pats, opts)
2656 m.bad = lambda x, y: False
2656 m.bad = lambda x, y: False
2657 for abs in repo.walk(m):
2657 for abs in repo.walk(m):
2658 names[abs] = m.rel(abs), m.exact(abs)
2658 names[abs] = m.rel(abs), m.exact(abs)
2659
2659
2660 # walk target manifest.
2660 # walk target manifest.
2661
2661
2662 def badfn(path, msg):
2662 def badfn(path, msg):
2663 if path in names:
2663 if path in names:
2664 return
2664 return
2665 path_ = path + '/'
2665 path_ = path + '/'
2666 for f in names:
2666 for f in names:
2667 if f.startswith(path_):
2667 if f.startswith(path_):
2668 return
2668 return
2669 ui.warn("%s: %s\n" % (m.rel(path), msg))
2669 ui.warn("%s: %s\n" % (m.rel(path), msg))
2670
2670
2671 m = cmdutil.match(repo, pats, opts)
2671 m = cmdutil.match(repo, pats, opts)
2672 m.bad = badfn
2672 m.bad = badfn
2673 for abs in repo[node].walk(m):
2673 for abs in repo[node].walk(m):
2674 if abs not in names:
2674 if abs not in names:
2675 names[abs] = m.rel(abs), m.exact(abs)
2675 names[abs] = m.rel(abs), m.exact(abs)
2676
2676
2677 m = cmdutil.matchfiles(repo, names)
2677 m = cmdutil.matchfiles(repo, names)
2678 changes = repo.status(match=m)[:4]
2678 changes = repo.status(match=m)[:4]
2679 modified, added, removed, deleted = map(set, changes)
2679 modified, added, removed, deleted = map(set, changes)
2680
2680
2681 # if f is a rename, also revert the source
2681 # if f is a rename, also revert the source
2682 cwd = repo.getcwd()
2682 cwd = repo.getcwd()
2683 for f in added:
2683 for f in added:
2684 src = repo.dirstate.copied(f)
2684 src = repo.dirstate.copied(f)
2685 if src and src not in names and repo.dirstate[src] == 'r':
2685 if src and src not in names and repo.dirstate[src] == 'r':
2686 removed.add(src)
2686 removed.add(src)
2687 names[src] = (repo.pathto(src, cwd), True)
2687 names[src] = (repo.pathto(src, cwd), True)
2688
2688
2689 def removeforget(abs):
2689 def removeforget(abs):
2690 if repo.dirstate[abs] == 'a':
2690 if repo.dirstate[abs] == 'a':
2691 return _('forgetting %s\n')
2691 return _('forgetting %s\n')
2692 return _('removing %s\n')
2692 return _('removing %s\n')
2693
2693
2694 revert = ([], _('reverting %s\n'))
2694 revert = ([], _('reverting %s\n'))
2695 add = ([], _('adding %s\n'))
2695 add = ([], _('adding %s\n'))
2696 remove = ([], removeforget)
2696 remove = ([], removeforget)
2697 undelete = ([], _('undeleting %s\n'))
2697 undelete = ([], _('undeleting %s\n'))
2698
2698
2699 disptable = (
2699 disptable = (
2700 # dispatch table:
2700 # dispatch table:
2701 # file state
2701 # file state
2702 # action if in target manifest
2702 # action if in target manifest
2703 # action if not in target manifest
2703 # action if not in target manifest
2704 # make backup if in target manifest
2704 # make backup if in target manifest
2705 # make backup if not in target manifest
2705 # make backup if not in target manifest
2706 (modified, revert, remove, True, True),
2706 (modified, revert, remove, True, True),
2707 (added, revert, remove, True, False),
2707 (added, revert, remove, True, False),
2708 (removed, undelete, None, False, False),
2708 (removed, undelete, None, False, False),
2709 (deleted, revert, remove, False, False),
2709 (deleted, revert, remove, False, False),
2710 )
2710 )
2711
2711
2712 for abs, (rel, exact) in sorted(names.items()):
2712 for abs, (rel, exact) in sorted(names.items()):
2713 mfentry = mf.get(abs)
2713 mfentry = mf.get(abs)
2714 target = repo.wjoin(abs)
2714 target = repo.wjoin(abs)
2715 def handle(xlist, dobackup):
2715 def handle(xlist, dobackup):
2716 xlist[0].append(abs)
2716 xlist[0].append(abs)
2717 if dobackup and not opts.get('no_backup') and util.lexists(target):
2717 if dobackup and not opts.get('no_backup') and util.lexists(target):
2718 bakname = "%s.orig" % rel
2718 bakname = "%s.orig" % rel
2719 ui.note(_('saving current version of %s as %s\n') %
2719 ui.note(_('saving current version of %s as %s\n') %
2720 (rel, bakname))
2720 (rel, bakname))
2721 if not opts.get('dry_run'):
2721 if not opts.get('dry_run'):
2722 util.copyfile(target, bakname)
2722 util.copyfile(target, bakname)
2723 if ui.verbose or not exact:
2723 if ui.verbose or not exact:
2724 msg = xlist[1]
2724 msg = xlist[1]
2725 if not isinstance(msg, basestring):
2725 if not isinstance(msg, basestring):
2726 msg = msg(abs)
2726 msg = msg(abs)
2727 ui.status(msg % rel)
2727 ui.status(msg % rel)
2728 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2728 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2729 if abs not in table:
2729 if abs not in table:
2730 continue
2730 continue
2731 # file has changed in dirstate
2731 # file has changed in dirstate
2732 if mfentry:
2732 if mfentry:
2733 handle(hitlist, backuphit)
2733 handle(hitlist, backuphit)
2734 elif misslist is not None:
2734 elif misslist is not None:
2735 handle(misslist, backupmiss)
2735 handle(misslist, backupmiss)
2736 break
2736 break
2737 else:
2737 else:
2738 if abs not in repo.dirstate:
2738 if abs not in repo.dirstate:
2739 if mfentry:
2739 if mfentry:
2740 handle(add, True)
2740 handle(add, True)
2741 elif exact:
2741 elif exact:
2742 ui.warn(_('file not managed: %s\n') % rel)
2742 ui.warn(_('file not managed: %s\n') % rel)
2743 continue
2743 continue
2744 # file has not changed in dirstate
2744 # file has not changed in dirstate
2745 if node == parent:
2745 if node == parent:
2746 if exact:
2746 if exact:
2747 ui.warn(_('no changes needed to %s\n') % rel)
2747 ui.warn(_('no changes needed to %s\n') % rel)
2748 continue
2748 continue
2749 if pmf is None:
2749 if pmf is None:
2750 # only need parent manifest in this unlikely case,
2750 # only need parent manifest in this unlikely case,
2751 # so do not read by default
2751 # so do not read by default
2752 pmf = repo[parent].manifest()
2752 pmf = repo[parent].manifest()
2753 if abs in pmf:
2753 if abs in pmf:
2754 if mfentry:
2754 if mfentry:
2755 # if version of file is same in parent and target
2755 # if version of file is same in parent and target
2756 # manifests, do nothing
2756 # manifests, do nothing
2757 if (pmf[abs] != mfentry or
2757 if (pmf[abs] != mfentry or
2758 pmf.flags(abs) != mf.flags(abs)):
2758 pmf.flags(abs) != mf.flags(abs)):
2759 handle(revert, False)
2759 handle(revert, False)
2760 else:
2760 else:
2761 handle(remove, False)
2761 handle(remove, False)
2762
2762
2763 if not opts.get('dry_run'):
2763 if not opts.get('dry_run'):
2764 def checkout(f):
2764 def checkout(f):
2765 fc = ctx[f]
2765 fc = ctx[f]
2766 repo.wwrite(f, fc.data(), fc.flags())
2766 repo.wwrite(f, fc.data(), fc.flags())
2767
2767
2768 audit_path = util.path_auditor(repo.root)
2768 audit_path = util.path_auditor(repo.root)
2769 for f in remove[0]:
2769 for f in remove[0]:
2770 if repo.dirstate[f] == 'a':
2770 if repo.dirstate[f] == 'a':
2771 repo.dirstate.forget(f)
2771 repo.dirstate.forget(f)
2772 continue
2772 continue
2773 audit_path(f)
2773 audit_path(f)
2774 try:
2774 try:
2775 util.unlink(repo.wjoin(f))
2775 util.unlink(repo.wjoin(f))
2776 except OSError:
2776 except OSError:
2777 pass
2777 pass
2778 repo.dirstate.remove(f)
2778 repo.dirstate.remove(f)
2779
2779
2780 normal = None
2780 normal = None
2781 if node == parent:
2781 if node == parent:
2782 # We're reverting to our parent. If possible, we'd like status
2782 # We're reverting to our parent. If possible, we'd like status
2783 # to report the file as clean. We have to use normallookup for
2783 # to report the file as clean. We have to use normallookup for
2784 # merges to avoid losing information about merged/dirty files.
2784 # merges to avoid losing information about merged/dirty files.
2785 if p2 != nullid:
2785 if p2 != nullid:
2786 normal = repo.dirstate.normallookup
2786 normal = repo.dirstate.normallookup
2787 else:
2787 else:
2788 normal = repo.dirstate.normal
2788 normal = repo.dirstate.normal
2789 for f in revert[0]:
2789 for f in revert[0]:
2790 checkout(f)
2790 checkout(f)
2791 if normal:
2791 if normal:
2792 normal(f)
2792 normal(f)
2793
2793
2794 for f in add[0]:
2794 for f in add[0]:
2795 checkout(f)
2795 checkout(f)
2796 repo.dirstate.add(f)
2796 repo.dirstate.add(f)
2797
2797
2798 normal = repo.dirstate.normallookup
2798 normal = repo.dirstate.normallookup
2799 if node == parent and p2 == nullid:
2799 if node == parent and p2 == nullid:
2800 normal = repo.dirstate.normal
2800 normal = repo.dirstate.normal
2801 for f in undelete[0]:
2801 for f in undelete[0]:
2802 checkout(f)
2802 checkout(f)
2803 normal(f)
2803 normal(f)
2804
2804
2805 finally:
2805 finally:
2806 wlock.release()
2806 wlock.release()
2807
2807
2808 def rollback(ui, repo):
2808 def rollback(ui, repo):
2809 """roll back the last transaction
2809 """roll back the last transaction
2810
2810
2811 This command should be used with care. There is only one level of
2811 This command should be used with care. There is only one level of
2812 rollback, and there is no way to undo a rollback. It will also
2812 rollback, and there is no way to undo a rollback. It will also
2813 restore the dirstate at the time of the last transaction, losing
2813 restore the dirstate at the time of the last transaction, losing
2814 any dirstate changes since that time. This command does not alter
2814 any dirstate changes since that time. This command does not alter
2815 the working directory.
2815 the working directory.
2816
2816
2817 Transactions are used to encapsulate the effects of all commands
2817 Transactions are used to encapsulate the effects of all commands
2818 that create new changesets or propagate existing changesets into a
2818 that create new changesets or propagate existing changesets into a
2819 repository. For example, the following commands are transactional,
2819 repository. For example, the following commands are transactional,
2820 and their effects can be rolled back:
2820 and their effects can be rolled back:
2821
2821
2822 - commit
2822 - commit
2823 - import
2823 - import
2824 - pull
2824 - pull
2825 - push (with this repository as the destination)
2825 - push (with this repository as the destination)
2826 - unbundle
2826 - unbundle
2827
2827
2828 This command is not intended for use on public repositories. Once
2828 This command is not intended for use on public repositories. Once
2829 changes are visible for pull by other users, rolling a transaction
2829 changes are visible for pull by other users, rolling a transaction
2830 back locally is ineffective (someone else may already have pulled
2830 back locally is ineffective (someone else may already have pulled
2831 the changes). Furthermore, a race is possible with readers of the
2831 the changes). Furthermore, a race is possible with readers of the
2832 repository; for example an in-progress pull from the repository
2832 repository; for example an in-progress pull from the repository
2833 may fail if a rollback is performed.
2833 may fail if a rollback is performed.
2834 """
2834 """
2835 repo.rollback()
2835 repo.rollback()
2836
2836
2837 def root(ui, repo):
2837 def root(ui, repo):
2838 """print the root (top) of the current working directory
2838 """print the root (top) of the current working directory
2839
2839
2840 Print the root directory of the current repository.
2840 Print the root directory of the current repository.
2841 """
2841 """
2842 ui.write(repo.root + "\n")
2842 ui.write(repo.root + "\n")
2843
2843
2844 def serve(ui, repo, **opts):
2844 def serve(ui, repo, **opts):
2845 """export the repository via HTTP
2845 """export the repository via HTTP
2846
2846
2847 Start a local HTTP repository browser and pull server.
2847 Start a local HTTP repository browser and pull server.
2848
2848
2849 By default, the server logs accesses to stdout and errors to
2849 By default, the server logs accesses to stdout and errors to
2850 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
2850 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
2851 files.
2851 files.
2852 """
2852 """
2853
2853
2854 if opts["stdio"]:
2854 if opts["stdio"]:
2855 if repo is None:
2855 if repo is None:
2856 raise error.RepoError(_("There is no Mercurial repository here"
2856 raise error.RepoError(_("There is no Mercurial repository here"
2857 " (.hg not found)"))
2857 " (.hg not found)"))
2858 s = sshserver.sshserver(ui, repo)
2858 s = sshserver.sshserver(ui, repo)
2859 s.serve_forever()
2859 s.serve_forever()
2860
2860
2861 baseui = repo and repo.baseui or ui
2861 baseui = repo and repo.baseui or ui
2862 optlist = ("name templates style address port prefix ipv6"
2862 optlist = ("name templates style address port prefix ipv6"
2863 " accesslog errorlog webdir_conf certificate encoding")
2863 " accesslog errorlog webdir_conf certificate encoding")
2864 for o in optlist.split():
2864 for o in optlist.split():
2865 if opts.get(o, None):
2865 if opts.get(o, None):
2866 baseui.setconfig("web", o, str(opts[o]))
2866 baseui.setconfig("web", o, str(opts[o]))
2867 if (repo is not None) and (repo.ui != baseui):
2867 if (repo is not None) and (repo.ui != baseui):
2868 repo.ui.setconfig("web", o, str(opts[o]))
2868 repo.ui.setconfig("web", o, str(opts[o]))
2869
2869
2870 if repo is None and not ui.config("web", "webdir_conf"):
2870 if repo is None and not ui.config("web", "webdir_conf"):
2871 raise error.RepoError(_("There is no Mercurial repository here"
2871 raise error.RepoError(_("There is no Mercurial repository here"
2872 " (.hg not found)"))
2872 " (.hg not found)"))
2873
2873
2874 class service(object):
2874 class service(object):
2875 def init(self):
2875 def init(self):
2876 util.set_signal_handler()
2876 util.set_signal_handler()
2877 self.httpd = server.create_server(baseui, repo)
2877 self.httpd = server.create_server(baseui, repo)
2878
2878
2879 if not ui.verbose:
2879 if not ui.verbose:
2880 return
2880 return
2881
2881
2882 if self.httpd.prefix:
2882 if self.httpd.prefix:
2883 prefix = self.httpd.prefix.strip('/') + '/'
2883 prefix = self.httpd.prefix.strip('/') + '/'
2884 else:
2884 else:
2885 prefix = ''
2885 prefix = ''
2886
2886
2887 port = ':%d' % self.httpd.port
2887 port = ':%d' % self.httpd.port
2888 if port == ':80':
2888 if port == ':80':
2889 port = ''
2889 port = ''
2890
2890
2891 bindaddr = self.httpd.addr
2891 bindaddr = self.httpd.addr
2892 if bindaddr == '0.0.0.0':
2892 if bindaddr == '0.0.0.0':
2893 bindaddr = '*'
2893 bindaddr = '*'
2894 elif ':' in bindaddr: # IPv6
2894 elif ':' in bindaddr: # IPv6
2895 bindaddr = '[%s]' % bindaddr
2895 bindaddr = '[%s]' % bindaddr
2896
2896
2897 fqaddr = self.httpd.fqaddr
2897 fqaddr = self.httpd.fqaddr
2898 if ':' in fqaddr:
2898 if ':' in fqaddr:
2899 fqaddr = '[%s]' % fqaddr
2899 fqaddr = '[%s]' % fqaddr
2900 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2900 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2901 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2901 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2902
2902
2903 def run(self):
2903 def run(self):
2904 self.httpd.serve_forever()
2904 self.httpd.serve_forever()
2905
2905
2906 service = service()
2906 service = service()
2907
2907
2908 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2908 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2909
2909
2910 def status(ui, repo, *pats, **opts):
2910 def status(ui, repo, *pats, **opts):
2911 """show changed files in the working directory
2911 """show changed files in the working directory
2912
2912
2913 Show status of files in the repository. If names are given, only
2913 Show status of files in the repository. If names are given, only
2914 files that match are shown. Files that are clean or ignored or
2914 files that match are shown. Files that are clean or ignored or
2915 the source of a copy/move operation, are not listed unless
2915 the source of a copy/move operation, are not listed unless
2916 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
2916 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
2917 Unless options described with "show only ..." are given, the
2917 Unless options described with "show only ..." are given, the
2918 options -mardu are used.
2918 options -mardu are used.
2919
2919
2920 Option -q/--quiet hides untracked (unknown and ignored) files
2920 Option -q/--quiet hides untracked (unknown and ignored) files
2921 unless explicitly requested with -u/--unknown or -i/--ignored.
2921 unless explicitly requested with -u/--unknown or -i/--ignored.
2922
2922
2923 NOTE: status may appear to disagree with diff if permissions have
2923 NOTE: status may appear to disagree with diff if permissions have
2924 changed or a merge has occurred. The standard diff format does not
2924 changed or a merge has occurred. The standard diff format does not
2925 report permission changes and diff only reports changes relative
2925 report permission changes and diff only reports changes relative
2926 to one merge parent.
2926 to one merge parent.
2927
2927
2928 If one revision is given, it is used as the base revision.
2928 If one revision is given, it is used as the base revision.
2929 If two revisions are given, the differences between them are
2929 If two revisions are given, the differences between them are
2930 shown. The --change option can also be used as a shortcut to list
2930 shown. The --change option can also be used as a shortcut to list
2931 the changed files of a revision from its first parent.
2931 the changed files of a revision from its first parent.
2932
2932
2933 The codes used to show the status of files are::
2933 The codes used to show the status of files are::
2934
2934
2935 M = modified
2935 M = modified
2936 A = added
2936 A = added
2937 R = removed
2937 R = removed
2938 C = clean
2938 C = clean
2939 ! = missing (deleted by non-hg command, but still tracked)
2939 ! = missing (deleted by non-hg command, but still tracked)
2940 ? = not tracked
2940 ? = not tracked
2941 I = ignored
2941 I = ignored
2942 = origin of the previous file listed as A (added)
2942 = origin of the previous file listed as A (added)
2943 """
2943 """
2944
2944
2945 revs = opts.get('rev')
2945 revs = opts.get('rev')
2946 change = opts.get('change')
2946 change = opts.get('change')
2947
2947
2948 if revs and change:
2948 if revs and change:
2949 msg = _('cannot specify --rev and --change at the same time')
2949 msg = _('cannot specify --rev and --change at the same time')
2950 raise util.Abort(msg)
2950 raise util.Abort(msg)
2951 elif change:
2951 elif change:
2952 node2 = repo.lookup(change)
2952 node2 = repo.lookup(change)
2953 node1 = repo[node2].parents()[0].node()
2953 node1 = repo[node2].parents()[0].node()
2954 else:
2954 else:
2955 node1, node2 = cmdutil.revpair(repo, revs)
2955 node1, node2 = cmdutil.revpair(repo, revs)
2956
2956
2957 cwd = (pats and repo.getcwd()) or ''
2957 cwd = (pats and repo.getcwd()) or ''
2958 end = opts.get('print0') and '\0' or '\n'
2958 end = opts.get('print0') and '\0' or '\n'
2959 copy = {}
2959 copy = {}
2960 states = 'modified added removed deleted unknown ignored clean'.split()
2960 states = 'modified added removed deleted unknown ignored clean'.split()
2961 show = [k for k in states if opts.get(k)]
2961 show = [k for k in states if opts.get(k)]
2962 if opts.get('all'):
2962 if opts.get('all'):
2963 show += ui.quiet and (states[:4] + ['clean']) or states
2963 show += ui.quiet and (states[:4] + ['clean']) or states
2964 if not show:
2964 if not show:
2965 show = ui.quiet and states[:4] or states[:5]
2965 show = ui.quiet and states[:4] or states[:5]
2966
2966
2967 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
2967 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
2968 'ignored' in show, 'clean' in show, 'unknown' in show)
2968 'ignored' in show, 'clean' in show, 'unknown' in show)
2969 changestates = zip(states, 'MAR!?IC', stat)
2969 changestates = zip(states, 'MAR!?IC', stat)
2970
2970
2971 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
2971 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
2972 ctxn = repo[nullid]
2972 ctxn = repo[nullid]
2973 ctx1 = repo[node1]
2973 ctx1 = repo[node1]
2974 ctx2 = repo[node2]
2974 ctx2 = repo[node2]
2975 added = stat[1]
2975 added = stat[1]
2976 if node2 is None:
2976 if node2 is None:
2977 added = stat[0] + stat[1] # merged?
2977 added = stat[0] + stat[1] # merged?
2978
2978
2979 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
2979 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
2980 if k in added:
2980 if k in added:
2981 copy[k] = v
2981 copy[k] = v
2982 elif v in added:
2982 elif v in added:
2983 copy[v] = k
2983 copy[v] = k
2984
2984
2985 for state, char, files in changestates:
2985 for state, char, files in changestates:
2986 if state in show:
2986 if state in show:
2987 format = "%s %%s%s" % (char, end)
2987 format = "%s %%s%s" % (char, end)
2988 if opts.get('no_status'):
2988 if opts.get('no_status'):
2989 format = "%%s%s" % end
2989 format = "%%s%s" % end
2990
2990
2991 for f in files:
2991 for f in files:
2992 ui.write(format % repo.pathto(f, cwd))
2992 ui.write(format % repo.pathto(f, cwd))
2993 if f in copy:
2993 if f in copy:
2994 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2994 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2995
2995
2996 def summary(ui, repo, **opts):
2996 def summary(ui, repo, **opts):
2997 """summarize working directory state
2997 """summarize working directory state
2998
2998
2999 This generates a brief summary of the working directory state,
2999 This generates a brief summary of the working directory state,
3000 including parents, branch, commit status, and available updates.
3000 including parents, branch, commit status, and available updates.
3001
3001
3002 With the --remote option, this will check the default paths for
3002 With the --remote option, this will check the default paths for
3003 incoming and outgoing changes. This can be time-consuming.
3003 incoming and outgoing changes. This can be time-consuming.
3004 """
3004 """
3005
3005
3006 ctx = repo[None]
3006 ctx = repo[None]
3007 parents = ctx.parents()
3007 parents = ctx.parents()
3008 pnode = parents[0].node()
3008 pnode = parents[0].node()
3009 tags = repo.tags()
3009 tags = repo.tags()
3010
3010
3011 for p in parents:
3011 for p in parents:
3012 t = ' '.join([t for t in tags if tags[t] == p.node()])
3012 t = ' '.join([t for t in tags if tags[t] == p.node()])
3013 if p.rev() == -1:
3013 if p.rev() == -1:
3014 if not len(repo):
3014 if not len(repo):
3015 t += _(' (empty repository)')
3015 t += _(' (empty repository)')
3016 else:
3016 else:
3017 t += _(' (no revision checked out)')
3017 t += _(' (no revision checked out)')
3018 ui.write(_('parent: %d:%s %s\n') % (p.rev(), str(p), t))
3018 ui.write(_('parent: %d:%s %s\n') % (p.rev(), str(p), t))
3019 if p.description():
3019 if p.description():
3020 ui.status(' ' + p.description().splitlines()[0].strip() + '\n')
3020 ui.status(' ' + p.description().splitlines()[0].strip() + '\n')
3021
3021
3022 branch = ctx.branch()
3022 branch = ctx.branch()
3023 bheads = repo.branchheads(branch)
3023 bheads = repo.branchheads(branch)
3024 m = _('branch: %s\n') % branch
3024 m = _('branch: %s\n') % branch
3025 if branch != 'default':
3025 if branch != 'default':
3026 ui.write(m)
3026 ui.write(m)
3027 else:
3027 else:
3028 ui.status(m)
3028 ui.status(m)
3029
3029
3030 st = list(repo.status(unknown=True))[:6]
3030 st = list(repo.status(unknown=True))[:6]
3031 ms = merge_.mergestate(repo)
3031 ms = merge_.mergestate(repo)
3032 st.append([f for f in ms if ms[f] == 'u'])
3032 st.append([f for f in ms if ms[f] == 'u'])
3033 labels = [_('%d modified'), _('%d added'), _('%d removed'),
3033 labels = [_('%d modified'), _('%d added'), _('%d removed'),
3034 _('%d deleted'), _('%d unknown'), _('%d ignored'),
3034 _('%d deleted'), _('%d unknown'), _('%d ignored'),
3035 _('%d unresolved')]
3035 _('%d unresolved')]
3036 t = []
3036 t = []
3037 for s, l in zip(st, labels):
3037 for s, l in zip(st, labels):
3038 if s:
3038 if s:
3039 t.append(l % len(s))
3039 t.append(l % len(s))
3040
3040
3041 t = ', '.join(t)
3041 t = ', '.join(t)
3042 cleanworkdir = False
3042 cleanworkdir = False
3043
3043
3044 if len(parents) > 1:
3044 if len(parents) > 1:
3045 t += _(' (merge)')
3045 t += _(' (merge)')
3046 elif branch != parents[0].branch():
3046 elif branch != parents[0].branch():
3047 t += _(' (new branch)')
3047 t += _(' (new branch)')
3048 elif (not st[0] and not st[1] and not st[2]):
3048 elif (not st[0] and not st[1] and not st[2]):
3049 t += _(' (clean)')
3049 t += _(' (clean)')
3050 cleanworkdir = True
3050 cleanworkdir = True
3051 elif pnode not in bheads:
3051 elif pnode not in bheads:
3052 t += _(' (new branch head)')
3052 t += _(' (new branch head)')
3053
3053
3054 if cleanworkdir:
3054 if cleanworkdir:
3055 ui.status(_('commit: %s\n') % t.strip())
3055 ui.status(_('commit: %s\n') % t.strip())
3056 else:
3056 else:
3057 ui.write(_('commit: %s\n') % t.strip())
3057 ui.write(_('commit: %s\n') % t.strip())
3058
3058
3059 # all ancestors of branch heads - all ancestors of parent = new csets
3059 # all ancestors of branch heads - all ancestors of parent = new csets
3060 new = [0] * len(repo)
3060 new = [0] * len(repo)
3061 cl = repo.changelog
3061 cl = repo.changelog
3062 for a in [cl.rev(n) for n in bheads]:
3062 for a in [cl.rev(n) for n in bheads]:
3063 new[a] = 1
3063 new[a] = 1
3064 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
3064 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
3065 new[a] = 1
3065 new[a] = 1
3066 for a in [p.rev() for p in parents]:
3066 for a in [p.rev() for p in parents]:
3067 if a >= 0:
3067 if a >= 0:
3068 new[a] = 0
3068 new[a] = 0
3069 for a in cl.ancestors(*[p.rev() for p in parents]):
3069 for a in cl.ancestors(*[p.rev() for p in parents]):
3070 new[a] = 0
3070 new[a] = 0
3071 new = sum(new)
3071 new = sum(new)
3072
3072
3073 if new == 0:
3073 if new == 0:
3074 ui.status(_('update: (current)\n'))
3074 ui.status(_('update: (current)\n'))
3075 elif pnode not in bheads:
3075 elif pnode not in bheads:
3076 ui.write(_('update: %d new changesets (update)\n') % new)
3076 ui.write(_('update: %d new changesets (update)\n') % new)
3077 else:
3077 else:
3078 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
3078 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
3079 (new, len(bheads)))
3079 (new, len(bheads)))
3080
3080
3081 if opts.get('remote'):
3081 if opts.get('remote'):
3082 t = []
3082 t = []
3083 source, branches = hg.parseurl(ui.expandpath('default'))
3083 source, branches = hg.parseurl(ui.expandpath('default'))
3084 other = hg.repository(cmdutil.remoteui(repo, {}), source)
3084 other = hg.repository(cmdutil.remoteui(repo, {}), source)
3085 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3085 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3086 ui.debug('comparing with %s\n' % url.hidepassword(source))
3086 ui.debug('comparing with %s\n' % url.hidepassword(source))
3087 repo.ui.pushbuffer()
3087 repo.ui.pushbuffer()
3088 common, incoming, rheads = repo.findcommonincoming(other)
3088 common, incoming, rheads = repo.findcommonincoming(other)
3089 repo.ui.popbuffer()
3089 repo.ui.popbuffer()
3090 if incoming:
3090 if incoming:
3091 t.append(_('1 or more incoming'))
3091 t.append(_('1 or more incoming'))
3092
3092
3093 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
3093 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
3094 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3094 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3095 other = hg.repository(cmdutil.remoteui(repo, {}), dest)
3095 other = hg.repository(cmdutil.remoteui(repo, {}), dest)
3096 ui.debug('comparing with %s\n' % url.hidepassword(dest))
3096 ui.debug('comparing with %s\n' % url.hidepassword(dest))
3097 repo.ui.pushbuffer()
3097 repo.ui.pushbuffer()
3098 o = repo.findoutgoing(other)
3098 o = repo.findoutgoing(other)
3099 repo.ui.popbuffer()
3099 repo.ui.popbuffer()
3100 o = repo.changelog.nodesbetween(o, None)[0]
3100 o = repo.changelog.nodesbetween(o, None)[0]
3101 if o:
3101 if o:
3102 t.append(_('%d outgoing') % len(o))
3102 t.append(_('%d outgoing') % len(o))
3103
3103
3104 if t:
3104 if t:
3105 ui.write(_('remote: %s\n') % (', '.join(t)))
3105 ui.write(_('remote: %s\n') % (', '.join(t)))
3106 else:
3106 else:
3107 ui.status(_('remote: (synced)\n'))
3107 ui.status(_('remote: (synced)\n'))
3108
3108
3109 def tag(ui, repo, name1, *names, **opts):
3109 def tag(ui, repo, name1, *names, **opts):
3110 """add one or more tags for the current or given revision
3110 """add one or more tags for the current or given revision
3111
3111
3112 Name a particular revision using <name>.
3112 Name a particular revision using <name>.
3113
3113
3114 Tags are used to name particular revisions of the repository and are
3114 Tags are used to name particular revisions of the repository and are
3115 very useful to compare different revisions, to go back to significant
3115 very useful to compare different revisions, to go back to significant
3116 earlier versions or to mark branch points as releases, etc.
3116 earlier versions or to mark branch points as releases, etc.
3117
3117
3118 If no revision is given, the parent of the working directory is
3118 If no revision is given, the parent of the working directory is
3119 used, or tip if no revision is checked out.
3119 used, or tip if no revision is checked out.
3120
3120
3121 To facilitate version control, distribution, and merging of tags,
3121 To facilitate version control, distribution, and merging of tags,
3122 they are stored as a file named ".hgtags" which is managed
3122 they are stored as a file named ".hgtags" which is managed
3123 similarly to other project files and can be hand-edited if
3123 similarly to other project files and can be hand-edited if
3124 necessary. The file '.hg/localtags' is used for local tags (not
3124 necessary. The file '.hg/localtags' is used for local tags (not
3125 shared among repositories).
3125 shared among repositories).
3126
3126
3127 See 'hg help dates' for a list of formats valid for -d/--date.
3127 See 'hg help dates' for a list of formats valid for -d/--date.
3128 """
3128 """
3129
3129
3130 rev_ = "."
3130 rev_ = "."
3131 names = (name1,) + names
3131 names = (name1,) + names
3132 if len(names) != len(set(names)):
3132 if len(names) != len(set(names)):
3133 raise util.Abort(_('tag names must be unique'))
3133 raise util.Abort(_('tag names must be unique'))
3134 for n in names:
3134 for n in names:
3135 if n in ['tip', '.', 'null']:
3135 if n in ['tip', '.', 'null']:
3136 raise util.Abort(_('the name \'%s\' is reserved') % n)
3136 raise util.Abort(_('the name \'%s\' is reserved') % n)
3137 if opts.get('rev') and opts.get('remove'):
3137 if opts.get('rev') and opts.get('remove'):
3138 raise util.Abort(_("--rev and --remove are incompatible"))
3138 raise util.Abort(_("--rev and --remove are incompatible"))
3139 if opts.get('rev'):
3139 if opts.get('rev'):
3140 rev_ = opts['rev']
3140 rev_ = opts['rev']
3141 message = opts.get('message')
3141 message = opts.get('message')
3142 if opts.get('remove'):
3142 if opts.get('remove'):
3143 expectedtype = opts.get('local') and 'local' or 'global'
3143 expectedtype = opts.get('local') and 'local' or 'global'
3144 for n in names:
3144 for n in names:
3145 if not repo.tagtype(n):
3145 if not repo.tagtype(n):
3146 raise util.Abort(_('tag \'%s\' does not exist') % n)
3146 raise util.Abort(_('tag \'%s\' does not exist') % n)
3147 if repo.tagtype(n) != expectedtype:
3147 if repo.tagtype(n) != expectedtype:
3148 if expectedtype == 'global':
3148 if expectedtype == 'global':
3149 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
3149 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
3150 else:
3150 else:
3151 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
3151 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
3152 rev_ = nullid
3152 rev_ = nullid
3153 if not message:
3153 if not message:
3154 # we don't translate commit messages
3154 # we don't translate commit messages
3155 message = 'Removed tag %s' % ', '.join(names)
3155 message = 'Removed tag %s' % ', '.join(names)
3156 elif not opts.get('force'):
3156 elif not opts.get('force'):
3157 for n in names:
3157 for n in names:
3158 if n in repo.tags():
3158 if n in repo.tags():
3159 raise util.Abort(_('tag \'%s\' already exists '
3159 raise util.Abort(_('tag \'%s\' already exists '
3160 '(use -f to force)') % n)
3160 '(use -f to force)') % n)
3161 if not rev_ and repo.dirstate.parents()[1] != nullid:
3161 if not rev_ and repo.dirstate.parents()[1] != nullid:
3162 raise util.Abort(_('uncommitted merge - please provide a '
3162 raise util.Abort(_('uncommitted merge - please provide a '
3163 'specific revision'))
3163 'specific revision'))
3164 r = repo[rev_].node()
3164 r = repo[rev_].node()
3165
3165
3166 if not message:
3166 if not message:
3167 # we don't translate commit messages
3167 # we don't translate commit messages
3168 message = ('Added tag %s for changeset %s' %
3168 message = ('Added tag %s for changeset %s' %
3169 (', '.join(names), short(r)))
3169 (', '.join(names), short(r)))
3170
3170
3171 date = opts.get('date')
3171 date = opts.get('date')
3172 if date:
3172 if date:
3173 date = util.parsedate(date)
3173 date = util.parsedate(date)
3174
3174
3175 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
3175 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
3176
3176
3177 def tags(ui, repo):
3177 def tags(ui, repo):
3178 """list repository tags
3178 """list repository tags
3179
3179
3180 This lists both regular and local tags. When the -v/--verbose
3180 This lists both regular and local tags. When the -v/--verbose
3181 switch is used, a third column "local" is printed for local tags.
3181 switch is used, a third column "local" is printed for local tags.
3182 """
3182 """
3183
3183
3184 hexfunc = ui.debugflag and hex or short
3184 hexfunc = ui.debugflag and hex or short
3185 tagtype = ""
3185 tagtype = ""
3186
3186
3187 for t, n in reversed(repo.tagslist()):
3187 for t, n in reversed(repo.tagslist()):
3188 if ui.quiet:
3188 if ui.quiet:
3189 ui.write("%s\n" % t)
3189 ui.write("%s\n" % t)
3190 continue
3190 continue
3191
3191
3192 try:
3192 try:
3193 hn = hexfunc(n)
3193 hn = hexfunc(n)
3194 r = "%5d:%s" % (repo.changelog.rev(n), hn)
3194 r = "%5d:%s" % (repo.changelog.rev(n), hn)
3195 except error.LookupError:
3195 except error.LookupError:
3196 r = " ?:%s" % hn
3196 r = " ?:%s" % hn
3197 else:
3197 else:
3198 spaces = " " * (30 - encoding.colwidth(t))
3198 spaces = " " * (30 - encoding.colwidth(t))
3199 if ui.verbose:
3199 if ui.verbose:
3200 if repo.tagtype(t) == 'local':
3200 if repo.tagtype(t) == 'local':
3201 tagtype = " local"
3201 tagtype = " local"
3202 else:
3202 else:
3203 tagtype = ""
3203 tagtype = ""
3204 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
3204 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
3205
3205
3206 def tip(ui, repo, **opts):
3206 def tip(ui, repo, **opts):
3207 """show the tip revision
3207 """show the tip revision
3208
3208
3209 The tip revision (usually just called the tip) is the changeset
3209 The tip revision (usually just called the tip) is the changeset
3210 most recently added to the repository (and therefore the most
3210 most recently added to the repository (and therefore the most
3211 recently changed head).
3211 recently changed head).
3212
3212
3213 If you have just made a commit, that commit will be the tip. If
3213 If you have just made a commit, that commit will be the tip. If
3214 you have just pulled changes from another repository, the tip of
3214 you have just pulled changes from another repository, the tip of
3215 that repository becomes the current tip. The "tip" tag is special
3215 that repository becomes the current tip. The "tip" tag is special
3216 and cannot be renamed or assigned to a different changeset.
3216 and cannot be renamed or assigned to a different changeset.
3217 """
3217 """
3218 displayer = cmdutil.show_changeset(ui, repo, opts)
3218 displayer = cmdutil.show_changeset(ui, repo, opts)
3219 displayer.show(repo[len(repo) - 1])
3219 displayer.show(repo[len(repo) - 1])
3220 displayer.close()
3220 displayer.close()
3221
3221
3222 def unbundle(ui, repo, fname1, *fnames, **opts):
3222 def unbundle(ui, repo, fname1, *fnames, **opts):
3223 """apply one or more changegroup files
3223 """apply one or more changegroup files
3224
3224
3225 Apply one or more compressed changegroup files generated by the
3225 Apply one or more compressed changegroup files generated by the
3226 bundle command.
3226 bundle command.
3227 """
3227 """
3228 fnames = (fname1,) + fnames
3228 fnames = (fname1,) + fnames
3229
3229
3230 lock = repo.lock()
3230 lock = repo.lock()
3231 try:
3231 try:
3232 for fname in fnames:
3232 for fname in fnames:
3233 f = url.open(ui, fname)
3233 f = url.open(ui, fname)
3234 gen = changegroup.readbundle(f, fname)
3234 gen = changegroup.readbundle(f, fname)
3235 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
3235 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
3236 finally:
3236 finally:
3237 lock.release()
3237 lock.release()
3238
3238
3239 return postincoming(ui, repo, modheads, opts.get('update'), None)
3239 return postincoming(ui, repo, modheads, opts.get('update'), None)
3240
3240
3241 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
3241 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
3242 """update working directory
3242 """update working directory
3243
3243
3244 Update the repository's working directory to the specified
3244 Update the repository's working directory to the specified
3245 changeset.
3245 changeset.
3246
3246
3247 If no changeset is specified, attempt to update to the head of the
3247 If no changeset is specified, attempt to update to the head of the
3248 current branch. If this head is a descendant of the working
3248 current branch. If this head is a descendant of the working
3249 directory's parent, update to it, otherwise abort.
3249 directory's parent, update to it, otherwise abort.
3250
3250
3251 The following rules apply when the working directory contains
3251 The following rules apply when the working directory contains
3252 uncommitted changes:
3252 uncommitted changes:
3253
3253
3254 1. If neither -c/--check nor -C/--clean is specified, and if
3254 1. If neither -c/--check nor -C/--clean is specified, and if
3255 the requested changeset is an ancestor or descendant of
3255 the requested changeset is an ancestor or descendant of
3256 the working directory's parent, the uncommitted changes
3256 the working directory's parent, the uncommitted changes
3257 are merged into the requested changeset and the merged
3257 are merged into the requested changeset and the merged
3258 result is left uncommitted. If the requested changeset is
3258 result is left uncommitted. If the requested changeset is
3259 not an ancestor or descendant (that is, it is on another
3259 not an ancestor or descendant (that is, it is on another
3260 branch), the update is aborted and the uncommitted changes
3260 branch), the update is aborted and the uncommitted changes
3261 are preserved.
3261 are preserved.
3262
3262
3263 2. With the -c/--check option, the update is aborted and the
3263 2. With the -c/--check option, the update is aborted and the
3264 uncommitted changes are preserved.
3264 uncommitted changes are preserved.
3265
3265
3266 3. With the -C/--clean option, uncommitted changes are discarded and
3266 3. With the -C/--clean option, uncommitted changes are discarded and
3267 the working directory is updated to the requested changeset.
3267 the working directory is updated to the requested changeset.
3268
3268
3269 Use null as the changeset to remove the working directory (like 'hg
3269 Use null as the changeset to remove the working directory (like 'hg
3270 clone -U').
3270 clone -U').
3271
3271
3272 If you want to update just one file to an older changeset, use 'hg revert'.
3272 If you want to update just one file to an older changeset, use 'hg revert'.
3273
3273
3274 See 'hg help dates' for a list of formats valid for -d/--date.
3274 See 'hg help dates' for a list of formats valid for -d/--date.
3275 """
3275 """
3276 if rev and node:
3276 if rev and node:
3277 raise util.Abort(_("please specify just one revision"))
3277 raise util.Abort(_("please specify just one revision"))
3278
3278
3279 if not rev:
3279 if not rev:
3280 rev = node
3280 rev = node
3281
3281
3282 if check and clean:
3282 if check and clean:
3283 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
3283 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
3284
3284
3285 if check:
3285 if check:
3286 # we could use dirty() but we can ignore merge and branch trivia
3286 # we could use dirty() but we can ignore merge and branch trivia
3287 c = repo[None]
3287 c = repo[None]
3288 if c.modified() or c.added() or c.removed():
3288 if c.modified() or c.added() or c.removed():
3289 raise util.Abort(_("uncommitted local changes"))
3289 raise util.Abort(_("uncommitted local changes"))
3290
3290
3291 if date:
3291 if date:
3292 if rev:
3292 if rev:
3293 raise util.Abort(_("you can't specify a revision and a date"))
3293 raise util.Abort(_("you can't specify a revision and a date"))
3294 rev = cmdutil.finddate(ui, repo, date)
3294 rev = cmdutil.finddate(ui, repo, date)
3295
3295
3296 if clean or check:
3296 if clean or check:
3297 return hg.clean(repo, rev)
3297 return hg.clean(repo, rev)
3298 else:
3298 else:
3299 return hg.update(repo, rev)
3299 return hg.update(repo, rev)
3300
3300
3301 def verify(ui, repo):
3301 def verify(ui, repo):
3302 """verify the integrity of the repository
3302 """verify the integrity of the repository
3303
3303
3304 Verify the integrity of the current repository.
3304 Verify the integrity of the current repository.
3305
3305
3306 This will perform an extensive check of the repository's
3306 This will perform an extensive check of the repository's
3307 integrity, validating the hashes and checksums of each entry in
3307 integrity, validating the hashes and checksums of each entry in
3308 the changelog, manifest, and tracked files, as well as the
3308 the changelog, manifest, and tracked files, as well as the
3309 integrity of their crosslinks and indices.
3309 integrity of their crosslinks and indices.
3310 """
3310 """
3311 return hg.verify(repo)
3311 return hg.verify(repo)
3312
3312
3313 def version_(ui):
3313 def version_(ui):
3314 """output version and copyright information"""
3314 """output version and copyright information"""
3315 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3315 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3316 % util.version())
3316 % util.version())
3317 ui.status(_(
3317 ui.status(_(
3318 "\nCopyright (C) 2005-2010 Matt Mackall <mpm@selenic.com> and others\n"
3318 "\nCopyright (C) 2005-2010 Matt Mackall <mpm@selenic.com> and others\n"
3319 "This is free software; see the source for copying conditions. "
3319 "This is free software; see the source for copying conditions. "
3320 "There is NO\nwarranty; "
3320 "There is NO\nwarranty; "
3321 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3321 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3322 ))
3322 ))
3323
3323
3324 # Command options and aliases are listed here, alphabetically
3324 # Command options and aliases are listed here, alphabetically
3325
3325
3326 globalopts = [
3326 globalopts = [
3327 ('R', 'repository', '',
3327 ('R', 'repository', '',
3328 _('repository root directory or name of overlay bundle file')),
3328 _('repository root directory or name of overlay bundle file')),
3329 ('', 'cwd', '', _('change working directory')),
3329 ('', 'cwd', '', _('change working directory')),
3330 ('y', 'noninteractive', None,
3330 ('y', 'noninteractive', None,
3331 _('do not prompt, assume \'yes\' for any required answers')),
3331 _('do not prompt, assume \'yes\' for any required answers')),
3332 ('q', 'quiet', None, _('suppress output')),
3332 ('q', 'quiet', None, _('suppress output')),
3333 ('v', 'verbose', None, _('enable additional output')),
3333 ('v', 'verbose', None, _('enable additional output')),
3334 ('', 'config', [], _('set/override config option')),
3334 ('', 'config', [], _('set/override config option')),
3335 ('', 'debug', None, _('enable debugging output')),
3335 ('', 'debug', None, _('enable debugging output')),
3336 ('', 'debugger', None, _('start debugger')),
3336 ('', 'debugger', None, _('start debugger')),
3337 ('', 'encoding', encoding.encoding, _('set the charset encoding')),
3337 ('', 'encoding', encoding.encoding, _('set the charset encoding')),
3338 ('', 'encodingmode', encoding.encodingmode,
3338 ('', 'encodingmode', encoding.encodingmode,
3339 _('set the charset encoding mode')),
3339 _('set the charset encoding mode')),
3340 ('', 'traceback', None, _('always print a traceback on exception')),
3340 ('', 'traceback', None, _('always print a traceback on exception')),
3341 ('', 'time', None, _('time how long the command takes')),
3341 ('', 'time', None, _('time how long the command takes')),
3342 ('', 'profile', None, _('print command execution profile')),
3342 ('', 'profile', None, _('print command execution profile')),
3343 ('', 'version', None, _('output version information and exit')),
3343 ('', 'version', None, _('output version information and exit')),
3344 ('h', 'help', None, _('display help and exit')),
3344 ('h', 'help', None, _('display help and exit')),
3345 ]
3345 ]
3346
3346
3347 dryrunopts = [('n', 'dry-run', None,
3347 dryrunopts = [('n', 'dry-run', None,
3348 _('do not perform actions, just print output'))]
3348 _('do not perform actions, just print output'))]
3349
3349
3350 remoteopts = [
3350 remoteopts = [
3351 ('e', 'ssh', '', _('specify ssh command to use')),
3351 ('e', 'ssh', '', _('specify ssh command to use')),
3352 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
3352 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
3353 ]
3353 ]
3354
3354
3355 walkopts = [
3355 walkopts = [
3356 ('I', 'include', [], _('include names matching the given patterns')),
3356 ('I', 'include', [], _('include names matching the given patterns')),
3357 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3357 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3358 ]
3358 ]
3359
3359
3360 commitopts = [
3360 commitopts = [
3361 ('m', 'message', '', _('use <text> as commit message')),
3361 ('m', 'message', '', _('use <text> as commit message')),
3362 ('l', 'logfile', '', _('read commit message from <file>')),
3362 ('l', 'logfile', '', _('read commit message from <file>')),
3363 ]
3363 ]
3364
3364
3365 commitopts2 = [
3365 commitopts2 = [
3366 ('d', 'date', '', _('record datecode as commit date')),
3366 ('d', 'date', '', _('record datecode as commit date')),
3367 ('u', 'user', '', _('record the specified user as committer')),
3367 ('u', 'user', '', _('record the specified user as committer')),
3368 ]
3368 ]
3369
3369
3370 templateopts = [
3370 templateopts = [
3371 ('', 'style', '', _('display using template map file')),
3371 ('', 'style', '', _('display using template map file')),
3372 ('', 'template', '', _('display with template')),
3372 ('', 'template', '', _('display with template')),
3373 ]
3373 ]
3374
3374
3375 logopts = [
3375 logopts = [
3376 ('p', 'patch', None, _('show patch')),
3376 ('p', 'patch', None, _('show patch')),
3377 ('g', 'git', None, _('use git extended diff format')),
3377 ('g', 'git', None, _('use git extended diff format')),
3378 ('l', 'limit', '', _('limit number of changes displayed')),
3378 ('l', 'limit', '', _('limit number of changes displayed')),
3379 ('M', 'no-merges', None, _('do not show merges')),
3379 ('M', 'no-merges', None, _('do not show merges')),
3380 ] + templateopts
3380 ] + templateopts
3381
3381
3382 diffopts = [
3382 diffopts = [
3383 ('a', 'text', None, _('treat all files as text')),
3383 ('a', 'text', None, _('treat all files as text')),
3384 ('g', 'git', None, _('use git extended diff format')),
3384 ('g', 'git', None, _('use git extended diff format')),
3385 ('', 'nodates', None, _('omit dates from diff headers'))
3385 ('', 'nodates', None, _('omit dates from diff headers'))
3386 ]
3386 ]
3387
3387
3388 diffopts2 = [
3388 diffopts2 = [
3389 ('p', 'show-function', None, _('show which function each change is in')),
3389 ('p', 'show-function', None, _('show which function each change is in')),
3390 ('', 'reverse', None, _('produce a diff that undoes the changes')),
3390 ('', 'reverse', None, _('produce a diff that undoes the changes')),
3391 ('w', 'ignore-all-space', None,
3391 ('w', 'ignore-all-space', None,
3392 _('ignore white space when comparing lines')),
3392 _('ignore white space when comparing lines')),
3393 ('b', 'ignore-space-change', None,
3393 ('b', 'ignore-space-change', None,
3394 _('ignore changes in the amount of white space')),
3394 _('ignore changes in the amount of white space')),
3395 ('B', 'ignore-blank-lines', None,
3395 ('B', 'ignore-blank-lines', None,
3396 _('ignore changes whose lines are all blank')),
3396 _('ignore changes whose lines are all blank')),
3397 ('U', 'unified', '', _('number of lines of context to show')),
3397 ('U', 'unified', '', _('number of lines of context to show')),
3398 ('', 'stat', None, _('output diffstat-style summary of changes')),
3398 ('', 'stat', None, _('output diffstat-style summary of changes')),
3399 ]
3399 ]
3400
3400
3401 similarityopts = [
3401 similarityopts = [
3402 ('s', 'similarity', '',
3402 ('s', 'similarity', '',
3403 _('guess renamed files by similarity (0<=s<=100)'))
3403 _('guess renamed files by similarity (0<=s<=100)'))
3404 ]
3404 ]
3405
3405
3406 table = {
3406 table = {
3407 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3407 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3408 "addremove":
3408 "addremove":
3409 (addremove, similarityopts + walkopts + dryrunopts,
3409 (addremove, similarityopts + walkopts + dryrunopts,
3410 _('[OPTION]... [FILE]...')),
3410 _('[OPTION]... [FILE]...')),
3411 "^annotate|blame":
3411 "^annotate|blame":
3412 (annotate,
3412 (annotate,
3413 [('r', 'rev', '', _('annotate the specified revision')),
3413 [('r', 'rev', '', _('annotate the specified revision')),
3414 ('', 'follow', None, _('follow copies and renames (DEPRECATED)')),
3414 ('', 'follow', None, _('follow copies and renames (DEPRECATED)')),
3415 ('', 'no-follow', None, _("don't follow copies and renames")),
3415 ('', 'no-follow', None, _("don't follow copies and renames")),
3416 ('a', 'text', None, _('treat all files as text')),
3416 ('a', 'text', None, _('treat all files as text')),
3417 ('u', 'user', None, _('list the author (long with -v)')),
3417 ('u', 'user', None, _('list the author (long with -v)')),
3418 ('f', 'file', None, _('list the filename')),
3418 ('f', 'file', None, _('list the filename')),
3419 ('d', 'date', None, _('list the date (short with -q)')),
3419 ('d', 'date', None, _('list the date (short with -q)')),
3420 ('n', 'number', None, _('list the revision number (default)')),
3420 ('n', 'number', None, _('list the revision number (default)')),
3421 ('c', 'changeset', None, _('list the changeset')),
3421 ('c', 'changeset', None, _('list the changeset')),
3422 ('l', 'line-number', None,
3422 ('l', 'line-number', None,
3423 _('show line number at the first appearance'))
3423 _('show line number at the first appearance'))
3424 ] + walkopts,
3424 ] + walkopts,
3425 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3425 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3426 "archive":
3426 "archive":
3427 (archive,
3427 (archive,
3428 [('', 'no-decode', None, _('do not pass files through decoders')),
3428 [('', 'no-decode', None, _('do not pass files through decoders')),
3429 ('p', 'prefix', '', _('directory prefix for files in archive')),
3429 ('p', 'prefix', '', _('directory prefix for files in archive')),
3430 ('r', 'rev', '', _('revision to distribute')),
3430 ('r', 'rev', '', _('revision to distribute')),
3431 ('t', 'type', '', _('type of distribution to create')),
3431 ('t', 'type', '', _('type of distribution to create')),
3432 ] + walkopts,
3432 ] + walkopts,
3433 _('[OPTION]... DEST')),
3433 _('[OPTION]... DEST')),
3434 "backout":
3434 "backout":
3435 (backout,
3435 (backout,
3436 [('', 'merge', None,
3436 [('', 'merge', None,
3437 _('merge with old dirstate parent after backout')),
3437 _('merge with old dirstate parent after backout')),
3438 ('', 'parent', '', _('parent to choose when backing out merge')),
3438 ('', 'parent', '', _('parent to choose when backing out merge')),
3439 ('r', 'rev', '', _('revision to backout')),
3439 ('r', 'rev', '', _('revision to backout')),
3440 ] + walkopts + commitopts + commitopts2,
3440 ] + walkopts + commitopts + commitopts2,
3441 _('[OPTION]... [-r] REV')),
3441 _('[OPTION]... [-r] REV')),
3442 "bisect":
3442 "bisect":
3443 (bisect,
3443 (bisect,
3444 [('r', 'reset', False, _('reset bisect state')),
3444 [('r', 'reset', False, _('reset bisect state')),
3445 ('g', 'good', False, _('mark changeset good')),
3445 ('g', 'good', False, _('mark changeset good')),
3446 ('b', 'bad', False, _('mark changeset bad')),
3446 ('b', 'bad', False, _('mark changeset bad')),
3447 ('s', 'skip', False, _('skip testing changeset')),
3447 ('s', 'skip', False, _('skip testing changeset')),
3448 ('c', 'command', '', _('use command to check changeset state')),
3448 ('c', 'command', '', _('use command to check changeset state')),
3449 ('U', 'noupdate', False, _('do not update to target'))],
3449 ('U', 'noupdate', False, _('do not update to target'))],
3450 _("[-gbsr] [-U] [-c CMD] [REV]")),
3450 _("[-gbsr] [-U] [-c CMD] [REV]")),
3451 "branch":
3451 "branch":
3452 (branch,
3452 (branch,
3453 [('f', 'force', None,
3453 [('f', 'force', None,
3454 _('set branch name even if it shadows an existing branch')),
3454 _('set branch name even if it shadows an existing branch')),
3455 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3455 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3456 _('[-fC] [NAME]')),
3456 _('[-fC] [NAME]')),
3457 "branches":
3457 "branches":
3458 (branches,
3458 (branches,
3459 [('a', 'active', False,
3459 [('a', 'active', False,
3460 _('show only branches that have unmerged heads')),
3460 _('show only branches that have unmerged heads')),
3461 ('c', 'closed', False,
3461 ('c', 'closed', False,
3462 _('show normal and closed branches'))],
3462 _('show normal and closed branches'))],
3463 _('[-ac]')),
3463 _('[-ac]')),
3464 "bundle":
3464 "bundle":
3465 (bundle,
3465 (bundle,
3466 [('f', 'force', None,
3466 [('f', 'force', None,
3467 _('run even when the destination is unrelated')),
3467 _('run even when the destination is unrelated')),
3468 ('r', 'rev', [],
3468 ('r', 'rev', [],
3469 _('a changeset intended to be added to the destination')),
3469 _('a changeset intended to be added to the destination')),
3470 ('b', 'branch', [],
3470 ('b', 'branch', [],
3471 _('a specific branch you would like to bundle')),
3471 _('a specific branch you would like to bundle')),
3472 ('', 'base', [],
3472 ('', 'base', [],
3473 _('a base changeset assumed to be available at the destination')),
3473 _('a base changeset assumed to be available at the destination')),
3474 ('a', 'all', None, _('bundle all changesets in the repository')),
3474 ('a', 'all', None, _('bundle all changesets in the repository')),
3475 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3475 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3476 ] + remoteopts,
3476 ] + remoteopts,
3477 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3477 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3478 "cat":
3478 "cat":
3479 (cat,
3479 (cat,
3480 [('o', 'output', '', _('print output to file with formatted name')),
3480 [('o', 'output', '', _('print output to file with formatted name')),
3481 ('r', 'rev', '', _('print the given revision')),
3481 ('r', 'rev', '', _('print the given revision')),
3482 ('', 'decode', None, _('apply any matching decode filter')),
3482 ('', 'decode', None, _('apply any matching decode filter')),
3483 ] + walkopts,
3483 ] + walkopts,
3484 _('[OPTION]... FILE...')),
3484 _('[OPTION]... FILE...')),
3485 "^clone":
3485 "^clone":
3486 (clone,
3486 (clone,
3487 [('U', 'noupdate', None,
3487 [('U', 'noupdate', None,
3488 _('the clone will include an empty working copy (only a repository)')),
3488 _('the clone will include an empty working copy (only a repository)')),
3489 ('u', 'updaterev', '',
3489 ('u', 'updaterev', '',
3490 _('revision, tag or branch to check out')),
3490 _('revision, tag or branch to check out')),
3491 ('r', 'rev', [],
3491 ('r', 'rev', [],
3492 _('include the specified changeset')),
3492 _('include the specified changeset')),
3493 ('b', 'branch', [],
3493 ('b', 'branch', [],
3494 _('clone only the specified branch')),
3494 _('clone only the specified branch')),
3495 ('', 'pull', None, _('use pull protocol to copy metadata')),
3495 ('', 'pull', None, _('use pull protocol to copy metadata')),
3496 ('', 'uncompressed', None,
3496 ('', 'uncompressed', None,
3497 _('use uncompressed transfer (fast over LAN)')),
3497 _('use uncompressed transfer (fast over LAN)')),
3498 ] + remoteopts,
3498 ] + remoteopts,
3499 _('[OPTION]... SOURCE [DEST]')),
3499 _('[OPTION]... SOURCE [DEST]')),
3500 "^commit|ci":
3500 "^commit|ci":
3501 (commit,
3501 (commit,
3502 [('A', 'addremove', None,
3502 [('A', 'addremove', None,
3503 _('mark new/missing files as added/removed before committing')),
3503 _('mark new/missing files as added/removed before committing')),
3504 ('', 'close-branch', None,
3504 ('', 'close-branch', None,
3505 _('mark a branch as closed, hiding it from the branch list')),
3505 _('mark a branch as closed, hiding it from the branch list')),
3506 ] + walkopts + commitopts + commitopts2,
3506 ] + walkopts + commitopts + commitopts2,
3507 _('[OPTION]... [FILE]...')),
3507 _('[OPTION]... [FILE]...')),
3508 "copy|cp":
3508 "copy|cp":
3509 (copy,
3509 (copy,
3510 [('A', 'after', None, _('record a copy that has already occurred')),
3510 [('A', 'after', None, _('record a copy that has already occurred')),
3511 ('f', 'force', None,
3511 ('f', 'force', None,
3512 _('forcibly copy over an existing managed file')),
3512 _('forcibly copy over an existing managed file')),
3513 ] + walkopts + dryrunopts,
3513 ] + walkopts + dryrunopts,
3514 _('[OPTION]... [SOURCE]... DEST')),
3514 _('[OPTION]... [SOURCE]... DEST')),
3515 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3515 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3516 "debugcheckstate": (debugcheckstate, [], ''),
3516 "debugcheckstate": (debugcheckstate, [], ''),
3517 "debugcommands": (debugcommands, [], _('[COMMAND]')),
3517 "debugcommands": (debugcommands, [], _('[COMMAND]')),
3518 "debugcomplete":
3518 "debugcomplete":
3519 (debugcomplete,
3519 (debugcomplete,
3520 [('o', 'options', None, _('show the command options'))],
3520 [('o', 'options', None, _('show the command options'))],
3521 _('[-o] CMD')),
3521 _('[-o] CMD')),
3522 "debugdate":
3522 "debugdate":
3523 (debugdate,
3523 (debugdate,
3524 [('e', 'extended', None, _('try extended date formats'))],
3524 [('e', 'extended', None, _('try extended date formats'))],
3525 _('[-e] DATE [RANGE]')),
3525 _('[-e] DATE [RANGE]')),
3526 "debugdata": (debugdata, [], _('FILE REV')),
3526 "debugdata": (debugdata, [], _('FILE REV')),
3527 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3527 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3528 "debugindex": (debugindex, [], _('FILE')),
3528 "debugindex": (debugindex, [], _('FILE')),
3529 "debugindexdot": (debugindexdot, [], _('FILE')),
3529 "debugindexdot": (debugindexdot, [], _('FILE')),
3530 "debuginstall": (debuginstall, [], ''),
3530 "debuginstall": (debuginstall, [], ''),
3531 "debugrebuildstate":
3531 "debugrebuildstate":
3532 (debugrebuildstate,
3532 (debugrebuildstate,
3533 [('r', 'rev', '', _('revision to rebuild to'))],
3533 [('r', 'rev', '', _('revision to rebuild to'))],
3534 _('[-r REV] [REV]')),
3534 _('[-r REV] [REV]')),
3535 "debugrename":
3535 "debugrename":
3536 (debugrename,
3536 (debugrename,
3537 [('r', 'rev', '', _('revision to debug'))],
3537 [('r', 'rev', '', _('revision to debug'))],
3538 _('[-r REV] FILE')),
3538 _('[-r REV] FILE')),
3539 "debugsetparents":
3539 "debugsetparents":
3540 (debugsetparents, [], _('REV1 [REV2]')),
3540 (debugsetparents, [], _('REV1 [REV2]')),
3541 "debugstate":
3541 "debugstate":
3542 (debugstate,
3542 (debugstate,
3543 [('', 'nodates', None, _('do not display the saved mtime'))],
3543 [('', 'nodates', None, _('do not display the saved mtime'))],
3544 _('[OPTION]...')),
3544 _('[OPTION]...')),
3545 "debugsub":
3545 "debugsub":
3546 (debugsub,
3546 (debugsub,
3547 [('r', 'rev', '', _('revision to check'))],
3547 [('r', 'rev', '', _('revision to check'))],
3548 _('[-r REV] [REV]')),
3548 _('[-r REV] [REV]')),
3549 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3549 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3550 "^diff":
3550 "^diff":
3551 (diff,
3551 (diff,
3552 [('r', 'rev', [], _('revision')),
3552 [('r', 'rev', [], _('revision')),
3553 ('c', 'change', '', _('change made by revision'))
3553 ('c', 'change', '', _('change made by revision'))
3554 ] + diffopts + diffopts2 + walkopts,
3554 ] + diffopts + diffopts2 + walkopts,
3555 _('[OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3555 _('[OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3556 "^export":
3556 "^export":
3557 (export,
3557 (export,
3558 [('o', 'output', '', _('print output to file with formatted name')),
3558 [('o', 'output', '', _('print output to file with formatted name')),
3559 ('', 'switch-parent', None, _('diff against the second parent')),
3559 ('', 'switch-parent', None, _('diff against the second parent')),
3560 ('r', 'rev', [], _('revisions to export')),
3560 ('r', 'rev', [], _('revisions to export')),
3561 ] + diffopts,
3561 ] + diffopts,
3562 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3562 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3563 "^forget":
3563 "^forget":
3564 (forget,
3564 (forget,
3565 [] + walkopts,
3565 [] + walkopts,
3566 _('[OPTION]... FILE...')),
3566 _('[OPTION]... FILE...')),
3567 "grep":
3567 "grep":
3568 (grep,
3568 (grep,
3569 [('0', 'print0', None, _('end fields with NUL')),
3569 [('0', 'print0', None, _('end fields with NUL')),
3570 ('', 'all', None, _('print all revisions that match')),
3570 ('', 'all', None, _('print all revisions that match')),
3571 ('f', 'follow', None,
3571 ('f', 'follow', None,
3572 _('follow changeset history,'
3572 _('follow changeset history,'
3573 ' or file history across copies and renames')),
3573 ' or file history across copies and renames')),
3574 ('i', 'ignore-case', None, _('ignore case when matching')),
3574 ('i', 'ignore-case', None, _('ignore case when matching')),
3575 ('l', 'files-with-matches', None,
3575 ('l', 'files-with-matches', None,
3576 _('print only filenames and revisions that match')),
3576 _('print only filenames and revisions that match')),
3577 ('n', 'line-number', None, _('print matching line numbers')),
3577 ('n', 'line-number', None, _('print matching line numbers')),
3578 ('r', 'rev', [], _('search in given revision range')),
3578 ('r', 'rev', [], _('search in given revision range')),
3579 ('u', 'user', None, _('list the author (long with -v)')),
3579 ('u', 'user', None, _('list the author (long with -v)')),
3580 ('d', 'date', None, _('list the date (short with -q)')),
3580 ('d', 'date', None, _('list the date (short with -q)')),
3581 ] + walkopts,
3581 ] + walkopts,
3582 _('[OPTION]... PATTERN [FILE]...')),
3582 _('[OPTION]... PATTERN [FILE]...')),
3583 "heads":
3583 "heads":
3584 (heads,
3584 (heads,
3585 [('r', 'rev', '', _('show only heads which are descendants of REV')),
3585 [('r', 'rev', '', _('show only heads which are descendants of REV')),
3586 ('t', 'topo', False, _('show topological heads only')),
3586 ('t', 'topo', False, _('show topological heads only')),
3587 ('a', 'active', False,
3587 ('a', 'active', False,
3588 _('show active branchheads only [DEPRECATED]')),
3588 _('show active branchheads only [DEPRECATED]')),
3589 ('c', 'closed', False,
3589 ('c', 'closed', False,
3590 _('show normal and closed branch heads')),
3590 _('show normal and closed branch heads')),
3591 ] + templateopts,
3591 ] + templateopts,
3592 _('[-ac] [-r STARTREV] [REV]...')),
3592 _('[-ac] [-r STARTREV] [REV]...')),
3593 "help": (help_, [], _('[TOPIC]')),
3593 "help": (help_, [], _('[TOPIC]')),
3594 "identify|id":
3594 "identify|id":
3595 (identify,
3595 (identify,
3596 [('r', 'rev', '', _('identify the specified revision')),
3596 [('r', 'rev', '', _('identify the specified revision')),
3597 ('n', 'num', None, _('show local revision number')),
3597 ('n', 'num', None, _('show local revision number')),
3598 ('i', 'id', None, _('show global revision id')),
3598 ('i', 'id', None, _('show global revision id')),
3599 ('b', 'branch', None, _('show branch')),
3599 ('b', 'branch', None, _('show branch')),
3600 ('t', 'tags', None, _('show tags'))],
3600 ('t', 'tags', None, _('show tags'))],
3601 _('[-nibt] [-r REV] [SOURCE]')),
3601 _('[-nibt] [-r REV] [SOURCE]')),
3602 "import|patch":
3602 "import|patch":
3603 (import_,
3603 (import_,
3604 [('p', 'strip', 1,
3604 [('p', 'strip', 1,
3605 _('directory strip option for patch. This has the same '
3605 _('directory strip option for patch. This has the same '
3606 'meaning as the corresponding patch option')),
3606 'meaning as the corresponding patch option')),
3607 ('b', 'base', '', _('base path')),
3607 ('b', 'base', '', _('base path')),
3608 ('f', 'force', None,
3608 ('f', 'force', None,
3609 _('skip check for outstanding uncommitted changes')),
3609 _('skip check for outstanding uncommitted changes')),
3610 ('', 'no-commit', None,
3610 ('', 'no-commit', None,
3611 _("don't commit, just update the working directory")),
3611 _("don't commit, just update the working directory")),
3612 ('', 'exact', None,
3612 ('', 'exact', None,
3613 _('apply patch to the nodes from which it was generated')),
3613 _('apply patch to the nodes from which it was generated')),
3614 ('', 'import-branch', None,
3614 ('', 'import-branch', None,
3615 _('use any branch information in patch (implied by --exact)'))] +
3615 _('use any branch information in patch (implied by --exact)'))] +
3616 commitopts + commitopts2 + similarityopts,
3616 commitopts + commitopts2 + similarityopts,
3617 _('[OPTION]... PATCH...')),
3617 _('[OPTION]... PATCH...')),
3618 "incoming|in":
3618 "incoming|in":
3619 (incoming,
3619 (incoming,
3620 [('f', 'force', None,
3620 [('f', 'force', None,
3621 _('run even if remote repository is unrelated')),
3621 _('run even if remote repository is unrelated')),
3622 ('n', 'newest-first', None, _('show newest record first')),
3622 ('n', 'newest-first', None, _('show newest record first')),
3623 ('', 'bundle', '', _('file to store the bundles into')),
3623 ('', 'bundle', '', _('file to store the bundles into')),
3624 ('r', 'rev', [],
3624 ('r', 'rev', [],
3625 _('a remote changeset intended to be added')),
3625 _('a remote changeset intended to be added')),
3626 ('b', 'branch', [],
3626 ('b', 'branch', [],
3627 _('a specific branch you would like to pull')),
3627 _('a specific branch you would like to pull')),
3628 ] + logopts + remoteopts,
3628 ] + logopts + remoteopts,
3629 _('[-p] [-n] [-M] [-f] [-r REV]...'
3629 _('[-p] [-n] [-M] [-f] [-r REV]...'
3630 ' [--bundle FILENAME] [SOURCE]')),
3630 ' [--bundle FILENAME] [SOURCE]')),
3631 "^init":
3631 "^init":
3632 (init,
3632 (init,
3633 remoteopts,
3633 remoteopts,
3634 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3634 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3635 "locate":
3635 "locate":
3636 (locate,
3636 (locate,
3637 [('r', 'rev', '', _('search the repository as it is in REV')),
3637 [('r', 'rev', '', _('search the repository as it is in REV')),
3638 ('0', 'print0', None,
3638 ('0', 'print0', None,
3639 _('end filenames with NUL, for use with xargs')),
3639 _('end filenames with NUL, for use with xargs')),
3640 ('f', 'fullpath', None,
3640 ('f', 'fullpath', None,
3641 _('print complete paths from the filesystem root')),
3641 _('print complete paths from the filesystem root')),
3642 ] + walkopts,
3642 ] + walkopts,
3643 _('[OPTION]... [PATTERN]...')),
3643 _('[OPTION]... [PATTERN]...')),
3644 "^log|history":
3644 "^log|history":
3645 (log,
3645 (log,
3646 [('f', 'follow', None,
3646 [('f', 'follow', None,
3647 _('follow changeset history,'
3647 _('follow changeset history,'
3648 ' or file history across copies and renames')),
3648 ' or file history across copies and renames')),
3649 ('', 'follow-first', None,
3649 ('', 'follow-first', None,
3650 _('only follow the first parent of merge changesets')),
3650 _('only follow the first parent of merge changesets')),
3651 ('d', 'date', '', _('show revisions matching date spec')),
3651 ('d', 'date', '', _('show revisions matching date spec')),
3652 ('C', 'copies', None, _('show copied files')),
3652 ('C', 'copies', None, _('show copied files')),
3653 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3653 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3654 ('r', 'rev', [], _('show the specified revision or range')),
3654 ('r', 'rev', [], _('show the specified revision or range')),
3655 ('', 'removed', None, _('include revisions where files were removed')),
3655 ('', 'removed', None, _('include revisions where files were removed')),
3656 ('m', 'only-merges', None, _('show only merges')),
3656 ('m', 'only-merges', None, _('show only merges')),
3657 ('u', 'user', [], _('revisions committed by user')),
3657 ('u', 'user', [], _('revisions committed by user')),
3658 ('b', 'only-branch', [],
3658 ('b', 'only-branch', [],
3659 _('show only changesets within the given named branch')),
3659 _('show only changesets within the given named branch')),
3660 ('P', 'prune', [],
3660 ('P', 'prune', [],
3661 _('do not display revision or any of its ancestors')),
3661 _('do not display revision or any of its ancestors')),
3662 ] + logopts + walkopts,
3662 ] + logopts + walkopts,
3663 _('[OPTION]... [FILE]')),
3663 _('[OPTION]... [FILE]')),
3664 "manifest":
3664 "manifest":
3665 (manifest,
3665 (manifest,
3666 [('r', 'rev', '', _('revision to display'))],
3666 [('r', 'rev', '', _('revision to display'))],
3667 _('[-r REV]')),
3667 _('[-r REV]')),
3668 "^merge":
3668 "^merge":
3669 (merge,
3669 (merge,
3670 [('f', 'force', None, _('force a merge with outstanding changes')),
3670 [('f', 'force', None, _('force a merge with outstanding changes')),
3671 ('r', 'rev', '', _('revision to merge')),
3671 ('r', 'rev', '', _('revision to merge')),
3672 ('P', 'preview', None,
3672 ('P', 'preview', None,
3673 _('review revisions to merge (no merge is performed)'))],
3673 _('review revisions to merge (no merge is performed)'))],
3674 _('[-P] [-f] [[-r] REV]')),
3674 _('[-P] [-f] [[-r] REV]')),
3675 "outgoing|out":
3675 "outgoing|out":
3676 (outgoing,
3676 (outgoing,
3677 [('f', 'force', None,
3677 [('f', 'force', None,
3678 _('run even when the destination is unrelated')),
3678 _('run even when the destination is unrelated')),
3679 ('r', 'rev', [],
3679 ('r', 'rev', [],
3680 _('a changeset intended to be included in the destination')),
3680 _('a changeset intended to be included in the destination')),
3681 ('n', 'newest-first', None, _('show newest record first')),
3681 ('n', 'newest-first', None, _('show newest record first')),
3682 ('b', 'branch', [],
3682 ('b', 'branch', [],
3683 _('a specific branch you would like to push')),
3683 _('a specific branch you would like to push')),
3684 ] + logopts + remoteopts,
3684 ] + logopts + remoteopts,
3685 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3685 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3686 "parents":
3686 "parents":
3687 (parents,
3687 (parents,
3688 [('r', 'rev', '', _('show parents of the specified revision')),
3688 [('r', 'rev', '', _('show parents of the specified revision')),
3689 ] + templateopts,
3689 ] + templateopts,
3690 _('[-r REV] [FILE]')),
3690 _('[-r REV] [FILE]')),
3691 "paths": (paths, [], _('[NAME]')),
3691 "paths": (paths, [], _('[NAME]')),
3692 "^pull":
3692 "^pull":
3693 (pull,
3693 (pull,
3694 [('u', 'update', None,
3694 [('u', 'update', None,
3695 _('update to new branch head if changesets were pulled')),
3695 _('update to new branch head if changesets were pulled')),
3696 ('f', 'force', None,
3696 ('f', 'force', None,
3697 _('run even when remote repository is unrelated')),
3697 _('run even when remote repository is unrelated')),
3698 ('r', 'rev', [],
3698 ('r', 'rev', [],
3699 _('a remote changeset intended to be added')),
3699 _('a remote changeset intended to be added')),
3700 ('b', 'branch', [],
3700 ('b', 'branch', [],
3701 _('a specific branch you would like to pull')),
3701 _('a specific branch you would like to pull')),
3702 ] + remoteopts,
3702 ] + remoteopts,
3703 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3703 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3704 "^push":
3704 "^push":
3705 (push,
3705 (push,
3706 [('f', 'force', None, _('force push')),
3706 [('f', 'force', None, _('force push')),
3707 ('r', 'rev', [],
3707 ('r', 'rev', [],
3708 _('a changeset intended to be included in the destination')),
3708 _('a changeset intended to be included in the destination')),
3709 ('b', 'branch', [],
3709 ('b', 'branch', [],
3710 _('a specific branch you would like to push')),
3710 _('a specific branch you would like to push')),
3711 ] + remoteopts,
3711 ] + remoteopts,
3712 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3712 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3713 "recover": (recover, []),
3713 "recover": (recover, []),
3714 "^remove|rm":
3714 "^remove|rm":
3715 (remove,
3715 (remove,
3716 [('A', 'after', None, _('record delete for missing files')),
3716 [('A', 'after', None, _('record delete for missing files')),
3717 ('f', 'force', None,
3717 ('f', 'force', None,
3718 _('remove (and delete) file even if added or modified')),
3718 _('remove (and delete) file even if added or modified')),
3719 ] + walkopts,
3719 ] + walkopts,
3720 _('[OPTION]... FILE...')),
3720 _('[OPTION]... FILE...')),
3721 "rename|mv":
3721 "rename|mv":
3722 (rename,
3722 (rename,
3723 [('A', 'after', None, _('record a rename that has already occurred')),
3723 [('A', 'after', None, _('record a rename that has already occurred')),
3724 ('f', 'force', None,
3724 ('f', 'force', None,
3725 _('forcibly copy over an existing managed file')),
3725 _('forcibly copy over an existing managed file')),
3726 ] + walkopts + dryrunopts,
3726 ] + walkopts + dryrunopts,
3727 _('[OPTION]... SOURCE... DEST')),
3727 _('[OPTION]... SOURCE... DEST')),
3728 "resolve":
3728 "resolve":
3729 (resolve,
3729 (resolve,
3730 [('a', 'all', None, _('select all unresolved files')),
3730 [('a', 'all', None, _('select all unresolved files')),
3731 ('l', 'list', None, _('list state of files needing merge')),
3731 ('l', 'list', None, _('list state of files needing merge')),
3732 ('m', 'mark', None, _('mark files as resolved')),
3732 ('m', 'mark', None, _('mark files as resolved')),
3733 ('u', 'unmark', None, _('unmark files as resolved')),
3733 ('u', 'unmark', None, _('unmark files as resolved')),
3734 ('n', 'no-status', None, _('hide status prefix'))]
3734 ('n', 'no-status', None, _('hide status prefix'))]
3735 + walkopts,
3735 + walkopts,
3736 _('[OPTION]... [FILE]...')),
3736 _('[OPTION]... [FILE]...')),
3737 "revert":
3737 "revert":
3738 (revert,
3738 (revert,
3739 [('a', 'all', None, _('revert all changes when no arguments given')),
3739 [('a', 'all', None, _('revert all changes when no arguments given')),
3740 ('d', 'date', '', _('tipmost revision matching date')),
3740 ('d', 'date', '', _('tipmost revision matching date')),
3741 ('r', 'rev', '', _('revert to the specified revision')),
3741 ('r', 'rev', '', _('revert to the specified revision')),
3742 ('', 'no-backup', None, _('do not save backup copies of files')),
3742 ('', 'no-backup', None, _('do not save backup copies of files')),
3743 ] + walkopts + dryrunopts,
3743 ] + walkopts + dryrunopts,
3744 _('[OPTION]... [-r REV] [NAME]...')),
3744 _('[OPTION]... [-r REV] [NAME]...')),
3745 "rollback": (rollback, []),
3745 "rollback": (rollback, []),
3746 "root": (root, []),
3746 "root": (root, []),
3747 "^serve":
3747 "^serve":
3748 (serve,
3748 (serve,
3749 [('A', 'accesslog', '', _('name of access log file to write to')),
3749 [('A', 'accesslog', '', _('name of access log file to write to')),
3750 ('d', 'daemon', None, _('run server in background')),
3750 ('d', 'daemon', None, _('run server in background')),
3751 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3751 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3752 ('E', 'errorlog', '', _('name of error log file to write to')),
3752 ('E', 'errorlog', '', _('name of error log file to write to')),
3753 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3753 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3754 ('a', 'address', '',
3754 ('a', 'address', '',
3755 _('address to listen on (default: all interfaces)')),
3755 _('address to listen on (default: all interfaces)')),
3756 ('', 'prefix', '',
3756 ('', 'prefix', '',
3757 _('prefix path to serve from (default: server root)')),
3757 _('prefix path to serve from (default: server root)')),
3758 ('n', 'name', '',
3758 ('n', 'name', '',
3759 _('name to show in web pages (default: working directory)')),
3759 _('name to show in web pages (default: working directory)')),
3760 ('', 'webdir-conf', '', _('name of the webdir config file'
3760 ('', 'webdir-conf', '', _('name of the webdir config file'
3761 ' (serve more than one repository)')),
3761 ' (serve more than one repository)')),
3762 ('', 'pid-file', '', _('name of file to write process ID to')),
3762 ('', 'pid-file', '', _('name of file to write process ID to')),
3763 ('', 'stdio', None, _('for remote clients')),
3763 ('', 'stdio', None, _('for remote clients')),
3764 ('t', 'templates', '', _('web templates to use')),
3764 ('t', 'templates', '', _('web templates to use')),
3765 ('', 'style', '', _('template style to use')),
3765 ('', 'style', '', _('template style to use')),
3766 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3766 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3767 ('', 'certificate', '', _('SSL certificate file'))],
3767 ('', 'certificate', '', _('SSL certificate file'))],
3768 _('[OPTION]...')),
3768 _('[OPTION]...')),
3769 "showconfig|debugconfig":
3769 "showconfig|debugconfig":
3770 (showconfig,
3770 (showconfig,
3771 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3771 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3772 _('[-u] [NAME]...')),
3772 _('[-u] [NAME]...')),
3773 "^summary|sum":
3773 "^summary|sum":
3774 (summary,
3774 (summary,
3775 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
3775 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
3776 "^status|st":
3776 "^status|st":
3777 (status,
3777 (status,
3778 [('A', 'all', None, _('show status of all files')),
3778 [('A', 'all', None, _('show status of all files')),
3779 ('m', 'modified', None, _('show only modified files')),
3779 ('m', 'modified', None, _('show only modified files')),
3780 ('a', 'added', None, _('show only added files')),
3780 ('a', 'added', None, _('show only added files')),
3781 ('r', 'removed', None, _('show only removed files')),
3781 ('r', 'removed', None, _('show only removed files')),
3782 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3782 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3783 ('c', 'clean', None, _('show only files without changes')),
3783 ('c', 'clean', None, _('show only files without changes')),
3784 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3784 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3785 ('i', 'ignored', None, _('show only ignored files')),
3785 ('i', 'ignored', None, _('show only ignored files')),
3786 ('n', 'no-status', None, _('hide status prefix')),
3786 ('n', 'no-status', None, _('hide status prefix')),
3787 ('C', 'copies', None, _('show source of copied files')),
3787 ('C', 'copies', None, _('show source of copied files')),
3788 ('0', 'print0', None,
3788 ('0', 'print0', None,
3789 _('end filenames with NUL, for use with xargs')),
3789 _('end filenames with NUL, for use with xargs')),
3790 ('', 'rev', [], _('show difference from revision')),
3790 ('', 'rev', [], _('show difference from revision')),
3791 ('', 'change', '', _('list the changed files of a revision')),
3791 ('', 'change', '', _('list the changed files of a revision')),
3792 ] + walkopts,
3792 ] + walkopts,
3793 _('[OPTION]... [FILE]...')),
3793 _('[OPTION]... [FILE]...')),
3794 "tag":
3794 "tag":
3795 (tag,
3795 (tag,
3796 [('f', 'force', None, _('replace existing tag')),
3796 [('f', 'force', None, _('replace existing tag')),
3797 ('l', 'local', None, _('make the tag local')),
3797 ('l', 'local', None, _('make the tag local')),
3798 ('r', 'rev', '', _('revision to tag')),
3798 ('r', 'rev', '', _('revision to tag')),
3799 ('', 'remove', None, _('remove a tag')),
3799 ('', 'remove', None, _('remove a tag')),
3800 # -l/--local is already there, commitopts cannot be used
3800 # -l/--local is already there, commitopts cannot be used
3801 ('m', 'message', '', _('use <text> as commit message')),
3801 ('m', 'message', '', _('use <text> as commit message')),
3802 ] + commitopts2,
3802 ] + commitopts2,
3803 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3803 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3804 "tags": (tags, [], ''),
3804 "tags": (tags, [], ''),
3805 "tip":
3805 "tip":
3806 (tip,
3806 (tip,
3807 [('p', 'patch', None, _('show patch')),
3807 [('p', 'patch', None, _('show patch')),
3808 ('g', 'git', None, _('use git extended diff format')),
3808 ('g', 'git', None, _('use git extended diff format')),
3809 ] + templateopts,
3809 ] + templateopts,
3810 _('[-p] [-g]')),
3810 _('[-p] [-g]')),
3811 "unbundle":
3811 "unbundle":
3812 (unbundle,
3812 (unbundle,
3813 [('u', 'update', None,
3813 [('u', 'update', None,
3814 _('update to new branch head if changesets were unbundled'))],
3814 _('update to new branch head if changesets were unbundled'))],
3815 _('[-u] FILE...')),
3815 _('[-u] FILE...')),
3816 "^update|up|checkout|co":
3816 "^update|up|checkout|co":
3817 (update,
3817 (update,
3818 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
3818 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
3819 ('c', 'check', None, _('check for uncommitted changes')),
3819 ('c', 'check', None, _('check for uncommitted changes')),
3820 ('d', 'date', '', _('tipmost revision matching date')),
3820 ('d', 'date', '', _('tipmost revision matching date')),
3821 ('r', 'rev', '', _('revision'))],
3821 ('r', 'rev', '', _('revision'))],
3822 _('[-c] [-C] [-d DATE] [[-r] REV]')),
3822 _('[-c] [-C] [-d DATE] [[-r] REV]')),
3823 "verify": (verify, []),
3823 "verify": (verify, []),
3824 "version": (version_, []),
3824 "version": (version_, []),
3825 }
3825 }
3826
3826
3827 norepo = ("clone init version help debugcommands debugcomplete debugdata"
3827 norepo = ("clone init version help debugcommands debugcomplete debugdata"
3828 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3828 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3829 optionalrepo = ("identify paths serve showconfig debugancestor")
3829 optionalrepo = ("identify paths serve showconfig debugancestor")
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now