[Pkg-bazaar-commits] ./bzr/unstable r792: - rsync upload/download plugins from John A Meinel

Martin Pool mbp at sourcefrog.net
Fri Apr 10 08:21:03 UTC 2009


------------------------------------------------------------
revno: 792
committer: Martin Pool <mbp at sourcefrog.net>
timestamp: Mon 2005-06-27 15:03:27 +1000
message:
  - rsync upload/download plugins from John A Meinel
added:
  plugins/rsync/
  plugins/rsync/__init__.py
  plugins/rsync/rsync_update.py
-------------- next part --------------
=== added directory 'plugins/rsync'
=== added file 'plugins/rsync/__init__.py'
--- a/plugins/rsync/__init__.py	1970-01-01 00:00:00 +0000
+++ b/plugins/rsync/__init__.py	2005-06-27 05:03:27 +0000
@@ -0,0 +1,85 @@
+#!/usr/bin/env python
+"""\
+This is a plugin for the Bazaar-NG revision control system.
+"""
+
+import os
+import bzrlib, bzrlib.commands
+
+class cmd_rsync_pull(bzrlib.commands.Command):
+    """Update the current working tree using rsync.
+    With no arguments, look for a .bzr/x-rsync-location file
+    to determine which remote system to rsync from.
+    Otherwise, you can specify a new location to rsync from.
+
+    Normally the first time you use it, you would write:
+        bzr rsync-pull . path/to/otherdirectory
+    """
+    takes_args = ['local?', 'remote?']
+    takes_options = ['verbose']
+    aliases = ['rpull']
+
+    def run(self, local=None, remote=None, verbose=True):
+        from rsync_update import get_branch_remote_update, \
+            check_should_pull, set_default_remote_info, pull
+
+        b, remote, last_revno, last_revision = \
+            get_branch_remote_update(local=local, remote=remote)
+
+        if not check_should_pull(b, last_revno, last_revision):
+            return 1
+        b = pull(b, remote, verbose=verbose)
+
+        set_default_remote_info(b, remote)
+
+class cmd_rsync_pull_bzr(cmd_rsync_pull):
+    takes_args = ['remote?']
+    def run(self, remote=None, verbose=True):
+        from rsync_update import get_branch_remote_update, \
+            check_should_pull, set_default_remote_info, pull
+
+        bzr_path = os.path.dirname(bzrlib.__path__[0])
+        b, remote, last_revno, last_revision = \
+            get_branch_remote_update(local=bzr_path, remote=remote
+                , alt_remote='bazaar-ng.org::bazaar-ng/bzr/bzr.dev/')
+
+        if not check_should_pull(b, last_revno, last_revision):
+            return 1
+        b = pull(b, remote, verbose=verbose)
+
+        set_default_remote_info(b, remote)
+
+class cmd_rsync_push(bzrlib.commands.Command):
+    """Update the remote tree using rsync.
+    With no arguments, look for a .bzr/x-rsync-location file
+    to determine which remote system to rsync to.
+    Otherwise, you can specify a new location to rsync to.
+    """
+    takes_args = ['local?', 'remote?']
+    takes_options = ['verbose']
+    aliases = ['rpush']
+
+    def run(self, local=None, remote=None, verbose=True):
+        from rsync_update import get_branch_remote_update, \
+            check_should_push, set_default_remote_info, push
+
+        b, remote, last_revno, last_revision = \
+            get_branch_remote_update(local=local, remote=remote)
+
+        if not check_should_push(b, last_revno, last_revision):
+            return 1
+
+        push(b, remote, verbose=verbose)
+
+        set_default_remote_info(b, remote)
+
+
+if hasattr(bzrlib.commands, 'register_plugin_command'):
+    bzrlib.commands.register_plugin_command(cmd_rsync_pull)
+    bzrlib.commands.register_plugin_command(cmd_rsync_pull_bzr)
+    bzrlib.commands.register_plugin_command(cmd_rsync_push)
+elif hasattr(bzrlib.commands, 'register_command'):
+    bzrlib.commands.register_command(cmd_rsync_pull)
+    bzrlib.commands.register_command(cmd_rsync_pull_bzr)
+    bzrlib.commands.register_command(cmd_rsync_push)
+

=== added file 'plugins/rsync/rsync_update.py'
--- a/plugins/rsync/rsync_update.py	1970-01-01 00:00:00 +0000
+++ b/plugins/rsync/rsync_update.py	2005-06-27 05:03:27 +0000
@@ -0,0 +1,351 @@
+#!/usr/bin/env python
+"""\
+This encapsulates the functionality for trying to rsync a local
+working tree to/from a remote rsync accessible location.
+"""
+
+import os
+import bzrlib
+
+_rsync_location = 'x-rsync-data'
+_parent_locations = ['parent', 'pull', 'x-pull']
+
+def temp_branch():
+    import tempfile
+    dirname = tempfile.mkdtemp("temp-branch")
+    return bzrlib.Branch(dirname, init=True)
+
+def rm_branch(branch):
+    import shutil
+    shutil.rmtree(branch.base)
+
+def is_clean(branch):
+    """
+    Return true if no files are modifed or unknown
+    >>> br = temp_branch()
+    >>> is_clean(br)
+    True
+    >>> fooname = os.path.join(br.base, "foo")
+    >>> file(fooname, "wb").write("bar")
+    >>> is_clean(br)
+    False
+    >>> bzrlib.add.smart_add([fooname])
+    >>> is_clean(br)
+    False
+    >>> br.commit("added file")
+    >>> is_clean(br)
+    True
+    >>> rm_branch(br)
+    """
+    old_tree = branch.basis_tree()
+    new_tree = branch.working_tree()
+    for path, file_class, kind, file_id in new_tree.list_files():
+        if file_class == '?':
+            return False
+    delta = bzrlib.compare_trees(old_tree, new_tree, want_unchanged=False)
+    if len(delta.added) > 0 or len(delta.removed) > 0 or \
+        len(delta.modified) > 0:
+        return False
+    return True
+
+def get_default_remote_info(branch):
+    """Return the value stored in .bzr/x-rsync-location if it exists.
+    
+    >>> br = temp_branch()
+    >>> get_default_remote_info(br)
+    (None, 0, None)
+    >>> import bzrlib.commit
+    >>> bzrlib.commit.commit(br, 'test commit', rev_id='test-id-12345')
+    >>> set_default_remote_info(br, 'http://somewhere')
+    >>> get_default_remote_info(br)
+    ('http://somewhere', 1, 'test-id-12345')
+    """
+    def_remote = None
+    revno = 0
+    revision = None
+    def_remote_filename = branch.controlfilename(_rsync_location)
+    if os.path.isfile(def_remote_filename):
+        [def_remote,revno, revision] = [x.strip() for x in open(def_remote_filename).readlines()]
+    return def_remote, int(revno), revision
+
+def set_default_remote_info(branch, location):
+    """Store the location into the .bzr/x-rsync-location.
+    
+    """
+    from bzrlib.atomicfile import AtomicFile
+    remote, revno, revision = get_default_remote_info(branch)
+    if (remote == location 
+        and revno == branch.revno()
+        and revision == branch.last_patch()):
+        return #Nothing would change, so skip it
+    # TODO: Consider adding to x-pull so that we can try a RemoteBranch
+    # for checking the need to update
+    f = AtomicFile(branch.controlfilename(_rsync_location))
+    f.write(location)
+    f.write('\n')
+    f.write(str(branch.revno()))
+    f.write('\n')
+    f.write(branch.last_patch())
+    f.write('\n')
+    f.commit()
+
+def get_parent_branch(branch):
+    """Try to get the pull location, in case this directory supports the normal bzr pull.
+    
+    The idea is that we can use RemoteBranch to see if we actually need to do anything,
+    and then we can decide whether to run rsync or not.
+    """
+    import errno
+    stored_loc = None
+    for fname in _parent_locations:
+        try:
+            stored_loc = branch.controlfile(fname, 'rb').read().rstrip('\n')
+        except IOError, e:
+            if e.errno != errno.ENOENT:
+                raise
+
+        if stored_loc:
+            break
+
+    if stored_loc:
+        from bzrlib.branch import find_branch
+        return find_branch(stored_loc)
+    return None
+
+def get_branch_remote_update(local=None, remote=None, alt_remote=None):
+    from bzrlib.errors import BzrCommandError
+    from bzrlib.branch import find_branch
+    if local is None:
+        local = '.'
+
+    if remote is not None and remote[-1:] != '/':
+        remote += '/'
+
+    if alt_remote is not None and alt_remote[-1:] != '/':
+        alt_remote += '/'
+
+    if not os.path.exists(local):
+        if remote is None:
+            remote = alt_remote
+        if remote is None:
+            raise BzrCommandError('No remote location specified while creating a new local location')
+        return local, remote, 0, None
+
+    b = find_branch(local)
+
+    def_remote, last_revno, last_revision = get_default_remote_info(b)
+    if remote is None:
+        if def_remote is None:
+            if alt_remote is None:
+                raise BzrCommandError('No remote location specified, and no default exists.')
+            else:
+                remote = alt_remote
+        else:
+            remote = def_remote
+
+    if remote[-1:] != '/':
+        remote += '/'
+
+    return b, remote, last_revno, last_revision
+
+def check_should_pull(branch, last_revno, last_revision):
+    if isinstance(branch, basestring): # We don't even have a local branch yet
+        return True
+
+    if not is_clean(branch):
+        print '** Local tree is not clean. Either has unknown or modified files.'
+        return False
+
+    b_parent = get_parent_branch(branch)
+    if b_parent is not None:
+        from bzrlib.branch import DivergedBranches
+        # This may throw a Diverged branches.
+        try:
+            missing_revisions = branch.missing_revisions(b_parent)
+        except DivergedBranches:
+            print '** Local tree history has diverged from remote.'
+            print '** Not allowing you to overwrite local changes.'
+            return False
+        if len(missing_revisions) == 0:
+            # There is nothing to do, the remote branch has no changes
+            missing_revisions = b_parent.missing_revisions(branch)
+            if len(missing_revisions) > 0:
+                print '** Local tree is up-to-date with remote.'
+                print '** But remote tree is missing local revisions.'
+                print '** Consider using bzr rsync-push'
+            else:
+                print '** Both trees fully up-to-date.'
+            return False
+        # We are sure that we are missing remote revisions
+        return True
+
+    if last_revno == branch.revno() and last_revision == branch.last_patch():
+        # We can go ahead and try
+        return True
+
+    print 'Local working directory has a different revision than last rsync.'
+    val = raw_input('Are you sure you want to download [y/N]? ')
+    if val.lower() in ('y', 'yes'):
+        return True
+    return False
+
+def check_should_push(branch, last_revno, last_revision):
+    if not is_clean(branch):
+        print '** Local tree is not clean (either modified or unknown files)'
+        return False
+
+    b_parent = get_parent_branch(branch)
+    if b_parent is not None:
+        from bzrlib.branch import DivergedBranches
+        # This may throw a Diverged branches.
+        try:
+            missing_revisions = b_parent.missing_revisions(branch)
+        except DivergedBranches:
+            print '** Local tree history has diverged from remote.'
+            print '** Not allowing you to overwrite remote changes.'
+            return False
+        if len(missing_revisions) == 0:
+            # There is nothing to do, the remote branch is up to date
+            missing_revisions = branch.missing_revisions(b_parent)
+            if len(missing_revisions) > 0:
+                print '** Remote tree is up-to-date with local.'
+                print '** But local tree is missing remote revisions.'
+                print '** Consider using bzr rsync-pull'
+            else:
+                print '** Both trees fully up-to-date.'
+            return False
+        # We are sure that we are missing remote revisions
+        return True
+
+    if last_revno is None and last_revision is None:
+        print 'Local tree does not have a valid last rsync revision.'
+        val = raw_input('push anyway [y/N]? ')
+        if val.lower() in ('y', 'yes'):
+            return True
+        return False
+
+    if last_revno == branch.revno() and last_revision == branch.last_patch():
+        print 'No new revisions.'
+        return False
+
+    return True
+
+
+def pull(branch, remote, verbose=False, dry_run=False):
+    """Update the local repository from the location specified by 'remote'
+
+    :param branch:  Either a string specifying a local path, or a Branch object.
+                    If a local path, the download will be performed, and then
+                    a Branch object will be created.
+
+    :return:    Return the branch object that was created
+    """
+    if isinstance(branch, basestring):
+        local = branch
+        cur_revno = 0
+    else:
+        local = branch.base
+        cur_revno = branch.revno()
+    if remote[-1:] != '/':
+        remote += '/'
+
+    rsyncopts = ['-rltp', '--delete'
+        # Don't pull in a new parent location
+        , "--exclude '**/.bzr/x-rsync*'", "--exclude '**/.bzr/x-pull*'" 
+        , "--exclude '**/.bzr/parent'", "--exclude '**/.bzr/pull'"
+        ]
+
+    # Note that when pulling, we do not delete excluded files
+    rsync_exclude = os.path.join(local, '.rsyncexclude')
+    if os.path.exists(rsync_exclude):
+        rsyncopts.append('--exclude-from "%s"' % rsync_exclude)
+    bzr_ignore = os.path.join(local, '.bzrignore')
+    if os.path.exists(bzr_ignore):
+        rsyncopts.append('--exclude-from "%s"' % bzr_ignore)
+
+    if verbose:
+        rsyncopts.append('-v')
+    if dry_run:
+        rsyncopts.append('--dry-run')
+
+    cmd = 'rsync %s "%s" "%s"' % (' '.join(rsyncopts), remote, local)
+    if verbose:
+        print cmd
+
+    status = os.system(cmd)
+    if status != 0:
+        from bzrlib.errors import BzrError
+        raise BzrError('Rsync failed with error code: %s' % status)
+
+
+    if isinstance(branch, basestring):
+        from bzrlib.branch import Branch
+        branch = Branch(branch)
+
+    new_revno = branch.revno()
+    if cur_revno == new_revno:
+        print '** tree is up-to-date'
+
+    if verbose:
+        if cur_revno != new_revno:
+            from bzrlib.log import show_log
+            show_log(branch, direction='forward',
+                    start_revision=cur_revno+1, end_revision=new_revno)
+
+    return branch
+
+
+def push(branch, remote, verbose=False, dry_run=False):
+    """Update the local repository from the location specified by 'remote'
+
+    :param branch:  Should always be a Branch object
+    """
+    if isinstance(branch, basestring):
+        from bzrlib.errors import BzrError
+        raise BzrError('rsync push requires a Branch object, not a string')
+    local = branch.base
+    if remote[-1:] != '/':
+        remote += '/'
+
+    rsyncopts = ['-rltp', '--include-from -'
+        , '--include .bzr'
+        # We don't want to push our local meta information to the remote
+        , "--exclude '.bzr/x-rsync*'", "--exclude '.bzr/x-pull*'" 
+        , "--exclude '.bzr/parent'", "--exclude '.bzr/pull'"
+        , "--include '.bzr/**'"
+        , "--exclude '*'", "--exclude '.*'"
+        , '--delete', '--delete-excluded'
+        ]
+
+    rsync_exclude = os.path.join(local, '.rsyncexclude')
+    if os.path.exists(rsync_exclude):
+        rsyncopts.append('--exclude-from "%s"' % rsync_exclude)
+    bzr_ignore = os.path.join(local, '.bzrignore')
+    if os.path.exists(bzr_ignore):
+        rsyncopts.append('--exclude-from "%s"' % bzr_ignore)
+
+    if verbose:
+        rsyncopts.append('-v')
+    if dry_run:
+        rsyncopts.append('--dry-run')
+
+    cmd = 'rsync %s "." "%s"' % (' '.join(rsyncopts), remote)
+    if verbose:
+        print cmd
+
+    pwd = os.getcwd()
+    try:
+        os.chdir(local)
+        child = os.popen(cmd, 'w')
+        inv = branch.read_working_inventory()
+        for path, entry in inv.entries():
+            child.write(path)
+            child.write('\n')
+        child.flush()
+        retval = child.close()
+        if retval is not None:
+            from bzrlib.errors import BzrError
+            raise BzrError('Rsync failed with error code: %s' % retval)
+    finally:
+        os.chdir(pwd)
+



More information about the Pkg-bazaar-commits mailing list