##// END OF EJS Templates
py3: fix infinitepush extension tests...
Mark Thomas -
r40288:090e5f39 default
parent child Browse files
Show More
@@ -1,620 +1,623 b''
1 test-abort-checkin.t
1 test-abort-checkin.t
2 test-absorb-filefixupstate.py
2 test-absorb-filefixupstate.py
3 test-absorb-phase.t
3 test-absorb-phase.t
4 test-absorb-rename.t
4 test-absorb-rename.t
5 test-absorb-strip.t
5 test-absorb-strip.t
6 test-absorb.t
6 test-absorb.t
7 test-add.t
7 test-add.t
8 test-addremove-similar.t
8 test-addremove-similar.t
9 test-addremove.t
9 test-addremove.t
10 test-alias.t
10 test-alias.t
11 test-amend-subrepo.t
11 test-amend-subrepo.t
12 test-amend.t
12 test-amend.t
13 test-ancestor.py
13 test-ancestor.py
14 test-annotate.py
14 test-annotate.py
15 test-annotate.t
15 test-annotate.t
16 test-archive-symlinks.t
16 test-archive-symlinks.t
17 test-archive.t
17 test-archive.t
18 test-atomictempfile.py
18 test-atomictempfile.py
19 test-audit-path.t
19 test-audit-path.t
20 test-audit-subrepo.t
20 test-audit-subrepo.t
21 test-automv.t
21 test-automv.t
22 test-backout.t
22 test-backout.t
23 test-backwards-remove.t
23 test-backwards-remove.t
24 test-bad-extension.t
24 test-bad-extension.t
25 test-bad-pull.t
25 test-bad-pull.t
26 test-basic.t
26 test-basic.t
27 test-bdiff.py
27 test-bdiff.py
28 test-bheads.t
28 test-bheads.t
29 test-bisect.t
29 test-bisect.t
30 test-bisect2.t
30 test-bisect2.t
31 test-bisect3.t
31 test-bisect3.t
32 test-blackbox.t
32 test-blackbox.t
33 test-bookmarks-current.t
33 test-bookmarks-current.t
34 test-bookmarks-merge.t
34 test-bookmarks-merge.t
35 test-bookmarks-pushpull.t
35 test-bookmarks-pushpull.t
36 test-bookmarks-rebase.t
36 test-bookmarks-rebase.t
37 test-bookmarks-strip.t
37 test-bookmarks-strip.t
38 test-bookmarks.t
38 test-bookmarks.t
39 test-branch-change.t
39 test-branch-change.t
40 test-branch-option.t
40 test-branch-option.t
41 test-branch-tag-confict.t
41 test-branch-tag-confict.t
42 test-branches.t
42 test-branches.t
43 test-bundle-phases.t
43 test-bundle-phases.t
44 test-bundle-r.t
44 test-bundle-r.t
45 test-bundle-type.t
45 test-bundle-type.t
46 test-bundle-vs-outgoing.t
46 test-bundle-vs-outgoing.t
47 test-bundle.t
47 test-bundle.t
48 test-bundle2-exchange.t
48 test-bundle2-exchange.t
49 test-bundle2-format.t
49 test-bundle2-format.t
50 test-bundle2-multiple-changegroups.t
50 test-bundle2-multiple-changegroups.t
51 test-bundle2-pushback.t
51 test-bundle2-pushback.t
52 test-bundle2-remote-changegroup.t
52 test-bundle2-remote-changegroup.t
53 test-cache-abuse.t
53 test-cache-abuse.t
54 test-cappedreader.py
54 test-cappedreader.py
55 test-casecollision.t
55 test-casecollision.t
56 test-cat.t
56 test-cat.t
57 test-cbor.py
57 test-cbor.py
58 test-censor.t
58 test-censor.t
59 test-changelog-exec.t
59 test-changelog-exec.t
60 test-check-code.t
60 test-check-code.t
61 test-check-commit.t
61 test-check-commit.t
62 test-check-execute.t
62 test-check-execute.t
63 test-check-interfaces.py
63 test-check-interfaces.py
64 test-check-module-imports.t
64 test-check-module-imports.t
65 test-check-py3-compat.t
65 test-check-py3-compat.t
66 test-check-pyflakes.t
66 test-check-pyflakes.t
67 test-check-pylint.t
67 test-check-pylint.t
68 test-check-shbang.t
68 test-check-shbang.t
69 test-children.t
69 test-children.t
70 test-churn.t
70 test-churn.t
71 test-clone-cgi.t
71 test-clone-cgi.t
72 test-clone-pull-corruption.t
72 test-clone-pull-corruption.t
73 test-clone-r.t
73 test-clone-r.t
74 test-clone-uncompressed.t
74 test-clone-uncompressed.t
75 test-clone-update-order.t
75 test-clone-update-order.t
76 test-clone.t
76 test-clone.t
77 test-clonebundles.t
77 test-clonebundles.t
78 test-close-head.t
78 test-close-head.t
79 test-commit-amend.t
79 test-commit-amend.t
80 test-commit-interactive.t
80 test-commit-interactive.t
81 test-commit-multiple.t
81 test-commit-multiple.t
82 test-commit-unresolved.t
82 test-commit-unresolved.t
83 test-commit.t
83 test-commit.t
84 test-committer.t
84 test-committer.t
85 test-completion.t
85 test-completion.t
86 test-config-env.py
86 test-config-env.py
87 test-config.t
87 test-config.t
88 test-conflict.t
88 test-conflict.t
89 test-confused-revert.t
89 test-confused-revert.t
90 test-context.py
90 test-context.py
91 test-contrib-check-code.t
91 test-contrib-check-code.t
92 test-contrib-check-commit.t
92 test-contrib-check-commit.t
93 test-contrib-dumprevlog.t
93 test-contrib-dumprevlog.t
94 test-contrib-perf.t
94 test-contrib-perf.t
95 test-contrib-relnotes.t
95 test-contrib-relnotes.t
96 test-contrib-testparseutil.t
96 test-contrib-testparseutil.t
97 test-convert-authormap.t
97 test-convert-authormap.t
98 test-convert-clonebranches.t
98 test-convert-clonebranches.t
99 test-convert-cvs-branch.t
99 test-convert-cvs-branch.t
100 test-convert-cvs-detectmerge.t
100 test-convert-cvs-detectmerge.t
101 test-convert-cvs-synthetic.t
101 test-convert-cvs-synthetic.t
102 test-convert-cvs.t
102 test-convert-cvs.t
103 test-convert-cvsnt-mergepoints.t
103 test-convert-cvsnt-mergepoints.t
104 test-convert-datesort.t
104 test-convert-datesort.t
105 test-convert-filemap.t
105 test-convert-filemap.t
106 test-convert-hg-sink.t
106 test-convert-hg-sink.t
107 test-convert-hg-source.t
107 test-convert-hg-source.t
108 test-convert-hg-startrev.t
108 test-convert-hg-startrev.t
109 test-convert-splicemap.t
109 test-convert-splicemap.t
110 test-convert-tagsbranch-topology.t
110 test-convert-tagsbranch-topology.t
111 test-copy-move-merge.t
111 test-copy-move-merge.t
112 test-copy.t
112 test-copy.t
113 test-copytrace-heuristics.t
113 test-copytrace-heuristics.t
114 test-debugbuilddag.t
114 test-debugbuilddag.t
115 test-debugbundle.t
115 test-debugbundle.t
116 test-debugcommands.t
116 test-debugcommands.t
117 test-debugextensions.t
117 test-debugextensions.t
118 test-debugindexdot.t
118 test-debugindexdot.t
119 test-debugrename.t
119 test-debugrename.t
120 test-default-push.t
120 test-default-push.t
121 test-diff-antipatience.t
121 test-diff-antipatience.t
122 test-diff-binary-file.t
122 test-diff-binary-file.t
123 test-diff-change.t
123 test-diff-change.t
124 test-diff-copy-depth.t
124 test-diff-copy-depth.t
125 test-diff-hashes.t
125 test-diff-hashes.t
126 test-diff-ignore-whitespace.t
126 test-diff-ignore-whitespace.t
127 test-diff-indent-heuristic.t
127 test-diff-indent-heuristic.t
128 test-diff-issue2761.t
128 test-diff-issue2761.t
129 test-diff-newlines.t
129 test-diff-newlines.t
130 test-diff-reverse.t
130 test-diff-reverse.t
131 test-diff-subdir.t
131 test-diff-subdir.t
132 test-diff-unified.t
132 test-diff-unified.t
133 test-diff-upgrade.t
133 test-diff-upgrade.t
134 test-diffdir.t
134 test-diffdir.t
135 test-diffstat.t
135 test-diffstat.t
136 test-directaccess.t
136 test-directaccess.t
137 test-dirstate-backup.t
137 test-dirstate-backup.t
138 test-dirstate-nonnormalset.t
138 test-dirstate-nonnormalset.t
139 test-dirstate.t
139 test-dirstate.t
140 test-dispatch.py
140 test-dispatch.py
141 test-doctest.py
141 test-doctest.py
142 test-double-merge.t
142 test-double-merge.t
143 test-drawdag.t
143 test-drawdag.t
144 test-duplicateoptions.py
144 test-duplicateoptions.py
145 test-editor-filename.t
145 test-editor-filename.t
146 test-empty-dir.t
146 test-empty-dir.t
147 test-empty-file.t
147 test-empty-file.t
148 test-empty-group.t
148 test-empty-group.t
149 test-empty.t
149 test-empty.t
150 test-encode.t
150 test-encode.t
151 test-encoding-func.py
151 test-encoding-func.py
152 test-encoding.t
152 test-encoding.t
153 test-eol-add.t
153 test-eol-add.t
154 test-eol-clone.t
154 test-eol-clone.t
155 test-eol-hook.t
155 test-eol-hook.t
156 test-eol-patch.t
156 test-eol-patch.t
157 test-eol-tag.t
157 test-eol-tag.t
158 test-eol-update.t
158 test-eol-update.t
159 test-eol.t
159 test-eol.t
160 test-eolfilename.t
160 test-eolfilename.t
161 test-excessive-merge.t
161 test-excessive-merge.t
162 test-exchange-obsmarkers-case-A1.t
162 test-exchange-obsmarkers-case-A1.t
163 test-exchange-obsmarkers-case-A2.t
163 test-exchange-obsmarkers-case-A2.t
164 test-exchange-obsmarkers-case-A3.t
164 test-exchange-obsmarkers-case-A3.t
165 test-exchange-obsmarkers-case-A4.t
165 test-exchange-obsmarkers-case-A4.t
166 test-exchange-obsmarkers-case-A5.t
166 test-exchange-obsmarkers-case-A5.t
167 test-exchange-obsmarkers-case-A6.t
167 test-exchange-obsmarkers-case-A6.t
168 test-exchange-obsmarkers-case-A7.t
168 test-exchange-obsmarkers-case-A7.t
169 test-exchange-obsmarkers-case-B1.t
169 test-exchange-obsmarkers-case-B1.t
170 test-exchange-obsmarkers-case-B2.t
170 test-exchange-obsmarkers-case-B2.t
171 test-exchange-obsmarkers-case-B3.t
171 test-exchange-obsmarkers-case-B3.t
172 test-exchange-obsmarkers-case-B4.t
172 test-exchange-obsmarkers-case-B4.t
173 test-exchange-obsmarkers-case-B5.t
173 test-exchange-obsmarkers-case-B5.t
174 test-exchange-obsmarkers-case-B6.t
174 test-exchange-obsmarkers-case-B6.t
175 test-exchange-obsmarkers-case-B7.t
175 test-exchange-obsmarkers-case-B7.t
176 test-exchange-obsmarkers-case-C1.t
176 test-exchange-obsmarkers-case-C1.t
177 test-exchange-obsmarkers-case-C2.t
177 test-exchange-obsmarkers-case-C2.t
178 test-exchange-obsmarkers-case-C3.t
178 test-exchange-obsmarkers-case-C3.t
179 test-exchange-obsmarkers-case-C4.t
179 test-exchange-obsmarkers-case-C4.t
180 test-exchange-obsmarkers-case-D1.t
180 test-exchange-obsmarkers-case-D1.t
181 test-exchange-obsmarkers-case-D2.t
181 test-exchange-obsmarkers-case-D2.t
182 test-exchange-obsmarkers-case-D3.t
182 test-exchange-obsmarkers-case-D3.t
183 test-exchange-obsmarkers-case-D4.t
183 test-exchange-obsmarkers-case-D4.t
184 test-execute-bit.t
184 test-execute-bit.t
185 test-export.t
185 test-export.t
186 test-extdata.t
186 test-extdata.t
187 test-extdiff.t
187 test-extdiff.t
188 test-extensions-afterloaded.t
188 test-extensions-afterloaded.t
189 test-extensions-wrapfunction.py
189 test-extensions-wrapfunction.py
190 test-extra-filelog-entry.t
190 test-extra-filelog-entry.t
191 test-fetch.t
191 test-fetch.t
192 test-filebranch.t
192 test-filebranch.t
193 test-filecache.py
193 test-filecache.py
194 test-filelog.py
194 test-filelog.py
195 test-fileset-generated.t
195 test-fileset-generated.t
196 test-fileset.t
196 test-fileset.t
197 test-fix-topology.t
197 test-fix-topology.t
198 test-flags.t
198 test-flags.t
199 test-generaldelta.t
199 test-generaldelta.t
200 test-getbundle.t
200 test-getbundle.t
201 test-git-export.t
201 test-git-export.t
202 test-globalopts.t
202 test-globalopts.t
203 test-glog-beautifygraph.t
203 test-glog-beautifygraph.t
204 test-glog-topological.t
204 test-glog-topological.t
205 test-glog.t
205 test-glog.t
206 test-gpg.t
206 test-gpg.t
207 test-graft.t
207 test-graft.t
208 test-grep.t
208 test-grep.t
209 test-hg-parseurl.py
209 test-hg-parseurl.py
210 test-hghave.t
210 test-hghave.t
211 test-hgignore.t
211 test-hgignore.t
212 test-hgk.t
212 test-hgk.t
213 test-hgrc.t
213 test-hgrc.t
214 test-hgweb-annotate-whitespace.t
214 test-hgweb-annotate-whitespace.t
215 test-hgweb-bundle.t
215 test-hgweb-bundle.t
216 test-hgweb-csp.t
216 test-hgweb-csp.t
217 test-hgweb-descend-empties.t
217 test-hgweb-descend-empties.t
218 test-hgweb-diffs.t
218 test-hgweb-diffs.t
219 test-hgweb-empty.t
219 test-hgweb-empty.t
220 test-hgweb-filelog.t
220 test-hgweb-filelog.t
221 test-hgweb-non-interactive.t
221 test-hgweb-non-interactive.t
222 test-hgweb-raw.t
222 test-hgweb-raw.t
223 test-hgweb-removed.t
223 test-hgweb-removed.t
224 test-hgweb.t
224 test-hgweb.t
225 test-hgwebdir-paths.py
225 test-hgwebdir-paths.py
226 test-hgwebdirsym.t
226 test-hgwebdirsym.t
227 test-histedit-arguments.t
227 test-histedit-arguments.t
228 test-histedit-base.t
228 test-histedit-base.t
229 test-histedit-bookmark-motion.t
229 test-histedit-bookmark-motion.t
230 test-histedit-commute.t
230 test-histedit-commute.t
231 test-histedit-drop.t
231 test-histedit-drop.t
232 test-histedit-edit.t
232 test-histedit-edit.t
233 test-histedit-fold-non-commute.t
233 test-histedit-fold-non-commute.t
234 test-histedit-fold.t
234 test-histedit-fold.t
235 test-histedit-no-backup.t
235 test-histedit-no-backup.t
236 test-histedit-no-change.t
236 test-histedit-no-change.t
237 test-histedit-non-commute-abort.t
237 test-histedit-non-commute-abort.t
238 test-histedit-non-commute.t
238 test-histedit-non-commute.t
239 test-histedit-obsolete.t
239 test-histedit-obsolete.t
240 test-histedit-outgoing.t
240 test-histedit-outgoing.t
241 test-histedit-templates.t
241 test-histedit-templates.t
242 test-http-branchmap.t
242 test-http-branchmap.t
243 test-http-bundle1.t
243 test-http-bundle1.t
244 test-http-clone-r.t
244 test-http-clone-r.t
245 test-http-permissions.t
245 test-http-permissions.t
246 test-http.t
246 test-http.t
247 test-hybridencode.py
247 test-hybridencode.py
248 test-i18n.t
248 test-i18n.t
249 test-identify.t
249 test-identify.t
250 test-impexp-branch.t
250 test-impexp-branch.t
251 test-import-bypass.t
251 test-import-bypass.t
252 test-import-eol.t
252 test-import-eol.t
253 test-import-merge.t
253 test-import-merge.t
254 test-import-unknown.t
254 test-import-unknown.t
255 test-import.t
255 test-import.t
256 test-imports-checker.t
256 test-imports-checker.t
257 test-incoming-outgoing.t
257 test-incoming-outgoing.t
258 test-infinitepush-bundlestore.t
259 test-infinitepush-ci.t
260 test-infinitepush.t
258 test-inherit-mode.t
261 test-inherit-mode.t
259 test-init.t
262 test-init.t
260 test-issue1089.t
263 test-issue1089.t
261 test-issue1102.t
264 test-issue1102.t
262 test-issue1175.t
265 test-issue1175.t
263 test-issue1306.t
266 test-issue1306.t
264 test-issue1438.t
267 test-issue1438.t
265 test-issue1502.t
268 test-issue1502.t
266 test-issue1802.t
269 test-issue1802.t
267 test-issue1877.t
270 test-issue1877.t
268 test-issue1993.t
271 test-issue1993.t
269 test-issue2137.t
272 test-issue2137.t
270 test-issue3084.t
273 test-issue3084.t
271 test-issue4074.t
274 test-issue4074.t
272 test-issue522.t
275 test-issue522.t
273 test-issue586.t
276 test-issue586.t
274 test-issue5979.t
277 test-issue5979.t
275 test-issue612.t
278 test-issue612.t
276 test-issue619.t
279 test-issue619.t
277 test-issue660.t
280 test-issue660.t
278 test-issue672.t
281 test-issue672.t
279 test-issue842.t
282 test-issue842.t
280 test-journal-exists.t
283 test-journal-exists.t
281 test-journal-share.t
284 test-journal-share.t
282 test-journal.t
285 test-journal.t
283 test-known.t
286 test-known.t
284 test-largefiles-cache.t
287 test-largefiles-cache.t
285 test-largefiles-misc.t
288 test-largefiles-misc.t
286 test-largefiles-small-disk.t
289 test-largefiles-small-disk.t
287 test-largefiles-update.t
290 test-largefiles-update.t
288 test-largefiles.t
291 test-largefiles.t
289 test-lfs-largefiles.t
292 test-lfs-largefiles.t
290 test-lfs-pointer.py
293 test-lfs-pointer.py
291 test-linelog.py
294 test-linelog.py
292 test-linerange.py
295 test-linerange.py
293 test-locate.t
296 test-locate.t
294 test-lock-badness.t
297 test-lock-badness.t
295 test-log-linerange.t
298 test-log-linerange.t
296 test-log.t
299 test-log.t
297 test-logexchange.t
300 test-logexchange.t
298 test-lrucachedict.py
301 test-lrucachedict.py
299 test-mactext.t
302 test-mactext.t
300 test-mailmap.t
303 test-mailmap.t
301 test-manifest-merging.t
304 test-manifest-merging.t
302 test-manifest.py
305 test-manifest.py
303 test-manifest.t
306 test-manifest.t
304 test-match.py
307 test-match.py
305 test-mdiff.py
308 test-mdiff.py
306 test-merge-changedelete.t
309 test-merge-changedelete.t
307 test-merge-closedheads.t
310 test-merge-closedheads.t
308 test-merge-commit.t
311 test-merge-commit.t
309 test-merge-criss-cross.t
312 test-merge-criss-cross.t
310 test-merge-default.t
313 test-merge-default.t
311 test-merge-force.t
314 test-merge-force.t
312 test-merge-halt.t
315 test-merge-halt.t
313 test-merge-internal-tools-pattern.t
316 test-merge-internal-tools-pattern.t
314 test-merge-local.t
317 test-merge-local.t
315 test-merge-no-file-change.t
318 test-merge-no-file-change.t
316 test-merge-remove.t
319 test-merge-remove.t
317 test-merge-revert.t
320 test-merge-revert.t
318 test-merge-revert2.t
321 test-merge-revert2.t
319 test-merge-subrepos.t
322 test-merge-subrepos.t
320 test-merge-symlinks.t
323 test-merge-symlinks.t
321 test-merge-tools.t
324 test-merge-tools.t
322 test-merge-types.t
325 test-merge-types.t
323 test-merge1.t
326 test-merge1.t
324 test-merge10.t
327 test-merge10.t
325 test-merge2.t
328 test-merge2.t
326 test-merge4.t
329 test-merge4.t
327 test-merge5.t
330 test-merge5.t
328 test-merge6.t
331 test-merge6.t
329 test-merge7.t
332 test-merge7.t
330 test-merge8.t
333 test-merge8.t
331 test-merge9.t
334 test-merge9.t
332 test-minifileset.py
335 test-minifileset.py
333 test-minirst.py
336 test-minirst.py
334 test-mq-git.t
337 test-mq-git.t
335 test-mq-guards.t
338 test-mq-guards.t
336 test-mq-header-date.t
339 test-mq-header-date.t
337 test-mq-header-from.t
340 test-mq-header-from.t
338 test-mq-merge.t
341 test-mq-merge.t
339 test-mq-pull-from-bundle.t
342 test-mq-pull-from-bundle.t
340 test-mq-qclone-http.t
343 test-mq-qclone-http.t
341 test-mq-qdelete.t
344 test-mq-qdelete.t
342 test-mq-qdiff.t
345 test-mq-qdiff.t
343 test-mq-qfold.t
346 test-mq-qfold.t
344 test-mq-qgoto.t
347 test-mq-qgoto.t
345 test-mq-qimport-fail-cleanup.t
348 test-mq-qimport-fail-cleanup.t
346 test-mq-qnew.t
349 test-mq-qnew.t
347 test-mq-qpush-exact.t
350 test-mq-qpush-exact.t
348 test-mq-qpush-fail.t
351 test-mq-qpush-fail.t
349 test-mq-qqueue.t
352 test-mq-qqueue.t
350 test-mq-qrefresh-interactive.t
353 test-mq-qrefresh-interactive.t
351 test-mq-qrefresh-replace-log-message.t
354 test-mq-qrefresh-replace-log-message.t
352 test-mq-qrefresh.t
355 test-mq-qrefresh.t
353 test-mq-qrename.t
356 test-mq-qrename.t
354 test-mq-qsave.t
357 test-mq-qsave.t
355 test-mq-safety.t
358 test-mq-safety.t
356 test-mq-subrepo.t
359 test-mq-subrepo.t
357 test-mq-symlinks.t
360 test-mq-symlinks.t
358 test-mq.t
361 test-mq.t
359 test-mv-cp-st-diff.t
362 test-mv-cp-st-diff.t
360 test-narrow-acl.t
363 test-narrow-acl.t
361 test-narrow-archive.t
364 test-narrow-archive.t
362 test-narrow-clone-no-ellipsis.t
365 test-narrow-clone-no-ellipsis.t
363 test-narrow-clone-non-narrow-server.t
366 test-narrow-clone-non-narrow-server.t
364 test-narrow-clone-nonlinear.t
367 test-narrow-clone-nonlinear.t
365 test-narrow-clone.t
368 test-narrow-clone.t
366 test-narrow-commit.t
369 test-narrow-commit.t
367 test-narrow-copies.t
370 test-narrow-copies.t
368 test-narrow-debugcommands.t
371 test-narrow-debugcommands.t
369 test-narrow-debugrebuilddirstate.t
372 test-narrow-debugrebuilddirstate.t
370 test-narrow-exchange-merges.t
373 test-narrow-exchange-merges.t
371 test-narrow-exchange.t
374 test-narrow-exchange.t
372 test-narrow-expanddirstate.t
375 test-narrow-expanddirstate.t
373 test-narrow-merge.t
376 test-narrow-merge.t
374 test-narrow-patch.t
377 test-narrow-patch.t
375 test-narrow-patterns.t
378 test-narrow-patterns.t
376 test-narrow-pull.t
379 test-narrow-pull.t
377 test-narrow-rebase.t
380 test-narrow-rebase.t
378 test-narrow-shallow-merges.t
381 test-narrow-shallow-merges.t
379 test-narrow-shallow.t
382 test-narrow-shallow.t
380 test-narrow-strip.t
383 test-narrow-strip.t
381 test-narrow-trackedcmd.t
384 test-narrow-trackedcmd.t
382 test-narrow-update.t
385 test-narrow-update.t
383 test-narrow-widen-no-ellipsis.t
386 test-narrow-widen-no-ellipsis.t
384 test-narrow-widen.t
387 test-narrow-widen.t
385 test-narrow.t
388 test-narrow.t
386 test-nested-repo.t
389 test-nested-repo.t
387 test-newbranch.t
390 test-newbranch.t
388 test-newercgi.t
391 test-newercgi.t
389 test-nointerrupt.t
392 test-nointerrupt.t
390 test-obshistory.t
393 test-obshistory.t
391 test-obsmarker-template.t
394 test-obsmarker-template.t
392 test-obsmarkers-effectflag.t
395 test-obsmarkers-effectflag.t
393 test-obsolete-bounds-checking.t
396 test-obsolete-bounds-checking.t
394 test-obsolete-bundle-strip.t
397 test-obsolete-bundle-strip.t
395 test-obsolete-changeset-exchange.t
398 test-obsolete-changeset-exchange.t
396 test-obsolete-checkheads.t
399 test-obsolete-checkheads.t
397 test-obsolete-distributed.t
400 test-obsolete-distributed.t
398 test-obsolete-divergent.t
401 test-obsolete-divergent.t
399 test-obsolete-tag-cache.t
402 test-obsolete-tag-cache.t
400 test-obsolete.t
403 test-obsolete.t
401 test-origbackup-conflict.t
404 test-origbackup-conflict.t
402 test-pager-legacy.t
405 test-pager-legacy.t
403 test-pager.t
406 test-pager.t
404 test-parents.t
407 test-parents.t
405 test-parseindex2.py
408 test-parseindex2.py
406 test-patch-offset.t
409 test-patch-offset.t
407 test-patch.t
410 test-patch.t
408 test-patchbomb-bookmark.t
411 test-patchbomb-bookmark.t
409 test-patchbomb-tls.t
412 test-patchbomb-tls.t
410 test-patchbomb.t
413 test-patchbomb.t
411 test-pathconflicts-basic.t
414 test-pathconflicts-basic.t
412 test-pathconflicts-merge.t
415 test-pathconflicts-merge.t
413 test-pathconflicts-update.t
416 test-pathconflicts-update.t
414 test-pathencode.py
417 test-pathencode.py
415 test-pending.t
418 test-pending.t
416 test-permissions.t
419 test-permissions.t
417 test-phases-exchange.t
420 test-phases-exchange.t
418 test-phases.t
421 test-phases.t
419 test-profile.t
422 test-profile.t
420 test-progress.t
423 test-progress.t
421 test-pull-branch.t
424 test-pull-branch.t
422 test-pull-http.t
425 test-pull-http.t
423 test-pull-permission.t
426 test-pull-permission.t
424 test-pull-pull-corruption.t
427 test-pull-pull-corruption.t
425 test-pull-r.t
428 test-pull-r.t
426 test-pull-update.t
429 test-pull-update.t
427 test-pull.t
430 test-pull.t
428 test-purge.t
431 test-purge.t
429 test-push-cgi.t
432 test-push-cgi.t
430 test-push-checkheads-partial-C1.t
433 test-push-checkheads-partial-C1.t
431 test-push-checkheads-partial-C2.t
434 test-push-checkheads-partial-C2.t
432 test-push-checkheads-partial-C3.t
435 test-push-checkheads-partial-C3.t
433 test-push-checkheads-partial-C4.t
436 test-push-checkheads-partial-C4.t
434 test-push-checkheads-pruned-B1.t
437 test-push-checkheads-pruned-B1.t
435 test-push-checkheads-pruned-B2.t
438 test-push-checkheads-pruned-B2.t
436 test-push-checkheads-pruned-B3.t
439 test-push-checkheads-pruned-B3.t
437 test-push-checkheads-pruned-B4.t
440 test-push-checkheads-pruned-B4.t
438 test-push-checkheads-pruned-B5.t
441 test-push-checkheads-pruned-B5.t
439 test-push-checkheads-pruned-B6.t
442 test-push-checkheads-pruned-B6.t
440 test-push-checkheads-pruned-B7.t
443 test-push-checkheads-pruned-B7.t
441 test-push-checkheads-pruned-B8.t
444 test-push-checkheads-pruned-B8.t
442 test-push-checkheads-superceed-A1.t
445 test-push-checkheads-superceed-A1.t
443 test-push-checkheads-superceed-A2.t
446 test-push-checkheads-superceed-A2.t
444 test-push-checkheads-superceed-A3.t
447 test-push-checkheads-superceed-A3.t
445 test-push-checkheads-superceed-A4.t
448 test-push-checkheads-superceed-A4.t
446 test-push-checkheads-superceed-A5.t
449 test-push-checkheads-superceed-A5.t
447 test-push-checkheads-superceed-A6.t
450 test-push-checkheads-superceed-A6.t
448 test-push-checkheads-superceed-A7.t
451 test-push-checkheads-superceed-A7.t
449 test-push-checkheads-superceed-A8.t
452 test-push-checkheads-superceed-A8.t
450 test-push-checkheads-unpushed-D1.t
453 test-push-checkheads-unpushed-D1.t
451 test-push-checkheads-unpushed-D2.t
454 test-push-checkheads-unpushed-D2.t
452 test-push-checkheads-unpushed-D3.t
455 test-push-checkheads-unpushed-D3.t
453 test-push-checkheads-unpushed-D4.t
456 test-push-checkheads-unpushed-D4.t
454 test-push-checkheads-unpushed-D5.t
457 test-push-checkheads-unpushed-D5.t
455 test-push-checkheads-unpushed-D6.t
458 test-push-checkheads-unpushed-D6.t
456 test-push-checkheads-unpushed-D7.t
459 test-push-checkheads-unpushed-D7.t
457 test-push-http.t
460 test-push-http.t
458 test-push-warn.t
461 test-push-warn.t
459 test-push.t
462 test-push.t
460 test-pushvars.t
463 test-pushvars.t
461 test-qrecord.t
464 test-qrecord.t
462 test-rebase-abort.t
465 test-rebase-abort.t
463 test-rebase-backup.t
466 test-rebase-backup.t
464 test-rebase-base-flag.t
467 test-rebase-base-flag.t
465 test-rebase-bookmarks.t
468 test-rebase-bookmarks.t
466 test-rebase-brute-force.t
469 test-rebase-brute-force.t
467 test-rebase-cache.t
470 test-rebase-cache.t
468 test-rebase-check-restore.t
471 test-rebase-check-restore.t
469 test-rebase-collapse.t
472 test-rebase-collapse.t
470 test-rebase-conflicts.t
473 test-rebase-conflicts.t
471 test-rebase-dest.t
474 test-rebase-dest.t
472 test-rebase-detach.t
475 test-rebase-detach.t
473 test-rebase-emptycommit.t
476 test-rebase-emptycommit.t
474 test-rebase-inmemory.t
477 test-rebase-inmemory.t
475 test-rebase-interruptions.t
478 test-rebase-interruptions.t
476 test-rebase-issue-noparam-single-rev.t
479 test-rebase-issue-noparam-single-rev.t
477 test-rebase-legacy.t
480 test-rebase-legacy.t
478 test-rebase-mq-skip.t
481 test-rebase-mq-skip.t
479 test-rebase-mq.t
482 test-rebase-mq.t
480 test-rebase-named-branches.t
483 test-rebase-named-branches.t
481 test-rebase-newancestor.t
484 test-rebase-newancestor.t
482 test-rebase-obsolete.t
485 test-rebase-obsolete.t
483 test-rebase-parameters.t
486 test-rebase-parameters.t
484 test-rebase-partial.t
487 test-rebase-partial.t
485 test-rebase-pull.t
488 test-rebase-pull.t
486 test-rebase-rename.t
489 test-rebase-rename.t
487 test-rebase-scenario-global.t
490 test-rebase-scenario-global.t
488 test-rebase-templates.t
491 test-rebase-templates.t
489 test-rebase-transaction.t
492 test-rebase-transaction.t
490 test-rebuildstate.t
493 test-rebuildstate.t
491 test-record.t
494 test-record.t
492 test-releasenotes-formatting.t
495 test-releasenotes-formatting.t
493 test-releasenotes-merging.t
496 test-releasenotes-merging.t
494 test-releasenotes-parsing.t
497 test-releasenotes-parsing.t
495 test-relink.t
498 test-relink.t
496 test-remove.t
499 test-remove.t
497 test-removeemptydirs.t
500 test-removeemptydirs.t
498 test-rename-after-merge.t
501 test-rename-after-merge.t
499 test-rename-dir-merge.t
502 test-rename-dir-merge.t
500 test-rename-merge1.t
503 test-rename-merge1.t
501 test-rename-merge2.t
504 test-rename-merge2.t
502 test-rename.t
505 test-rename.t
503 test-repair-strip.t
506 test-repair-strip.t
504 test-repo-compengines.t
507 test-repo-compengines.t
505 test-requires.t
508 test-requires.t
506 test-resolve.t
509 test-resolve.t
507 test-revert-flags.t
510 test-revert-flags.t
508 test-revert-interactive.t
511 test-revert-interactive.t
509 test-revert-unknown.t
512 test-revert-unknown.t
510 test-revert.t
513 test-revert.t
511 test-revisions.t
514 test-revisions.t
512 test-revlog-ancestry.py
515 test-revlog-ancestry.py
513 test-revlog-group-emptyiter.t
516 test-revlog-group-emptyiter.t
514 test-revlog-mmapindex.t
517 test-revlog-mmapindex.t
515 test-revlog-packentry.t
518 test-revlog-packentry.t
516 test-revlog-raw.py
519 test-revlog-raw.py
517 test-revlog-v2.t
520 test-revlog-v2.t
518 test-revset-dirstate-parents.t
521 test-revset-dirstate-parents.t
519 test-revset-legacy-lookup.t
522 test-revset-legacy-lookup.t
520 test-revset-outgoing.t
523 test-revset-outgoing.t
521 test-rollback.t
524 test-rollback.t
522 test-run-tests.py
525 test-run-tests.py
523 test-run-tests.t
526 test-run-tests.t
524 test-schemes.t
527 test-schemes.t
525 test-serve.t
528 test-serve.t
526 test-setdiscovery.t
529 test-setdiscovery.t
527 test-share.t
530 test-share.t
528 test-shelve.t
531 test-shelve.t
529 test-show-stack.t
532 test-show-stack.t
530 test-show-work.t
533 test-show-work.t
531 test-show.t
534 test-show.t
532 test-simple-update.t
535 test-simple-update.t
533 test-simplekeyvaluefile.py
536 test-simplekeyvaluefile.py
534 test-simplemerge.py
537 test-simplemerge.py
535 test-single-head.t
538 test-single-head.t
536 test-sparse-clear.t
539 test-sparse-clear.t
537 test-sparse-clone.t
540 test-sparse-clone.t
538 test-sparse-import.t
541 test-sparse-import.t
539 test-sparse-merges.t
542 test-sparse-merges.t
540 test-sparse-profiles.t
543 test-sparse-profiles.t
541 test-sparse-requirement.t
544 test-sparse-requirement.t
542 test-sparse-verbose-json.t
545 test-sparse-verbose-json.t
543 test-sparse.t
546 test-sparse.t
544 test-split.t
547 test-split.t
545 test-ssh-bundle1.t
548 test-ssh-bundle1.t
546 test-ssh-clone-r.t
549 test-ssh-clone-r.t
547 test-ssh-proto-unbundle.t
550 test-ssh-proto-unbundle.t
548 test-ssh-proto.t
551 test-ssh-proto.t
549 test-ssh-repoerror.t
552 test-ssh-repoerror.t
550 test-ssh.t
553 test-ssh.t
551 test-sshserver.py
554 test-sshserver.py
552 test-stack.t
555 test-stack.t
553 test-status-color.t
556 test-status-color.t
554 test-status-inprocess.py
557 test-status-inprocess.py
555 test-status-rev.t
558 test-status-rev.t
556 test-status-terse.t
559 test-status-terse.t
557 test-storage.py
560 test-storage.py
558 test-stream-bundle-v2.t
561 test-stream-bundle-v2.t
559 test-strict.t
562 test-strict.t
560 test-strip-cross.t
563 test-strip-cross.t
561 test-strip.t
564 test-strip.t
562 test-subrepo-deep-nested-change.t
565 test-subrepo-deep-nested-change.t
563 test-subrepo-missing.t
566 test-subrepo-missing.t
564 test-subrepo-paths.t
567 test-subrepo-paths.t
565 test-subrepo-recursion.t
568 test-subrepo-recursion.t
566 test-subrepo-relative-path.t
569 test-subrepo-relative-path.t
567 test-subrepo.t
570 test-subrepo.t
568 test-symlink-os-yes-fs-no.py
571 test-symlink-os-yes-fs-no.py
569 test-symlink-placeholder.t
572 test-symlink-placeholder.t
570 test-symlinks.t
573 test-symlinks.t
571 test-tag.t
574 test-tag.t
572 test-tags.t
575 test-tags.t
573 test-template-basic.t
576 test-template-basic.t
574 test-template-functions.t
577 test-template-functions.t
575 test-template-keywords.t
578 test-template-keywords.t
576 test-template-map.t
579 test-template-map.t
577 test-tools.t
580 test-tools.t
578 test-transplant.t
581 test-transplant.t
579 test-treemanifest.t
582 test-treemanifest.t
580 test-ui-color.py
583 test-ui-color.py
581 test-ui-config.py
584 test-ui-config.py
582 test-ui-verbosity.py
585 test-ui-verbosity.py
583 test-unamend.t
586 test-unamend.t
584 test-unbundlehash.t
587 test-unbundlehash.t
585 test-uncommit.t
588 test-uncommit.t
586 test-unified-test.t
589 test-unified-test.t
587 test-unionrepo.t
590 test-unionrepo.t
588 test-unrelated-pull.t
591 test-unrelated-pull.t
589 test-up-local-change.t
592 test-up-local-change.t
590 test-update-branches.t
593 test-update-branches.t
591 test-update-dest.t
594 test-update-dest.t
592 test-update-issue1456.t
595 test-update-issue1456.t
593 test-update-names.t
596 test-update-names.t
594 test-update-reverse.t
597 test-update-reverse.t
595 test-upgrade-repo.t
598 test-upgrade-repo.t
596 test-url-download.t
599 test-url-download.t
597 test-url-rev.t
600 test-url-rev.t
598 test-url.py
601 test-url.py
599 test-username-newline.t
602 test-username-newline.t
600 test-util.py
603 test-util.py
601 test-verify.t
604 test-verify.t
602 test-walk.t
605 test-walk.t
603 test-walkrepo.py
606 test-walkrepo.py
604 test-websub.t
607 test-websub.t
605 test-win32text.t
608 test-win32text.t
606 test-wireproto-clientreactor.py
609 test-wireproto-clientreactor.py
607 test-wireproto-command-branchmap.t
610 test-wireproto-command-branchmap.t
608 test-wireproto-command-changesetdata.t
611 test-wireproto-command-changesetdata.t
609 test-wireproto-command-filedata.t
612 test-wireproto-command-filedata.t
610 test-wireproto-command-filesdata.t
613 test-wireproto-command-filesdata.t
611 test-wireproto-command-heads.t
614 test-wireproto-command-heads.t
612 test-wireproto-command-listkeys.t
615 test-wireproto-command-listkeys.t
613 test-wireproto-command-lookup.t
616 test-wireproto-command-lookup.t
614 test-wireproto-command-manifestdata.t
617 test-wireproto-command-manifestdata.t
615 test-wireproto-command-pushkey.t
618 test-wireproto-command-pushkey.t
616 test-wireproto-framing.py
619 test-wireproto-framing.py
617 test-wireproto-serverreactor.py
620 test-wireproto-serverreactor.py
618 test-wireproto.py
621 test-wireproto.py
619 test-wsgirequest.py
622 test-wsgirequest.py
620 test-xdg.t
623 test-xdg.t
@@ -1,1187 +1,1186 b''
1 # Infinite push
1 # Infinite push
2 #
2 #
3 # Copyright 2016 Facebook, Inc.
3 # Copyright 2016 Facebook, Inc.
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 """ store some pushes in a remote blob store on the server (EXPERIMENTAL)
7 """ store some pushes in a remote blob store on the server (EXPERIMENTAL)
8
8
9 [infinitepush]
9 [infinitepush]
10 # Server-side and client-side option. Pattern of the infinitepush bookmark
10 # Server-side and client-side option. Pattern of the infinitepush bookmark
11 branchpattern = PATTERN
11 branchpattern = PATTERN
12
12
13 # Server or client
13 # Server or client
14 server = False
14 server = False
15
15
16 # Server-side option. Possible values: 'disk' or 'sql'. Fails if not set
16 # Server-side option. Possible values: 'disk' or 'sql'. Fails if not set
17 indextype = disk
17 indextype = disk
18
18
19 # Server-side option. Used only if indextype=sql.
19 # Server-side option. Used only if indextype=sql.
20 # Format: 'IP:PORT:DB_NAME:USER:PASSWORD'
20 # Format: 'IP:PORT:DB_NAME:USER:PASSWORD'
21 sqlhost = IP:PORT:DB_NAME:USER:PASSWORD
21 sqlhost = IP:PORT:DB_NAME:USER:PASSWORD
22
22
23 # Server-side option. Used only if indextype=disk.
23 # Server-side option. Used only if indextype=disk.
24 # Filesystem path to the index store
24 # Filesystem path to the index store
25 indexpath = PATH
25 indexpath = PATH
26
26
27 # Server-side option. Possible values: 'disk' or 'external'
27 # Server-side option. Possible values: 'disk' or 'external'
28 # Fails if not set
28 # Fails if not set
29 storetype = disk
29 storetype = disk
30
30
31 # Server-side option.
31 # Server-side option.
32 # Path to the binary that will save bundle to the bundlestore
32 # Path to the binary that will save bundle to the bundlestore
33 # Formatted cmd line will be passed to it (see `put_args`)
33 # Formatted cmd line will be passed to it (see `put_args`)
34 put_binary = put
34 put_binary = put
35
35
36 # Serser-side option. Used only if storetype=external.
36 # Serser-side option. Used only if storetype=external.
37 # Format cmd-line string for put binary. Placeholder: {filename}
37 # Format cmd-line string for put binary. Placeholder: {filename}
38 put_args = {filename}
38 put_args = {filename}
39
39
40 # Server-side option.
40 # Server-side option.
41 # Path to the binary that get bundle from the bundlestore.
41 # Path to the binary that get bundle from the bundlestore.
42 # Formatted cmd line will be passed to it (see `get_args`)
42 # Formatted cmd line will be passed to it (see `get_args`)
43 get_binary = get
43 get_binary = get
44
44
45 # Serser-side option. Used only if storetype=external.
45 # Serser-side option. Used only if storetype=external.
46 # Format cmd-line string for get binary. Placeholders: {filename} {handle}
46 # Format cmd-line string for get binary. Placeholders: {filename} {handle}
47 get_args = {filename} {handle}
47 get_args = {filename} {handle}
48
48
49 # Server-side option
49 # Server-side option
50 logfile = FIlE
50 logfile = FIlE
51
51
52 # Server-side option
52 # Server-side option
53 loglevel = DEBUG
53 loglevel = DEBUG
54
54
55 # Server-side option. Used only if indextype=sql.
55 # Server-side option. Used only if indextype=sql.
56 # Sets mysql wait_timeout option.
56 # Sets mysql wait_timeout option.
57 waittimeout = 300
57 waittimeout = 300
58
58
59 # Server-side option. Used only if indextype=sql.
59 # Server-side option. Used only if indextype=sql.
60 # Sets mysql innodb_lock_wait_timeout option.
60 # Sets mysql innodb_lock_wait_timeout option.
61 locktimeout = 120
61 locktimeout = 120
62
62
63 # Server-side option. Used only if indextype=sql.
63 # Server-side option. Used only if indextype=sql.
64 # Name of the repository
64 # Name of the repository
65 reponame = ''
65 reponame = ''
66
66
67 # Client-side option. Used by --list-remote option. List of remote scratch
67 # Client-side option. Used by --list-remote option. List of remote scratch
68 # patterns to list if no patterns are specified.
68 # patterns to list if no patterns are specified.
69 defaultremotepatterns = ['*']
69 defaultremotepatterns = ['*']
70
70
71 # Instructs infinitepush to forward all received bundle2 parts to the
71 # Instructs infinitepush to forward all received bundle2 parts to the
72 # bundle for storage. Defaults to False.
72 # bundle for storage. Defaults to False.
73 storeallparts = True
73 storeallparts = True
74
74
75 # routes each incoming push to the bundlestore. defaults to False
75 # routes each incoming push to the bundlestore. defaults to False
76 pushtobundlestore = True
76 pushtobundlestore = True
77
77
78 [remotenames]
78 [remotenames]
79 # Client-side option
79 # Client-side option
80 # This option should be set only if remotenames extension is enabled.
80 # This option should be set only if remotenames extension is enabled.
81 # Whether remote bookmarks are tracked by remotenames extension.
81 # Whether remote bookmarks are tracked by remotenames extension.
82 bookmarks = True
82 bookmarks = True
83 """
83 """
84
84
85 from __future__ import absolute_import
85 from __future__ import absolute_import
86
86
87 import collections
87 import collections
88 import contextlib
88 import contextlib
89 import errno
89 import errno
90 import functools
90 import functools
91 import logging
91 import logging
92 import os
92 import os
93 import random
93 import random
94 import re
94 import re
95 import socket
95 import socket
96 import subprocess
96 import subprocess
97 import time
97 import time
98
98
99 from mercurial.node import (
99 from mercurial.node import (
100 bin,
100 bin,
101 hex,
101 hex,
102 )
102 )
103
103
104 from mercurial.i18n import _
104 from mercurial.i18n import _
105
105
106 from mercurial.utils import (
106 from mercurial.utils import (
107 procutil,
107 procutil,
108 stringutil,
108 stringutil,
109 )
109 )
110
110
111 from mercurial import (
111 from mercurial import (
112 bundle2,
112 bundle2,
113 changegroup,
113 changegroup,
114 commands,
114 commands,
115 discovery,
115 discovery,
116 encoding,
116 encoding,
117 error,
117 error,
118 exchange,
118 exchange,
119 extensions,
119 extensions,
120 hg,
120 hg,
121 localrepo,
121 localrepo,
122 phases,
122 phases,
123 pushkey,
123 pushkey,
124 pycompat,
124 pycompat,
125 registrar,
125 registrar,
126 util,
126 util,
127 wireprototypes,
127 wireprototypes,
128 wireprotov1peer,
128 wireprotov1peer,
129 wireprotov1server,
129 wireprotov1server,
130 )
130 )
131
131
132 from . import (
132 from . import (
133 bundleparts,
133 bundleparts,
134 common,
134 common,
135 )
135 )
136
136
137 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
137 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
138 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
138 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
139 # be specifying the version(s) of Mercurial they are tested with, or
139 # be specifying the version(s) of Mercurial they are tested with, or
140 # leave the attribute unspecified.
140 # leave the attribute unspecified.
141 testedwith = 'ships-with-hg-core'
141 testedwith = 'ships-with-hg-core'
142
142
143 configtable = {}
143 configtable = {}
144 configitem = registrar.configitem(configtable)
144 configitem = registrar.configitem(configtable)
145
145
146 configitem('infinitepush', 'server',
146 configitem('infinitepush', 'server',
147 default=False,
147 default=False,
148 )
148 )
149 configitem('infinitepush', 'storetype',
149 configitem('infinitepush', 'storetype',
150 default='',
150 default='',
151 )
151 )
152 configitem('infinitepush', 'indextype',
152 configitem('infinitepush', 'indextype',
153 default='',
153 default='',
154 )
154 )
155 configitem('infinitepush', 'indexpath',
155 configitem('infinitepush', 'indexpath',
156 default='',
156 default='',
157 )
157 )
158 configitem('infinitepush', 'storeallparts',
158 configitem('infinitepush', 'storeallparts',
159 default=False,
159 default=False,
160 )
160 )
161 configitem('infinitepush', 'reponame',
161 configitem('infinitepush', 'reponame',
162 default='',
162 default='',
163 )
163 )
164 configitem('scratchbranch', 'storepath',
164 configitem('scratchbranch', 'storepath',
165 default='',
165 default='',
166 )
166 )
167 configitem('infinitepush', 'branchpattern',
167 configitem('infinitepush', 'branchpattern',
168 default='',
168 default='',
169 )
169 )
170 configitem('infinitepush', 'pushtobundlestore',
170 configitem('infinitepush', 'pushtobundlestore',
171 default=False,
171 default=False,
172 )
172 )
173 configitem('experimental', 'server-bundlestore-bookmark',
173 configitem('experimental', 'server-bundlestore-bookmark',
174 default='',
174 default='',
175 )
175 )
176 configitem('experimental', 'infinitepush-scratchpush',
176 configitem('experimental', 'infinitepush-scratchpush',
177 default=False,
177 default=False,
178 )
178 )
179
179
180 experimental = 'experimental'
180 experimental = 'experimental'
181 configbookmark = 'server-bundlestore-bookmark'
181 configbookmark = 'server-bundlestore-bookmark'
182 configscratchpush = 'infinitepush-scratchpush'
182 configscratchpush = 'infinitepush-scratchpush'
183
183
184 scratchbranchparttype = bundleparts.scratchbranchparttype
184 scratchbranchparttype = bundleparts.scratchbranchparttype
185 revsetpredicate = registrar.revsetpredicate()
185 revsetpredicate = registrar.revsetpredicate()
186 templatekeyword = registrar.templatekeyword()
186 templatekeyword = registrar.templatekeyword()
187 _scratchbranchmatcher = lambda x: False
187 _scratchbranchmatcher = lambda x: False
188 _maybehash = re.compile(r'^[a-f0-9]+$').search
188 _maybehash = re.compile(r'^[a-f0-9]+$').search
189
189
190 def _buildexternalbundlestore(ui):
190 def _buildexternalbundlestore(ui):
191 put_args = ui.configlist('infinitepush', 'put_args', [])
191 put_args = ui.configlist('infinitepush', 'put_args', [])
192 put_binary = ui.config('infinitepush', 'put_binary')
192 put_binary = ui.config('infinitepush', 'put_binary')
193 if not put_binary:
193 if not put_binary:
194 raise error.Abort('put binary is not specified')
194 raise error.Abort('put binary is not specified')
195 get_args = ui.configlist('infinitepush', 'get_args', [])
195 get_args = ui.configlist('infinitepush', 'get_args', [])
196 get_binary = ui.config('infinitepush', 'get_binary')
196 get_binary = ui.config('infinitepush', 'get_binary')
197 if not get_binary:
197 if not get_binary:
198 raise error.Abort('get binary is not specified')
198 raise error.Abort('get binary is not specified')
199 from . import store
199 from . import store
200 return store.externalbundlestore(put_binary, put_args, get_binary, get_args)
200 return store.externalbundlestore(put_binary, put_args, get_binary, get_args)
201
201
202 def _buildsqlindex(ui):
202 def _buildsqlindex(ui):
203 sqlhost = ui.config('infinitepush', 'sqlhost')
203 sqlhost = ui.config('infinitepush', 'sqlhost')
204 if not sqlhost:
204 if not sqlhost:
205 raise error.Abort(_('please set infinitepush.sqlhost'))
205 raise error.Abort(_('please set infinitepush.sqlhost'))
206 host, port, db, user, password = sqlhost.split(':')
206 host, port, db, user, password = sqlhost.split(':')
207 reponame = ui.config('infinitepush', 'reponame')
207 reponame = ui.config('infinitepush', 'reponame')
208 if not reponame:
208 if not reponame:
209 raise error.Abort(_('please set infinitepush.reponame'))
209 raise error.Abort(_('please set infinitepush.reponame'))
210
210
211 logfile = ui.config('infinitepush', 'logfile', '')
211 logfile = ui.config('infinitepush', 'logfile', '')
212 waittimeout = ui.configint('infinitepush', 'waittimeout', 300)
212 waittimeout = ui.configint('infinitepush', 'waittimeout', 300)
213 locktimeout = ui.configint('infinitepush', 'locktimeout', 120)
213 locktimeout = ui.configint('infinitepush', 'locktimeout', 120)
214 from . import sqlindexapi
214 from . import sqlindexapi
215 return sqlindexapi.sqlindexapi(
215 return sqlindexapi.sqlindexapi(
216 reponame, host, port, db, user, password,
216 reponame, host, port, db, user, password,
217 logfile, _getloglevel(ui), waittimeout=waittimeout,
217 logfile, _getloglevel(ui), waittimeout=waittimeout,
218 locktimeout=locktimeout)
218 locktimeout=locktimeout)
219
219
220 def _getloglevel(ui):
220 def _getloglevel(ui):
221 loglevel = ui.config('infinitepush', 'loglevel', 'DEBUG')
221 loglevel = ui.config('infinitepush', 'loglevel', 'DEBUG')
222 numeric_loglevel = getattr(logging, loglevel.upper(), None)
222 numeric_loglevel = getattr(logging, loglevel.upper(), None)
223 if not isinstance(numeric_loglevel, int):
223 if not isinstance(numeric_loglevel, int):
224 raise error.Abort(_('invalid log level %s') % loglevel)
224 raise error.Abort(_('invalid log level %s') % loglevel)
225 return numeric_loglevel
225 return numeric_loglevel
226
226
227 def _tryhoist(ui, remotebookmark):
227 def _tryhoist(ui, remotebookmark):
228 '''returns a bookmarks with hoisted part removed
228 '''returns a bookmarks with hoisted part removed
229
229
230 Remotenames extension has a 'hoist' config that allows to use remote
230 Remotenames extension has a 'hoist' config that allows to use remote
231 bookmarks without specifying remote path. For example, 'hg update master'
231 bookmarks without specifying remote path. For example, 'hg update master'
232 works as well as 'hg update remote/master'. We want to allow the same in
232 works as well as 'hg update remote/master'. We want to allow the same in
233 infinitepush.
233 infinitepush.
234 '''
234 '''
235
235
236 if common.isremotebooksenabled(ui):
236 if common.isremotebooksenabled(ui):
237 hoist = ui.config('remotenames', 'hoistedpeer') + '/'
237 hoist = ui.config('remotenames', 'hoistedpeer') + '/'
238 if remotebookmark.startswith(hoist):
238 if remotebookmark.startswith(hoist):
239 return remotebookmark[len(hoist):]
239 return remotebookmark[len(hoist):]
240 return remotebookmark
240 return remotebookmark
241
241
242 class bundlestore(object):
242 class bundlestore(object):
243 def __init__(self, repo):
243 def __init__(self, repo):
244 self._repo = repo
244 self._repo = repo
245 storetype = self._repo.ui.config('infinitepush', 'storetype')
245 storetype = self._repo.ui.config('infinitepush', 'storetype')
246 if storetype == 'disk':
246 if storetype == 'disk':
247 from . import store
247 from . import store
248 self.store = store.filebundlestore(self._repo.ui, self._repo)
248 self.store = store.filebundlestore(self._repo.ui, self._repo)
249 elif storetype == 'external':
249 elif storetype == 'external':
250 self.store = _buildexternalbundlestore(self._repo.ui)
250 self.store = _buildexternalbundlestore(self._repo.ui)
251 else:
251 else:
252 raise error.Abort(
252 raise error.Abort(
253 _('unknown infinitepush store type specified %s') % storetype)
253 _('unknown infinitepush store type specified %s') % storetype)
254
254
255 indextype = self._repo.ui.config('infinitepush', 'indextype')
255 indextype = self._repo.ui.config('infinitepush', 'indextype')
256 if indextype == 'disk':
256 if indextype == 'disk':
257 from . import fileindexapi
257 from . import fileindexapi
258 self.index = fileindexapi.fileindexapi(self._repo)
258 self.index = fileindexapi.fileindexapi(self._repo)
259 elif indextype == 'sql':
259 elif indextype == 'sql':
260 self.index = _buildsqlindex(self._repo.ui)
260 self.index = _buildsqlindex(self._repo.ui)
261 else:
261 else:
262 raise error.Abort(
262 raise error.Abort(
263 _('unknown infinitepush index type specified %s') % indextype)
263 _('unknown infinitepush index type specified %s') % indextype)
264
264
265 def _isserver(ui):
265 def _isserver(ui):
266 return ui.configbool('infinitepush', 'server')
266 return ui.configbool('infinitepush', 'server')
267
267
268 def reposetup(ui, repo):
268 def reposetup(ui, repo):
269 if _isserver(ui) and repo.local():
269 if _isserver(ui) and repo.local():
270 repo.bundlestore = bundlestore(repo)
270 repo.bundlestore = bundlestore(repo)
271
271
272 def extsetup(ui):
272 def extsetup(ui):
273 commonsetup(ui)
273 commonsetup(ui)
274 if _isserver(ui):
274 if _isserver(ui):
275 serverextsetup(ui)
275 serverextsetup(ui)
276 else:
276 else:
277 clientextsetup(ui)
277 clientextsetup(ui)
278
278
279 def commonsetup(ui):
279 def commonsetup(ui):
280 wireprotov1server.commands['listkeyspatterns'] = (
280 wireprotov1server.commands['listkeyspatterns'] = (
281 wireprotolistkeyspatterns, 'namespace patterns')
281 wireprotolistkeyspatterns, 'namespace patterns')
282 scratchbranchpat = ui.config('infinitepush', 'branchpattern')
282 scratchbranchpat = ui.config('infinitepush', 'branchpattern')
283 if scratchbranchpat:
283 if scratchbranchpat:
284 global _scratchbranchmatcher
284 global _scratchbranchmatcher
285 kind, pat, _scratchbranchmatcher = \
285 kind, pat, _scratchbranchmatcher = \
286 stringutil.stringmatcher(scratchbranchpat)
286 stringutil.stringmatcher(scratchbranchpat)
287
287
288 def serverextsetup(ui):
288 def serverextsetup(ui):
289 origpushkeyhandler = bundle2.parthandlermapping['pushkey']
289 origpushkeyhandler = bundle2.parthandlermapping['pushkey']
290
290
291 def newpushkeyhandler(*args, **kwargs):
291 def newpushkeyhandler(*args, **kwargs):
292 bundle2pushkey(origpushkeyhandler, *args, **kwargs)
292 bundle2pushkey(origpushkeyhandler, *args, **kwargs)
293 newpushkeyhandler.params = origpushkeyhandler.params
293 newpushkeyhandler.params = origpushkeyhandler.params
294 bundle2.parthandlermapping['pushkey'] = newpushkeyhandler
294 bundle2.parthandlermapping['pushkey'] = newpushkeyhandler
295
295
296 orighandlephasehandler = bundle2.parthandlermapping['phase-heads']
296 orighandlephasehandler = bundle2.parthandlermapping['phase-heads']
297 newphaseheadshandler = lambda *args, **kwargs: \
297 newphaseheadshandler = lambda *args, **kwargs: \
298 bundle2handlephases(orighandlephasehandler, *args, **kwargs)
298 bundle2handlephases(orighandlephasehandler, *args, **kwargs)
299 newphaseheadshandler.params = orighandlephasehandler.params
299 newphaseheadshandler.params = orighandlephasehandler.params
300 bundle2.parthandlermapping['phase-heads'] = newphaseheadshandler
300 bundle2.parthandlermapping['phase-heads'] = newphaseheadshandler
301
301
302 extensions.wrapfunction(localrepo.localrepository, 'listkeys',
302 extensions.wrapfunction(localrepo.localrepository, 'listkeys',
303 localrepolistkeys)
303 localrepolistkeys)
304 wireprotov1server.commands['lookup'] = (
304 wireprotov1server.commands['lookup'] = (
305 _lookupwrap(wireprotov1server.commands['lookup'][0]), 'key')
305 _lookupwrap(wireprotov1server.commands['lookup'][0]), 'key')
306 extensions.wrapfunction(exchange, 'getbundlechunks', getbundlechunks)
306 extensions.wrapfunction(exchange, 'getbundlechunks', getbundlechunks)
307
307
308 extensions.wrapfunction(bundle2, 'processparts', processparts)
308 extensions.wrapfunction(bundle2, 'processparts', processparts)
309
309
310 def clientextsetup(ui):
310 def clientextsetup(ui):
311 entry = extensions.wrapcommand(commands.table, 'push', _push)
311 entry = extensions.wrapcommand(commands.table, 'push', _push)
312
312
313 entry[1].append(
313 entry[1].append(
314 ('', 'bundle-store', None,
314 ('', 'bundle-store', None,
315 _('force push to go to bundle store (EXPERIMENTAL)')))
315 _('force push to go to bundle store (EXPERIMENTAL)')))
316
316
317 extensions.wrapcommand(commands.table, 'pull', _pull)
317 extensions.wrapcommand(commands.table, 'pull', _pull)
318
318
319 extensions.wrapfunction(discovery, 'checkheads', _checkheads)
319 extensions.wrapfunction(discovery, 'checkheads', _checkheads)
320
320
321 wireprotov1peer.wirepeer.listkeyspatterns = listkeyspatterns
321 wireprotov1peer.wirepeer.listkeyspatterns = listkeyspatterns
322
322
323 partorder = exchange.b2partsgenorder
323 partorder = exchange.b2partsgenorder
324 index = partorder.index('changeset')
324 index = partorder.index('changeset')
325 partorder.insert(
325 partorder.insert(
326 index, partorder.pop(partorder.index(scratchbranchparttype)))
326 index, partorder.pop(partorder.index(scratchbranchparttype)))
327
327
328 def _checkheads(orig, pushop):
328 def _checkheads(orig, pushop):
329 if pushop.ui.configbool(experimental, configscratchpush, False):
329 if pushop.ui.configbool(experimental, configscratchpush, False):
330 return
330 return
331 return orig(pushop)
331 return orig(pushop)
332
332
333 def wireprotolistkeyspatterns(repo, proto, namespace, patterns):
333 def wireprotolistkeyspatterns(repo, proto, namespace, patterns):
334 patterns = wireprototypes.decodelist(patterns)
334 patterns = wireprototypes.decodelist(patterns)
335 d = repo.listkeys(encoding.tolocal(namespace), patterns).iteritems()
335 d = repo.listkeys(encoding.tolocal(namespace), patterns).iteritems()
336 return pushkey.encodekeys(d)
336 return pushkey.encodekeys(d)
337
337
338 def localrepolistkeys(orig, self, namespace, patterns=None):
338 def localrepolistkeys(orig, self, namespace, patterns=None):
339 if namespace == 'bookmarks' and patterns:
339 if namespace == 'bookmarks' and patterns:
340 index = self.bundlestore.index
340 index = self.bundlestore.index
341 results = {}
341 results = {}
342 bookmarks = orig(self, namespace)
342 bookmarks = orig(self, namespace)
343 for pattern in patterns:
343 for pattern in patterns:
344 results.update(index.getbookmarks(pattern))
344 results.update(index.getbookmarks(pattern))
345 if pattern.endswith('*'):
345 if pattern.endswith('*'):
346 pattern = 're:^' + pattern[:-1] + '.*'
346 pattern = 're:^' + pattern[:-1] + '.*'
347 kind, pat, matcher = stringutil.stringmatcher(pattern)
347 kind, pat, matcher = stringutil.stringmatcher(pattern)
348 for bookmark, node in bookmarks.iteritems():
348 for bookmark, node in bookmarks.iteritems():
349 if matcher(bookmark):
349 if matcher(bookmark):
350 results[bookmark] = node
350 results[bookmark] = node
351 return results
351 return results
352 else:
352 else:
353 return orig(self, namespace)
353 return orig(self, namespace)
354
354
355 @wireprotov1peer.batchable
355 @wireprotov1peer.batchable
356 def listkeyspatterns(self, namespace, patterns):
356 def listkeyspatterns(self, namespace, patterns):
357 if not self.capable('pushkey'):
357 if not self.capable('pushkey'):
358 yield {}, None
358 yield {}, None
359 f = wireprotov1peer.future()
359 f = wireprotov1peer.future()
360 self.ui.debug('preparing listkeys for "%s" with pattern "%s"\n' %
360 self.ui.debug('preparing listkeys for "%s"\n' % namespace)
361 (namespace, patterns))
362 yield {
361 yield {
363 'namespace': encoding.fromlocal(namespace),
362 'namespace': encoding.fromlocal(namespace),
364 'patterns': wireprototypes.encodelist(patterns)
363 'patterns': wireprototypes.encodelist(patterns)
365 }, f
364 }, f
366 d = f.value
365 d = f.value
367 self.ui.debug('received listkey for "%s": %i bytes\n'
366 self.ui.debug('received listkey for "%s": %i bytes\n'
368 % (namespace, len(d)))
367 % (namespace, len(d)))
369 yield pushkey.decodekeys(d)
368 yield pushkey.decodekeys(d)
370
369
371 def _readbundlerevs(bundlerepo):
370 def _readbundlerevs(bundlerepo):
372 return list(bundlerepo.revs('bundle()'))
371 return list(bundlerepo.revs('bundle()'))
373
372
374 def _includefilelogstobundle(bundlecaps, bundlerepo, bundlerevs, ui):
373 def _includefilelogstobundle(bundlecaps, bundlerepo, bundlerevs, ui):
375 '''Tells remotefilelog to include all changed files to the changegroup
374 '''Tells remotefilelog to include all changed files to the changegroup
376
375
377 By default remotefilelog doesn't include file content to the changegroup.
376 By default remotefilelog doesn't include file content to the changegroup.
378 But we need to include it if we are fetching from bundlestore.
377 But we need to include it if we are fetching from bundlestore.
379 '''
378 '''
380 changedfiles = set()
379 changedfiles = set()
381 cl = bundlerepo.changelog
380 cl = bundlerepo.changelog
382 for r in bundlerevs:
381 for r in bundlerevs:
383 # [3] means changed files
382 # [3] means changed files
384 changedfiles.update(cl.read(r)[3])
383 changedfiles.update(cl.read(r)[3])
385 if not changedfiles:
384 if not changedfiles:
386 return bundlecaps
385 return bundlecaps
387
386
388 changedfiles = '\0'.join(changedfiles)
387 changedfiles = '\0'.join(changedfiles)
389 newcaps = []
388 newcaps = []
390 appended = False
389 appended = False
391 for cap in (bundlecaps or []):
390 for cap in (bundlecaps or []):
392 if cap.startswith('excludepattern='):
391 if cap.startswith('excludepattern='):
393 newcaps.append('\0'.join((cap, changedfiles)))
392 newcaps.append('\0'.join((cap, changedfiles)))
394 appended = True
393 appended = True
395 else:
394 else:
396 newcaps.append(cap)
395 newcaps.append(cap)
397 if not appended:
396 if not appended:
398 # Not found excludepattern cap. Just append it
397 # Not found excludepattern cap. Just append it
399 newcaps.append('excludepattern=' + changedfiles)
398 newcaps.append('excludepattern=' + changedfiles)
400
399
401 return newcaps
400 return newcaps
402
401
403 def _rebundle(bundlerepo, bundleroots, unknownhead):
402 def _rebundle(bundlerepo, bundleroots, unknownhead):
404 '''
403 '''
405 Bundle may include more revision then user requested. For example,
404 Bundle may include more revision then user requested. For example,
406 if user asks for revision but bundle also consists its descendants.
405 if user asks for revision but bundle also consists its descendants.
407 This function will filter out all revision that user is not requested.
406 This function will filter out all revision that user is not requested.
408 '''
407 '''
409 parts = []
408 parts = []
410
409
411 version = '02'
410 version = '02'
412 outgoing = discovery.outgoing(bundlerepo, commonheads=bundleroots,
411 outgoing = discovery.outgoing(bundlerepo, commonheads=bundleroots,
413 missingheads=[unknownhead])
412 missingheads=[unknownhead])
414 cgstream = changegroup.makestream(bundlerepo, outgoing, version, 'pull')
413 cgstream = changegroup.makestream(bundlerepo, outgoing, version, 'pull')
415 cgstream = util.chunkbuffer(cgstream).read()
414 cgstream = util.chunkbuffer(cgstream).read()
416 cgpart = bundle2.bundlepart('changegroup', data=cgstream)
415 cgpart = bundle2.bundlepart('changegroup', data=cgstream)
417 cgpart.addparam('version', version)
416 cgpart.addparam('version', version)
418 parts.append(cgpart)
417 parts.append(cgpart)
419
418
420 return parts
419 return parts
421
420
422 def _getbundleroots(oldrepo, bundlerepo, bundlerevs):
421 def _getbundleroots(oldrepo, bundlerepo, bundlerevs):
423 cl = bundlerepo.changelog
422 cl = bundlerepo.changelog
424 bundleroots = []
423 bundleroots = []
425 for rev in bundlerevs:
424 for rev in bundlerevs:
426 node = cl.node(rev)
425 node = cl.node(rev)
427 parents = cl.parents(node)
426 parents = cl.parents(node)
428 for parent in parents:
427 for parent in parents:
429 # include all revs that exist in the main repo
428 # include all revs that exist in the main repo
430 # to make sure that bundle may apply client-side
429 # to make sure that bundle may apply client-side
431 if parent in oldrepo:
430 if parent in oldrepo:
432 bundleroots.append(parent)
431 bundleroots.append(parent)
433 return bundleroots
432 return bundleroots
434
433
435 def _needsrebundling(head, bundlerepo):
434 def _needsrebundling(head, bundlerepo):
436 bundleheads = list(bundlerepo.revs('heads(bundle())'))
435 bundleheads = list(bundlerepo.revs('heads(bundle())'))
437 return not (len(bundleheads) == 1 and
436 return not (len(bundleheads) == 1 and
438 bundlerepo[bundleheads[0]].node() == head)
437 bundlerepo[bundleheads[0]].node() == head)
439
438
440 def _generateoutputparts(head, bundlerepo, bundleroots, bundlefile):
439 def _generateoutputparts(head, bundlerepo, bundleroots, bundlefile):
441 '''generates bundle that will be send to the user
440 '''generates bundle that will be send to the user
442
441
443 returns tuple with raw bundle string and bundle type
442 returns tuple with raw bundle string and bundle type
444 '''
443 '''
445 parts = []
444 parts = []
446 if not _needsrebundling(head, bundlerepo):
445 if not _needsrebundling(head, bundlerepo):
447 with util.posixfile(bundlefile, "rb") as f:
446 with util.posixfile(bundlefile, "rb") as f:
448 unbundler = exchange.readbundle(bundlerepo.ui, f, bundlefile)
447 unbundler = exchange.readbundle(bundlerepo.ui, f, bundlefile)
449 if isinstance(unbundler, changegroup.cg1unpacker):
448 if isinstance(unbundler, changegroup.cg1unpacker):
450 part = bundle2.bundlepart('changegroup',
449 part = bundle2.bundlepart('changegroup',
451 data=unbundler._stream.read())
450 data=unbundler._stream.read())
452 part.addparam('version', '01')
451 part.addparam('version', '01')
453 parts.append(part)
452 parts.append(part)
454 elif isinstance(unbundler, bundle2.unbundle20):
453 elif isinstance(unbundler, bundle2.unbundle20):
455 haschangegroup = False
454 haschangegroup = False
456 for part in unbundler.iterparts():
455 for part in unbundler.iterparts():
457 if part.type == 'changegroup':
456 if part.type == 'changegroup':
458 haschangegroup = True
457 haschangegroup = True
459 newpart = bundle2.bundlepart(part.type, data=part.read())
458 newpart = bundle2.bundlepart(part.type, data=part.read())
460 for key, value in part.params.iteritems():
459 for key, value in part.params.iteritems():
461 newpart.addparam(key, value)
460 newpart.addparam(key, value)
462 parts.append(newpart)
461 parts.append(newpart)
463
462
464 if not haschangegroup:
463 if not haschangegroup:
465 raise error.Abort(
464 raise error.Abort(
466 'unexpected bundle without changegroup part, ' +
465 'unexpected bundle without changegroup part, ' +
467 'head: %s' % hex(head),
466 'head: %s' % hex(head),
468 hint='report to administrator')
467 hint='report to administrator')
469 else:
468 else:
470 raise error.Abort('unknown bundle type')
469 raise error.Abort('unknown bundle type')
471 else:
470 else:
472 parts = _rebundle(bundlerepo, bundleroots, head)
471 parts = _rebundle(bundlerepo, bundleroots, head)
473
472
474 return parts
473 return parts
475
474
476 def getbundlechunks(orig, repo, source, heads=None, bundlecaps=None, **kwargs):
475 def getbundlechunks(orig, repo, source, heads=None, bundlecaps=None, **kwargs):
477 heads = heads or []
476 heads = heads or []
478 # newheads are parents of roots of scratch bundles that were requested
477 # newheads are parents of roots of scratch bundles that were requested
479 newphases = {}
478 newphases = {}
480 scratchbundles = []
479 scratchbundles = []
481 newheads = []
480 newheads = []
482 scratchheads = []
481 scratchheads = []
483 nodestobundle = {}
482 nodestobundle = {}
484 allbundlestocleanup = []
483 allbundlestocleanup = []
485 try:
484 try:
486 for head in heads:
485 for head in heads:
487 if head not in repo.changelog.nodemap:
486 if head not in repo.changelog.nodemap:
488 if head not in nodestobundle:
487 if head not in nodestobundle:
489 newbundlefile = common.downloadbundle(repo, head)
488 newbundlefile = common.downloadbundle(repo, head)
490 bundlepath = "bundle:%s+%s" % (repo.root, newbundlefile)
489 bundlepath = "bundle:%s+%s" % (repo.root, newbundlefile)
491 bundlerepo = hg.repository(repo.ui, bundlepath)
490 bundlerepo = hg.repository(repo.ui, bundlepath)
492
491
493 allbundlestocleanup.append((bundlerepo, newbundlefile))
492 allbundlestocleanup.append((bundlerepo, newbundlefile))
494 bundlerevs = set(_readbundlerevs(bundlerepo))
493 bundlerevs = set(_readbundlerevs(bundlerepo))
495 bundlecaps = _includefilelogstobundle(
494 bundlecaps = _includefilelogstobundle(
496 bundlecaps, bundlerepo, bundlerevs, repo.ui)
495 bundlecaps, bundlerepo, bundlerevs, repo.ui)
497 cl = bundlerepo.changelog
496 cl = bundlerepo.changelog
498 bundleroots = _getbundleroots(repo, bundlerepo, bundlerevs)
497 bundleroots = _getbundleroots(repo, bundlerepo, bundlerevs)
499 for rev in bundlerevs:
498 for rev in bundlerevs:
500 node = cl.node(rev)
499 node = cl.node(rev)
501 newphases[hex(node)] = str(phases.draft)
500 newphases[hex(node)] = str(phases.draft)
502 nodestobundle[node] = (bundlerepo, bundleroots,
501 nodestobundle[node] = (bundlerepo, bundleroots,
503 newbundlefile)
502 newbundlefile)
504
503
505 scratchbundles.append(
504 scratchbundles.append(
506 _generateoutputparts(head, *nodestobundle[head]))
505 _generateoutputparts(head, *nodestobundle[head]))
507 newheads.extend(bundleroots)
506 newheads.extend(bundleroots)
508 scratchheads.append(head)
507 scratchheads.append(head)
509 finally:
508 finally:
510 for bundlerepo, bundlefile in allbundlestocleanup:
509 for bundlerepo, bundlefile in allbundlestocleanup:
511 bundlerepo.close()
510 bundlerepo.close()
512 try:
511 try:
513 os.unlink(bundlefile)
512 os.unlink(bundlefile)
514 except (IOError, OSError):
513 except (IOError, OSError):
515 # if we can't cleanup the file then just ignore the error,
514 # if we can't cleanup the file then just ignore the error,
516 # no need to fail
515 # no need to fail
517 pass
516 pass
518
517
519 pullfrombundlestore = bool(scratchbundles)
518 pullfrombundlestore = bool(scratchbundles)
520 wrappedchangegrouppart = False
519 wrappedchangegrouppart = False
521 wrappedlistkeys = False
520 wrappedlistkeys = False
522 oldchangegrouppart = exchange.getbundle2partsmapping['changegroup']
521 oldchangegrouppart = exchange.getbundle2partsmapping['changegroup']
523 try:
522 try:
524 def _changegrouppart(bundler, *args, **kwargs):
523 def _changegrouppart(bundler, *args, **kwargs):
525 # Order is important here. First add non-scratch part
524 # Order is important here. First add non-scratch part
526 # and only then add parts with scratch bundles because
525 # and only then add parts with scratch bundles because
527 # non-scratch part contains parents of roots of scratch bundles.
526 # non-scratch part contains parents of roots of scratch bundles.
528 result = oldchangegrouppart(bundler, *args, **kwargs)
527 result = oldchangegrouppart(bundler, *args, **kwargs)
529 for bundle in scratchbundles:
528 for bundle in scratchbundles:
530 for part in bundle:
529 for part in bundle:
531 bundler.addpart(part)
530 bundler.addpart(part)
532 return result
531 return result
533
532
534 exchange.getbundle2partsmapping['changegroup'] = _changegrouppart
533 exchange.getbundle2partsmapping['changegroup'] = _changegrouppart
535 wrappedchangegrouppart = True
534 wrappedchangegrouppart = True
536
535
537 def _listkeys(orig, self, namespace):
536 def _listkeys(orig, self, namespace):
538 origvalues = orig(self, namespace)
537 origvalues = orig(self, namespace)
539 if namespace == 'phases' and pullfrombundlestore:
538 if namespace == 'phases' and pullfrombundlestore:
540 if origvalues.get('publishing') == 'True':
539 if origvalues.get('publishing') == 'True':
541 # Make repo non-publishing to preserve draft phase
540 # Make repo non-publishing to preserve draft phase
542 del origvalues['publishing']
541 del origvalues['publishing']
543 origvalues.update(newphases)
542 origvalues.update(newphases)
544 return origvalues
543 return origvalues
545
544
546 extensions.wrapfunction(localrepo.localrepository, 'listkeys',
545 extensions.wrapfunction(localrepo.localrepository, 'listkeys',
547 _listkeys)
546 _listkeys)
548 wrappedlistkeys = True
547 wrappedlistkeys = True
549 heads = list((set(newheads) | set(heads)) - set(scratchheads))
548 heads = list((set(newheads) | set(heads)) - set(scratchheads))
550 result = orig(repo, source, heads=heads,
549 result = orig(repo, source, heads=heads,
551 bundlecaps=bundlecaps, **kwargs)
550 bundlecaps=bundlecaps, **kwargs)
552 finally:
551 finally:
553 if wrappedchangegrouppart:
552 if wrappedchangegrouppart:
554 exchange.getbundle2partsmapping['changegroup'] = oldchangegrouppart
553 exchange.getbundle2partsmapping['changegroup'] = oldchangegrouppart
555 if wrappedlistkeys:
554 if wrappedlistkeys:
556 extensions.unwrapfunction(localrepo.localrepository, 'listkeys',
555 extensions.unwrapfunction(localrepo.localrepository, 'listkeys',
557 _listkeys)
556 _listkeys)
558 return result
557 return result
559
558
560 def _lookupwrap(orig):
559 def _lookupwrap(orig):
561 def _lookup(repo, proto, key):
560 def _lookup(repo, proto, key):
562 localkey = encoding.tolocal(key)
561 localkey = encoding.tolocal(key)
563
562
564 if isinstance(localkey, str) and _scratchbranchmatcher(localkey):
563 if isinstance(localkey, str) and _scratchbranchmatcher(localkey):
565 scratchnode = repo.bundlestore.index.getnode(localkey)
564 scratchnode = repo.bundlestore.index.getnode(localkey)
566 if scratchnode:
565 if scratchnode:
567 return "%d %s\n" % (1, scratchnode)
566 return "%d %s\n" % (1, scratchnode)
568 else:
567 else:
569 return "%d %s\n" % (0, 'scratch branch %s not found' % localkey)
568 return "%d %s\n" % (0, 'scratch branch %s not found' % localkey)
570 else:
569 else:
571 try:
570 try:
572 r = hex(repo.lookup(localkey))
571 r = hex(repo.lookup(localkey))
573 return "%d %s\n" % (1, r)
572 return "%d %s\n" % (1, r)
574 except Exception as inst:
573 except Exception as inst:
575 if repo.bundlestore.index.getbundle(localkey):
574 if repo.bundlestore.index.getbundle(localkey):
576 return "%d %s\n" % (1, localkey)
575 return "%d %s\n" % (1, localkey)
577 else:
576 else:
578 r = stringutil.forcebytestr(inst)
577 r = stringutil.forcebytestr(inst)
579 return "%d %s\n" % (0, r)
578 return "%d %s\n" % (0, r)
580 return _lookup
579 return _lookup
581
580
582 def _pull(orig, ui, repo, source="default", **opts):
581 def _pull(orig, ui, repo, source="default", **opts):
583 opts = pycompat.byteskwargs(opts)
582 opts = pycompat.byteskwargs(opts)
584 # Copy paste from `pull` command
583 # Copy paste from `pull` command
585 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
584 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
586
585
587 scratchbookmarks = {}
586 scratchbookmarks = {}
588 unfi = repo.unfiltered()
587 unfi = repo.unfiltered()
589 unknownnodes = []
588 unknownnodes = []
590 for rev in opts.get('rev', []):
589 for rev in opts.get('rev', []):
591 if rev not in unfi:
590 if rev not in unfi:
592 unknownnodes.append(rev)
591 unknownnodes.append(rev)
593 if opts.get('bookmark'):
592 if opts.get('bookmark'):
594 bookmarks = []
593 bookmarks = []
595 revs = opts.get('rev') or []
594 revs = opts.get('rev') or []
596 for bookmark in opts.get('bookmark'):
595 for bookmark in opts.get('bookmark'):
597 if _scratchbranchmatcher(bookmark):
596 if _scratchbranchmatcher(bookmark):
598 # rev is not known yet
597 # rev is not known yet
599 # it will be fetched with listkeyspatterns next
598 # it will be fetched with listkeyspatterns next
600 scratchbookmarks[bookmark] = 'REVTOFETCH'
599 scratchbookmarks[bookmark] = 'REVTOFETCH'
601 else:
600 else:
602 bookmarks.append(bookmark)
601 bookmarks.append(bookmark)
603
602
604 if scratchbookmarks:
603 if scratchbookmarks:
605 other = hg.peer(repo, opts, source)
604 other = hg.peer(repo, opts, source)
606 fetchedbookmarks = other.listkeyspatterns(
605 fetchedbookmarks = other.listkeyspatterns(
607 'bookmarks', patterns=scratchbookmarks)
606 'bookmarks', patterns=scratchbookmarks)
608 for bookmark in scratchbookmarks:
607 for bookmark in scratchbookmarks:
609 if bookmark not in fetchedbookmarks:
608 if bookmark not in fetchedbookmarks:
610 raise error.Abort('remote bookmark %s not found!' %
609 raise error.Abort('remote bookmark %s not found!' %
611 bookmark)
610 bookmark)
612 scratchbookmarks[bookmark] = fetchedbookmarks[bookmark]
611 scratchbookmarks[bookmark] = fetchedbookmarks[bookmark]
613 revs.append(fetchedbookmarks[bookmark])
612 revs.append(fetchedbookmarks[bookmark])
614 opts['bookmark'] = bookmarks
613 opts['bookmark'] = bookmarks
615 opts['rev'] = revs
614 opts['rev'] = revs
616
615
617 if scratchbookmarks or unknownnodes:
616 if scratchbookmarks or unknownnodes:
618 # Set anyincoming to True
617 # Set anyincoming to True
619 extensions.wrapfunction(discovery, 'findcommonincoming',
618 extensions.wrapfunction(discovery, 'findcommonincoming',
620 _findcommonincoming)
619 _findcommonincoming)
621 try:
620 try:
622 # Remote scratch bookmarks will be deleted because remotenames doesn't
621 # Remote scratch bookmarks will be deleted because remotenames doesn't
623 # know about them. Let's save it before pull and restore after
622 # know about them. Let's save it before pull and restore after
624 remotescratchbookmarks = _readscratchremotebookmarks(ui, repo, source)
623 remotescratchbookmarks = _readscratchremotebookmarks(ui, repo, source)
625 result = orig(ui, repo, source, **pycompat.strkwargs(opts))
624 result = orig(ui, repo, source, **pycompat.strkwargs(opts))
626 # TODO(stash): race condition is possible
625 # TODO(stash): race condition is possible
627 # if scratch bookmarks was updated right after orig.
626 # if scratch bookmarks was updated right after orig.
628 # But that's unlikely and shouldn't be harmful.
627 # But that's unlikely and shouldn't be harmful.
629 if common.isremotebooksenabled(ui):
628 if common.isremotebooksenabled(ui):
630 remotescratchbookmarks.update(scratchbookmarks)
629 remotescratchbookmarks.update(scratchbookmarks)
631 _saveremotebookmarks(repo, remotescratchbookmarks, source)
630 _saveremotebookmarks(repo, remotescratchbookmarks, source)
632 else:
631 else:
633 _savelocalbookmarks(repo, scratchbookmarks)
632 _savelocalbookmarks(repo, scratchbookmarks)
634 return result
633 return result
635 finally:
634 finally:
636 if scratchbookmarks:
635 if scratchbookmarks:
637 extensions.unwrapfunction(discovery, 'findcommonincoming')
636 extensions.unwrapfunction(discovery, 'findcommonincoming')
638
637
639 def _readscratchremotebookmarks(ui, repo, other):
638 def _readscratchremotebookmarks(ui, repo, other):
640 if common.isremotebooksenabled(ui):
639 if common.isremotebooksenabled(ui):
641 remotenamesext = extensions.find('remotenames')
640 remotenamesext = extensions.find('remotenames')
642 remotepath = remotenamesext.activepath(repo.ui, other)
641 remotepath = remotenamesext.activepath(repo.ui, other)
643 result = {}
642 result = {}
644 # Let's refresh remotenames to make sure we have it up to date
643 # Let's refresh remotenames to make sure we have it up to date
645 # Seems that `repo.names['remotebookmarks']` may return stale bookmarks
644 # Seems that `repo.names['remotebookmarks']` may return stale bookmarks
646 # and it results in deleting scratch bookmarks. Our best guess how to
645 # and it results in deleting scratch bookmarks. Our best guess how to
647 # fix it is to use `clearnames()`
646 # fix it is to use `clearnames()`
648 repo._remotenames.clearnames()
647 repo._remotenames.clearnames()
649 for remotebookmark in repo.names['remotebookmarks'].listnames(repo):
648 for remotebookmark in repo.names['remotebookmarks'].listnames(repo):
650 path, bookname = remotenamesext.splitremotename(remotebookmark)
649 path, bookname = remotenamesext.splitremotename(remotebookmark)
651 if path == remotepath and _scratchbranchmatcher(bookname):
650 if path == remotepath and _scratchbranchmatcher(bookname):
652 nodes = repo.names['remotebookmarks'].nodes(repo,
651 nodes = repo.names['remotebookmarks'].nodes(repo,
653 remotebookmark)
652 remotebookmark)
654 if nodes:
653 if nodes:
655 result[bookname] = hex(nodes[0])
654 result[bookname] = hex(nodes[0])
656 return result
655 return result
657 else:
656 else:
658 return {}
657 return {}
659
658
660 def _saveremotebookmarks(repo, newbookmarks, remote):
659 def _saveremotebookmarks(repo, newbookmarks, remote):
661 remotenamesext = extensions.find('remotenames')
660 remotenamesext = extensions.find('remotenames')
662 remotepath = remotenamesext.activepath(repo.ui, remote)
661 remotepath = remotenamesext.activepath(repo.ui, remote)
663 branches = collections.defaultdict(list)
662 branches = collections.defaultdict(list)
664 bookmarks = {}
663 bookmarks = {}
665 remotenames = remotenamesext.readremotenames(repo)
664 remotenames = remotenamesext.readremotenames(repo)
666 for hexnode, nametype, remote, rname in remotenames:
665 for hexnode, nametype, remote, rname in remotenames:
667 if remote != remotepath:
666 if remote != remotepath:
668 continue
667 continue
669 if nametype == 'bookmarks':
668 if nametype == 'bookmarks':
670 if rname in newbookmarks:
669 if rname in newbookmarks:
671 # It's possible if we have a normal bookmark that matches
670 # It's possible if we have a normal bookmark that matches
672 # scratch branch pattern. In this case just use the current
671 # scratch branch pattern. In this case just use the current
673 # bookmark node
672 # bookmark node
674 del newbookmarks[rname]
673 del newbookmarks[rname]
675 bookmarks[rname] = hexnode
674 bookmarks[rname] = hexnode
676 elif nametype == 'branches':
675 elif nametype == 'branches':
677 # saveremotenames expects 20 byte binary nodes for branches
676 # saveremotenames expects 20 byte binary nodes for branches
678 branches[rname].append(bin(hexnode))
677 branches[rname].append(bin(hexnode))
679
678
680 for bookmark, hexnode in newbookmarks.iteritems():
679 for bookmark, hexnode in newbookmarks.iteritems():
681 bookmarks[bookmark] = hexnode
680 bookmarks[bookmark] = hexnode
682 remotenamesext.saveremotenames(repo, remotepath, branches, bookmarks)
681 remotenamesext.saveremotenames(repo, remotepath, branches, bookmarks)
683
682
684 def _savelocalbookmarks(repo, bookmarks):
683 def _savelocalbookmarks(repo, bookmarks):
685 if not bookmarks:
684 if not bookmarks:
686 return
685 return
687 with repo.wlock(), repo.lock(), repo.transaction('bookmark') as tr:
686 with repo.wlock(), repo.lock(), repo.transaction('bookmark') as tr:
688 changes = []
687 changes = []
689 for scratchbook, node in bookmarks.iteritems():
688 for scratchbook, node in bookmarks.iteritems():
690 changectx = repo[node]
689 changectx = repo[node]
691 changes.append((scratchbook, changectx.node()))
690 changes.append((scratchbook, changectx.node()))
692 repo._bookmarks.applychanges(repo, tr, changes)
691 repo._bookmarks.applychanges(repo, tr, changes)
693
692
694 def _findcommonincoming(orig, *args, **kwargs):
693 def _findcommonincoming(orig, *args, **kwargs):
695 common, inc, remoteheads = orig(*args, **kwargs)
694 common, inc, remoteheads = orig(*args, **kwargs)
696 return common, True, remoteheads
695 return common, True, remoteheads
697
696
698 def _push(orig, ui, repo, dest=None, *args, **opts):
697 def _push(orig, ui, repo, dest=None, *args, **opts):
699
698 opts = pycompat.byteskwargs(opts)
700 bookmark = opts.get(r'bookmark')
699 bookmark = opts.get('bookmark')
701 # we only support pushing one infinitepush bookmark at once
700 # we only support pushing one infinitepush bookmark at once
702 if len(bookmark) == 1:
701 if len(bookmark) == 1:
703 bookmark = bookmark[0]
702 bookmark = bookmark[0]
704 else:
703 else:
705 bookmark = ''
704 bookmark = ''
706
705
707 oldphasemove = None
706 oldphasemove = None
708 overrides = {(experimental, configbookmark): bookmark}
707 overrides = {(experimental, configbookmark): bookmark}
709
708
710 with ui.configoverride(overrides, 'infinitepush'):
709 with ui.configoverride(overrides, 'infinitepush'):
711 scratchpush = opts.get('bundle_store')
710 scratchpush = opts.get('bundle_store')
712 if _scratchbranchmatcher(bookmark):
711 if _scratchbranchmatcher(bookmark):
713 scratchpush = True
712 scratchpush = True
714 # bundle2 can be sent back after push (for example, bundle2
713 # bundle2 can be sent back after push (for example, bundle2
715 # containing `pushkey` part to update bookmarks)
714 # containing `pushkey` part to update bookmarks)
716 ui.setconfig(experimental, 'bundle2.pushback', True)
715 ui.setconfig(experimental, 'bundle2.pushback', True)
717
716
718 if scratchpush:
717 if scratchpush:
719 # this is an infinitepush, we don't want the bookmark to be applied
718 # this is an infinitepush, we don't want the bookmark to be applied
720 # rather that should be stored in the bundlestore
719 # rather that should be stored in the bundlestore
721 opts[r'bookmark'] = []
720 opts['bookmark'] = []
722 ui.setconfig(experimental, configscratchpush, True)
721 ui.setconfig(experimental, configscratchpush, True)
723 oldphasemove = extensions.wrapfunction(exchange,
722 oldphasemove = extensions.wrapfunction(exchange,
724 '_localphasemove',
723 '_localphasemove',
725 _phasemove)
724 _phasemove)
726 # Copy-paste from `push` command
725 # Copy-paste from `push` command
727 path = ui.paths.getpath(dest, default=('default-push', 'default'))
726 path = ui.paths.getpath(dest, default=('default-push', 'default'))
728 if not path:
727 if not path:
729 raise error.Abort(_('default repository not configured!'),
728 raise error.Abort(_('default repository not configured!'),
730 hint=_("see 'hg help config.paths'"))
729 hint=_("see 'hg help config.paths'"))
731 destpath = path.pushloc or path.loc
730 destpath = path.pushloc or path.loc
732 # Remote scratch bookmarks will be deleted because remotenames doesn't
731 # Remote scratch bookmarks will be deleted because remotenames doesn't
733 # know about them. Let's save it before push and restore after
732 # know about them. Let's save it before push and restore after
734 remotescratchbookmarks = _readscratchremotebookmarks(ui, repo, destpath)
733 remotescratchbookmarks = _readscratchremotebookmarks(ui, repo, destpath)
735 result = orig(ui, repo, dest, *args, **opts)
734 result = orig(ui, repo, dest, *args, **pycompat.strkwargs(opts))
736 if common.isremotebooksenabled(ui):
735 if common.isremotebooksenabled(ui):
737 if bookmark and scratchpush:
736 if bookmark and scratchpush:
738 other = hg.peer(repo, opts, destpath)
737 other = hg.peer(repo, opts, destpath)
739 fetchedbookmarks = other.listkeyspatterns('bookmarks',
738 fetchedbookmarks = other.listkeyspatterns('bookmarks',
740 patterns=[bookmark])
739 patterns=[bookmark])
741 remotescratchbookmarks.update(fetchedbookmarks)
740 remotescratchbookmarks.update(fetchedbookmarks)
742 _saveremotebookmarks(repo, remotescratchbookmarks, destpath)
741 _saveremotebookmarks(repo, remotescratchbookmarks, destpath)
743 if oldphasemove:
742 if oldphasemove:
744 exchange._localphasemove = oldphasemove
743 exchange._localphasemove = oldphasemove
745 return result
744 return result
746
745
747 def _deleteinfinitepushbookmarks(ui, repo, path, names):
746 def _deleteinfinitepushbookmarks(ui, repo, path, names):
748 """Prune remote names by removing the bookmarks we don't want anymore,
747 """Prune remote names by removing the bookmarks we don't want anymore,
749 then writing the result back to disk
748 then writing the result back to disk
750 """
749 """
751 remotenamesext = extensions.find('remotenames')
750 remotenamesext = extensions.find('remotenames')
752
751
753 # remotename format is:
752 # remotename format is:
754 # (node, nametype ("branches" or "bookmarks"), remote, name)
753 # (node, nametype ("branches" or "bookmarks"), remote, name)
755 nametype_idx = 1
754 nametype_idx = 1
756 remote_idx = 2
755 remote_idx = 2
757 name_idx = 3
756 name_idx = 3
758 remotenames = [remotename for remotename in \
757 remotenames = [remotename for remotename in \
759 remotenamesext.readremotenames(repo) \
758 remotenamesext.readremotenames(repo) \
760 if remotename[remote_idx] == path]
759 if remotename[remote_idx] == path]
761 remote_bm_names = [remotename[name_idx] for remotename in \
760 remote_bm_names = [remotename[name_idx] for remotename in \
762 remotenames if remotename[nametype_idx] == "bookmarks"]
761 remotenames if remotename[nametype_idx] == "bookmarks"]
763
762
764 for name in names:
763 for name in names:
765 if name not in remote_bm_names:
764 if name not in remote_bm_names:
766 raise error.Abort(_("infinitepush bookmark '{}' does not exist "
765 raise error.Abort(_("infinitepush bookmark '{}' does not exist "
767 "in path '{}'").format(name, path))
766 "in path '{}'").format(name, path))
768
767
769 bookmarks = {}
768 bookmarks = {}
770 branches = collections.defaultdict(list)
769 branches = collections.defaultdict(list)
771 for node, nametype, remote, name in remotenames:
770 for node, nametype, remote, name in remotenames:
772 if nametype == "bookmarks" and name not in names:
771 if nametype == "bookmarks" and name not in names:
773 bookmarks[name] = node
772 bookmarks[name] = node
774 elif nametype == "branches":
773 elif nametype == "branches":
775 # saveremotenames wants binary nodes for branches
774 # saveremotenames wants binary nodes for branches
776 branches[name].append(bin(node))
775 branches[name].append(bin(node))
777
776
778 remotenamesext.saveremotenames(repo, path, branches, bookmarks)
777 remotenamesext.saveremotenames(repo, path, branches, bookmarks)
779
778
780 def _phasemove(orig, pushop, nodes, phase=phases.public):
779 def _phasemove(orig, pushop, nodes, phase=phases.public):
781 """prevent commits from being marked public
780 """prevent commits from being marked public
782
781
783 Since these are going to a scratch branch, they aren't really being
782 Since these are going to a scratch branch, they aren't really being
784 published."""
783 published."""
785
784
786 if phase != phases.public:
785 if phase != phases.public:
787 orig(pushop, nodes, phase)
786 orig(pushop, nodes, phase)
788
787
789 @exchange.b2partsgenerator(scratchbranchparttype)
788 @exchange.b2partsgenerator(scratchbranchparttype)
790 def partgen(pushop, bundler):
789 def partgen(pushop, bundler):
791 bookmark = pushop.ui.config(experimental, configbookmark)
790 bookmark = pushop.ui.config(experimental, configbookmark)
792 scratchpush = pushop.ui.configbool(experimental, configscratchpush)
791 scratchpush = pushop.ui.configbool(experimental, configscratchpush)
793 if 'changesets' in pushop.stepsdone or not scratchpush:
792 if 'changesets' in pushop.stepsdone or not scratchpush:
794 return
793 return
795
794
796 if scratchbranchparttype not in bundle2.bundle2caps(pushop.remote):
795 if scratchbranchparttype not in bundle2.bundle2caps(pushop.remote):
797 return
796 return
798
797
799 pushop.stepsdone.add('changesets')
798 pushop.stepsdone.add('changesets')
800 if not pushop.outgoing.missing:
799 if not pushop.outgoing.missing:
801 pushop.ui.status(_('no changes found\n'))
800 pushop.ui.status(_('no changes found\n'))
802 pushop.cgresult = 0
801 pushop.cgresult = 0
803 return
802 return
804
803
805 # This parameter tells the server that the following bundle is an
804 # This parameter tells the server that the following bundle is an
806 # infinitepush. This let's it switch the part processing to our infinitepush
805 # infinitepush. This let's it switch the part processing to our infinitepush
807 # code path.
806 # code path.
808 bundler.addparam("infinitepush", "True")
807 bundler.addparam("infinitepush", "True")
809
808
810 scratchparts = bundleparts.getscratchbranchparts(pushop.repo,
809 scratchparts = bundleparts.getscratchbranchparts(pushop.repo,
811 pushop.remote,
810 pushop.remote,
812 pushop.outgoing,
811 pushop.outgoing,
813 pushop.ui,
812 pushop.ui,
814 bookmark)
813 bookmark)
815
814
816 for scratchpart in scratchparts:
815 for scratchpart in scratchparts:
817 bundler.addpart(scratchpart)
816 bundler.addpart(scratchpart)
818
817
819 def handlereply(op):
818 def handlereply(op):
820 # server either succeeds or aborts; no code to read
819 # server either succeeds or aborts; no code to read
821 pushop.cgresult = 1
820 pushop.cgresult = 1
822
821
823 return handlereply
822 return handlereply
824
823
825 bundle2.capabilities[bundleparts.scratchbranchparttype] = ()
824 bundle2.capabilities[bundleparts.scratchbranchparttype] = ()
826
825
827 def _getrevs(bundle, oldnode, force, bookmark):
826 def _getrevs(bundle, oldnode, force, bookmark):
828 'extracts and validates the revs to be imported'
827 'extracts and validates the revs to be imported'
829 revs = [bundle[r] for r in bundle.revs('sort(bundle())')]
828 revs = [bundle[r] for r in bundle.revs('sort(bundle())')]
830
829
831 # new bookmark
830 # new bookmark
832 if oldnode is None:
831 if oldnode is None:
833 return revs
832 return revs
834
833
835 # Fast forward update
834 # Fast forward update
836 if oldnode in bundle and list(bundle.set('bundle() & %s::', oldnode)):
835 if oldnode in bundle and list(bundle.set('bundle() & %s::', oldnode)):
837 return revs
836 return revs
838
837
839 return revs
838 return revs
840
839
841 @contextlib.contextmanager
840 @contextlib.contextmanager
842 def logservicecall(logger, service, **kwargs):
841 def logservicecall(logger, service, **kwargs):
843 start = time.time()
842 start = time.time()
844 logger(service, eventtype='start', **kwargs)
843 logger(service, eventtype='start', **kwargs)
845 try:
844 try:
846 yield
845 yield
847 logger(service, eventtype='success',
846 logger(service, eventtype='success',
848 elapsedms=(time.time() - start) * 1000, **kwargs)
847 elapsedms=(time.time() - start) * 1000, **kwargs)
849 except Exception as e:
848 except Exception as e:
850 logger(service, eventtype='failure',
849 logger(service, eventtype='failure',
851 elapsedms=(time.time() - start) * 1000, errormsg=str(e),
850 elapsedms=(time.time() - start) * 1000, errormsg=str(e),
852 **kwargs)
851 **kwargs)
853 raise
852 raise
854
853
855 def _getorcreateinfinitepushlogger(op):
854 def _getorcreateinfinitepushlogger(op):
856 logger = op.records['infinitepushlogger']
855 logger = op.records['infinitepushlogger']
857 if not logger:
856 if not logger:
858 ui = op.repo.ui
857 ui = op.repo.ui
859 try:
858 try:
860 username = procutil.getuser()
859 username = procutil.getuser()
861 except Exception:
860 except Exception:
862 username = 'unknown'
861 username = 'unknown'
863 # Generate random request id to be able to find all logged entries
862 # Generate random request id to be able to find all logged entries
864 # for the same request. Since requestid is pseudo-generated it may
863 # for the same request. Since requestid is pseudo-generated it may
865 # not be unique, but we assume that (hostname, username, requestid)
864 # not be unique, but we assume that (hostname, username, requestid)
866 # is unique.
865 # is unique.
867 random.seed()
866 random.seed()
868 requestid = random.randint(0, 2000000000)
867 requestid = random.randint(0, 2000000000)
869 hostname = socket.gethostname()
868 hostname = socket.gethostname()
870 logger = functools.partial(ui.log, 'infinitepush', user=username,
869 logger = functools.partial(ui.log, 'infinitepush', user=username,
871 requestid=requestid, hostname=hostname,
870 requestid=requestid, hostname=hostname,
872 reponame=ui.config('infinitepush',
871 reponame=ui.config('infinitepush',
873 'reponame'))
872 'reponame'))
874 op.records.add('infinitepushlogger', logger)
873 op.records.add('infinitepushlogger', logger)
875 else:
874 else:
876 logger = logger[0]
875 logger = logger[0]
877 return logger
876 return logger
878
877
879 def storetobundlestore(orig, repo, op, unbundler):
878 def storetobundlestore(orig, repo, op, unbundler):
880 """stores the incoming bundle coming from push command to the bundlestore
879 """stores the incoming bundle coming from push command to the bundlestore
881 instead of applying on the revlogs"""
880 instead of applying on the revlogs"""
882
881
883 repo.ui.status(_("storing changesets on the bundlestore\n"))
882 repo.ui.status(_("storing changesets on the bundlestore\n"))
884 bundler = bundle2.bundle20(repo.ui)
883 bundler = bundle2.bundle20(repo.ui)
885
884
886 # processing each part and storing it in bundler
885 # processing each part and storing it in bundler
887 with bundle2.partiterator(repo, op, unbundler) as parts:
886 with bundle2.partiterator(repo, op, unbundler) as parts:
888 for part in parts:
887 for part in parts:
889 bundlepart = None
888 bundlepart = None
890 if part.type == 'replycaps':
889 if part.type == 'replycaps':
891 # This configures the current operation to allow reply parts.
890 # This configures the current operation to allow reply parts.
892 bundle2._processpart(op, part)
891 bundle2._processpart(op, part)
893 else:
892 else:
894 bundlepart = bundle2.bundlepart(part.type, data=part.read())
893 bundlepart = bundle2.bundlepart(part.type, data=part.read())
895 for key, value in part.params.iteritems():
894 for key, value in part.params.iteritems():
896 bundlepart.addparam(key, value)
895 bundlepart.addparam(key, value)
897
896
898 # Certain parts require a response
897 # Certain parts require a response
899 if part.type in ('pushkey', 'changegroup'):
898 if part.type in ('pushkey', 'changegroup'):
900 if op.reply is not None:
899 if op.reply is not None:
901 rpart = op.reply.newpart('reply:%s' % part.type)
900 rpart = op.reply.newpart('reply:%s' % part.type)
902 rpart.addparam('in-reply-to', str(part.id),
901 rpart.addparam('in-reply-to', b'%d' % part.id,
903 mandatory=False)
902 mandatory=False)
904 rpart.addparam('return', '1', mandatory=False)
903 rpart.addparam('return', '1', mandatory=False)
905
904
906 op.records.add(part.type, {
905 op.records.add(part.type, {
907 'return': 1,
906 'return': 1,
908 })
907 })
909 if bundlepart:
908 if bundlepart:
910 bundler.addpart(bundlepart)
909 bundler.addpart(bundlepart)
911
910
912 # storing the bundle in the bundlestore
911 # storing the bundle in the bundlestore
913 buf = util.chunkbuffer(bundler.getchunks())
912 buf = util.chunkbuffer(bundler.getchunks())
914 fd, bundlefile = pycompat.mkstemp()
913 fd, bundlefile = pycompat.mkstemp()
915 try:
914 try:
916 try:
915 try:
917 fp = os.fdopen(fd, r'wb')
916 fp = os.fdopen(fd, r'wb')
918 fp.write(buf.read())
917 fp.write(buf.read())
919 finally:
918 finally:
920 fp.close()
919 fp.close()
921 storebundle(op, {}, bundlefile)
920 storebundle(op, {}, bundlefile)
922 finally:
921 finally:
923 try:
922 try:
924 os.unlink(bundlefile)
923 os.unlink(bundlefile)
925 except Exception:
924 except Exception:
926 # we would rather see the original exception
925 # we would rather see the original exception
927 pass
926 pass
928
927
929 def processparts(orig, repo, op, unbundler):
928 def processparts(orig, repo, op, unbundler):
930
929
931 # make sure we don't wrap processparts in case of `hg unbundle`
930 # make sure we don't wrap processparts in case of `hg unbundle`
932 if op.source == 'unbundle':
931 if op.source == 'unbundle':
933 return orig(repo, op, unbundler)
932 return orig(repo, op, unbundler)
934
933
935 # this server routes each push to bundle store
934 # this server routes each push to bundle store
936 if repo.ui.configbool('infinitepush', 'pushtobundlestore'):
935 if repo.ui.configbool('infinitepush', 'pushtobundlestore'):
937 return storetobundlestore(orig, repo, op, unbundler)
936 return storetobundlestore(orig, repo, op, unbundler)
938
937
939 if unbundler.params.get('infinitepush') != 'True':
938 if unbundler.params.get('infinitepush') != 'True':
940 return orig(repo, op, unbundler)
939 return orig(repo, op, unbundler)
941
940
942 handleallparts = repo.ui.configbool('infinitepush', 'storeallparts')
941 handleallparts = repo.ui.configbool('infinitepush', 'storeallparts')
943
942
944 bundler = bundle2.bundle20(repo.ui)
943 bundler = bundle2.bundle20(repo.ui)
945 cgparams = None
944 cgparams = None
946 with bundle2.partiterator(repo, op, unbundler) as parts:
945 with bundle2.partiterator(repo, op, unbundler) as parts:
947 for part in parts:
946 for part in parts:
948 bundlepart = None
947 bundlepart = None
949 if part.type == 'replycaps':
948 if part.type == 'replycaps':
950 # This configures the current operation to allow reply parts.
949 # This configures the current operation to allow reply parts.
951 bundle2._processpart(op, part)
950 bundle2._processpart(op, part)
952 elif part.type == bundleparts.scratchbranchparttype:
951 elif part.type == bundleparts.scratchbranchparttype:
953 # Scratch branch parts need to be converted to normal
952 # Scratch branch parts need to be converted to normal
954 # changegroup parts, and the extra parameters stored for later
953 # changegroup parts, and the extra parameters stored for later
955 # when we upload to the store. Eventually those parameters will
954 # when we upload to the store. Eventually those parameters will
956 # be put on the actual bundle instead of this part, then we can
955 # be put on the actual bundle instead of this part, then we can
957 # send a vanilla changegroup instead of the scratchbranch part.
956 # send a vanilla changegroup instead of the scratchbranch part.
958 cgversion = part.params.get('cgversion', '01')
957 cgversion = part.params.get('cgversion', '01')
959 bundlepart = bundle2.bundlepart('changegroup', data=part.read())
958 bundlepart = bundle2.bundlepart('changegroup', data=part.read())
960 bundlepart.addparam('version', cgversion)
959 bundlepart.addparam('version', cgversion)
961 cgparams = part.params
960 cgparams = part.params
962
961
963 # If we're not dumping all parts into the new bundle, we need to
962 # If we're not dumping all parts into the new bundle, we need to
964 # alert the future pushkey and phase-heads handler to skip
963 # alert the future pushkey and phase-heads handler to skip
965 # the part.
964 # the part.
966 if not handleallparts:
965 if not handleallparts:
967 op.records.add(scratchbranchparttype + '_skippushkey', True)
966 op.records.add(scratchbranchparttype + '_skippushkey', True)
968 op.records.add(scratchbranchparttype + '_skipphaseheads',
967 op.records.add(scratchbranchparttype + '_skipphaseheads',
969 True)
968 True)
970 else:
969 else:
971 if handleallparts:
970 if handleallparts:
972 # Ideally we would not process any parts, and instead just
971 # Ideally we would not process any parts, and instead just
973 # forward them to the bundle for storage, but since this
972 # forward them to the bundle for storage, but since this
974 # differs from previous behavior, we need to put it behind a
973 # differs from previous behavior, we need to put it behind a
975 # config flag for incremental rollout.
974 # config flag for incremental rollout.
976 bundlepart = bundle2.bundlepart(part.type, data=part.read())
975 bundlepart = bundle2.bundlepart(part.type, data=part.read())
977 for key, value in part.params.iteritems():
976 for key, value in part.params.iteritems():
978 bundlepart.addparam(key, value)
977 bundlepart.addparam(key, value)
979
978
980 # Certain parts require a response
979 # Certain parts require a response
981 if part.type == 'pushkey':
980 if part.type == 'pushkey':
982 if op.reply is not None:
981 if op.reply is not None:
983 rpart = op.reply.newpart('reply:pushkey')
982 rpart = op.reply.newpart('reply:pushkey')
984 rpart.addparam('in-reply-to', str(part.id),
983 rpart.addparam('in-reply-to', str(part.id),
985 mandatory=False)
984 mandatory=False)
986 rpart.addparam('return', '1', mandatory=False)
985 rpart.addparam('return', '1', mandatory=False)
987 else:
986 else:
988 bundle2._processpart(op, part)
987 bundle2._processpart(op, part)
989
988
990 if handleallparts:
989 if handleallparts:
991 op.records.add(part.type, {
990 op.records.add(part.type, {
992 'return': 1,
991 'return': 1,
993 })
992 })
994 if bundlepart:
993 if bundlepart:
995 bundler.addpart(bundlepart)
994 bundler.addpart(bundlepart)
996
995
997 # If commits were sent, store them
996 # If commits were sent, store them
998 if cgparams:
997 if cgparams:
999 buf = util.chunkbuffer(bundler.getchunks())
998 buf = util.chunkbuffer(bundler.getchunks())
1000 fd, bundlefile = pycompat.mkstemp()
999 fd, bundlefile = pycompat.mkstemp()
1001 try:
1000 try:
1002 try:
1001 try:
1003 fp = os.fdopen(fd, r'wb')
1002 fp = os.fdopen(fd, r'wb')
1004 fp.write(buf.read())
1003 fp.write(buf.read())
1005 finally:
1004 finally:
1006 fp.close()
1005 fp.close()
1007 storebundle(op, cgparams, bundlefile)
1006 storebundle(op, cgparams, bundlefile)
1008 finally:
1007 finally:
1009 try:
1008 try:
1010 os.unlink(bundlefile)
1009 os.unlink(bundlefile)
1011 except Exception:
1010 except Exception:
1012 # we would rather see the original exception
1011 # we would rather see the original exception
1013 pass
1012 pass
1014
1013
1015 def storebundle(op, params, bundlefile):
1014 def storebundle(op, params, bundlefile):
1016 log = _getorcreateinfinitepushlogger(op)
1015 log = _getorcreateinfinitepushlogger(op)
1017 parthandlerstart = time.time()
1016 parthandlerstart = time.time()
1018 log(scratchbranchparttype, eventtype='start')
1017 log(scratchbranchparttype, eventtype='start')
1019 index = op.repo.bundlestore.index
1018 index = op.repo.bundlestore.index
1020 store = op.repo.bundlestore.store
1019 store = op.repo.bundlestore.store
1021 op.records.add(scratchbranchparttype + '_skippushkey', True)
1020 op.records.add(scratchbranchparttype + '_skippushkey', True)
1022
1021
1023 bundle = None
1022 bundle = None
1024 try: # guards bundle
1023 try: # guards bundle
1025 bundlepath = "bundle:%s+%s" % (op.repo.root, bundlefile)
1024 bundlepath = "bundle:%s+%s" % (op.repo.root, bundlefile)
1026 bundle = hg.repository(op.repo.ui, bundlepath)
1025 bundle = hg.repository(op.repo.ui, bundlepath)
1027
1026
1028 bookmark = params.get('bookmark')
1027 bookmark = params.get('bookmark')
1029 bookprevnode = params.get('bookprevnode', '')
1028 bookprevnode = params.get('bookprevnode', '')
1030 force = params.get('force')
1029 force = params.get('force')
1031
1030
1032 if bookmark:
1031 if bookmark:
1033 oldnode = index.getnode(bookmark)
1032 oldnode = index.getnode(bookmark)
1034 else:
1033 else:
1035 oldnode = None
1034 oldnode = None
1036 bundleheads = bundle.revs('heads(bundle())')
1035 bundleheads = bundle.revs('heads(bundle())')
1037 if bookmark and len(bundleheads) > 1:
1036 if bookmark and len(bundleheads) > 1:
1038 raise error.Abort(
1037 raise error.Abort(
1039 _('cannot push more than one head to a scratch branch'))
1038 _('cannot push more than one head to a scratch branch'))
1040
1039
1041 revs = _getrevs(bundle, oldnode, force, bookmark)
1040 revs = _getrevs(bundle, oldnode, force, bookmark)
1042
1041
1043 # Notify the user of what is being pushed
1042 # Notify the user of what is being pushed
1044 plural = 's' if len(revs) > 1 else ''
1043 plural = 's' if len(revs) > 1 else ''
1045 op.repo.ui.warn(_("pushing %d commit%s:\n") % (len(revs), plural))
1044 op.repo.ui.warn(_("pushing %d commit%s:\n") % (len(revs), plural))
1046 maxoutput = 10
1045 maxoutput = 10
1047 for i in range(0, min(len(revs), maxoutput)):
1046 for i in range(0, min(len(revs), maxoutput)):
1048 firstline = bundle[revs[i]].description().split('\n')[0][:50]
1047 firstline = bundle[revs[i]].description().split('\n')[0][:50]
1049 op.repo.ui.warn((" %s %s\n") % (revs[i], firstline))
1048 op.repo.ui.warn((" %s %s\n") % (revs[i], firstline))
1050
1049
1051 if len(revs) > maxoutput + 1:
1050 if len(revs) > maxoutput + 1:
1052 op.repo.ui.warn((" ...\n"))
1051 op.repo.ui.warn((" ...\n"))
1053 firstline = bundle[revs[-1]].description().split('\n')[0][:50]
1052 firstline = bundle[revs[-1]].description().split('\n')[0][:50]
1054 op.repo.ui.warn((" %s %s\n") % (revs[-1], firstline))
1053 op.repo.ui.warn((" %s %s\n") % (revs[-1], firstline))
1055
1054
1056 nodesctx = [bundle[rev] for rev in revs]
1055 nodesctx = [bundle[rev] for rev in revs]
1057 inindex = lambda rev: bool(index.getbundle(bundle[rev].hex()))
1056 inindex = lambda rev: bool(index.getbundle(bundle[rev].hex()))
1058 if bundleheads:
1057 if bundleheads:
1059 newheadscount = sum(not inindex(rev) for rev in bundleheads)
1058 newheadscount = sum(not inindex(rev) for rev in bundleheads)
1060 else:
1059 else:
1061 newheadscount = 0
1060 newheadscount = 0
1062 # If there's a bookmark specified, there should be only one head,
1061 # If there's a bookmark specified, there should be only one head,
1063 # so we choose the last node, which will be that head.
1062 # so we choose the last node, which will be that head.
1064 # If a bug or malicious client allows there to be a bookmark
1063 # If a bug or malicious client allows there to be a bookmark
1065 # with multiple heads, we will place the bookmark on the last head.
1064 # with multiple heads, we will place the bookmark on the last head.
1066 bookmarknode = nodesctx[-1].hex() if nodesctx else None
1065 bookmarknode = nodesctx[-1].hex() if nodesctx else None
1067 key = None
1066 key = None
1068 if newheadscount:
1067 if newheadscount:
1069 with open(bundlefile, 'rb') as f:
1068 with open(bundlefile, 'rb') as f:
1070 bundledata = f.read()
1069 bundledata = f.read()
1071 with logservicecall(log, 'bundlestore',
1070 with logservicecall(log, 'bundlestore',
1072 bundlesize=len(bundledata)):
1071 bundlesize=len(bundledata)):
1073 bundlesizelimit = 100 * 1024 * 1024 # 100 MB
1072 bundlesizelimit = 100 * 1024 * 1024 # 100 MB
1074 if len(bundledata) > bundlesizelimit:
1073 if len(bundledata) > bundlesizelimit:
1075 error_msg = ('bundle is too big: %d bytes. ' +
1074 error_msg = ('bundle is too big: %d bytes. ' +
1076 'max allowed size is 100 MB')
1075 'max allowed size is 100 MB')
1077 raise error.Abort(error_msg % (len(bundledata),))
1076 raise error.Abort(error_msg % (len(bundledata),))
1078 key = store.write(bundledata)
1077 key = store.write(bundledata)
1079
1078
1080 with logservicecall(log, 'index', newheadscount=newheadscount), index:
1079 with logservicecall(log, 'index', newheadscount=newheadscount), index:
1081 if key:
1080 if key:
1082 index.addbundle(key, nodesctx)
1081 index.addbundle(key, nodesctx)
1083 if bookmark:
1082 if bookmark:
1084 index.addbookmark(bookmark, bookmarknode)
1083 index.addbookmark(bookmark, bookmarknode)
1085 _maybeaddpushbackpart(op, bookmark, bookmarknode,
1084 _maybeaddpushbackpart(op, bookmark, bookmarknode,
1086 bookprevnode, params)
1085 bookprevnode, params)
1087 log(scratchbranchparttype, eventtype='success',
1086 log(scratchbranchparttype, eventtype='success',
1088 elapsedms=(time.time() - parthandlerstart) * 1000)
1087 elapsedms=(time.time() - parthandlerstart) * 1000)
1089
1088
1090 except Exception as e:
1089 except Exception as e:
1091 log(scratchbranchparttype, eventtype='failure',
1090 log(scratchbranchparttype, eventtype='failure',
1092 elapsedms=(time.time() - parthandlerstart) * 1000,
1091 elapsedms=(time.time() - parthandlerstart) * 1000,
1093 errormsg=str(e))
1092 errormsg=str(e))
1094 raise
1093 raise
1095 finally:
1094 finally:
1096 if bundle:
1095 if bundle:
1097 bundle.close()
1096 bundle.close()
1098
1097
1099 @bundle2.parthandler(scratchbranchparttype,
1098 @bundle2.parthandler(scratchbranchparttype,
1100 ('bookmark', 'bookprevnode', 'force',
1099 ('bookmark', 'bookprevnode', 'force',
1101 'pushbackbookmarks', 'cgversion'))
1100 'pushbackbookmarks', 'cgversion'))
1102 def bundle2scratchbranch(op, part):
1101 def bundle2scratchbranch(op, part):
1103 '''unbundle a bundle2 part containing a changegroup to store'''
1102 '''unbundle a bundle2 part containing a changegroup to store'''
1104
1103
1105 bundler = bundle2.bundle20(op.repo.ui)
1104 bundler = bundle2.bundle20(op.repo.ui)
1106 cgversion = part.params.get('cgversion', '01')
1105 cgversion = part.params.get('cgversion', '01')
1107 cgpart = bundle2.bundlepart('changegroup', data=part.read())
1106 cgpart = bundle2.bundlepart('changegroup', data=part.read())
1108 cgpart.addparam('version', cgversion)
1107 cgpart.addparam('version', cgversion)
1109 bundler.addpart(cgpart)
1108 bundler.addpart(cgpart)
1110 buf = util.chunkbuffer(bundler.getchunks())
1109 buf = util.chunkbuffer(bundler.getchunks())
1111
1110
1112 fd, bundlefile = pycompat.mkstemp()
1111 fd, bundlefile = pycompat.mkstemp()
1113 try:
1112 try:
1114 try:
1113 try:
1115 fp = os.fdopen(fd, r'wb')
1114 fp = os.fdopen(fd, r'wb')
1116 fp.write(buf.read())
1115 fp.write(buf.read())
1117 finally:
1116 finally:
1118 fp.close()
1117 fp.close()
1119 storebundle(op, part.params, bundlefile)
1118 storebundle(op, part.params, bundlefile)
1120 finally:
1119 finally:
1121 try:
1120 try:
1122 os.unlink(bundlefile)
1121 os.unlink(bundlefile)
1123 except OSError as e:
1122 except OSError as e:
1124 if e.errno != errno.ENOENT:
1123 if e.errno != errno.ENOENT:
1125 raise
1124 raise
1126
1125
1127 return 1
1126 return 1
1128
1127
1129 def _maybeaddpushbackpart(op, bookmark, newnode, oldnode, params):
1128 def _maybeaddpushbackpart(op, bookmark, newnode, oldnode, params):
1130 if params.get('pushbackbookmarks'):
1129 if params.get('pushbackbookmarks'):
1131 if op.reply and 'pushback' in op.reply.capabilities:
1130 if op.reply and 'pushback' in op.reply.capabilities:
1132 params = {
1131 params = {
1133 'namespace': 'bookmarks',
1132 'namespace': 'bookmarks',
1134 'key': bookmark,
1133 'key': bookmark,
1135 'new': newnode,
1134 'new': newnode,
1136 'old': oldnode,
1135 'old': oldnode,
1137 }
1136 }
1138 op.reply.newpart('pushkey', mandatoryparams=params.iteritems())
1137 op.reply.newpart('pushkey', mandatoryparams=params.iteritems())
1139
1138
1140 def bundle2pushkey(orig, op, part):
1139 def bundle2pushkey(orig, op, part):
1141 '''Wrapper of bundle2.handlepushkey()
1140 '''Wrapper of bundle2.handlepushkey()
1142
1141
1143 The only goal is to skip calling the original function if flag is set.
1142 The only goal is to skip calling the original function if flag is set.
1144 It's set if infinitepush push is happening.
1143 It's set if infinitepush push is happening.
1145 '''
1144 '''
1146 if op.records[scratchbranchparttype + '_skippushkey']:
1145 if op.records[scratchbranchparttype + '_skippushkey']:
1147 if op.reply is not None:
1146 if op.reply is not None:
1148 rpart = op.reply.newpart('reply:pushkey')
1147 rpart = op.reply.newpart('reply:pushkey')
1149 rpart.addparam('in-reply-to', str(part.id), mandatory=False)
1148 rpart.addparam('in-reply-to', str(part.id), mandatory=False)
1150 rpart.addparam('return', '1', mandatory=False)
1149 rpart.addparam('return', '1', mandatory=False)
1151 return 1
1150 return 1
1152
1151
1153 return orig(op, part)
1152 return orig(op, part)
1154
1153
1155 def bundle2handlephases(orig, op, part):
1154 def bundle2handlephases(orig, op, part):
1156 '''Wrapper of bundle2.handlephases()
1155 '''Wrapper of bundle2.handlephases()
1157
1156
1158 The only goal is to skip calling the original function if flag is set.
1157 The only goal is to skip calling the original function if flag is set.
1159 It's set if infinitepush push is happening.
1158 It's set if infinitepush push is happening.
1160 '''
1159 '''
1161
1160
1162 if op.records[scratchbranchparttype + '_skipphaseheads']:
1161 if op.records[scratchbranchparttype + '_skipphaseheads']:
1163 return
1162 return
1164
1163
1165 return orig(op, part)
1164 return orig(op, part)
1166
1165
1167 def _asyncsavemetadata(root, nodes):
1166 def _asyncsavemetadata(root, nodes):
1168 '''starts a separate process that fills metadata for the nodes
1167 '''starts a separate process that fills metadata for the nodes
1169
1168
1170 This function creates a separate process and doesn't wait for it's
1169 This function creates a separate process and doesn't wait for it's
1171 completion. This was done to avoid slowing down pushes
1170 completion. This was done to avoid slowing down pushes
1172 '''
1171 '''
1173
1172
1174 maxnodes = 50
1173 maxnodes = 50
1175 if len(nodes) > maxnodes:
1174 if len(nodes) > maxnodes:
1176 return
1175 return
1177 nodesargs = []
1176 nodesargs = []
1178 for node in nodes:
1177 for node in nodes:
1179 nodesargs.append('--node')
1178 nodesargs.append('--node')
1180 nodesargs.append(node)
1179 nodesargs.append(node)
1181 with open(os.devnull, 'w+b') as devnull:
1180 with open(os.devnull, 'w+b') as devnull:
1182 cmdline = [util.hgexecutable(), 'debugfillinfinitepushmetadata',
1181 cmdline = [util.hgexecutable(), 'debugfillinfinitepushmetadata',
1183 '-R', root] + nodesargs
1182 '-R', root] + nodesargs
1184 # Process will run in background. We don't care about the return code
1183 # Process will run in background. We don't care about the return code
1185 subprocess.Popen(pycompat.rapply(procutil.tonativestr, cmdline),
1184 subprocess.Popen(pycompat.rapply(procutil.tonativestr, cmdline),
1186 close_fds=True, shell=False,
1185 close_fds=True, shell=False,
1187 stdin=devnull, stdout=devnull, stderr=devnull)
1186 stdin=devnull, stdout=devnull, stderr=devnull)
@@ -1,48 +1,48 b''
1 # Copyright 2017 Facebook, Inc.
1 # Copyright 2017 Facebook, Inc.
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 from __future__ import absolute_import
6 from __future__ import absolute_import
7
7
8 import os
8 import os
9
9
10 from mercurial.node import hex
10 from mercurial.node import hex
11
11
12 from mercurial import (
12 from mercurial import (
13 error,
13 error,
14 extensions,
14 extensions,
15 pycompat,
15 pycompat,
16 )
16 )
17
17
18 def isremotebooksenabled(ui):
18 def isremotebooksenabled(ui):
19 return ('remotenames' in extensions._extensions and
19 return ('remotenames' in extensions._extensions and
20 ui.configbool('remotenames', 'bookmarks'))
20 ui.configbool('remotenames', 'bookmarks'))
21
21
22 def downloadbundle(repo, unknownbinhead):
22 def downloadbundle(repo, unknownbinhead):
23 index = repo.bundlestore.index
23 index = repo.bundlestore.index
24 store = repo.bundlestore.store
24 store = repo.bundlestore.store
25 bundleid = index.getbundle(hex(unknownbinhead))
25 bundleid = index.getbundle(hex(unknownbinhead))
26 if bundleid is None:
26 if bundleid is None:
27 raise error.Abort('%s head is not known' % hex(unknownbinhead))
27 raise error.Abort('%s head is not known' % hex(unknownbinhead))
28 bundleraw = store.read(bundleid)
28 bundleraw = store.read(bundleid)
29 return _makebundlefromraw(bundleraw)
29 return _makebundlefromraw(bundleraw)
30
30
31 def _makebundlefromraw(data):
31 def _makebundlefromraw(data):
32 fp = None
32 fp = None
33 fd, bundlefile = pycompat.mkstemp()
33 fd, bundlefile = pycompat.mkstemp()
34 try: # guards bundlefile
34 try: # guards bundlefile
35 try: # guards fp
35 try: # guards fp
36 fp = os.fdopen(fd, 'wb')
36 fp = os.fdopen(fd, r'wb')
37 fp.write(data)
37 fp.write(data)
38 finally:
38 finally:
39 fp.close()
39 fp.close()
40 except Exception:
40 except Exception:
41 try:
41 try:
42 os.unlink(bundlefile)
42 os.unlink(bundlefile)
43 except Exception:
43 except Exception:
44 # we would rather see the original exception
44 # we would rather see the original exception
45 pass
45 pass
46 raise
46 raise
47
47
48 return bundlefile
48 return bundlefile
@@ -1,166 +1,167 b''
1 # This software may be used and distributed according to the terms of the
1 # This software may be used and distributed according to the terms of the
2 # GNU General Public License version 2 or any later version.
2 # GNU General Public License version 2 or any later version.
3
3
4 # based on bundleheads extension by Gregory Szorc <gps@mozilla.com>
4 # based on bundleheads extension by Gregory Szorc <gps@mozilla.com>
5
5
6 from __future__ import absolute_import
6 from __future__ import absolute_import
7
7
8 import abc
8 import abc
9 import hashlib
9 import hashlib
10 import os
10 import os
11 import subprocess
11 import subprocess
12 import tempfile
12 import tempfile
13
13
14 from mercurial import (
14 from mercurial import (
15 node,
15 pycompat,
16 pycompat,
16 )
17 )
17 from mercurial.utils import (
18 from mercurial.utils import (
18 procutil,
19 procutil,
19 )
20 )
20
21
21 NamedTemporaryFile = tempfile.NamedTemporaryFile
22 NamedTemporaryFile = tempfile.NamedTemporaryFile
22
23
23 class BundleWriteException(Exception):
24 class BundleWriteException(Exception):
24 pass
25 pass
25
26
26 class BundleReadException(Exception):
27 class BundleReadException(Exception):
27 pass
28 pass
28
29
29 class abstractbundlestore(object):
30 class abstractbundlestore(object):
30 """Defines the interface for bundle stores.
31 """Defines the interface for bundle stores.
31
32
32 A bundle store is an entity that stores raw bundle data. It is a simple
33 A bundle store is an entity that stores raw bundle data. It is a simple
33 key-value store. However, the keys are chosen by the store. The keys can
34 key-value store. However, the keys are chosen by the store. The keys can
34 be any Python object understood by the corresponding bundle index (see
35 be any Python object understood by the corresponding bundle index (see
35 ``abstractbundleindex`` below).
36 ``abstractbundleindex`` below).
36 """
37 """
37 __metaclass__ = abc.ABCMeta
38 __metaclass__ = abc.ABCMeta
38
39
39 @abc.abstractmethod
40 @abc.abstractmethod
40 def write(self, data):
41 def write(self, data):
41 """Write bundle data to the store.
42 """Write bundle data to the store.
42
43
43 This function receives the raw data to be written as a str.
44 This function receives the raw data to be written as a str.
44 Throws BundleWriteException
45 Throws BundleWriteException
45 The key of the written data MUST be returned.
46 The key of the written data MUST be returned.
46 """
47 """
47
48
48 @abc.abstractmethod
49 @abc.abstractmethod
49 def read(self, key):
50 def read(self, key):
50 """Obtain bundle data for a key.
51 """Obtain bundle data for a key.
51
52
52 Returns None if the bundle isn't known.
53 Returns None if the bundle isn't known.
53 Throws BundleReadException
54 Throws BundleReadException
54 The returned object should be a file object supporting read()
55 The returned object should be a file object supporting read()
55 and close().
56 and close().
56 """
57 """
57
58
58 class filebundlestore(object):
59 class filebundlestore(object):
59 """bundle store in filesystem
60 """bundle store in filesystem
60
61
61 meant for storing bundles somewhere on disk and on network filesystems
62 meant for storing bundles somewhere on disk and on network filesystems
62 """
63 """
63 def __init__(self, ui, repo):
64 def __init__(self, ui, repo):
64 self.ui = ui
65 self.ui = ui
65 self.repo = repo
66 self.repo = repo
66 self.storepath = ui.configpath('scratchbranch', 'storepath')
67 self.storepath = ui.configpath('scratchbranch', 'storepath')
67 if not self.storepath:
68 if not self.storepath:
68 self.storepath = self.repo.vfs.join("scratchbranches",
69 self.storepath = self.repo.vfs.join("scratchbranches",
69 "filebundlestore")
70 "filebundlestore")
70 if not os.path.exists(self.storepath):
71 if not os.path.exists(self.storepath):
71 os.makedirs(self.storepath)
72 os.makedirs(self.storepath)
72
73
73 def _dirpath(self, hashvalue):
74 def _dirpath(self, hashvalue):
74 """First two bytes of the hash are the name of the upper
75 """First two bytes of the hash are the name of the upper
75 level directory, next two bytes are the name of the
76 level directory, next two bytes are the name of the
76 next level directory"""
77 next level directory"""
77 return os.path.join(self.storepath, hashvalue[0:2], hashvalue[2:4])
78 return os.path.join(self.storepath, hashvalue[0:2], hashvalue[2:4])
78
79
79 def _filepath(self, filename):
80 def _filepath(self, filename):
80 return os.path.join(self._dirpath(filename), filename)
81 return os.path.join(self._dirpath(filename), filename)
81
82
82 def write(self, data):
83 def write(self, data):
83 filename = hashlib.sha1(data).hexdigest()
84 filename = node.hex(hashlib.sha1(data).digest())
84 dirpath = self._dirpath(filename)
85 dirpath = self._dirpath(filename)
85
86
86 if not os.path.exists(dirpath):
87 if not os.path.exists(dirpath):
87 os.makedirs(dirpath)
88 os.makedirs(dirpath)
88
89
89 with open(self._filepath(filename), 'wb') as f:
90 with open(self._filepath(filename), 'wb') as f:
90 f.write(data)
91 f.write(data)
91
92
92 return filename
93 return filename
93
94
94 def read(self, key):
95 def read(self, key):
95 try:
96 try:
96 with open(self._filepath(key), 'rb') as f:
97 with open(self._filepath(key), 'rb') as f:
97 return f.read()
98 return f.read()
98 except IOError:
99 except IOError:
99 return None
100 return None
100
101
101 class externalbundlestore(abstractbundlestore):
102 class externalbundlestore(abstractbundlestore):
102 def __init__(self, put_binary, put_args, get_binary, get_args):
103 def __init__(self, put_binary, put_args, get_binary, get_args):
103 """
104 """
104 `put_binary` - path to binary file which uploads bundle to external
105 `put_binary` - path to binary file which uploads bundle to external
105 storage and prints key to stdout
106 storage and prints key to stdout
106 `put_args` - format string with additional args to `put_binary`
107 `put_args` - format string with additional args to `put_binary`
107 {filename} replacement field can be used.
108 {filename} replacement field can be used.
108 `get_binary` - path to binary file which accepts filename and key
109 `get_binary` - path to binary file which accepts filename and key
109 (in that order), downloads bundle from store and saves it to file
110 (in that order), downloads bundle from store and saves it to file
110 `get_args` - format string with additional args to `get_binary`.
111 `get_args` - format string with additional args to `get_binary`.
111 {filename} and {handle} replacement field can be used.
112 {filename} and {handle} replacement field can be used.
112 """
113 """
113
114
114 self.put_args = put_args
115 self.put_args = put_args
115 self.get_args = get_args
116 self.get_args = get_args
116 self.put_binary = put_binary
117 self.put_binary = put_binary
117 self.get_binary = get_binary
118 self.get_binary = get_binary
118
119
119 def _call_binary(self, args):
120 def _call_binary(self, args):
120 p = subprocess.Popen(
121 p = subprocess.Popen(
121 pycompat.rapply(procutil.tonativestr, args),
122 pycompat.rapply(procutil.tonativestr, args),
122 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
123 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
123 close_fds=True)
124 close_fds=True)
124 stdout, stderr = p.communicate()
125 stdout, stderr = p.communicate()
125 returncode = p.returncode
126 returncode = p.returncode
126 return returncode, stdout, stderr
127 return returncode, stdout, stderr
127
128
128 def write(self, data):
129 def write(self, data):
129 # Won't work on windows because you can't open file second time without
130 # Won't work on windows because you can't open file second time without
130 # closing it
131 # closing it
131 # TODO: rewrite without str.format() and replace NamedTemporaryFile()
132 # TODO: rewrite without str.format() and replace NamedTemporaryFile()
132 # with pycompat.namedtempfile()
133 # with pycompat.namedtempfile()
133 with NamedTemporaryFile() as temp:
134 with NamedTemporaryFile() as temp:
134 temp.write(data)
135 temp.write(data)
135 temp.flush()
136 temp.flush()
136 temp.seek(0)
137 temp.seek(0)
137 formatted_args = [arg.format(filename=temp.name)
138 formatted_args = [arg.format(filename=temp.name)
138 for arg in self.put_args]
139 for arg in self.put_args]
139 returncode, stdout, stderr = self._call_binary(
140 returncode, stdout, stderr = self._call_binary(
140 [self.put_binary] + formatted_args)
141 [self.put_binary] + formatted_args)
141
142
142 if returncode != 0:
143 if returncode != 0:
143 raise BundleWriteException(
144 raise BundleWriteException(
144 'Failed to upload to external store: %s' % stderr)
145 'Failed to upload to external store: %s' % stderr)
145 stdout_lines = stdout.splitlines()
146 stdout_lines = stdout.splitlines()
146 if len(stdout_lines) == 1:
147 if len(stdout_lines) == 1:
147 return stdout_lines[0]
148 return stdout_lines[0]
148 else:
149 else:
149 raise BundleWriteException(
150 raise BundleWriteException(
150 'Bad output from %s: %s' % (self.put_binary, stdout))
151 'Bad output from %s: %s' % (self.put_binary, stdout))
151
152
152 def read(self, handle):
153 def read(self, handle):
153 # Won't work on windows because you can't open file second time without
154 # Won't work on windows because you can't open file second time without
154 # closing it
155 # closing it
155 # TODO: rewrite without str.format() and replace NamedTemporaryFile()
156 # TODO: rewrite without str.format() and replace NamedTemporaryFile()
156 # with pycompat.namedtempfile()
157 # with pycompat.namedtempfile()
157 with NamedTemporaryFile() as temp:
158 with NamedTemporaryFile() as temp:
158 formatted_args = [arg.format(filename=temp.name, handle=handle)
159 formatted_args = [arg.format(filename=temp.name, handle=handle)
159 for arg in self.get_args]
160 for arg in self.get_args]
160 returncode, stdout, stderr = self._call_binary(
161 returncode, stdout, stderr = self._call_binary(
161 [self.get_binary] + formatted_args)
162 [self.get_binary] + formatted_args)
162
163
163 if returncode != 0:
164 if returncode != 0:
164 raise BundleReadException(
165 raise BundleReadException(
165 'Failed to download from external store: %s' % stderr)
166 'Failed to download from external store: %s' % stderr)
166 return temp.read()
167 return temp.read()
General Comments 0
You need to be logged in to leave comments. Login now