def _select_backup(src='db'): ''' Select from available list of backups. ''' def listfiles(path): a = [s for s in os.listdir(path) if os.path.isfile(os.path.join(path, s))] a.sort(key=lambda s: os.path.getmtime(os.path.join(path, s))) return a if not os.path.exists(src): print '%s directory does not exist' return None, None selected = None backups = listfiles(src) # return nothing if there are no backup files if len(backups) == 0: print 'There are no backups available' return None, None if 'last' in backups: f = open(os.path.join(src, 'last'), 'r') last = md5sum(f) f.close() else: last = None while selected is None: print 'Available backups (sorted by creation date):' position = 0 for file in backups: position += 1 name, ext = os.path.splitext(file) f = open(os.path.join(src, file), 'r') current = md5sum(f) f.close() if last == current and file != 'last': print ' %s %s (last)' % (position, name) else: print ' %s %s' % (position, name) selected = prompt('Enter backup number to import: ') if not selected.isdigit(): print 'Please enter a number between 1 and %s\n' % len(backups) selected = None elif int(selected) < 0 or int(selected) > len(backups): print 'Please enter a number between 1 and %s\n' % len(backups) selected = None backup_name = backups[int(selected)-1] env.backup = (backup_name, os.path.join(src, backup_name)) return env.backup
def generate(): ''' Generate patch for specific branch. ''' repo = Repo('.') if repo.is_dirty: abort('Working tree is dirty. Working tree must be clean to perform this operation.') if confirm('Use master as base?'): for branch in repo.branches: if branch.name == 'master': master = branch else: branch, master = prompt_branch_select(repo, 'Select base branch') name, selected_branch = prompt_branch_select(repo) while exists(path(name)): name = prompt('Directory already exists: %s.\nWhat should I call this patch?'%path(name)) os.mkdir(path(name)) rawpath = join(path(name), 'raw.diff') local('git diff --binary %s %s > %s'%(selected_branch.commit, master.commit, rawpath)) with open(rawpath, 'r') as raw: changed = raw.read() config = RawConfigParser() config.add_section('target') config.add_section('iteration') config.set('target', 'branch', master.name) config.set('target', 'commit', master.commit) config.set('iteration', 'branch', selected_branch.name) config.set('iteration', 'commit', selected_branch.commit) print("Generated %s patch directory."%name) diffs = Diff.list_from_string(repo, changed) def filter(diff): def match(pattern): if fnmatch.fnmatch(diff.b_path, pattern): return pattern matches = [match for match in map(match, patterns) if not match is None] if not bool(matches): return diff ignore = os.path.join(os.getcwd(), '.diffignore') if os.path.exists(ignore): patterns = open(ignore, 'r').read().split('\n') diffs = [diff for diff in map(filter, diffs) if not diff is None] binary_ext = ['.png', '.gif', '.jpg', '.jpeg', '.flv', '.swf','.zip', '.gz', '.rar', '.fla'] # TODO: change this to use git generated binary marker binaries = [diff for diff in diffs if splitext(diff.b_path)[1] in binary_ext] text = set(diffs) - set(binaries) config.add_section('patch') config.set('patch', 'binary', int(len(binaries) > 0)) config.set('patch', 'text', int(len(text) > 0)) print 'Generated patch config file' if text: text_patch = join(path(name), 'text.patch') with open(text_patch, 'w') as pf: for diff in text: pf.write('%s\n\n'%diff.diff) print 'Generated %s text patch'%name else: text_patch = None if binaries: binary_list = join(path(name), 'binary.changes') binary_base = join(path(name), 'binaries') os.mkdir(binary_base) with open(binary_list, 'w') as pf: for binary in binaries: def mkdirs(path): path = join(binary_base, path) try: os.makedirs(path) except: pass return path def extractfile(commit, path): dirs, file = os.path.split(path) dirs = mkdirs('%s/%s'%(commit, dirs)) with settings(show('stdout'), warn_only = True): if commit == 'target': commit_id = master.commit else: commit_id = selected_branch.commit get_file(commit_id, path, join(binary_base, commit, path)) if binary.a_commit is None: # new file is being created extractfile('iteration', binary.b_path) elif binary.b_commit is None: # file is being removed extractfile('target', binary.a_path) else: # file is being updated or renamed extractfile('target', binary.a_path) extractfile('iteration', binary.b_path) if binary.new_file: pf.write('new %s\n'%binary.b_path) elif binary.deleted_file: pf.write('rm %s\n'%binary.b_path) elif binary.renamed: pf.write('mv %s %s\n'%(binary.a_path, binary.b_path)) else: pf.write('up %s\n'%binary.b_path) print 'Generated %s'%binary_list else: binary_list = None with open(join(path(name), 'patch.cfg'), 'wb') as configfile: config.write(configfile) os.mkdir(join(path(name), 'db')) source = join(path(name), 'db', 'source.sql') target = join(path(name), 'db', 'target.sql') get_file(selected_branch.commit, join('db', 'last'), source) get_file(master.commit, join('db', 'last'), target) with open(join(path(name), 'db', 'source.sql'), 'r') as sfp: with open(join(path(name), 'db', 'target.sql'), 'r') as tfp: dbchanged = md5sum(sfp) != md5sum(tfp) if dbchanged and confirm('Generate database patch?'): dbpatch.generate(join(path(name), 'db'), source, target) else: shutil.rmtree(join(path(name), 'db'), ignore_errors=True) config.set('patch', 'schema', 0) config.set('patch', 'data', 0) with open(join(path(name), 'patch.cfg'), 'wb') as configfile: config.write(configfile) return name, path(name)