-
Notifications
You must be signed in to change notification settings - Fork 0
/
danny_svn.py
executable file
·200 lines (165 loc) · 6.53 KB
/
danny_svn.py
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
#!/usr/bin/python
import pprint
import os
import errno
import svn.core
import svn.fs
import svn.repos
from tempfile import NamedTemporaryFile
def dostuff():
"""Do Stuff
"""
print 'Hello, world'
def list(root, path):
"""List the contents of 'path' under repository rooted at 'root'
"""
print "root is ", root
print "path is ", path
#clean_root = svn.core.svn_dirent_canonicalize(root)
#repos_obj = svn.repos.open(clean_root)
repos_obj = svn.repos.open(root)
fs_obj = svn.repos.fs(repos_obj)
head_rev = svn.fs.youngest_rev(fs_obj)
root_obj = svn.fs.revision_root(fs_obj, head_rev)
print 'Fetching revision ' + str(head_rev) + ' from directory ' + path + \
' in repo file://' + root \
entries = svn.fs.dir_entries(root_obj, path)
names = entries.keys()
for name in names:
suffix = ""
if svn.fs.is_dir(root_obj, path + '/' + name):
suffix = "/"
print name + suffix
def get_changes(repo, rev):
"""Return a list of files and their changes in revision 'rev'
The list might look something like (file=>{add=true, del=false,
propchange=[aprop,anotherprop], prop_aprop=newvalue, modify=false})
"""
base_dir = "/tmp/blah"
repos_obj = svn.repos.open(repo)
fs_obj = svn.repos.fs(repos_obj)
root_obj = svn.fs.revision_root(fs_obj, rev)
changes = sorted(svn.fs.paths_changed2(root_obj).items())
# should probably set umask here? get from config file?
# should probably also chdir to base_dir?
# c_info contains:
# text_mod and prop_mod booleans
# copyfrom_known boolean
# (which also adds copyfrom_rev and copyfrom_path attrs)
# change_kind (can be path_change_{delete,add,replace,modify})
# node_kind (can be svn_node_{none,file,dir,unknown})
for c_path, c_info in changes:
# stupid join; if any part starts with '/', preceeding parts are ignored
export_path = os.path.join(base_dir, './' + c_path)
##########
# most likely test goes first
if c_info.node_kind == svn.core.svn_node_file:
if c_info.change_kind == svn.fs.path_change_delete:
# deleted a file? Unlink it now
print 'deleted file ' + c_path
elif c_info.change_kind == svn.fs.path_change_modify:
# if it's a modification, we only care about text changes.
# Maybe properties. I'm not sure. Possibly svn:eol-type...
if c_info.text_mod:
print 'modified text in file ' + c_path
_export_file(root_obj, c_path, export_path)
elif c_info.change_kind == svn.fs.path_change_add \
or c_info.change_kind == svn.fs.path_change_replace:
print 'added or replaced file ' + c_path
_export_file(root_obj, c_path, export_path)
else:
print 'I\'ve lost my way on file ' + c_path
##########
# directory is really the only other thing we should see
elif c_info.node_kind == svn.core.svn_node_dir:
# deleted a directory? Remember it and remove at the end
if c_info.change_kind == svn.fs.path_change_delete:
# do something
print 'deleted dir ' + c_path
# modified a directory? Must be a property change.
elif c_info.change_kind == svn.fs.path_change_modify:
if c_info.prop_mod:
# we probably only care if the directory has externals
print 'modified property in dir ' + c_path
# otherwise it was an add or a replace.
elif c_info.change_kind == svn.fs.path_change_add \
or c_info.change_kind == svn.fs.path_change_replace:
print 'added or replaced dir ' + c_path
_export_dir(root_obj, c_path, export_path)
else:
print 'I\'ve lost my way on dir ' + c_path
##########
elif c_info.node_kind == svn.core.svn_node_none:
# I don't know what to do with a none. Shouldn't happen.
print 'did something to a none node?'
elif c_info.node_kind == svn.core.svn_node_unknown:
# I don't know what to do with an unknown. Also shouldn't happen.
print 'did something to an unknown node?'
else:
# This *really* shouldn't happen.
print 'big ol\' failure'
#return
def _export_file(root, path, export):
""" export the file at PATH in ROOT to EXPORT
Creates a temp file, then rename()'s the tempfile to the real name.
This makes the export atomic, hopefully eliminating problems with
the partially-sync'd files.
"""
export_dir = os.path.dirname(export)
if os.path.exists(export) and not os.path.isfile(export):
print "file exits as non-file!"
return
# make parent dirs if needed
try:
os.makedirs(export_dir)
except OSError as e:
if e.errno == errno.EEXIST and os.path.isdir(export_dir):
pass
else:
raise
# get file contents from svn
try:
stream = svn.fs.file_contents(root, path)
data = svn.core.svn_string_from_stream(stream)
svn.core.svn_stream_close(stream)
except Exception:
return None
# get temp file and put data in there
f = NamedTemporaryFile(
mode = 'w+b',
prefix = os.path.basename(export),
dir = export_dir,
delete = False,
)
f.write( data )
# I'm pretty sure there should be some error handling here
# also, the rename() should check for OSError
os.rename( f.name, export )
def _export_dir(root, path, export):
""" export the directory named PATH from ROOT to EXPORT
"""
# check this for error exceptions, indicating that the directory already
# exists (which we should have already checked for) or that creation failed
try:
os.makedirs(export)
except OSError as e:
if e.errno == errno.EEXIST and os.path.isdir(export):
pass
else:
raise
def _remove_dir(export):
"""Remove a directory
"""
if os.path.ismount(export):
# we can't remove a mountpoint
print "Fail! Can't remove mountpoint"
return
# check for OSError
os.rmdir(export)
def _remove_file(export):
"""Remove a file
This should happen before any _remove_dir() calls, since that will fail on
non-empty directories
"""
# check for errors
os.unlink(export)