svn2git3 3.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/ChangeLog.markdown +212 -0
- data/MIT-LICENSE +20 -0
- data/README.markdown +255 -0
- data/Rakefile +31 -0
- data/VERSION.yml +5 -0
- data/bin/svn2git +27 -0
- data/lib/svn2git/migration.rb +492 -0
- data/lib/svn2git.rb +2 -0
- data/svn2git3.gemspec +50 -0
- data/test/commands_test.rb +9 -0
- data/test/escape_quotes_test.rb +22 -0
- data/test/test_helper.rb +12 -0
- metadata +73 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 2dcb41613df716117522bfeca54b2036bcb996698a1bc1d1b373fec0abdd54fc
|
4
|
+
data.tar.gz: f88a6ca5f57dd70cb861520e99f41dc7379f7b9b86e1edf74d53c2947b70c2fc
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a16bc8cfd604e308b1d1366b99df0a92cbdf1fed43c6b089cd53f3f8dacfb3144de869798986392ed0d332eefa01569f38a8bf5bfede77cfbed611e52570df1c
|
7
|
+
data.tar.gz: 4b60d8894eb6877c642e7dd8b58161d739b9887ca3939aab7518eb6a9fae11febf9e8fbed32305351f09219de5d252f7e66a0521d33a62abd2f748b4972f6c49
|
data/ChangeLog.markdown
ADDED
@@ -0,0 +1,212 @@
|
|
1
|
+
# 3.0.3 - 2022-06-08
|
2
|
+
|
3
|
+
This is a bugfix release.
|
4
|
+
|
5
|
+
* Forked from jesteves.
|
6
|
+
|
7
|
+
# 2.4.1 - 2021-12-17
|
8
|
+
|
9
|
+
Since 'git-svn' does not support '--password' anymore, this release modifies the mechanism to supply a password when needed.
|
10
|
+
|
11
|
+
* Removed support for '--password' option (jesteves; PR merge pending).
|
12
|
+
* Added support for detecting and using the value of the environment variable 'SVN2GIT_PASSWORD' when `svn git` is invoked (jesteves; PR merge pending).
|
13
|
+
|
14
|
+
# 2.4.0 - 2016-10-30
|
15
|
+
|
16
|
+
This release introduces the ability to supply a password for SVN repositories that can't authenticate by other means.
|
17
|
+
It also adds the ability to specify the '--branches' and '--tags' arguments multiple times to better support those with
|
18
|
+
more complicated SVN repository layouts.
|
19
|
+
|
20
|
+
* Added support for the '--password' option for authentication (thanks edpbx).
|
21
|
+
* Added the ability to specify the '--branches' and '--tags' arguments multiple times (thanks pdf).
|
22
|
+
* Fixed a problem with processing of the '--exclude' argument (improper quoting internally) (thanks pdf).
|
23
|
+
|
24
|
+
# 2.3.2 - 2014-06-08
|
25
|
+
|
26
|
+
This is a bugfix release. It fixes issues running with Windows using MRI ruby and fixes a problem with Ruby 1.8.7.
|
27
|
+
|
28
|
+
* Removed open4 dependency. svn2git no longer has any runtime dependencies and things work well on Windows again.
|
29
|
+
* Fixed an issue with Ruby 1.8.7, which doesn't implicitly require the 'thread' library meaning classes that library weren't in scope.
|
30
|
+
|
31
|
+
|
32
|
+
# 2.3.1 - 2014-05-14
|
33
|
+
|
34
|
+
This is a critical bugfix release if you're running git >= 1.8.3.2. In the days of svn2git 1.x we supported syncing
|
35
|
+
local branches with upstream by tracking the branch as we set them up. This allowed one to checkout the branch and
|
36
|
+
issue a "git pull" to fetch the changes. git-svn ceased allowing this in 1.8.3.2, which broke svn2git with that
|
37
|
+
version of git and all subsequent versions. The rationale seemed to be in order to prevent pushing changes from
|
38
|
+
git-svn back up and breaking the remote link, but this was never something svn2git supported anyway.
|
39
|
+
|
40
|
+
Acknowledging the new reality of upstream, the old behavior is retained but deprecated for users of git < 1.8.3.2.
|
41
|
+
We'll be removing the establishment of remote tracking SVN branches in the 2.5.0 release. If you wish to sync back
|
42
|
+
with upstream, run `svn2git --rebase`. If you're on git >= 1.8.3.2 your only option for resynchronizing is to
|
43
|
+
use `svn2git --rebase`.
|
44
|
+
|
45
|
+
Many thanks to ktdreyer for modernizing the test suite and Daniel Ruf (DanielRuf) for pushing on the git compatibility
|
46
|
+
issue.
|
47
|
+
|
48
|
+
* Fixed creating local branches for remote SVN branches in git >= 1.8.3.2.
|
49
|
+
* Fixed verbose logging of sub-process STDERR stream.
|
50
|
+
* Added MIT license metadata to gemspec.
|
51
|
+
* Switched to minitest to get tests working on Ruby 1.9+ with minitest 5+ installed.
|
52
|
+
|
53
|
+
|
54
|
+
# 2.3.0 - 2014-05-14
|
55
|
+
|
56
|
+
This release passes STDIN through to the underlying git-svn process, allowing users to interact with the
|
57
|
+
git-svn prompt. Principally, it will allow users to choose what to do when prompted about unverified
|
58
|
+
SSL certificates.
|
59
|
+
|
60
|
+
* Pass STDIN through to the underlying git-svn process so users can respond to prompts.
|
61
|
+
|
62
|
+
# 2.2.5 - 2014-03-09
|
63
|
+
|
64
|
+
This is a bugfix release. It improves handling of quotes in SVN commit messages.
|
65
|
+
|
66
|
+
|
67
|
+
* Fixed an with single quote escaping (thanks aucl).
|
68
|
+
* Escape double quotes (e.g., if they appear in a commit message) before passing to the shell (thanks aucl).
|
69
|
+
|
70
|
+
# 2.2.4 - 2014-03-08
|
71
|
+
|
72
|
+
There was a permissions problem with some of the files packed in 2.2.3. This was caught immediately after the gem
|
73
|
+
was pushed, so it was yanked as it simply wouldn't work for anyone. 2.2.4 contains everything 2.2.3 did, but with
|
74
|
+
proper packaging.
|
75
|
+
|
76
|
+
# 2.2.3 - 2014-03-08
|
77
|
+
|
78
|
+
This is a bugfix release. First change done by FeeJai.
|
79
|
+
|
80
|
+
* Fixed an issue with password protected svn-repositories. The prompt to enter the password is now displayed.
|
81
|
+
* Fixed an issue with server certificates. If the certificate is untrusted, the prompt to confirm or deny the certificate is now shown.
|
82
|
+
* Fixed an issue with using the `--local` flag for `git config` in git versions < 1.7.4.
|
83
|
+
|
84
|
+
|
85
|
+
# 2.2.2 - 2012-10-07
|
86
|
+
|
87
|
+
This is an overdue bugfix release. No new features were added, but several long-standing bugs fixed by the community
|
88
|
+
have been merged. Many thanks to Edson de Lima (edsonlima), Rudger (Rud5G), Ben Wolfe (bwolfe), CyberTech, PowerKiKi, and Philipp Riemer (ruderphilipp) for the pull requests.
|
89
|
+
|
90
|
+
* Fixed an issue working with repositories that contained a space in the name (thanks edsonlima).
|
91
|
+
* Fixed an issue working with tags that contain a hyphen (thanks Rud5G).
|
92
|
+
* Fixed an issue with fixing tags during a rebase (thanks PowerKiKi).
|
93
|
+
* Double-quote git-svn commands working with tags to avoid issues with special strings (thanks CyberTech).
|
94
|
+
* Improved the documentation example of fetching the author list for an SVN repository (thanks bwolfe).
|
95
|
+
* Set the git committer date for tags in a more cross-platform manner (thanks CyberTech).
|
96
|
+
* Improved documentation formatting (thanks ruderphilipp).
|
97
|
+
|
98
|
+
# 2.2.1 - 2012-02-25
|
99
|
+
|
100
|
+
This is a critical bugfix release if your repository has tags. Thanks to David Zülke (dzuelke) for the patches making up this release.
|
101
|
+
|
102
|
+
* Added the ability to specify an end revision for migration (thanks dzuelke).
|
103
|
+
* Fixed an issue with initial conversion if the repo had tags (thanks dzuelke).
|
104
|
+
|
105
|
+
# 2.2.0 - 2012-01-25
|
106
|
+
|
107
|
+
Thanks to Craig Hobbs (craigahobbs) and Simon Chiang (thinkerbot) for the patches making up this release.
|
108
|
+
It rounds out our tag support by handling tags with special characters and preserving original tag author info.
|
109
|
+
|
110
|
+
* Fixed an issue with not quoting tag names (thanks craigahobbs and thinkerbot)
|
111
|
+
* Fixed an issue whereby the person running the svn2git conversion became the author of every tag (i.e., we lost the
|
112
|
+
original tag committer info) (thanks thinkerbot)
|
113
|
+
|
114
|
+
# 2.1.2 - 2011-12-28
|
115
|
+
|
116
|
+
* Fixed a regression in improperly quoting branch names (thanks ziangsong).
|
117
|
+
|
118
|
+
# 2.1.1 - 2011-12-27
|
119
|
+
|
120
|
+
* Fixed SVN branch detection (thanks thinkerbot).
|
121
|
+
* Stop processing when a git subprocess fails (thanks thinkerbot).
|
122
|
+
* Fixed an issue with SVN branches containing shell special characters (thanks sleicht).
|
123
|
+
|
124
|
+
# 2.1.0 - 2011-04-03
|
125
|
+
|
126
|
+
Thanks to Francois Rey (fmjrey), Sven Axelsson (svenax), and Julian Taylor (juliantaylor) for submitting all the patches
|
127
|
+
that comprise this release. svn2git now works with a much wider array SVN repositories because of their efforts.
|
128
|
+
|
129
|
+
* Added --no-minimize-url option for migrating specific subprojects from an SVN repo containing several projects (thanks fmjrey).
|
130
|
+
* Added --username option for migrating password-protected repositories (thanks svenax).
|
131
|
+
* Added --revision option for specifying the revision to start importing from (thanks svenax).
|
132
|
+
* Fixed compatibility with older versions of git (thanks juliantaylor).
|
133
|
+
|
134
|
+
# 2.0.0 - 2010-05-29
|
135
|
+
|
136
|
+
This release adds the oft requested incremental SVN update support. If you run svn2git with the `--rebase` option on an existing
|
137
|
+
repository that you've converted with svn2git, it will fetch new branches & tags from SVN and update existing ones. There are
|
138
|
+
two important things to note:
|
139
|
+
|
140
|
+
* This will not work on already converted repositories because the tracking information isn't set up correctly. You could do that
|
141
|
+
yourself, but it's probably a lot easier to do the conversion over.
|
142
|
+
* svn2git now maintains remote tracking information. If this is a problem for you because you don't want any links to the SVN server
|
143
|
+
you can either stick with a 1.x release of svn2git or simply clone the repo created with svn2git, which will lose the tracking information.
|
144
|
+
|
145
|
+
A great deal of thanks to Nathaniel McCallum (npmccallum) for coming up with an elegant solution and then providing the patch for this release.
|
146
|
+
|
147
|
+
# 1.3.3 - 2010-03-31
|
148
|
+
|
149
|
+
Thanks to Jeff Ramnani (jramnani) for finding a problem with with the --excludes tag and providing a patch.
|
150
|
+
|
151
|
+
* Fix error when using '--exclude' option.
|
152
|
+
|
153
|
+
# 1.3.2 - 2010-03-12
|
154
|
+
|
155
|
+
Thanks to Rajit Singh (rajit) for finding a problem with quoting in tag comments that were causing issues with svn2git's internal
|
156
|
+
quoting and providing a patch.
|
157
|
+
|
158
|
+
* Deal cleanly with any single quotes found in tag comments so that the 'git tag' commands run correctly.
|
159
|
+
|
160
|
+
# 1.3.1 - 2009-06-09
|
161
|
+
|
162
|
+
Thanks to KUBO Atsuhiro (iteman) for finding a problem with the tagging process and providing a patch.
|
163
|
+
|
164
|
+
* Fixed a problem with creating actual git tags when the SVN tags path was named anything other than 'tags.'
|
165
|
+
|
166
|
+
# 1.3.0 - 2009-06-09
|
167
|
+
|
168
|
+
Many thanks to Malte S. Stretz (mss) for the patches making up most of this release.
|
169
|
+
|
170
|
+
* Fixed a problem where tags didn't get the original date and time.
|
171
|
+
* New switch --exclude which can be used to specify a PCRE pattern to exclude paths from the import.
|
172
|
+
* New switches --no{trunk,branches,tags} to skip import of those.
|
173
|
+
* Improved docs.
|
174
|
+
|
175
|
+
# 1.2.4 - 2009-05-04
|
176
|
+
|
177
|
+
* No changes. I ran the jeweler command twice inadvertently. Tearing down the release would be more harmful than helpful.
|
178
|
+
|
179
|
+
# 1.2.3 - 2009-05-04
|
180
|
+
|
181
|
+
* Yanked out the code referencing the gem by name. This shouldn't be necessary at all.
|
182
|
+
|
183
|
+
# 1.2.2 - 2009-05-04
|
184
|
+
|
185
|
+
* Updated the reference gem in the binary to use this one and not the one on RubyForge.
|
186
|
+
|
187
|
+
# 1.2.1 - 2009-04-19
|
188
|
+
|
189
|
+
* Fixed a problem with the svn2git binary not loading command-line args properly.
|
190
|
+
|
191
|
+
# 1.2.0 - 2009-04-17
|
192
|
+
|
193
|
+
* Reworked command-line options so they work similarly to every other app in the world.
|
194
|
+
* Better error messaging when no URL provided.
|
195
|
+
* Improved docs.
|
196
|
+
|
197
|
+
# 1.1.1 - 2009-04-15
|
198
|
+
|
199
|
+
* Started using Jeweler for gem management.
|
200
|
+
* Fixed issue with not loading up RubyGems appropriately.
|
201
|
+
|
202
|
+
# 1.1.0 - 2009-01-02
|
203
|
+
|
204
|
+
* First release since nirvdrum fork.
|
205
|
+
|
206
|
+
* Fixed issues with handling of tags and branches.
|
207
|
+
* Added better logging of output from git-svn.
|
208
|
+
* Wrap external command processing to capture failures.
|
209
|
+
|
210
|
+
# 1.0.0 - 2008-07-19
|
211
|
+
|
212
|
+
* Forked version from jcoglan.
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 James Coglan, Kevin Menard
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
20
|
+
|
data/README.markdown
ADDED
@@ -0,0 +1,255 @@
|
|
1
|
+
svn2git
|
2
|
+
=======
|
3
|
+
|
4
|
+
_svn2git_ is a tiny utility for migrating projects from Subversion to Git
|
5
|
+
while keeping the trunk, branches and tags where they should be. It uses
|
6
|
+
git-svn to clone an svn repository and does some clean-up to make sure
|
7
|
+
branches and tags are imported in a meaningful way, and that the code checked
|
8
|
+
into master ends up being what's currently in your svn trunk rather than
|
9
|
+
whichever svn branch your last commit was in.
|
10
|
+
|
11
|
+
Examples
|
12
|
+
--------
|
13
|
+
|
14
|
+
Say I have this code in svn:
|
15
|
+
|
16
|
+
trunk
|
17
|
+
...
|
18
|
+
branches
|
19
|
+
1.x
|
20
|
+
2.x
|
21
|
+
tags
|
22
|
+
1.0.0
|
23
|
+
1.0.1
|
24
|
+
1.0.2
|
25
|
+
1.1.0
|
26
|
+
2.0.0
|
27
|
+
|
28
|
+
git-svn will go through the commit history to build a new git repo. It will
|
29
|
+
import all branches and tags as remote svn branches, whereas what you really
|
30
|
+
want is git-native local branches and git tag objects. So after importing this
|
31
|
+
project I'll get:
|
32
|
+
|
33
|
+
$ git branch
|
34
|
+
* master
|
35
|
+
$ git branch -a
|
36
|
+
* master
|
37
|
+
1.x
|
38
|
+
2.x
|
39
|
+
tags/1.0.0
|
40
|
+
tags/1.0.1
|
41
|
+
tags/1.0.2
|
42
|
+
tags/1.1.0
|
43
|
+
tags/2.0.0
|
44
|
+
trunk
|
45
|
+
$ git tag -l
|
46
|
+
[ empty ]
|
47
|
+
|
48
|
+
After svn2git is done with your project, you'll get this instead:
|
49
|
+
|
50
|
+
$ git branch
|
51
|
+
* master
|
52
|
+
1.x
|
53
|
+
2.x
|
54
|
+
$ git tag -l
|
55
|
+
1.0.0
|
56
|
+
1.0.1
|
57
|
+
1.0.2
|
58
|
+
1.1.0
|
59
|
+
2.0.0
|
60
|
+
|
61
|
+
Finally, it makes sure the HEAD of master is the same as the current trunk of
|
62
|
+
the svn repo.
|
63
|
+
|
64
|
+
Installation
|
65
|
+
------------
|
66
|
+
|
67
|
+
Make sure you have git, git-svn, and ruby installed. svn2git is a ruby wrapper around git's native SVN support through git-svn. It is possible to have git installed without git-svn installed, so please do verify that you can run `$ git svn` successfully. For a Debian-based system, the installation of the prerequisites would look like:
|
68
|
+
|
69
|
+
$ sudo apt-get install git-core git-svn ruby
|
70
|
+
|
71
|
+
Once you have the necessary software on your system, you can install svn2git through rubygems, which will add the `svn2git` command to your PATH.
|
72
|
+
|
73
|
+
$ sudo gem install svn2git3
|
74
|
+
|
75
|
+
|
76
|
+
Usage
|
77
|
+
-----
|
78
|
+
|
79
|
+
### Initial Conversion ###
|
80
|
+
|
81
|
+
There are several ways you can create a git repo from an existing
|
82
|
+
svn repo. The differentiating factor is the svn repo layout. Below is an
|
83
|
+
enumerated listing of the varying supported layouts and the proper way to
|
84
|
+
create a git repo from a svn repo in the specified layout.
|
85
|
+
|
86
|
+
1. The svn repo is in the standard layout of (trunk, branches, tags) at the
|
87
|
+
root level of the repo.
|
88
|
+
|
89
|
+
$ svn2git http://svn.example.com/path/to/repo
|
90
|
+
|
91
|
+
2. The svn repo is NOT in standard layout and has only a trunk and tags at the
|
92
|
+
root level of the repo.
|
93
|
+
|
94
|
+
$ svn2git http://svn.example.com/path/to/repo --trunk dev --tags rel --nobranches
|
95
|
+
|
96
|
+
3. The svn repo is NOT in standard layout and has only a trunk at the root
|
97
|
+
level of the repo.
|
98
|
+
|
99
|
+
$ svn2git http://svn.example.com/path/to/repo --trunk trunk --nobranches --notags
|
100
|
+
|
101
|
+
4. The svn repo is NOT in standard layout and has no trunk, branches, or tags
|
102
|
+
at the root level of the repo. Instead the root level of the repo is
|
103
|
+
equivalent to the trunk and there are no tags or branches.
|
104
|
+
|
105
|
+
$ svn2git http://svn.example.com/path/to/repo --rootistrunk
|
106
|
+
|
107
|
+
5. The svn repo is in the standard layout but you want to exclude the massive
|
108
|
+
doc directory and the backup files you once accidently added.
|
109
|
+
|
110
|
+
$ svn2git http://svn.example.com/path/to/repo --exclude doc --exclude '.*~$'
|
111
|
+
|
112
|
+
6. The svn repo actually tracks several projects and you only want to migrate
|
113
|
+
one of them.
|
114
|
+
|
115
|
+
$ svn2git http://svn.example.com/path/to/repo/nested_project --no-minimize-url
|
116
|
+
|
117
|
+
7. The svn repo is password protected.
|
118
|
+
|
119
|
+
$ svn2git http://svn.example.com/path/to/repo --username <<user_with_perms>>
|
120
|
+
|
121
|
+
If this doesn't cooperate and you need to specify a password on the command-line:
|
122
|
+
|
123
|
+
$ SVN2GIT_PASSWORD=<<password>> svn2git http://svn.example.com/path/to/repo --username <<user_with_perms>>
|
124
|
+
|
125
|
+
8. You need to migrate starting at a specific svn revision number.
|
126
|
+
|
127
|
+
$ svn2git http://svn.example.com/path/to/repo --revision <<starting_revision_number>>
|
128
|
+
|
129
|
+
9. You need to migrate starting at a specific svn revision number, ending at a specific revision number.
|
130
|
+
|
131
|
+
$ svn2git http://svn.example.com/path/to/repo --revision <<starting_revision_number>>:<<ending_revision_number>>
|
132
|
+
|
133
|
+
10. Include metadata (git-svn-id) in git logs.
|
134
|
+
|
135
|
+
$ svn2git http://svn.example.com/path/to/repo --metadata
|
136
|
+
|
137
|
+
The above will create a git repository in the current directory with the git
|
138
|
+
version of the svn repository. Hence, you need to make a directory that you
|
139
|
+
want your new git repo to exist in, change into it and then run one of the
|
140
|
+
above commands. Note that in the above cases the trunk, branches, tags options
|
141
|
+
are simply folder names relative to the provided repo path. For example if you
|
142
|
+
specified trunk=foo branches=bar and tags=foobar it would be referencing
|
143
|
+
http://svn.example.com/path/to/repo/foo as your trunk, and so on. However, in
|
144
|
+
case 4 it references the root of the repo as trunk.
|
145
|
+
|
146
|
+
### Repository Updates ###
|
147
|
+
|
148
|
+
As of svn2git 2.0 there is a new feature to pull in the latest changes from SVN into your
|
149
|
+
git repository created with svn2git. This is a one way sync, but allows you to use svn2git
|
150
|
+
as a mirroring tool for your SVN repositories.
|
151
|
+
|
152
|
+
The command to call is:
|
153
|
+
|
154
|
+
$ cd <EXISTING_REPO> && svn2git --rebase
|
155
|
+
|
156
|
+
Authors
|
157
|
+
-------
|
158
|
+
|
159
|
+
To convert all your svn authors to git format, create a file somewhere on your
|
160
|
+
system with the list of conversions to make, one per line, for example:
|
161
|
+
|
162
|
+
jcoglan = James Coglan <jcoglan@never-you-mind.com>
|
163
|
+
stnick = Santa Claus <nicholas@lapland.com>
|
164
|
+
|
165
|
+
Then pass an _authors_ option to svn2git pointing to your file:
|
166
|
+
|
167
|
+
$ svn2git http://svn.example.com/path/to/repo --authors ~/authors.txt
|
168
|
+
|
169
|
+
Alternatively, you can place the authors file into `~/.svn2git/authors` and
|
170
|
+
svn2git will load it out of there. This allows you to build up one authors
|
171
|
+
file for all your projects and have it loaded for each repository that you
|
172
|
+
migrate.
|
173
|
+
|
174
|
+
If you need a jump start on figuring out what users made changes in your
|
175
|
+
svn repositories the following command sequence might help. It grabs all
|
176
|
+
the logs from the svn repository, pulls out all the names from the commits,
|
177
|
+
sorts them, and then reduces the list to only unique names. So, in the end
|
178
|
+
it outputs a list of usernames of the people that made commits to the svn
|
179
|
+
repository which name on its own line. This would allow you to easily
|
180
|
+
redirect the output of this command sequence to `~/.svn2git/authors` and have
|
181
|
+
a very good starting point for your mapping.
|
182
|
+
|
183
|
+
$ svn log --quiet | grep -E "r[0-9]+ \| .+ \|" | cut -d'|' -f2 | sed 's/ //g' | sort | uniq
|
184
|
+
|
185
|
+
Or, for a remote URL:
|
186
|
+
|
187
|
+
$ svn log --quiet http://path/to/root/of/project | grep -E "r[0-9]+ \| .+ \|" | cut -d'|' -f2 | sed 's/ //g' | sort | uniq
|
188
|
+
|
189
|
+
Debugging
|
190
|
+
---------
|
191
|
+
|
192
|
+
If you're having problems with converting your repository and you're not sure why,
|
193
|
+
try turning on verbose logging. This will print out more information from the
|
194
|
+
underlying git-svn process.
|
195
|
+
|
196
|
+
You can turn on verbose logging with the `-v` or `--verbose` flags, like so:
|
197
|
+
|
198
|
+
$ svn2git http://svn.yoursite.com/path/to/repo --verbose
|
199
|
+
|
200
|
+
Options Reference
|
201
|
+
-----------------
|
202
|
+
|
203
|
+
$ svn2git --help
|
204
|
+
Usage: svn2git SVN_URL [options]
|
205
|
+
|
206
|
+
Specific options:
|
207
|
+
--rebase Instead of cloning a new project, rebase an existing one against SVN
|
208
|
+
--username NAME Username for transports that needs it (http(s), svn)
|
209
|
+
--trunk TRUNK_PATH Subpath to trunk from repository URL (default: trunk)
|
210
|
+
--branches BRANCHES_PATH Subpath to branches from repository URL (default: branches); can be used multiple times
|
211
|
+
--tags TAGS_PATH Subpath to tags from repository URL (default: tags); can be used multiple times
|
212
|
+
--rootistrunk Use this if the root level of the repo is equivalent to the trunk and there are no tags or branches
|
213
|
+
--notrunk Do not import anything from trunk
|
214
|
+
--nobranches Do not try to import any branches
|
215
|
+
--notags Do not try to import any tags
|
216
|
+
--no-minimize-url Accept URLs as-is without attempting to connect to a higher level directory
|
217
|
+
--revision START_REV[:END_REV]
|
218
|
+
Start importing from SVN revision START_REV; optionally end at END_REV
|
219
|
+
-m, --metadata Include metadata in git logs (git-svn-id)
|
220
|
+
--authors AUTHORS_FILE Path to file containing svn-to-git authors mapping (default: ~/.svn2git/authors)
|
221
|
+
--exclude REGEX Specify a Perl regular expression to filter paths when fetching; can be used multiple times
|
222
|
+
-v, --verbose Be verbose in logging -- useful for debugging issues
|
223
|
+
|
224
|
+
-h, --help Show this message
|
225
|
+
|
226
|
+
FAQ
|
227
|
+
---
|
228
|
+
|
229
|
+
1. Why don't the tags show up in the master branch?
|
230
|
+
|
231
|
+
The tags won't show up in the master branch because the tags are actually
|
232
|
+
tied to the commits that were created in svn when the user made the tag.
|
233
|
+
Those commits are the first (head) commit of branch in svn that is
|
234
|
+
associated with that tag. If you want to see all the branches and tags
|
235
|
+
and their relationships in gitk you can run the following: gitk --all
|
236
|
+
|
237
|
+
For further details please refer to FAQ #2.
|
238
|
+
|
239
|
+
2. Why don't you reference the parent of the tag commits instead?
|
240
|
+
|
241
|
+
In svn you are forced to create what are known in git as annotated tags.
|
242
|
+
It just so happens that svn annotated tags allow you to commit change
|
243
|
+
sets along with the tagging action. This means that the svn annotated tag
|
244
|
+
is a bit more complex then just an annotated tag it is a commit which is
|
245
|
+
treated as an annotated tag. Hence, for there to be a true 1-to-1 mapping
|
246
|
+
between git and svn we have to transfer over the svn commit which acts as
|
247
|
+
an annotated tag and then tag that commit in git using an annotated tag.
|
248
|
+
|
249
|
+
If we were to reference the parent of this svn tagged commit there could
|
250
|
+
potentially be situations where a developer would checkout a tag in git
|
251
|
+
and the resulting code base would be different than if they checked out
|
252
|
+
that very same tag in the original svn repo. This is only due to the fact
|
253
|
+
that the svn tags allow changesets in them, making them not just annotated
|
254
|
+
tags.
|
255
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rubygems/package_task'
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'jeweler'
|
7
|
+
Jeweler::Tasks.new do |spec|
|
8
|
+
spec.name = "svn2git3"
|
9
|
+
spec.summary = "A tool for migrating svn projects to git"
|
10
|
+
spec.authors = ["James Coglan", "Kevin Menard", "Abao"]
|
11
|
+
spec.homepage = "https://github.com/ibaoger/svn2git3"
|
12
|
+
spec.email = "ibaoger@hotmail.com"
|
13
|
+
spec.license = 'MIT'
|
14
|
+
spec.add_development_dependency 'minitest'
|
15
|
+
end
|
16
|
+
Jeweler::GemcutterTasks.new
|
17
|
+
|
18
|
+
rescue LoadError
|
19
|
+
puts "Jeweler not available. Install it with: gem install jeweler"
|
20
|
+
end
|
21
|
+
|
22
|
+
desc 'Test the rubber plugin.'
|
23
|
+
Rake::TestTask.new(:test) do |t|
|
24
|
+
t.libs << 'lib'
|
25
|
+
t.libs << 'test'
|
26
|
+
t.pattern = 'test/**/*_test.rb'
|
27
|
+
t.verbose = true
|
28
|
+
end
|
29
|
+
|
30
|
+
desc 'Default: run unit tests.'
|
31
|
+
task :default => :test
|
data/VERSION.yml
ADDED
data/bin/svn2git
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Copyright (c) 2008 James Coglan
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
# THE SOFTWARE.
|
22
|
+
|
23
|
+
require 'rubygems'
|
24
|
+
require 'svn2git'
|
25
|
+
|
26
|
+
migration = Svn2Git::Migration.new(ARGV)
|
27
|
+
migration.run!
|
@@ -0,0 +1,492 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'pp'
|
3
|
+
require 'timeout'
|
4
|
+
require 'thread'
|
5
|
+
|
6
|
+
module Svn2Git
|
7
|
+
DEFAULT_AUTHORS_FILE = "~/.svn2git/authors"
|
8
|
+
|
9
|
+
class Migration
|
10
|
+
|
11
|
+
attr_reader :dir
|
12
|
+
|
13
|
+
def initialize(args)
|
14
|
+
@options = parse(args)
|
15
|
+
if @options[:rebase]
|
16
|
+
show_help_message('Too many arguments') if args.size > 0
|
17
|
+
verify_working_tree_is_clean
|
18
|
+
elsif @options[:rebasebranch]
|
19
|
+
show_help_message('Too many arguments') if args.size > 0
|
20
|
+
verify_working_tree_is_clean
|
21
|
+
else
|
22
|
+
show_help_message('Missing SVN_URL parameter') if args.empty?
|
23
|
+
show_help_message('Too many arguments') if args.size > 1
|
24
|
+
@url = args.first.gsub(' ', "\\ ")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def run!
|
29
|
+
if @options[:rebase]
|
30
|
+
get_branches
|
31
|
+
elsif @options[:rebasebranch]
|
32
|
+
get_rebasebranch
|
33
|
+
else
|
34
|
+
clone!
|
35
|
+
end
|
36
|
+
fix_branches
|
37
|
+
fix_tags
|
38
|
+
fix_trunk
|
39
|
+
optimize_repos
|
40
|
+
end
|
41
|
+
|
42
|
+
def parse(args)
|
43
|
+
# Set up reasonable defaults for options.
|
44
|
+
options = {}
|
45
|
+
options[:verbose] = false
|
46
|
+
options[:metadata] = false
|
47
|
+
options[:nominimizeurl] = false
|
48
|
+
options[:rootistrunk] = false
|
49
|
+
options[:trunk] = 'trunk'
|
50
|
+
options[:branches] = []
|
51
|
+
options[:tags] = []
|
52
|
+
options[:exclude] = []
|
53
|
+
options[:revision] = nil
|
54
|
+
options[:username] = nil
|
55
|
+
options[:rebasebranch] = false
|
56
|
+
|
57
|
+
if File.exists?(File.expand_path(DEFAULT_AUTHORS_FILE))
|
58
|
+
options[:authors] = DEFAULT_AUTHORS_FILE
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
# Parse the command-line arguments.
|
63
|
+
@opts = OptionParser.new do |opts|
|
64
|
+
opts.banner = 'Usage: svn2git SVN_URL [options]'
|
65
|
+
|
66
|
+
opts.separator ''
|
67
|
+
opts.separator 'Specific options:'
|
68
|
+
|
69
|
+
opts.on('--rebase', 'Instead of cloning a new project, rebase an existing one against SVN') do
|
70
|
+
options[:rebase] = true
|
71
|
+
end
|
72
|
+
|
73
|
+
opts.on('--username NAME', 'Username for transports that needs it (http(s), svn)') do |username|
|
74
|
+
options[:username] = username
|
75
|
+
end
|
76
|
+
|
77
|
+
opts.on('--trunk TRUNK_PATH', 'Subpath to trunk from repository URL (default: trunk)') do |trunk|
|
78
|
+
options[:trunk] = trunk
|
79
|
+
end
|
80
|
+
|
81
|
+
opts.on('--branches BRANCHES_PATH', 'Subpath to branches from repository URL (default: branches); can be used multiple times') do |branches|
|
82
|
+
options[:branches] << branches
|
83
|
+
end
|
84
|
+
|
85
|
+
opts.on('--tags TAGS_PATH', 'Subpath to tags from repository URL (default: tags); can be used multiple times') do |tags|
|
86
|
+
options[:tags] << tags
|
87
|
+
end
|
88
|
+
|
89
|
+
opts.on('--rootistrunk', 'Use this if the root level of the repo is equivalent to the trunk and there are no tags or branches') do
|
90
|
+
options[:rootistrunk] = true
|
91
|
+
options[:trunk] = nil
|
92
|
+
options[:branches] = nil
|
93
|
+
options[:tags] = nil
|
94
|
+
end
|
95
|
+
|
96
|
+
opts.on('--notrunk', 'Do not import anything from trunk') do
|
97
|
+
options[:trunk] = nil
|
98
|
+
end
|
99
|
+
|
100
|
+
opts.on('--nobranches', 'Do not try to import any branches') do
|
101
|
+
options[:branches] = nil
|
102
|
+
end
|
103
|
+
|
104
|
+
opts.on('--notags', 'Do not try to import any tags') do
|
105
|
+
options[:tags] = nil
|
106
|
+
end
|
107
|
+
|
108
|
+
opts.on('--no-minimize-url', 'Accept URLs as-is without attempting to connect to a higher level directory') do
|
109
|
+
options[:nominimizeurl] = true
|
110
|
+
end
|
111
|
+
|
112
|
+
opts.on('--revision START_REV[:END_REV]', 'Start importing from SVN revision START_REV; optionally end at END_REV') do |revision|
|
113
|
+
options[:revision] = revision
|
114
|
+
end
|
115
|
+
|
116
|
+
opts.on('-m', '--metadata', 'Include metadata in git logs (git-svn-id)') do
|
117
|
+
options[:metadata] = true
|
118
|
+
end
|
119
|
+
|
120
|
+
opts.on('--authors AUTHORS_FILE', "Path to file containing svn-to-git authors mapping (default: #{DEFAULT_AUTHORS_FILE})") do |authors|
|
121
|
+
options[:authors] = authors
|
122
|
+
end
|
123
|
+
|
124
|
+
opts.on('--exclude REGEX', 'Specify a Perl regular expression to filter paths when fetching; can be used multiple times') do |regex|
|
125
|
+
options[:exclude] << regex
|
126
|
+
end
|
127
|
+
|
128
|
+
opts.on('-v', '--verbose', 'Be verbose in logging -- useful for debugging issues') do
|
129
|
+
options[:verbose] = true
|
130
|
+
end
|
131
|
+
|
132
|
+
opts.on('--rebasebranch REBASEBRANCH', 'Rebase specified branch.') do |rebasebranch|
|
133
|
+
options[:rebasebranch] = rebasebranch
|
134
|
+
end
|
135
|
+
|
136
|
+
opts.separator ""
|
137
|
+
|
138
|
+
# No argument, shows at tail. This will print an options summary.
|
139
|
+
# Try it and see!
|
140
|
+
opts.on_tail('-h', '--help', 'Show this message') do
|
141
|
+
puts opts
|
142
|
+
exit
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
@opts.parse! args
|
147
|
+
options
|
148
|
+
end
|
149
|
+
|
150
|
+
def self.escape_quotes(str)
|
151
|
+
str.gsub(/'|"/) { |c| "\\#{c}" }
|
152
|
+
end
|
153
|
+
|
154
|
+
def escape_quotes(str)
|
155
|
+
Svn2Git::Migration.escape_quotes(str)
|
156
|
+
end
|
157
|
+
|
158
|
+
def self.checkout_svn_branch(branch)
|
159
|
+
"git checkout -b \"#{branch}\" \"remotes/svn/#{branch}\""
|
160
|
+
end
|
161
|
+
|
162
|
+
private
|
163
|
+
|
164
|
+
def clone!
|
165
|
+
trunk = @options[:trunk]
|
166
|
+
branches = @options[:branches]
|
167
|
+
tags = @options[:tags]
|
168
|
+
metadata = @options[:metadata]
|
169
|
+
nominimizeurl = @options[:nominimizeurl]
|
170
|
+
rootistrunk = @options[:rootistrunk]
|
171
|
+
authors = @options[:authors]
|
172
|
+
exclude = @options[:exclude]
|
173
|
+
revision = @options[:revision]
|
174
|
+
username = @options[:username]
|
175
|
+
|
176
|
+
if rootistrunk
|
177
|
+
# Non-standard repository layout. The repository root is effectively 'trunk.'
|
178
|
+
cmd = "git svn init --prefix=svn/ "
|
179
|
+
cmd += "--username='#{username}' " unless username.nil?
|
180
|
+
cmd += "--no-metadata " unless metadata
|
181
|
+
if nominimizeurl
|
182
|
+
cmd += "--no-minimize-url "
|
183
|
+
end
|
184
|
+
cmd += "--trunk='#{@url}'"
|
185
|
+
run_command(cmd, true, true)
|
186
|
+
|
187
|
+
else
|
188
|
+
cmd = "git svn init --prefix=svn/ "
|
189
|
+
|
190
|
+
# Add each component to the command that was passed as an argument.
|
191
|
+
cmd += "--username='#{username}' " unless username.nil?
|
192
|
+
cmd += "--no-metadata " unless metadata
|
193
|
+
if nominimizeurl
|
194
|
+
cmd += "--no-minimize-url "
|
195
|
+
end
|
196
|
+
cmd += "--trunk='#{trunk}' " unless trunk.nil?
|
197
|
+
unless tags.nil?
|
198
|
+
# Fill default tags here so that they can be filtered later
|
199
|
+
tags = ['tags'] if tags.empty?
|
200
|
+
# Process default or user-supplied tags
|
201
|
+
tags.each do |tag|
|
202
|
+
cmd += "--tags='#{tag}' "
|
203
|
+
end
|
204
|
+
end
|
205
|
+
unless branches.nil?
|
206
|
+
# Fill default branches here so that they can be filtered later
|
207
|
+
branches = ['branches'] if branches.empty?
|
208
|
+
# Process default or user-supplied branches
|
209
|
+
branches.each do |branch|
|
210
|
+
cmd += "--branches='#{branch}' "
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
cmd += @url
|
215
|
+
|
216
|
+
run_command(cmd, true, true)
|
217
|
+
end
|
218
|
+
|
219
|
+
run_command("#{git_config_command} svn.authorsfile #{authors}") unless authors.nil?
|
220
|
+
|
221
|
+
cmd = "git svn fetch "
|
222
|
+
unless revision.nil?
|
223
|
+
range = revision.split(":")
|
224
|
+
range[1] = "HEAD" unless range[1]
|
225
|
+
cmd += "-r #{range[0]}:#{range[1]} "
|
226
|
+
end
|
227
|
+
unless exclude.empty?
|
228
|
+
# Add exclude paths to the command line; some versions of git support
|
229
|
+
# this for fetch only, later also for init.
|
230
|
+
regex = []
|
231
|
+
unless rootistrunk
|
232
|
+
regex << "#{trunk}[/]" unless trunk.nil?
|
233
|
+
tags.each{|tag| regex << "#{tag}[/][^/]+[/]"} unless tags.nil? or tags.empty?
|
234
|
+
branches.each{|branch| regex << "#{branch}[/][^/]+[/]"} unless branches.nil? or branches.empty?
|
235
|
+
end
|
236
|
+
regex = '^(?:' + regex.join('|') + ')(?:' + exclude.join('|') + ')'
|
237
|
+
cmd += "--ignore-paths='#{regex}' "
|
238
|
+
end
|
239
|
+
run_command(cmd, true, true)
|
240
|
+
|
241
|
+
get_branches
|
242
|
+
end
|
243
|
+
|
244
|
+
def get_branches
|
245
|
+
# Get the list of local and remote branches, taking care to ignore console color codes and ignoring the
|
246
|
+
# '*' character used to indicate the currently selected branch.
|
247
|
+
@local = run_command("git branch -l --no-color").split(/\n/).collect{ |b| b.gsub(/\*/,'').strip }
|
248
|
+
@remote = run_command("git branch -r --no-color").split(/\n/).collect{ |b| b.gsub(/\*/,'').strip }
|
249
|
+
|
250
|
+
# Tags are remote branches that start with "tags/".
|
251
|
+
@tags = @remote.find_all { |b| b.strip =~ %r{^svn\/tags\/} }
|
252
|
+
|
253
|
+
end
|
254
|
+
|
255
|
+
def get_rebasebranch
|
256
|
+
get_branches
|
257
|
+
@local = @local.find_all{|l| l == @options[:rebasebranch]}
|
258
|
+
@remote = @remote.find_all{|r| r.include? @options[:rebasebranch]}
|
259
|
+
|
260
|
+
if @local.count > 1
|
261
|
+
pp "To many matching branches found (#{@local})."
|
262
|
+
exit 1
|
263
|
+
elsif @local.count == 0
|
264
|
+
pp "No local branch named \"#{@options[:rebasebranch]}\" found."
|
265
|
+
exit 1
|
266
|
+
end
|
267
|
+
|
268
|
+
if @remote.count > 2 # 1 if remote is not pushed, 2 if its pushed to remote
|
269
|
+
pp "To many matching remotes found (#{@remotes})"
|
270
|
+
exit 1
|
271
|
+
elsif @remote.count == 0
|
272
|
+
pp "No remote branch named \"#{@options[:rebasebranch]}\" found."
|
273
|
+
exit 1
|
274
|
+
end
|
275
|
+
pp "Local branches \"#{@local}\" found"
|
276
|
+
pp "Remote branches \"#{@remote}\" found"
|
277
|
+
|
278
|
+
@tags = [] # We only rebase the specified branch
|
279
|
+
|
280
|
+
end
|
281
|
+
|
282
|
+
def fix_tags
|
283
|
+
current = {}
|
284
|
+
current['user.name'] = run_command("#{git_config_command} --get user.name", false)
|
285
|
+
current['user.email'] = run_command("#{git_config_command} --get user.email", false)
|
286
|
+
|
287
|
+
@tags.each do |tag|
|
288
|
+
tag = tag.strip
|
289
|
+
id = tag.gsub(%r{^svn\/tags\/}, '').strip
|
290
|
+
subject = run_command("git log -1 --pretty=format:'%s' \"#{escape_quotes(tag)}\"").chomp("'").reverse.chomp("'").reverse
|
291
|
+
date = run_command("git log -1 --pretty=format:'%ci' \"#{escape_quotes(tag)}\"").chomp("'").reverse.chomp("'").reverse
|
292
|
+
author = run_command("git log -1 --pretty=format:'%an' \"#{escape_quotes(tag)}\"").chomp("'").reverse.chomp("'").reverse
|
293
|
+
email = run_command("git log -1 --pretty=format:'%ae' \"#{escape_quotes(tag)}\"").chomp("'").reverse.chomp("'").reverse
|
294
|
+
run_command("#{git_config_command} user.name \"#{escape_quotes(author)}\"")
|
295
|
+
run_command("#{git_config_command} user.email \"#{escape_quotes(email)}\"")
|
296
|
+
|
297
|
+
original_git_committer_date = ENV['GIT_COMMITTER_DATE']
|
298
|
+
ENV['GIT_COMMITTER_DATE'] = escape_quotes(date)
|
299
|
+
run_command("git tag -a -m \"#{escape_quotes(subject)}\" \"#{escape_quotes(id)}\" \"#{escape_quotes(tag)}\"")
|
300
|
+
ENV['GIT_COMMITTER_DATE'] = original_git_committer_date
|
301
|
+
|
302
|
+
run_command("git branch -d -r \"#{escape_quotes(tag)}\"")
|
303
|
+
end
|
304
|
+
|
305
|
+
ensure
|
306
|
+
# We only change the git config values if there are @tags available. So it stands to reason we should revert them only in that case.
|
307
|
+
unless @tags.empty?
|
308
|
+
current.each_pair do |name, value|
|
309
|
+
# If a line was read, then there was a config value so restore it.
|
310
|
+
# Otherwise unset the value because originally there was none.
|
311
|
+
if value.strip != ''
|
312
|
+
run_command("#{git_config_command} #{name} \"#{value.strip}\"")
|
313
|
+
else
|
314
|
+
run_command("#{git_config_command} --unset #{name}")
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
def fix_branches
|
321
|
+
svn_branches = @remote - @tags
|
322
|
+
svn_branches.delete_if { |b| b.strip !~ %r{^svn\/} }
|
323
|
+
|
324
|
+
if @options[:rebase]
|
325
|
+
run_command("git svn fetch", true, true)
|
326
|
+
end
|
327
|
+
|
328
|
+
svn_branches.each do |branch|
|
329
|
+
branch = branch.gsub(/^svn\//,'').strip
|
330
|
+
if @options[:rebase] && (@local.include?(branch) || branch == 'trunk')
|
331
|
+
lbranch = branch
|
332
|
+
lbranch = 'master' if branch == 'trunk'
|
333
|
+
run_command("git checkout -f \"#{lbranch}\"")
|
334
|
+
run_command("git rebase \"remotes/svn/#{branch}\"")
|
335
|
+
next
|
336
|
+
end
|
337
|
+
|
338
|
+
next if branch == 'trunk' || @local.include?(branch)
|
339
|
+
|
340
|
+
if @cannot_setup_tracking_information
|
341
|
+
run_command(Svn2Git::Migration.checkout_svn_branch(branch))
|
342
|
+
else
|
343
|
+
status = run_command("git branch --track \"#{branch}\" \"remotes/svn/#{branch}\"", false)
|
344
|
+
|
345
|
+
# As of git 1.8.3.2, tracking information cannot be set up for remote SVN branches:
|
346
|
+
# http://git.661346.n2.nabble.com/git-svn-Use-prefix-by-default-td7594288.html#a7597159
|
347
|
+
#
|
348
|
+
# Older versions of git can do it and it should be safe as long as remotes aren't pushed.
|
349
|
+
# Our --rebase option obviates the need for read-only tracked remotes, however. So, we'll
|
350
|
+
# deprecate the old option, informing those relying on the old behavior that they should
|
351
|
+
# use the newer --rebase otion.
|
352
|
+
if status =~ /Cannot setup tracking information/m
|
353
|
+
@cannot_setup_tracking_information = true
|
354
|
+
run_command(Svn2Git::Migration.checkout_svn_branch(branch))
|
355
|
+
else
|
356
|
+
unless @legacy_svn_branch_tracking_message_displayed
|
357
|
+
warn '*' * 68
|
358
|
+
warn "svn2git warning: Tracking remote SVN branches is deprecated."
|
359
|
+
warn "In a future release local branches will be created without tracking."
|
360
|
+
warn "If you must resync your branches, run: svn2git --rebase"
|
361
|
+
warn '*' * 68
|
362
|
+
end
|
363
|
+
|
364
|
+
@legacy_svn_branch_tracking_message_displayed = true
|
365
|
+
|
366
|
+
run_command("git checkout \"#{branch}\"")
|
367
|
+
end
|
368
|
+
end
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
def fix_trunk
|
373
|
+
trunk = @remote.find { |b| b.strip == 'trunk' }
|
374
|
+
if trunk && ! @options[:rebase]
|
375
|
+
run_command("git checkout svn/trunk")
|
376
|
+
run_command("git branch -D master")
|
377
|
+
run_command("git checkout -f -b master")
|
378
|
+
else
|
379
|
+
run_command("git checkout -f master")
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
def optimize_repos
|
384
|
+
run_command("git gc")
|
385
|
+
end
|
386
|
+
|
387
|
+
def run_command(cmd, exit_on_error=true, printout_output=false)
|
388
|
+
if ( ( cmd =~ /^git[ ]svn/ ) and ( ENV.key?('SVN2GIT_PASSWORD') ) )
|
389
|
+
password = ENV['SVN2GIT_PASSWORD']
|
390
|
+
cmd = "echo #{password} | " + cmd
|
391
|
+
end
|
392
|
+
|
393
|
+
_run_command( cmd, exit_on_error, printout_output )
|
394
|
+
end
|
395
|
+
|
396
|
+
def _run_command(cmd, exit_on_error=true, printout_output=false)
|
397
|
+
log "Running command: #{cmd}\n"
|
398
|
+
|
399
|
+
ret = ''
|
400
|
+
@stdin_queue ||= Queue.new
|
401
|
+
|
402
|
+
# We need to fetch input from the user to pass through to the underlying sub-process. We'll constantly listen
|
403
|
+
# for input and place any received values on a queue for consumption by a pass-through thread that will forward
|
404
|
+
# the contents to the underlying sub-process's stdin pipe.
|
405
|
+
@stdin_thread ||= Thread.new do
|
406
|
+
loop { @stdin_queue << $stdin.gets.chomp }
|
407
|
+
end
|
408
|
+
|
409
|
+
# Open4 forks, which JRuby doesn't support. But JRuby added a popen4-compatible method on the IO class,
|
410
|
+
# so we can use that instead.
|
411
|
+
IO.popen("2>&1 #{cmd}") do |output|
|
412
|
+
threads = []
|
413
|
+
|
414
|
+
threads << Thread.new(output) do |output|
|
415
|
+
# git-svn seems to do all of its prompting for user input via STDERR. When it prompts for input, it will
|
416
|
+
# not terminate the line with a newline character, so we can't split the input up by newline. It will,
|
417
|
+
# however, use a space to separate the user input from the prompt. So we split on word boundaries here
|
418
|
+
# while draining STDERR.
|
419
|
+
output.each(' ') do |word|
|
420
|
+
ret << word
|
421
|
+
|
422
|
+
if printout_output
|
423
|
+
$stdout.print word
|
424
|
+
else
|
425
|
+
log word
|
426
|
+
end
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
|
+
# Simple pass-through thread to take anything the user types via STDIN and passes it through to the
|
431
|
+
# sub-process's stdin pipe.
|
432
|
+
Thread.new do
|
433
|
+
loop do
|
434
|
+
user_reply = @stdin_queue.pop
|
435
|
+
|
436
|
+
# nil is our cue to stop looping (pun intended).
|
437
|
+
break if user_reply.nil?
|
438
|
+
|
439
|
+
stdin.puts user_reply
|
440
|
+
stdin.close
|
441
|
+
end
|
442
|
+
end
|
443
|
+
|
444
|
+
threads.each(&:join)
|
445
|
+
|
446
|
+
# Push nil to the stdin_queue to gracefully exit the STDIN pass-through thread.
|
447
|
+
@stdin_queue << nil
|
448
|
+
end
|
449
|
+
|
450
|
+
if exit_on_error && $?.exitstatus != 0
|
451
|
+
$stderr.puts "command failed:\n#{cmd}"
|
452
|
+
exit -1
|
453
|
+
end
|
454
|
+
|
455
|
+
ret
|
456
|
+
end
|
457
|
+
|
458
|
+
def log(msg)
|
459
|
+
print msg if @options[:verbose]
|
460
|
+
end
|
461
|
+
|
462
|
+
def show_help_message(msg)
|
463
|
+
puts "Error starting script: #{msg}\n\n"
|
464
|
+
puts @opts.help
|
465
|
+
exit
|
466
|
+
end
|
467
|
+
|
468
|
+
def verify_working_tree_is_clean
|
469
|
+
status = run_command('git status --porcelain --untracked-files=no')
|
470
|
+
unless status.strip == ''
|
471
|
+
puts 'You have local pending changes. The working tree must be clean in order to continue.'
|
472
|
+
exit -1
|
473
|
+
end
|
474
|
+
end
|
475
|
+
|
476
|
+
def git_config_command
|
477
|
+
if @git_config_command.nil?
|
478
|
+
status = run_command('git config --local --get user.name', false)
|
479
|
+
|
480
|
+
@git_config_command = if status =~ /unknown option/m
|
481
|
+
'git config'
|
482
|
+
else
|
483
|
+
'git config --local'
|
484
|
+
end
|
485
|
+
end
|
486
|
+
|
487
|
+
@git_config_command
|
488
|
+
end
|
489
|
+
|
490
|
+
end
|
491
|
+
end
|
492
|
+
|
data/lib/svn2git.rb
ADDED
data/svn2git3.gemspec
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
# stub: svn2git3 3.0.3 ruby lib
|
6
|
+
|
7
|
+
Gem::Specification.new do |s|
|
8
|
+
s.name = "svn2git3".freeze
|
9
|
+
s.version = "3.0.3"
|
10
|
+
|
11
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
12
|
+
s.require_paths = ["lib".freeze]
|
13
|
+
s.authors = ["James Coglan".freeze, "Kevin Menard".freeze, "Abao".freeze]
|
14
|
+
s.date = "2022-06-07"
|
15
|
+
s.email = "ibaoger@hotmail.com".freeze
|
16
|
+
s.executables = ["svn2git".freeze]
|
17
|
+
s.extra_rdoc_files = [
|
18
|
+
"ChangeLog.markdown",
|
19
|
+
"README.markdown"
|
20
|
+
]
|
21
|
+
s.files = [
|
22
|
+
"ChangeLog.markdown",
|
23
|
+
"MIT-LICENSE",
|
24
|
+
"README.markdown",
|
25
|
+
"Rakefile",
|
26
|
+
"VERSION.yml",
|
27
|
+
"bin/svn2git",
|
28
|
+
"lib/svn2git.rb",
|
29
|
+
"lib/svn2git/migration.rb",
|
30
|
+
"svn2git3.gemspec",
|
31
|
+
"test/commands_test.rb",
|
32
|
+
"test/escape_quotes_test.rb",
|
33
|
+
"test/test_helper.rb"
|
34
|
+
]
|
35
|
+
s.homepage = "https://github.com/ibaoger/svn2git3".freeze
|
36
|
+
s.licenses = ["MIT".freeze]
|
37
|
+
s.rubygems_version = "3.1.2".freeze
|
38
|
+
s.summary = "A tool for migrating svn projects to git".freeze
|
39
|
+
|
40
|
+
if s.respond_to? :specification_version then
|
41
|
+
s.specification_version = 4
|
42
|
+
end
|
43
|
+
|
44
|
+
if s.respond_to? :add_runtime_dependency then
|
45
|
+
s.add_development_dependency(%q<minitest>.freeze, [">= 0"])
|
46
|
+
else
|
47
|
+
s.add_dependency(%q<minitest>.freeze, [">= 0"])
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require File.expand_path(File.join(__FILE__, '..', 'test_helper'))
|
2
|
+
|
3
|
+
class CommandsTest < Minitest::Test
|
4
|
+
def test_checkout_svn_branch
|
5
|
+
actual = Svn2Git::Migration.checkout_svn_branch('blah')
|
6
|
+
|
7
|
+
assert_equal 'git checkout -b "blah" "remotes/svn/blah"', actual
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require File.expand_path(File.join(__FILE__, '..', 'test_helper'))
|
2
|
+
|
3
|
+
class EscapeQuotesTest < Minitest::Test
|
4
|
+
def test_identity
|
5
|
+
expected = 'A string without any need to escape.'
|
6
|
+
actual = Svn2Git::Migration.escape_quotes(expected)
|
7
|
+
|
8
|
+
assert_equal expected, actual
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_escape_single_quotes
|
12
|
+
actual = Svn2Git::Migration.escape_quotes("Here's a message with 'single quotes.'")
|
13
|
+
|
14
|
+
assert_equal "Here\\'s a message with \\'single quotes.\\'", actual
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_escape_double_quotes
|
18
|
+
actual = Svn2Git::Migration.escape_quotes('Here is a message with "double quotes."')
|
19
|
+
|
20
|
+
assert_equal 'Here is a message with \\"double quotes.\\"', actual
|
21
|
+
end
|
22
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
$:.unshift "#{File.dirname(__FILE__)}/../lib"
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'svn2git'
|
5
|
+
require 'minitest/autorun'
|
6
|
+
|
7
|
+
if Minitest.const_defined?('Test')
|
8
|
+
# We're on Minitest 5+. Nothing to do here.
|
9
|
+
else
|
10
|
+
# Minitest 4 doesn't have Minitest::Test yet.
|
11
|
+
Minitest::Test = MiniTest::Unit::TestCase
|
12
|
+
end
|
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: svn2git3
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 3.0.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- James Coglan
|
8
|
+
- Kevin Menard
|
9
|
+
- Abao
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2022-06-07 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: minitest
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- - ">="
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: '0'
|
29
|
+
description:
|
30
|
+
email: ibaoger@hotmail.com
|
31
|
+
executables:
|
32
|
+
- svn2git
|
33
|
+
extensions: []
|
34
|
+
extra_rdoc_files:
|
35
|
+
- ChangeLog.markdown
|
36
|
+
- README.markdown
|
37
|
+
files:
|
38
|
+
- ChangeLog.markdown
|
39
|
+
- MIT-LICENSE
|
40
|
+
- README.markdown
|
41
|
+
- Rakefile
|
42
|
+
- VERSION.yml
|
43
|
+
- bin/svn2git
|
44
|
+
- lib/svn2git.rb
|
45
|
+
- lib/svn2git/migration.rb
|
46
|
+
- svn2git3.gemspec
|
47
|
+
- test/commands_test.rb
|
48
|
+
- test/escape_quotes_test.rb
|
49
|
+
- test/test_helper.rb
|
50
|
+
homepage: https://github.com/ibaoger/svn2git3
|
51
|
+
licenses:
|
52
|
+
- MIT
|
53
|
+
metadata: {}
|
54
|
+
post_install_message:
|
55
|
+
rdoc_options: []
|
56
|
+
require_paths:
|
57
|
+
- lib
|
58
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
requirements: []
|
69
|
+
rubygems_version: 3.1.2
|
70
|
+
signing_key:
|
71
|
+
specification_version: 4
|
72
|
+
summary: A tool for migrating svn projects to git
|
73
|
+
test_files: []
|