📄 svn-backup-dumps.py
字号:
#!/usr/bin/env python## svn-backup-dumps.py -- Create dumpfiles to backup a subversion repository.## ====================================================================# Copyright (c) 2006 CollabNet. All rights reserved.## This software is licensed as described in the file COPYING, which# you should have received as part of this distribution. The terms# are also available at http://subversion.tigris.org/license-1.html.# If newer versions of this license are posted there, you may use a# newer version instead, at your option.## This software consists of voluntary contributions made by many# individuals. For exact contribution history, see the revision# history and logs, available at http://subversion.tigris.org/.# ====================================================================## This script creates dump files from a subversion repository on *IX.# It is intended for use in cron jobs and post-commit hooks.## The basic operation modes are:# 1. Create a full dump (revisions 0 to HEAD).# 2. Create incremental dumps containing at most N revisions.# 3. Create incremental single revision dumps (for use in post-commit).## All dump files are prefixed with the basename of the repository. All# examples below assume that the repository '/srv/svn/repos/src' is# dumped so all dumpfiles start with 'src'.## Optional functionality:# 4. Create gzipped dump files.# 5. Create bzipped dump files.# 6. Transfer the dumpfile to another host using ftp.# 7. Transfer the dumpfile to another host using smb.## See also 'svn-backup-dumps.py -h'.### 1. Create a full dump (revisions 0 to HEAD).## svn-backup-dumps.py <repos> <dumpdir>## <repos> Path to the repository.# <dumpdir> Directory for storing the dump file.## This creates a dump file named 'src.000000-NNNNNN.svndmp.gz'# where NNNNNN is the revision number of HEAD.## 2. Create incremental dumps containing at most N revisions.## svn-backup-dumps.py -c <count> <repos> <dumpdir>## <count> Count of revisions per dump file.# <repos> Path to the repository.# <dumpdir> Directory for storing the dump file.## When started the first time with a count of 1000 and if HEAD is# at 2923 it creates the following files:## src.000000-000999.svndmp.gz# src.001000-001999.svndmp.gz# src.002000-002923.svndmp.gz## Say the next time HEAD is at 3045 it creates these two files:## src.002000-002999.svndmp.gz# src.003000-003045.svndmp.gz### 3. Create incremental single revision dumps (for use in post-commit).## svn-backup-dumps.py -r <revnr> <repos> <dumpdir>## <revnr> A revision number.# <repos> Path to the repository.# <dumpdir> Directory for storing the dump file.## This creates a dump file named 'src.NNNNNN.svndmp.gz' where# NNNNNN is the revision number of HEAD.### 4. Create gzipped dump files.## svn-backup-dumps.py -z ...## ... More options, see 1-3, 6, 7.### 5. Create bzipped dump files.## svn-backup-dumps.py -b ...## ... More options, see 1-3, 6, 7.### 6. Transfer the dumpfile to another host using ftp.## svn-backup-dumps.py -t ftp:<host>:<user>:<password>:<path> ...## <host> Name of the FTP host.# <user> Username on the remote host.# <password> Password for the user.# <path> Subdirectory on the remote host.# ... More options, see 1-5.## If <path> contains the string '%r' it is replaced by the# repository name (basename of the repository path).### 7. Transfer the dumpfile to another host using smb.## svn-backup-dumps.py -t smb:<share>:<user>:<password>:<path> ...## <share> Name of an SMB share in the form '//host/share'.# <user> Username on the remote host.# <password> Password for the user.# <path> Subdirectory of the share.# ... More options, see 1-5.## If <path> contains the string '%r' it is replaced by the# repository name (basename of the repository path).#### TODO:# - find out how to report smbclient errors# - improve documentation#__version = "0.4"import sysimport osimport fcntlimport selectimport gzipimport os.pathfrom optparse import OptionParserfrom ftplib import FTPtry: import bz2 have_bz2 = Trueexcept ImportError: have_bz2 = Falseclass Popen24Compat: def __init__( self, args, bufsize=0, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=False, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0 ): if isinstance( args, list ): args = tuple( args ) elif not isinstance( args, tuple ): raise RipperException, "Popen24Compat: args is not tuple or list" self.stdin = None self.stdout = None self.stderr = None self.returncode = None if executable == None: executable = args[0] if stdin == PIPE: stdin, stdin_fd = os.pipe() self.stdin = os.fdopen( stdin_fd ) elif stdin == None: stdin = 0 else: stdin = stdin.fileno() if stdout == PIPE: stdout_fd, stdout = os.pipe() self.stdout = os.fdopen( stdout_fd ) elif stdout == None: stdout = 1 else: stdout = stdout.fileno() if stderr == PIPE: stderr_fd, stderr = os.pipe() self.stderr = os.fdopen( stderr_fd ) elif stderr == None: stderr = 2 else: stderr = stderr.fileno() # error pipe err_read, err_write = os.pipe() fcntl.fcntl( err_write, fcntl.F_SETFD, fcntl.FD_CLOEXEC ) self.pid = os.fork() if self.pid < 0: raise Exception, "Popen24Compat: fork" if self.pid == 0: # child os.close( err_read ) fcntl.fcntl( err_write, fcntl.F_SETFD, fcntl.FD_CLOEXEC ) if self.stdin: self.stdin.close() if self.stdout: self.stdout.close() if self.stderr: self.stderr.close() if stdin != 0: os.dup2( stdin, 0 ) os.close( stdin ) if stdout != 1: os.dup2( stdout, 1 ) os.close( stdout ) if stderr != 2: os.dup2( stderr, 2 ) os.close( stderr ) try: if shell: # should spawn a shell here... os.execvp( executable, args ) else: os.execvp( executable, args ) except: err = sys.exc_info()[1] # exec error os.write( err_write, str(err) ) os._exit( 255 ) else: # parent os.close( err_write ) if stdin != 0: os.close( stdin ) if stdout != 0: os.close( stdout ) if stderr != 0: os.close( stderr ) sr, sw, se = select.select( [ err_read ], [], [ err_read ] ) if len( se ) == 1: os.close( err_read ) raise Exception, "Popen24Compat: err pipe read error" if len( sr ) == 1: err = os.read( err_read, 1024 ) os.close( err_read ) if len( err ) != 0: raise Exception, "Popen24Compat: exec error: " + err def poll( self ): self.__wait( os.WNOHANG ) return self.returncode def wait( self ): self.__wait( 0 ) return self.returncode def __wait( self, options ): pid, rc = os.waitpid( self.pid, options ) if pid != 0: self.returncode = rcdef PopenConstr( args, bufsize=0, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=False, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0 ): return Popen24Compat( args, bufsize=bufsize, executable=executable, stdin=stdin, stdout=stdout, stderr=stderr, preexec_fn=preexec_fn, close_fds=close_fds, shell=shell, cwd=cwd, env=env, universal_newlines=universal_newlines, startupinfo=startupinfo, creationflags=creationflags )try: from subprocess import Popen, PIPEexcept ImportError: Popen = PopenConstr PIPE = -1class SvnBackupOutput: def __init__( self, absfilename ): self.__filename = absfilename def open( self ): pass def write( self, data ): pass def close( self ): pass def get_filename( self ): return self.__filenameclass SvnBackupOutputPlain(SvnBackupOutput): def __init__( self, absfilename ): SvnBackupOutput.__init__( self, absfilename ) def open( self ): self.__ofd = open( self.get_filename(), "wb" ) def write( self, data ): self.__ofd.write( data ) def close( self ): self.__ofd.close()class SvnBackupOutputGzip(SvnBackupOutput): def __init__( self, absfilename ): SvnBackupOutput.__init__( self, absfilename + ".gz" ) def open( self ): self.__compressor = gzip.GzipFile( filename=self.get_filename(), mode="wb" ) def write( self, data ): self.__compressor.write( data ) def close( self ): self.__compressor.flush() self.__compressor.close()class SvnBackupOutputBzip2(SvnBackupOutput): def __init__( self, absfilename ): SvnBackupOutput.__init__( self, absfilename + ".bz2" )
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -