2 from yap
import YapPlugin
, YapError
3 from yap
.util
import get_output
, takes_options
, run_command
, run_safely
, short_help
10 class RepoBlob(object):
11 def __init__(self
, url
, fetch
):
20 def add_metadata(self
, branch
):
21 assert branch
not in self
.metadata
22 gitdir
= get_output("git rev-parse --git-dir")
24 revmap
= os
.path
.join(gitdir
[0], "svn", "svn", branch
, ".rev_map*")
25 revmap
= glob
.glob(revmap
)[0]
26 uuid
= revmap
.split('.')[-1]
29 assert self
.uuid
== uuid
30 data
= file(revmap
).read()
31 self
.metadata
[branch
] = data
33 class SvnPlugin(YapPlugin
):
34 "Allow yap to interoperate with Subversion repositories"
35 def __init__(self
, yap
):
38 def _get_root(self
, url
):
39 root
= get_output("svn info %s 2>/dev/null | gawk '/Repository Root:/{print $3}'" % url
)
41 raise YapError("Not an SVN repo: %s" % url
)
44 def _configure_repo(self
, url
, fetch
=None):
45 root
= self
._get
_root
(url
)
46 os
.system("git config svn-remote.svn.url %s" % root
)
48 trunk
= url
.replace(root
, '').strip('/')
50 trunk
= fetch
.split(':')[0]
51 os
.system("git config svn-remote.svn.fetch %s:refs/remotes/svn/trunk"
54 branches
= trunk
.replace('trunk', 'branches')
56 os
.system("git config svn-remote.svn.branches %s/*:refs/remotes/svn/*" % branches
)
57 tags
= trunk
.replace('trunk', 'tags')
59 os
.system("git config svn-remote.svn.tags %s/*:refs/tags/*" % tags
)
60 self
.yap
.cmd_repo("svn", url
)
61 os
.system("git config yap.svn.enabled 1")
63 def _create_tagged_blob(self
):
64 url
= get_output("git config svn-remote.svn.url")[0]
65 fetch
= get_output("git config --get-all svn-remote.svn.fetch")
66 blob
= RepoBlob(url
, fetch
)
67 for b
in get_output("git for-each-ref --format='%(refname)' 'refs/remotes/svn/*'"):
68 b
= b
.replace('refs/remotes/svn/', '')
71 fd_w
, fd_r
= os
.popen2("git hash-object -w --stdin")
73 __builtin__
.RepoBlob
= RepoBlob
74 pickle
.dump(blob
, fd_w
)
76 hash = fd_r
.readline().strip()
77 run_safely("git tag -f yap-svn %s" % hash)
79 def _clone_svn(self
, url
, directory
=None, **flags
):
82 directory
= url
.rsplit('/')[-1]
83 directory
= directory
.replace('.git', '')
88 raise YapError("Directory exists: %s" % directory
)
92 run_command("git config svn-remote.svn.noMetadata 1")
93 self
._configure
_repo
(url
)
94 os
.system("git svn fetch -r %s:HEAD" % flags
.get('-r', '1'))
95 self
._create
_tagged
_blob
()
97 def _push_svn(self
, branch
, **flags
):
99 raise YapError("Deleting svn branches not supported")
100 print "Verifying branch is up-to-date"
101 run_safely("git svn fetch svn")
103 branch
= branch
.replace('refs/heads/', '')
104 rev
= get_output("git rev-parse --verify refs/remotes/svn/%s" % branch
)
106 # Create the branch if requested
108 if '-c' not in flags
:
109 raise YapError("No matching branch on the repo. Use -c to create a new branch there.")
110 src
= get_output("git svn info | gawk '/URL:/{print $2}'")[0]
111 brev
= get_output("git svn info | gawk '/Revision:/{print $2}'")[0]
112 root
= get_output("git config svn-remote.svn.url")[0]
113 branch_path
= get_output("git config svn-remote.svn.branches")[0].split(':')[0]
114 branch_path
= branch_path
.rstrip('/*')
115 dst
= '/'.join((root
, branch_path
, branch
))
117 # Create the branch in svn
118 run_safely("svn cp -r%s %s %s -m 'create branch %s'"
119 % (brev
, src
, dst
, branch
))
120 run_safely("git svn fetch svn")
121 rev
= get_output("git rev-parse refs/remotes/svn/%s 2>/dev/null" % branch
)
122 base
= get_output("git svn find-rev r%s" % brev
)
124 # Apply our commits to the new branch
126 fd
, tmpfile
= tempfile
.mkstemp("yap")
129 os
.system("git format-patch -k --stdout '%s' > %s"
130 % (base
[0], tmpfile
))
131 start
= get_output("git rev-parse HEAD")
132 self
.yap
.cmd_point("refs/remotes/svn/%s"
133 % branch
, **{'-f': True})
135 stat
= os
.stat(tmpfile
)
138 rc
= run_command("git am -3 %s" % tmpfile
)
140 self
.yap
.cmd_point(start
[0], **{'-f': True})
141 raise YapError("Failed to port changes to new svn branch")
145 base
= get_output("git merge-base HEAD %s" % rev
[0])
146 if base
[0] != rev
[0]:
147 raise YapError("Branch not up-to-date. Update first.")
148 current
= get_output("git symbolic-ref HEAD")
150 raise YapError("Not on a branch!")
151 current
= current
[0].replace('refs/heads/', '')
152 self
.yap
._confirm
_push
(current
, branch
, "svn")
153 if run_command("git update-index --refresh"):
154 raise YapError("Can't push with uncommitted changes")
156 master
= get_output("git rev-parse --verify refs/heads/master 2>/dev/null")
157 os
.system("git svn dcommit")
158 run_safely("git svn rebase")
160 master
= get_output("git rev-parse --verify refs/heads/master 2>/dev/null")
162 run_safely("git update-ref -d refs/heads/master %s" % master
[0])
165 enabled
= get_output("git config yap.svn.enabled")
168 # Ensure users don't accidentally kill our "svn" repo
169 def pre_repo(self
, args
, flags
):
170 if not self
._enabled
():
172 if '-d' in flags
and args
and args
[0] == "svn":
173 raise YapError("Refusing to delete special svn repository")
175 # Configure git-svn if we just cloned a yap-svn repo
176 def post_clone(self
):
181 run_safely("git fetch origin --tags")
182 hash = get_output("git rev-parse --verify refs/tags/yap-svn 2>/dev/null")
186 fd
= os
.popen("git cat-file blob %s" % hash[0])
188 __builtin__
.RepoBlob
= RepoBlob
189 blob
= pickle
.load(fd
)
190 self
._configure
_repo
(blob
.url
, blob
.fetch
[0])
191 for extra
in blob
.fetch
[1:]:
192 run_safely("git config svn-remote.svn.fetch %s" % extra
)
194 for b
in blob
.metadata
.keys():
195 branch
= os
.path
.join(".git", "svn", "svn", b
)
197 fd
= file(os
.path
.join(branch
, ".rev_map.%s" % blob
.uuid
), "w")
198 fd
.write(blob
.metadata
[b
])
199 run_safely("git fetch origin 'refs/remotes/svn/*:refs/remotes/svn/*'")
202 def cmd_clone(self
, *args
, **flags
):
203 if args
and not run_command("svn info %s" % args
[0]):
204 self
._clone
_svn
(*args
, **flags
)
206 self
.yap
._call
_base
("cmd_clone", *args
, **flags
)
208 def cmd_fetch(self
, *args
, **flags
):
210 if args
and args
[0] == 'svn':
211 os
.system("git svn fetch svn")
212 self
._create
_tagged
_blob
()
215 current
= get_output("git symbolic-ref HEAD")
217 raise YapError("Not on a branch!")
219 current
= current
[0].replace('refs/heads/', '')
220 remote
, merge
= self
.yap
._get
_tracking
(current
)
222 os
.system("git svn fetch svn")
223 self
._create
_tagged
_blob
()
225 self
.yap
._call
_base
("cmd_fetch", *args
, **flags
)
227 def cmd_push(self
, *args
, **flags
):
229 if args
and args
[0] == 'svn':
231 raise YapError("Need a branch name")
232 self
._push
_svn
(args
[1], **flags
)
235 current
= get_output("git symbolic-ref HEAD")
237 raise YapError("Not on a branch!")
239 current
= current
[0].replace('refs/heads/', '')
240 remote
, merge
= self
.yap
._get
_tracking
(current
)
242 self
._push
_svn
(merge
, **flags
)
245 self
.yap
._call
_base
("cmd_push", *args
, **flags
)
247 @short_help("change options for the svn plugin")
248 def cmd_svn(self
, subcmd
):
251 if subcmd
not in ["enable"]:
254 if "svn" in [x
[0] for x
in self
.yap
._list
_remotes
()]:
255 raise YapError("A remote named 'svn' already exists")
258 if not run_command("git config svn-remote.svn.branches"):
259 raise YapError("Cannot currently enable in a repository with svn branches")
261 url
= get_output("git config svn-remote.svn.url")
263 raise YapError("Not a git-svn repository?")
264 fetch
= get_output("git config svn-remote.svn.fetch")
266 lhs
, rhs
= fetch
[0].split(':')
269 rev
= get_output("git rev-parse %s" % rhs
)
271 run_safely("git update-ref refs/remotes/svn/trunk %s" % rev
[0])
273 url
= '/'.join((url
[0], lhs
))
274 self
._configure
_repo
(url
)
275 run_safely("git update-ref -d %s %s" % (rhs
, rev
[0]))