Show More
@@ -267,6 +267,198 b' class cg1unpacker(object):' | |||
|
267 | 267 | pos = next |
|
268 | 268 | yield closechunk() |
|
269 | 269 | |
|
270 | def apply(self, repo, srctype, url, emptyok=False, | |
|
271 | targetphase=phases.draft, expectedtotal=None): | |
|
272 | """Add the changegroup returned by source.read() to this repo. | |
|
273 | srctype is a string like 'push', 'pull', or 'unbundle'. url is | |
|
274 | the URL of the repo where this changegroup is coming from. | |
|
275 | ||
|
276 | Return an integer summarizing the change to this repo: | |
|
277 | - nothing changed or no source: 0 | |
|
278 | - more heads than before: 1+added heads (2..n) | |
|
279 | - fewer heads than before: -1-removed heads (-2..-n) | |
|
280 | - number of heads stays the same: 1 | |
|
281 | """ | |
|
282 | repo = repo.unfiltered() | |
|
283 | def csmap(x): | |
|
284 | repo.ui.debug("add changeset %s\n" % short(x)) | |
|
285 | return len(cl) | |
|
286 | ||
|
287 | def revmap(x): | |
|
288 | return cl.rev(x) | |
|
289 | ||
|
290 | changesets = files = revisions = 0 | |
|
291 | ||
|
292 | tr = repo.transaction("\n".join([srctype, util.hidepassword(url)])) | |
|
293 | # The transaction could have been created before and already | |
|
294 | # carries source information. In this case we use the top | |
|
295 | # level data. We overwrite the argument because we need to use | |
|
296 | # the top level value (if they exist) in this function. | |
|
297 | srctype = tr.hookargs.setdefault('source', srctype) | |
|
298 | url = tr.hookargs.setdefault('url', url) | |
|
299 | ||
|
300 | # write changelog data to temp files so concurrent readers will not see | |
|
301 | # inconsistent view | |
|
302 | cl = repo.changelog | |
|
303 | cl.delayupdate(tr) | |
|
304 | oldheads = cl.heads() | |
|
305 | try: | |
|
306 | repo.hook('prechangegroup', throw=True, **tr.hookargs) | |
|
307 | ||
|
308 | trp = weakref.proxy(tr) | |
|
309 | # pull off the changeset group | |
|
310 | repo.ui.status(_("adding changesets\n")) | |
|
311 | clstart = len(cl) | |
|
312 | class prog(object): | |
|
313 | def __init__(self, step, total): | |
|
314 | self._step = step | |
|
315 | self._total = total | |
|
316 | self._count = 1 | |
|
317 | def __call__(self): | |
|
318 | repo.ui.progress(self._step, self._count, unit=_('chunks'), | |
|
319 | total=self._total) | |
|
320 | self._count += 1 | |
|
321 | self.callback = prog(_('changesets'), expectedtotal) | |
|
322 | ||
|
323 | efiles = set() | |
|
324 | def onchangelog(cl, node): | |
|
325 | efiles.update(cl.read(node)[3]) | |
|
326 | ||
|
327 | self.changelogheader() | |
|
328 | srccontent = cl.addgroup(self, csmap, trp, | |
|
329 | addrevisioncb=onchangelog) | |
|
330 | efiles = len(efiles) | |
|
331 | ||
|
332 | if not (srccontent or emptyok): | |
|
333 | raise error.Abort(_("received changelog group is empty")) | |
|
334 | clend = len(cl) | |
|
335 | changesets = clend - clstart | |
|
336 | repo.ui.progress(_('changesets'), None) | |
|
337 | ||
|
338 | # pull off the manifest group | |
|
339 | repo.ui.status(_("adding manifests\n")) | |
|
340 | # manifests <= changesets | |
|
341 | self.callback = prog(_('manifests'), changesets) | |
|
342 | # no need to check for empty manifest group here: | |
|
343 | # if the result of the merge of 1 and 2 is the same in 3 and 4, | |
|
344 | # no new manifest will be created and the manifest group will | |
|
345 | # be empty during the pull | |
|
346 | self.manifestheader() | |
|
347 | repo.manifest.addgroup(self, revmap, trp) | |
|
348 | repo.ui.progress(_('manifests'), None) | |
|
349 | ||
|
350 | needfiles = {} | |
|
351 | if repo.ui.configbool('server', 'validate', default=False): | |
|
352 | # validate incoming csets have their manifests | |
|
353 | for cset in xrange(clstart, clend): | |
|
354 | mfnode = repo.changelog.read(repo.changelog.node(cset))[0] | |
|
355 | mfest = repo.manifest.readdelta(mfnode) | |
|
356 | # store file nodes we must see | |
|
357 | for f, n in mfest.iteritems(): | |
|
358 | needfiles.setdefault(f, set()).add(n) | |
|
359 | ||
|
360 | # process the files | |
|
361 | repo.ui.status(_("adding file changes\n")) | |
|
362 | self.callback = None | |
|
363 | pr = prog(_('files'), efiles) | |
|
364 | newrevs, newfiles = addchangegroupfiles(repo, self, revmap, trp, pr, | |
|
365 | needfiles) | |
|
366 | revisions += newrevs | |
|
367 | files += newfiles | |
|
368 | ||
|
369 | dh = 0 | |
|
370 | if oldheads: | |
|
371 | heads = cl.heads() | |
|
372 | dh = len(heads) - len(oldheads) | |
|
373 | for h in heads: | |
|
374 | if h not in oldheads and repo[h].closesbranch(): | |
|
375 | dh -= 1 | |
|
376 | htext = "" | |
|
377 | if dh: | |
|
378 | htext = _(" (%+d heads)") % dh | |
|
379 | ||
|
380 | repo.ui.status(_("added %d changesets" | |
|
381 | " with %d changes to %d files%s\n") | |
|
382 | % (changesets, revisions, files, htext)) | |
|
383 | repo.invalidatevolatilesets() | |
|
384 | ||
|
385 | if changesets > 0: | |
|
386 | p = lambda: tr.writepending() and repo.root or "" | |
|
387 | if 'node' not in tr.hookargs: | |
|
388 | tr.hookargs['node'] = hex(cl.node(clstart)) | |
|
389 | hookargs = dict(tr.hookargs) | |
|
390 | else: | |
|
391 | hookargs = dict(tr.hookargs) | |
|
392 | hookargs['node'] = hex(cl.node(clstart)) | |
|
393 | repo.hook('pretxnchangegroup', throw=True, pending=p, | |
|
394 | **hookargs) | |
|
395 | ||
|
396 | added = [cl.node(r) for r in xrange(clstart, clend)] | |
|
397 | publishing = repo.publishing() | |
|
398 | if srctype in ('push', 'serve'): | |
|
399 | # Old servers can not push the boundary themselves. | |
|
400 | # New servers won't push the boundary if changeset already | |
|
401 | # exists locally as secret | |
|
402 | # | |
|
403 | # We should not use added here but the list of all change in | |
|
404 | # the bundle | |
|
405 | if publishing: | |
|
406 | phases.advanceboundary(repo, tr, phases.public, srccontent) | |
|
407 | else: | |
|
408 | # Those changesets have been pushed from the outside, their | |
|
409 | # phases are going to be pushed alongside. Therefor | |
|
410 | # `targetphase` is ignored. | |
|
411 | phases.advanceboundary(repo, tr, phases.draft, srccontent) | |
|
412 | phases.retractboundary(repo, tr, phases.draft, added) | |
|
413 | elif srctype != 'strip': | |
|
414 | # publishing only alter behavior during push | |
|
415 | # | |
|
416 | # strip should not touch boundary at all | |
|
417 | phases.retractboundary(repo, tr, targetphase, added) | |
|
418 | ||
|
419 | if changesets > 0: | |
|
420 | if srctype != 'strip': | |
|
421 | # During strip, branchcache is invalid but coming call to | |
|
422 | # `destroyed` will repair it. | |
|
423 | # In other case we can safely update cache on disk. | |
|
424 | branchmap.updatecache(repo.filtered('served')) | |
|
425 | ||
|
426 | def runhooks(): | |
|
427 | # These hooks run when the lock releases, not when the | |
|
428 | # transaction closes. So it's possible for the changelog | |
|
429 | # to have changed since we last saw it. | |
|
430 | if clstart >= len(repo): | |
|
431 | return | |
|
432 | ||
|
433 | # forcefully update the on-disk branch cache | |
|
434 | repo.ui.debug("updating the branch cache\n") | |
|
435 | repo.hook("changegroup", **hookargs) | |
|
436 | ||
|
437 | for n in added: | |
|
438 | args = hookargs.copy() | |
|
439 | args['node'] = hex(n) | |
|
440 | repo.hook("incoming", **args) | |
|
441 | ||
|
442 | newheads = [h for h in repo.heads() if h not in oldheads] | |
|
443 | repo.ui.log("incoming", | |
|
444 | "%s incoming changes - new heads: %s\n", | |
|
445 | len(added), | |
|
446 | ', '.join([hex(c[:6]) for c in newheads])) | |
|
447 | ||
|
448 | tr.addpostclose('changegroup-runhooks-%020i' % clstart, | |
|
449 | lambda tr: repo._afterlock(runhooks)) | |
|
450 | ||
|
451 | tr.close() | |
|
452 | ||
|
453 | finally: | |
|
454 | tr.release() | |
|
455 | repo.ui.flush() | |
|
456 | # never return 0 here: | |
|
457 | if dh < 0: | |
|
458 | return dh - 1 | |
|
459 | else: | |
|
460 | return dh + 1 | |
|
461 | ||
|
270 | 462 | class cg2unpacker(cg1unpacker): |
|
271 | 463 | deltaheader = _CHANGEGROUPV2_DELTA_HEADER |
|
272 | 464 | deltaheadersize = struct.calcsize(deltaheader) |
@@ -720,194 +912,9 b' def addchangegroupfiles(repo, source, re' | |||
|
720 | 912 | |
|
721 | 913 | def addchangegroup(repo, source, srctype, url, emptyok=False, |
|
722 | 914 | targetphase=phases.draft, expectedtotal=None): |
|
723 | """Add the changegroup returned by source.read() to this repo. | |
|
724 | srctype is a string like 'push', 'pull', or 'unbundle'. url is | |
|
725 | the URL of the repo where this changegroup is coming from. | |
|
726 | ||
|
727 | Return an integer summarizing the change to this repo: | |
|
728 | - nothing changed or no source: 0 | |
|
729 | - more heads than before: 1+added heads (2..n) | |
|
730 | - fewer heads than before: -1-removed heads (-2..-n) | |
|
731 | - number of heads stays the same: 1 | |
|
732 | """ | |
|
915 | """Legacy forwarding method to cg?unpacker.apply() to be removed soon.""" | |
|
733 | 916 | if not source: |
|
734 | 917 | return 0 |
|
735 | 918 | |
|
736 | repo = repo.unfiltered() | |
|
737 | def csmap(x): | |
|
738 | repo.ui.debug("add changeset %s\n" % short(x)) | |
|
739 | return len(cl) | |
|
740 | ||
|
741 | def revmap(x): | |
|
742 | return cl.rev(x) | |
|
743 | ||
|
744 | changesets = files = revisions = 0 | |
|
745 | ||
|
746 | tr = repo.transaction("\n".join([srctype, util.hidepassword(url)])) | |
|
747 | # The transaction could have been created before and already carries source | |
|
748 | # information. In this case we use the top level data. We overwrite the | |
|
749 | # argument because we need to use the top level value (if they exist) in | |
|
750 | # this function. | |
|
751 | srctype = tr.hookargs.setdefault('source', srctype) | |
|
752 | url = tr.hookargs.setdefault('url', url) | |
|
753 | ||
|
754 | # write changelog data to temp files so concurrent readers will not see | |
|
755 | # inconsistent view | |
|
756 | cl = repo.changelog | |
|
757 | cl.delayupdate(tr) | |
|
758 | oldheads = cl.heads() | |
|
759 | try: | |
|
760 | repo.hook('prechangegroup', throw=True, **tr.hookargs) | |
|
761 | ||
|
762 | trp = weakref.proxy(tr) | |
|
763 | # pull off the changeset group | |
|
764 | repo.ui.status(_("adding changesets\n")) | |
|
765 | clstart = len(cl) | |
|
766 | class prog(object): | |
|
767 | def __init__(self, step, total): | |
|
768 | self._step = step | |
|
769 | self._total = total | |
|
770 | self._count = 1 | |
|
771 | def __call__(self): | |
|
772 | repo.ui.progress(self._step, self._count, unit=_('chunks'), | |
|
773 | total=self._total) | |
|
774 | self._count += 1 | |
|
775 | source.callback = prog(_('changesets'), expectedtotal) | |
|
776 | ||
|
777 | efiles = set() | |
|
778 | def onchangelog(cl, node): | |
|
779 | efiles.update(cl.read(node)[3]) | |
|
780 | ||
|
781 | source.changelogheader() | |
|
782 | srccontent = cl.addgroup(source, csmap, trp, | |
|
783 | addrevisioncb=onchangelog) | |
|
784 | efiles = len(efiles) | |
|
785 | ||
|
786 | if not (srccontent or emptyok): | |
|
787 | raise error.Abort(_("received changelog group is empty")) | |
|
788 | clend = len(cl) | |
|
789 | changesets = clend - clstart | |
|
790 | repo.ui.progress(_('changesets'), None) | |
|
791 | ||
|
792 | # pull off the manifest group | |
|
793 | repo.ui.status(_("adding manifests\n")) | |
|
794 | # manifests <= changesets | |
|
795 | source.callback = prog(_('manifests'), changesets) | |
|
796 | # no need to check for empty manifest group here: | |
|
797 | # if the result of the merge of 1 and 2 is the same in 3 and 4, | |
|
798 | # no new manifest will be created and the manifest group will | |
|
799 | # be empty during the pull | |
|
800 | source.manifestheader() | |
|
801 | repo.manifest.addgroup(source, revmap, trp) | |
|
802 | repo.ui.progress(_('manifests'), None) | |
|
803 | ||
|
804 | needfiles = {} | |
|
805 | if repo.ui.configbool('server', 'validate', default=False): | |
|
806 | # validate incoming csets have their manifests | |
|
807 | for cset in xrange(clstart, clend): | |
|
808 | mfnode = repo.changelog.read(repo.changelog.node(cset))[0] | |
|
809 | mfest = repo.manifest.readdelta(mfnode) | |
|
810 | # store file nodes we must see | |
|
811 | for f, n in mfest.iteritems(): | |
|
812 | needfiles.setdefault(f, set()).add(n) | |
|
813 | ||
|
814 | # process the files | |
|
815 | repo.ui.status(_("adding file changes\n")) | |
|
816 | source.callback = None | |
|
817 | pr = prog(_('files'), efiles) | |
|
818 | newrevs, newfiles = addchangegroupfiles(repo, source, revmap, trp, pr, | |
|
819 | needfiles) | |
|
820 | revisions += newrevs | |
|
821 | files += newfiles | |
|
822 | ||
|
823 | dh = 0 | |
|
824 | if oldheads: | |
|
825 | heads = cl.heads() | |
|
826 | dh = len(heads) - len(oldheads) | |
|
827 | for h in heads: | |
|
828 | if h not in oldheads and repo[h].closesbranch(): | |
|
829 | dh -= 1 | |
|
830 | htext = "" | |
|
831 | if dh: | |
|
832 | htext = _(" (%+d heads)") % dh | |
|
833 | ||
|
834 | repo.ui.status(_("added %d changesets" | |
|
835 | " with %d changes to %d files%s\n") | |
|
836 | % (changesets, revisions, files, htext)) | |
|
837 | repo.invalidatevolatilesets() | |
|
838 | ||
|
839 | if changesets > 0: | |
|
840 | p = lambda: tr.writepending() and repo.root or "" | |
|
841 | if 'node' not in tr.hookargs: | |
|
842 | tr.hookargs['node'] = hex(cl.node(clstart)) | |
|
843 | hookargs = dict(tr.hookargs) | |
|
844 | else: | |
|
845 | hookargs = dict(tr.hookargs) | |
|
846 | hookargs['node'] = hex(cl.node(clstart)) | |
|
847 | repo.hook('pretxnchangegroup', throw=True, pending=p, **hookargs) | |
|
848 | ||
|
849 | added = [cl.node(r) for r in xrange(clstart, clend)] | |
|
850 | publishing = repo.publishing() | |
|
851 | if srctype in ('push', 'serve'): | |
|
852 | # Old servers can not push the boundary themselves. | |
|
853 | # New servers won't push the boundary if changeset already | |
|
854 | # exists locally as secret | |
|
855 | # | |
|
856 | # We should not use added here but the list of all change in | |
|
857 | # the bundle | |
|
858 | if publishing: | |
|
859 | phases.advanceboundary(repo, tr, phases.public, srccontent) | |
|
860 | else: | |
|
861 | # Those changesets have been pushed from the outside, their | |
|
862 | # phases are going to be pushed alongside. Therefor | |
|
863 | # `targetphase` is ignored. | |
|
864 | phases.advanceboundary(repo, tr, phases.draft, srccontent) | |
|
865 | phases.retractboundary(repo, tr, phases.draft, added) | |
|
866 | elif srctype != 'strip': | |
|
867 | # publishing only alter behavior during push | |
|
868 | # | |
|
869 | # strip should not touch boundary at all | |
|
870 | phases.retractboundary(repo, tr, targetphase, added) | |
|
871 | ||
|
872 | if changesets > 0: | |
|
873 | if srctype != 'strip': | |
|
874 | # During strip, branchcache is invalid but coming call to | |
|
875 | # `destroyed` will repair it. | |
|
876 | # In other case we can safely update cache on disk. | |
|
877 | branchmap.updatecache(repo.filtered('served')) | |
|
878 | ||
|
879 | def runhooks(): | |
|
880 | # These hooks run when the lock releases, not when the | |
|
881 | # transaction closes. So it's possible for the changelog | |
|
882 | # to have changed since we last saw it. | |
|
883 | if clstart >= len(repo): | |
|
884 | return | |
|
885 | ||
|
886 | # forcefully update the on-disk branch cache | |
|
887 | repo.ui.debug("updating the branch cache\n") | |
|
888 | repo.hook("changegroup", **hookargs) | |
|
889 | ||
|
890 | for n in added: | |
|
891 | args = hookargs.copy() | |
|
892 | args['node'] = hex(n) | |
|
893 | repo.hook("incoming", **args) | |
|
894 | ||
|
895 | newheads = [h for h in repo.heads() if h not in oldheads] | |
|
896 | repo.ui.log("incoming", | |
|
897 | "%s incoming changes - new heads: %s\n", | |
|
898 | len(added), | |
|
899 | ', '.join([hex(c[:6]) for c in newheads])) | |
|
900 | ||
|
901 | tr.addpostclose('changegroup-runhooks-%020i' % clstart, | |
|
902 | lambda tr: repo._afterlock(runhooks)) | |
|
903 | ||
|
904 | tr.close() | |
|
905 | ||
|
906 | finally: | |
|
907 | tr.release() | |
|
908 | repo.ui.flush() | |
|
909 | # never return 0 here: | |
|
910 | if dh < 0: | |
|
911 | return dh - 1 | |
|
912 | else: | |
|
913 | return dh + 1 | |
|
919 | return source.apply(repo, srctype, url, emptyok=emptyok, | |
|
920 | targetphase=targetphase, expectedtotal=expectedtotal) |
General Comments 0
You need to be logged in to leave comments.
Login now