1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
|
# Copyright (C) 2014 Codethink Limited
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
import cliapp
import logging
import os
import urlparse
import morphlib
class AddBinaryPlugin(cliapp.Plugin):
'''Add a subcommand for dealing with large binary files.'''
def enable(self):
self.app.add_subcommand(
'add-binary', self.add_binary, arg_synopsis='FILENAME...')
def disable(self):
pass
def add_binary(self, binaries):
'''Add a binary file to the current repository.
Command line argument:
* `FILENAME...` is the binaries to be added to the repository.
This checks for the existence of a .gitfat file in the repository. If
there is one then a line is added to .gitattributes telling it that
the given binary should be handled by git-fat. If there is no .gitfat
file then it is created, with the rsync remote pointing at the correct
directory on the Trove host. A line is then added to .gitattributes to
say that the given binary should be handled by git-fat.
Example:
morph add-binary big_binary.tar.gz
'''
if not binaries:
raise morphlib.Error('add-binary must get at least one argument')
gd = morphlib.gitdir.GitDirectory(os.getcwd())
gd.fat_init()
if not gd.has_fat():
self._make_gitfat(gd)
self._handle_binaries(binaries, gd)
logging.info('Staged binaries for commit')
def _handle_binaries(self, binaries, gd):
'''Add a filter for the given file, and then add it to the repo.'''
# begin by ensuring all paths given are relative to the root directory
files = [gd.get_relpath(os.path.realpath(binary))
for binary in binaries]
# now add any files that aren't already mentioned in .gitattributes to
# the file so that git fat knows what to do
attr_path = gd.join_path('.gitattributes')
if '.gitattributes' in gd.list_files():
with open(attr_path, 'r') as attributes:
current = set(f.split()[0] for f in attributes)
else:
current = set()
to_add = set(files) - current
# if we don't need to change .gitattributes then we can just do
# `git add <binaries>`
if not to_add:
gd.get_index().add_files_from_working_tree(files)
return
with open(attr_path, 'a') as attributes:
for path in to_add:
attributes.write('%s filter=fat -crlf\n' % path)
# we changed .gitattributes, so need to stage it for committing
files.append(attr_path)
gd.get_index().add_files_from_working_tree(files)
def _make_gitfat(self, gd):
'''Make .gitfat point to the rsync directory for the repo.'''
remote = gd.get_remote('origin')
if not remote.get_push_url():
raise Exception(
'Remote `origin` does not have a push URL defined.')
url = urlparse.urlparse(remote.get_push_url())
if url.scheme != 'ssh':
raise Exception(
'Push URL for `origin` is not an SSH URL: %s' % url.geturl())
fat_store = '%s:%s' % (url.netloc, url.path)
fat_path = gd.join_path('.gitfat')
with open(fat_path, 'w+') as gitfat:
gitfat.write('[rsync]\n')
gitfat.write('remote = %s' % fat_store)
gd.get_index().add_files_from_working_tree([fat_path])
|