source: tailor/vcpx/dualwd.py @ 1282

Revision 1282, 4.2 KB checked in by lele@…, 7 years ago (diff)

UNDO: Fix ticket #75, at least with DisjunctWorkingDirectories
While fixing it with shared basedirs is a tricky business, with disjunct
basedirs it's very simple: first replay the changeset on the target, then
execute rsync to copy updated files.

Line 
1# -*- mode: python; coding: utf-8 -*-
2# :Progetto: vcpx -- Dual working directory
3# :Creato:   dom 20 giu 2004 11:02:01 CEST
4# :Autore:   Lele Gaifax <lele@nautilus.homeip.net>
5# :Licenza:  GNU General Public License
6#
7
8"""
9The easiest way to propagate changes from one VC control system to one
10of an another kind is having a single directory containing a live
11working copy shared between the two VC systems.
12
13In a slightly more elaborated way, the source and the target system may
14use separate directories, that gets rsynced when needed.
15
16This module implements `DualWorkingDir`, which instances have a
17`source` and `target` properties offering the right capabilities to do
18the job.
19"""
20
21__docformat__ = 'reStructuredText'
22
23from source import UpdatableSourceWorkingDir, InvocationError
24from target import SynchronizableTargetWorkingDir
25from shwrap import ExternalCommand
26from datetime import datetime
27
28IGNORED_METADIRS = []
29
30class DualWorkingDir(UpdatableSourceWorkingDir, SynchronizableTargetWorkingDir):
31    """
32    Dual working directory, one that is under two different VC systems at
33    the same time.
34
35    This class reimplements the two interfaces, dispatching the right method
36    to the right backend.
37    """
38
39    def __init__(self, source_repo, target_repo):
40        global IGNORED_METADIRS
41        from os.path import sep
42
43        self.source = source_repo.workingDir()
44        self.target = target_repo.workingDir()
45
46        sbdir = self.source.repository.basedir.rstrip(sep)+sep
47        tbdir = self.target.repository.basedir.rstrip(sep)+sep
48        if sbdir == tbdir:
49            shared = True
50        elif tbdir.startswith(sbdir):
51            raise InvocationError('Target base directory "%s" cannot be a '
52                                  'subdirectory of source directory "%s"' %(
53                (tbdir, sbdir)))
54        elif sbdir.startswith(tbdir):
55            shared = True
56        else:
57            shared = False
58        self.shared_basedirs = shared
59        self.source.shared_basedirs = shared
60        self.target.shared_basedirs = shared
61
62        IGNORED_METADIRS = filter(None, [source_repo.METADIR,
63                                         target_repo.METADIR])
64        IGNORED_METADIRS.extend(source_repo.EXTRA_METADIRS)
65        IGNORED_METADIRS.extend(target_repo.EXTRA_METADIRS)
66
67        self.source.prepareSourceRepository()
68        self.target.prepareTargetRepository()
69
70        # UpdatableSourceWorkingDir
71
72        self.getPendingChangesets = self.source.getPendingChangesets
73        self.checkoutUpstreamRevision = self.source.checkoutUpstreamRevision
74
75        # SynchronizableTargetWorkingDir
76
77        self.prepareWorkingDirectory = self.target.prepareWorkingDirectory
78
79    def setStateFile(self, state_file):
80        """
81        Set the state file used to store the revision and pending changesets.
82        """
83
84        self.source.setStateFile(state_file)
85        self.target.setStateFile(state_file)
86
87    def setLogfile(self, logfile):
88        """
89        Set the name of the logfile, just to ignore it.
90        """
91
92        self.target.logfile = logfile
93
94    def applyPendingChangesets(self, applyable=None, replay=None, applied=None):
95        return self.source.applyPendingChangesets(replay=self.replayChangeset,
96                                                  applyable=applyable,
97                                                  applied=applied)
98
99    def importFirstRevision(self, source_repo, changeset, initial):
100        if not self.shared_basedirs:
101            self._syncTargetWithSource()
102        self.target.importFirstRevision(source_repo, changeset, initial)
103
104    def replayChangeset(self, changeset):
105        if not self.shared_basedirs:
106            self._syncTargetWithSource()
107        self.target.replayChangeset(changeset)
108
109    def _syncTargetWithSource(self):
110        cmd = ['rsync', '--archive']
111        now = datetime.now()
112        if hasattr(self, '_last_rsync'):
113            last = self._last_rsync
114            if not (now-last).seconds:
115                cmd.append('--ignore-times')
116        self._last_rsync = now
117        for M in IGNORED_METADIRS:
118            cmd.extend(['--exclude', M])
119
120        rsync = ExternalCommand(command=cmd)
121        rsync.execute(self.source.repository.basedir+'/', self.target.repository.basedir)
Note: See TracBrowser for help on using the repository browser.