6 # GNU General Public License version 2 or any later version. |
6 # GNU General Public License version 2 or any later version. |
7 |
7 |
8 from __future__ import absolute_import |
8 from __future__ import absolute_import |
9 |
9 |
10 import contextlib |
10 import contextlib |
|
11 import errno |
11 import os |
12 import os |
12 import struct |
13 import struct |
13 |
14 |
14 from .i18n import _ |
15 from .i18n import _ |
15 from .pycompat import open |
16 from .pycompat import open |
16 from .interfaces import repository |
17 from .interfaces import repository |
17 from . import ( |
18 from . import ( |
|
19 bookmarks, |
18 cacheutil, |
20 cacheutil, |
19 error, |
21 error, |
20 narrowspec, |
22 narrowspec, |
21 phases, |
23 phases, |
22 pycompat, |
24 pycompat, |
23 requirements as requirementsmod, |
25 requirements as requirementsmod, |
24 scmutil, |
26 scmutil, |
25 store, |
27 store, |
26 util, |
28 util, |
|
29 ) |
|
30 from .utils import ( |
|
31 stringutil, |
27 ) |
32 ) |
28 |
33 |
29 |
34 |
30 def canperformstreamclone(pullop, bundle2=False): |
35 def canperformstreamclone(pullop, bundle2=False): |
31 """Whether it is possible to perform a streaming clone as part of pull. |
36 """Whether it is possible to perform a streaming clone as part of pull. |
769 ) |
774 ) |
770 repo.svfs.options = localrepo.resolvestorevfsoptions( |
775 repo.svfs.options = localrepo.resolvestorevfsoptions( |
771 repo.ui, repo.requirements, repo.features |
776 repo.ui, repo.requirements, repo.features |
772 ) |
777 ) |
773 scmutil.writereporequirements(repo) |
778 scmutil.writereporequirements(repo) |
|
779 |
|
780 |
|
781 def _copy_files(src_vfs_map, dst_vfs_map, entries, progress): |
|
782 hardlink = [True] |
|
783 |
|
784 def copy_used(): |
|
785 hardlink[0] = False |
|
786 progress.topic = _(b'copying') |
|
787 |
|
788 for k, path, size in entries: |
|
789 src_vfs = src_vfs_map[k] |
|
790 dst_vfs = dst_vfs_map[k] |
|
791 src_path = src_vfs.join(path) |
|
792 dst_path = dst_vfs.join(path) |
|
793 dirname = dst_vfs.dirname(path) |
|
794 if not dst_vfs.exists(dirname): |
|
795 dst_vfs.makedirs(dirname) |
|
796 dst_vfs.register_file(path) |
|
797 # XXX we could use the #nb_bytes argument. |
|
798 util.copyfile( |
|
799 src_path, |
|
800 dst_path, |
|
801 hardlink=hardlink[0], |
|
802 no_hardlink_cb=copy_used, |
|
803 check_fs_hardlink=False, |
|
804 ) |
|
805 progress.increment() |
|
806 return hardlink[0] |
|
807 |
|
808 |
|
809 def local_copy(src_repo, dest_repo): |
|
810 """copy all content from one local repository to another |
|
811 |
|
812 This is useful for local clone""" |
|
813 src_store_requirements = { |
|
814 r |
|
815 for r in src_repo.requirements |
|
816 if r not in requirementsmod.WORKING_DIR_REQUIREMENTS |
|
817 } |
|
818 dest_store_requirements = { |
|
819 r |
|
820 for r in dest_repo.requirements |
|
821 if r not in requirementsmod.WORKING_DIR_REQUIREMENTS |
|
822 } |
|
823 assert src_store_requirements == dest_store_requirements |
|
824 |
|
825 with dest_repo.lock(): |
|
826 with src_repo.lock(): |
|
827 entries, totalfilesize = _v2_walk( |
|
828 src_repo, |
|
829 includes=None, |
|
830 excludes=None, |
|
831 includeobsmarkers=True, |
|
832 ) |
|
833 src_vfs_map = _makemap(src_repo) |
|
834 dest_vfs_map = _makemap(dest_repo) |
|
835 progress = src_repo.ui.makeprogress( |
|
836 topic=_(b'linking'), |
|
837 total=len(entries), |
|
838 unit=_(b'files'), |
|
839 ) |
|
840 # copy files |
|
841 # |
|
842 # We could copy the full file while the source repository is locked |
|
843 # and the other one without the lock. However, in the linking case, |
|
844 # this would also requires checks that nobody is appending any data |
|
845 # to the files while we do the clone, so this is not done yet. We |
|
846 # could do this blindly when copying files. |
|
847 files = ((k, path, size) for k, path, ftype, size in entries) |
|
848 hardlink = _copy_files(src_vfs_map, dest_vfs_map, files, progress) |
|
849 |
|
850 # copy bookmarks over |
|
851 src_book_vfs = bookmarks.bookmarksvfs(src_repo) |
|
852 srcbookmarks = src_book_vfs.join(b'bookmarks') |
|
853 dst_book_vfs = bookmarks.bookmarksvfs(dest_repo) |
|
854 dstbookmarks = dst_book_vfs.join(b'bookmarks') |
|
855 if os.path.exists(srcbookmarks): |
|
856 util.copyfile(srcbookmarks, dstbookmarks) |
|
857 progress.complete() |
|
858 if hardlink: |
|
859 msg = b'linked %d files\n' |
|
860 else: |
|
861 msg = b'copied %d files\n' |
|
862 src_repo.ui.debug(msg % len(entries)) |
|
863 |
|
864 with dest_repo.transaction(b"localclone") as tr: |
|
865 dest_repo.store.write(tr) |
|
866 |
|
867 # clean up transaction file as they do not make sense |
|
868 undo_files = [(dest_repo.svfs, b'undo.backupfiles')] |
|
869 undo_files.extend(dest_repo.undofiles()) |
|
870 for undovfs, undofile in undo_files: |
|
871 try: |
|
872 undovfs.unlink(undofile) |
|
873 except OSError as e: |
|
874 if e.errno != errno.ENOENT: |
|
875 msg = _(b'error removing %s: %s\n') |
|
876 path = undovfs.join(undofile) |
|
877 e_msg = stringutil.forcebytestr(e) |
|
878 msg %= (path, e_msg) |
|
879 dest_repo.ui.warn(msg) |