sc ide: reinstate saving cursor positions of all open docs into session
[supercollider.git] / package / git-archive-all.py
blob3eff746fca70f8a7acc96b14df788e8b772b4640
1 #! /usr/bin/env python
3 import sys
4 from os import path, chdir
5 from subprocess import Popen, PIPE
6 from sys import argv, stdout
7 from fnmatch import fnmatch
10 class GitArchiver(object):
11 """
12 GitArchiver
14 Scan a git repository and export all tracked files, and submodules.
15 Checks for .gitattributes files in each directory and uses 'export-ignore'
16 pattern entries for ignore files in the archive.
18 Automatically detects output format extension: zip, tar, bz2, or gz
19 """
21 def __init__(self, prefix='', verbose=False, exclude=True, force_sub=False, extra=[]):
22 self.prefix = prefix
23 self.verbose = verbose
24 self.exclude = exclude
25 self.extra = extra
26 self.force_sub = force_sub
28 self._excludes = []
31 def create(self, output_file):
32 """
33 create(str output_file) -> None
35 Creates the archive, written to the given output_file
36 Filetype may be one of: gz, zip, bz2, tar, tgz
37 """
39 # determine the format
41 _, _, format = output_file.rpartition(".")
42 format = format.lower()
44 if format == 'zip':
45 from zipfile import ZipFile, ZIP_DEFLATED
46 output_archive = ZipFile(path.abspath(output_file), 'w')
47 add = lambda name, arcname: output_archive.write(name, self.prefix + arcname, ZIP_DEFLATED)
49 elif format in ['tar', 'bz2', 'gz', 'tgz']:
50 import tarfile
52 if format == 'tar':
53 t_mode = 'w'
54 elif format == 'tgz':
55 t_mode = 'w:gz'
56 else:
57 t_mode = ('w:%s' % format)
59 output_archive = tarfile.open(path.abspath(output_file), t_mode)
60 add = lambda name, arcname: output_archive.add(name, self.prefix + arcname)
61 else:
62 raise RuntimeError("Unknown format: '%s'" % format)
65 # compress
68 # extra files first (we may change folder later)
69 for name in self.extra:
70 if self.verbose:
71 toPath = '=> %s%s' % (self.prefix, name) if self.prefix else ""
72 print 'Compressing %s %s ...' % (name, toPath)
73 add(name, name)
75 self._excludes = []
77 for name, arcname in self.listFiles(path.abspath('')):
78 if self.verbose:
79 toPath = '=> %s%s' % (self.prefix, arcname) if self.prefix else ""
80 print 'Compressing %s %s ...' % (arcname, toPath)
81 add(name, arcname)
83 output_archive.close()
86 def listFiles(self, git_repositary_path, baselevel=''):
87 """
88 listFiles(str git_repository_path, str baselevel='') -> iterator
90 An iterator method that yields a tuple(filepath, fullpath)
91 for each file that should be included in the archive.
92 Skips those that match the exclusion patterns found in
93 any discovered .gitattributes files along the way.
95 Recurses into submodules as well.
96 """
97 for filepath in self.runShell('git ls-files --cached --full-name --no-empty-directory'):
98 filepath = filepath.decode('string_escape').strip('"')
99 fullpath = path.join(baselevel, filepath)
100 filename = path.basename(filepath)
102 if self.exclude and filename == '.gitattributes':
103 self._excludes = []
104 fh = open(filepath, 'r')
105 for line in fh:
106 if not line: break
107 tokens = line.strip().split()
108 if 'export-ignore' in tokens[1:]:
109 self._excludes.append(tokens[0])
110 fh.close()
112 # Only list symlinks and files that don't start with git
113 if not filename.startswith('.git') \
114 and (path.islink(filepath) or not path.isdir(filepath)):
116 # check the patterns first
117 ignore = False
118 for pattern in self._excludes:
119 if fnmatch(fullpath, pattern) or fnmatch(filename, pattern):
120 if self.verbose: print 'Exclude pattern matched (%s): %s' % (pattern, fullpath)
121 ignore = True
122 break
123 if ignore:
124 continue
126 # baselevel is needed to tell the arhiver where it have to extract file
127 yield filepath, fullpath
129 if self.force_sub :
130 self.runShell("git submodule init")
131 self.runShell("git submodule update")
133 # get paths for every submodule
134 for submodule in self.runShell("git submodule --quiet foreach 'pwd'"):
135 chdir(submodule)
136 # in order to get output path we need to exclude repository path from the submodule path
137 submodule = submodule[len(git_repositary_path)+1:]
138 # recursion allows us to process repositories with more than one level of submodules
139 for git_file in self.listFiles(git_repositary_path, submodule):
140 yield git_file
144 @staticmethod
145 def runShell(cmd):
146 return Popen(cmd, shell=True, stdout=PIPE).stdout.read().splitlines()
150 if __name__ == "__main__":
151 from optparse import OptionParser
153 parser = OptionParser(usage="usage: %prog [-v] [--prefix PREFIX] [--no-exclude] OUTPUT_FILE", version="%prog 1.3")
155 parser.add_option('--prefix', type='string', dest='prefix',
156 default='', help="prepend PREFIX to each filename in the archive")
158 parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='enable verbose mode')
160 parser.add_option('--no-exclude', action='store_false', dest='exclude',
161 default=True, help="Don't read .gitattributes files for patterns containing export-ignore attrib")
163 parser.add_option('--force-submodules', action='store_true', dest='force_sub',
164 help="Force a git submodule init && git submodule update at each level before iterating submodules.")
166 parser.add_option('--extra', action='append', dest='extra', default=[],
167 help="Any additional files to include in the archive.")
169 options, args = parser.parse_args()
171 if len(args) != 1:
172 parser.error('You must specify exactly one output file')
174 outFile = args[0]
176 if path.isdir(outFile):
177 parser.error('You cannot use directory as output')
179 archiver = GitArchiver(options.prefix,
180 options.verbose,
181 options.exclude,
182 options.force_sub,
183 options.extra)
185 try:
186 archiver.create(outFile)
187 except Exception, e:
188 parser.exit(2, "%s\n" % e)
190 sys.exit(0)