Skip to content

Commit 53be9bb

Browse files
authored
📣 Release 2.3.0
2 parents 50a0ed3 + eba9fed commit 53be9bb

File tree

14 files changed

+78
-28
lines changed

14 files changed

+78
-28
lines changed

README.md

+4
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ But it is also available from 🐳 [Docker Hub](https://hub.docker.com/repositor
1313
docker run --name svn2git -v /tmp/svn2git:/svn2git -p 8080:8080 yodamad/svn2git:latest
1414
```
1515

16+
### ⚠️ **For Windows OS users** ⚠️
17+
18+
Due to Windows OS mess *#troll*, it is highly recommended to either use docker image 🐳 or use [WSL2](https://docs.microsoft.com/fr-fr/windows/wsl/install) usage rather than directly launching through cmd or powershell
19+
1620
## ✨ Some quick tips to help you with the tool
1721

1822
💪 If you have large repositories to migrate, you may need to ↗️ JVM size :

pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
<groupId>fr.yodamad.svn2git</groupId>
66
<artifactId>svn-2-git</artifactId>
7-
<version>2.2.3</version>
7+
<version>2.3.0</version>
88
<packaging>jar</packaging>
99
<name>Svn 2 GitLab</name>
1010

src/main/kotlin/fr/yodamad/svn2git/functions/StringFunctions.kt

+2
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,5 @@ fun String.decode(): String = decode(this, "UTF-8")
1919
fun String.gitFormat(): String = this.decode().replace(" ", "_")
2020

2121
fun String.escape(): String = if (isWindows) this else this.replace("\\", "\\\\").replace("$", """\$""")
22+
23+
fun String.escapeParenthesis(): String = this.replace("(", "\\(").replace(")", "\\)")

src/main/kotlin/fr/yodamad/svn2git/service/Cleaner.kt

+10-8
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import fr.yodamad.svn2git.domain.enumeration.StatusEnum.DONE_WITH_WARNINGS
1313
import fr.yodamad.svn2git.domain.enumeration.StepEnum
1414
import fr.yodamad.svn2git.domain.enumeration.StepEnum.*
1515
import fr.yodamad.svn2git.domain.enumeration.SvnLayout
16+
import fr.yodamad.svn2git.domain.enumeration.SvnLayout.TAG
1617
import fr.yodamad.svn2git.functions.*
1718
import fr.yodamad.svn2git.io.Shell
1819
import fr.yodamad.svn2git.io.Shell.execCommand
@@ -75,7 +76,7 @@ open class Cleaner(val historyMgr: HistoryManager,
7576
branchName = branchName.replaceFirst("origin/".toRegex(), "")
7677
LOG.debug("Branch $branchName", branchName)
7778
// checkout new branchName from existing remote branch
78-
val gitCommand = String.format("git checkout -b %s %s", branchName, b)
79+
val gitCommand = String.format("git checkout -b \"%s\" \"%s\"", branchName, b)
7980
execCommand(workUnit.commandManager, workUnit.directory, gitCommand)
8081
// listCleanedFilesInSvnLocation
8182
val cleanedFilesBranch: CleanedFiles = listCleanedFilesInSvnLocation(workUnit, b.replace("origin", "branches"), SvnLayout.BRANCH)
@@ -97,10 +98,10 @@ open class Cleaner(val historyMgr: HistoryManager,
9798
Consumer { t: String ->
9899
try {
99100
// checkout new branch 'tmp_tag' from existing tag
100-
val gitCommand = String.format("git checkout -b tmp_tag %s", t)
101+
val gitCommand = String.format("git checkout -b tmp_tag \"%s\"", t)
101102
execCommand(workUnit.commandManager, workUnit.directory, gitCommand)
102103
// listCleanedFilesInSvnLocation
103-
val cleanedFilesTag: CleanedFiles = listCleanedFilesInSvnLocation(workUnit, t.replace("origin", "tags"), SvnLayout.TAG)
104+
val cleanedFilesTag: CleanedFiles = listCleanedFilesInSvnLocation(workUnit, t.replace("origin", "tags"), TAG)
104105
cleanedFilesMap[t.replace("origin", "tags")] = cleanedFilesTag
105106
// back to master
106107
execCommand(workUnit.commandManager, workUnit.directory, checkout())
@@ -293,7 +294,6 @@ open class Cleaner(val historyMgr: HistoryManager,
293294
open fun cleanForbiddenExtensions(workUnit: WorkUnit): Boolean {
294295
var clean = false
295296
if (!StringUtils.isEmpty(workUnit.migration.forbiddenFileExtensions)) {
296-
297297
// needed?
298298
execCommand(workUnit.commandManager, workUnit.directory, gc())
299299

@@ -418,15 +418,15 @@ open class Cleaner(val historyMgr: HistoryManager,
418418
val gitElementsToDelete: MutableList<String> = if (isTags) {
419419
Files.readAllLines(Paths.get(workUnit.directory, GIT_LIST))
420420
.stream()
421-
.map { l: String -> l.trim { it <= ' ' }.replace("origin/", "") }
421+
.map { l: String -> l.trim { it <= ' ' }.replace("origin/", "").decode().encode() }
422422
.filter { t: String -> t.startsWith("tags") }
423423
.map { l: String -> l.replace(TAGS, "") }
424424
.filter { l: String -> !l.equals(workUnit.migration.trunk, ignoreCase = true) }
425425
.collect(Collectors.toList())
426426
} else {
427427
Files.readAllLines(Paths.get(workUnit.directory, GIT_LIST))
428428
.stream()
429-
.map { l: String -> l.trim { it <= ' ' }.replace("origin/", "") }
429+
.map { l: String -> l.trim { it <= ' ' }.replace("origin/", "").decode().encode() }
430430
.filter { l: String -> !l.startsWith(TAGS) }
431431
.filter { l: String -> !l.equals(workUnit.migration.trunk, ignoreCase = true) }
432432
.collect(Collectors.toList())
@@ -480,12 +480,14 @@ open class Cleaner(val historyMgr: HistoryManager,
480480
// Remove none git branches
481481
gitElementsToDelete.forEach(Consumer { line: String ->
482482
try {
483-
var cleanCmd = String.format("git branch -d -r origin/%s", String.format("%s%s", if (isTags) TAGS else "", line))
483+
var cleanCmd = String.format("git branch -d -r origin/%s", String.format("\"%s%s\"", if (isTags) TAGS else "", line))
484484
execCommand(workUnit.commandManager, workUnit.directory, cleanCmd)
485485
cleanCmd = if (Shell.isWindows) {
486486
String.format("rd /s /q \".git\\svn\\refs\\remotes\\origin\\%s\\\"", String.format("%s%s", if (isTags) "tags\\" else "", line))
487487
} else {
488-
String.format("rm -rf .git/svn/refs/remotes/origin/%s", String.format("%s%s", if (isTags) TAGS else "", line))
488+
// var mutableLine = line
489+
// if (line.contains("(")) mutableLine = line.escapeParenthesis()
490+
String.format("rm -rf .git/svn/refs/remotes/origin/%s", String.format("\"%s%s\"", if (isTags) TAGS else "", line))
489491
}
490492
execCommand(workUnit.commandManager, workUnit.directory, cleanCmd)
491493
} catch (ex: IOException) {

src/main/kotlin/fr/yodamad/svn2git/service/GitManager.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ open class GitManager(val historyMgr: HistoryManager,
233233
// will have no parents and it will be the root of a new history totally disconnected from all the
234234
// other branches and commits
235235
execCommand(workUnit.commandManager, workUnit.directory,
236-
gitCommand(CHECKOUT, "--orphan", "TEMP_BRANCH_$branch"))
236+
gitCommand(CHECKOUT, "--orphan", "\"TEMP_BRANCH_$branch\""))
237237

238238
// Stage All (new, modified, deleted) files. Equivalent to git add . (in Git Version 2.x)
239239
execCommand(workUnit.commandManager, workUnit.directory, gitCommand("add", flags = "-A"))
@@ -264,7 +264,7 @@ open class GitManager(val historyMgr: HistoryManager,
264264
// create the remote
265265
addRemote(workUnit, true)
266266
// push to remote
267-
execCommand(workUnit.commandManager, workUnit.directory, gitCommand("push", "-f", "origin $branch"))
267+
execCommand(workUnit.commandManager, workUnit.directory, gitCommand("push", "-f", "origin \"$branch\""))
268268
historyMgr.endStep(history, StatusEnum.DONE, "Push $branch with no history")
269269
}
270270
} catch (gitEx: IOException) {

src/main/kotlin/fr/yodamad/svn2git/service/util/GitBranchManager.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ open class GitBranchManager(val gitManager: GitManager,
4343
}
4444

4545
try {
46-
execCommand(workUnit.commandManager, workUnit.directory, "git checkout -b \"$branchName\" $branch")
46+
execCommand(workUnit.commandManager, workUnit.directory, "git checkout -b \"$branchName\" \"$branch\"")
4747
} catch (iEx: IOException) {
4848
LOG.error(FAILED_TO_PUSH_BRANCH, iEx)
4949
historyMgr.endStep(history, StatusEnum.FAILED, iEx.message)
@@ -56,7 +56,7 @@ open class GitBranchManager(val gitManager: GitManager,
5656
if (workUnit.migration.svnHistory == "all") {
5757
try {
5858
gitManager.addRemote(workUnit, true)
59-
execCommand(workUnit.commandManager, workUnit.directory, "$GIT_PUSH --set-upstream origin $branchName")
59+
execCommand(workUnit.commandManager, workUnit.directory, "$GIT_PUSH --set-upstream origin \"$branchName\"")
6060
historyMgr.endStep(history, StatusEnum.DONE)
6161
} catch (iEx: IOException) {
6262
LOG.error(FAILED_TO_PUSH_BRANCH, iEx)

src/main/kotlin/fr/yodamad/svn2git/service/util/GitCommands.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ const val MASTER = "master"
1919
fun gitCommand(command: String, flags: String? = "", target: String? = "") = "git $command $flags $target"
2020

2121
// Branch management
22-
fun deleteBranch(branch: String) = gitCommand(BRANCH, "-D", branch)
23-
fun renameBranch(branch: String) = gitCommand(BRANCH, "-m", branch)
22+
fun deleteBranch(branch: String) = gitCommand(BRANCH, "-D", "\"$branch\"")
23+
fun renameBranch(branch: String) = gitCommand(BRANCH, "-m", "\"$branch\"")
2424

2525
// Pull management
2626
fun checkoutFromOrigin(branch: String) = gitCommand(CHECKOUT, "-b", "${branch.gitFormat()} refs/remotes/origin/${branch.encode()}")

src/main/kotlin/fr/yodamad/svn2git/service/util/GitTagManager.kt

+3-4
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,10 @@ open class GitTagManager(val gitManager: GitManager,
5353
val noHistory = workUnit.migration.svnHistory != "all"
5454

5555
// checkout a new branch using local tagName and remote tag name
56-
execCommand(workUnit.commandManager, workUnit.directory, "git checkout -b tmp_tag $tag")
56+
execCommand(workUnit.commandManager, workUnit.directory, "git checkout -b tmp_tag \"$tag\"")
5757

5858
// If this tag does not contain any files we will ignore it and add warning to logs.
5959
if (!isFileInFolder(workUnit.directory)) {
60-
6160
// Switch over to master
6261
execCommand(workUnit.commandManager, workUnit.directory, "git checkout master")
6362

@@ -75,14 +74,14 @@ open class GitTagManager(val gitManager: GitManager,
7574
execCommand(workUnit.commandManager, workUnit.directory, "git checkout master")
7675

7776
// create tag from tmp_tag branch.
78-
execCommand(workUnit.commandManager, workUnit.directory, "git tag $tagName tmp_tag")
77+
execCommand(workUnit.commandManager, workUnit.directory, "git tag \"$tagName\" tmp_tag")
7978

8079
// add remote to master
8180
gitManager.addRemote(workUnit, false)
8281

8382
// push the tag to remote
8483
// crashes if branch with same name so prefixing with refs/tags/
85-
execCommand(workUnit.commandManager, workUnit.directory, "git push -u origin refs/tags/$tagName")
84+
execCommand(workUnit.commandManager, workUnit.directory, "git push -u origin \"refs/tags/$tagName\"")
8685

8786
// delete the tmp_tag branch now that the tag has been created.
8887
execCommand(workUnit.commandManager, workUnit.directory, "git branch -D tmp_tag")

src/main/kotlin/fr/yodamad/svn2git/web/rest/SvnResource.kt

+4-2
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ open class SvnResource(val applicationProperties: ApplicationProperties) {
118118
val modulesFounds: MutableList<SvnModule> = ArrayList()
119119
list.receiver = ISvnObjectReceiver { _, `object`: SVNDirEntry ->
120120
val name = `object`.relativePath
121-
if (name != null && !name.isEmpty() && !keywords().contains(name)) {
121+
if (name != null && !name.isEmpty() && !keywords().contains(name) && module?.layoutElements.isNullOrEmpty()) {
122122

123123
// found a directory
124124
if (`object`.kind == SVNNodeKind.DIR) {
@@ -140,6 +140,8 @@ open class SvnResource(val applicationProperties: ApplicationProperties) {
140140
if (module != null) {
141141
log.info(String.format("Module %s with layout %s", module.name, name))
142142
module.layoutElements.add(name)
143+
module.flat = false
144+
modulesFounds.clear()
143145
} else {
144146
// Root level case
145147
modulesFounds.add(FakeModule())
@@ -159,7 +161,7 @@ open class SvnResource(val applicationProperties: ApplicationProperties) {
159161
modulesFounds.stream()
160162
.filter { m: SvnModule? -> m !is FakeModule }
161163
.map { e: SvnModule -> modules.add(e) }.count()
162-
if (!modules.isEmpty()) {
164+
if (modules.isNotEmpty()) {
163165
modules.forEach(
164166
Consumer { svnSubMod: SvnModule ->
165167
svnSubMod.subModules.addAll(

src/main/resources/templates/scripts/git-svn-clone.sh.hbs

+5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ proc gitSvnClone { user password } {
1616
exp_continue
1717
}
1818

19+
-gl "APR does not understand this error code*" {
20+
puts "catching error... continue with git svn fetch"
21+
return 1
22+
}
23+
eof { return 0 }
1924

2025
-gl "couldn't truncate file*" {
2126
puts "catching error... continue with git svn fetch"

src/test/java/fr/yodamad/svn2git/data/Repository.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public class Files {
6464
public static final String FLAT_FILE = "flat.file";
6565
public static final String ROOT_ANOTHER_BIN = "another.bin";
6666
public static final String ANOTHER_BIN = Dirs.FOLDER + ROOT_ANOTHER_BIN;
67-
public static final String EMPTY_DIR = Dirs.FOLDER + Dirs.EMPTY;
67+
public static final String EMPTY_DIR = "folder/empty";
6868
public static final String MAPPED_ANOTHER_BIN = Dirs.DIRECTORY + "another.bin";
6969
public static final String JAVA = Dirs.FOLDER + "App.java";
7070
public static final String MAPPED_JAVA = Dirs.DIRECTORY + "App.java";

src/test/java/fr/yodamad/svn2git/e2e/SimpleRepoTests.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
import org.gitlab4j.api.models.Tag;
1818
import org.junit.After;
1919
import org.junit.Before;
20-
import org.junit.Ignore;
2120
import org.junit.Test;
2221
import org.junit.runner.RunWith;
2322
import org.springframework.beans.factory.annotation.Autowired;
@@ -92,6 +91,7 @@ public void test_full_migration_on_simple_repo() throws ExecutionException, Inte
9291
migration.setTrunk("trunk");
9392
migration.setBranches("*");
9493
migration.setTags("*");
94+
migration.setEmptyDirs(false);
9595

9696
startAndCheck(migration);
9797

@@ -100,6 +100,7 @@ public void test_full_migration_on_simple_repo() throws ExecutionException, Inte
100100

101101
// Check files
102102
checkAllFiles(project);
103+
isFolderMissing(project.get(), EMPTY_DIR, false);
103104

104105
// Check branches
105106
List<Branch> branches = checkBranches(project);
@@ -111,7 +112,6 @@ public void test_full_migration_on_simple_repo() throws ExecutionException, Inte
111112
}
112113

113114
@Test
114-
@Ignore
115115
public void test_full_migration_on_simple_repo_with_empty_dirs() throws ExecutionException, InterruptedException, GitLabApiException {
116116
Migration migration = initSimpleMigration(applicationProperties);
117117
migration.setSvnHistory("all");
@@ -127,7 +127,7 @@ public void test_full_migration_on_simple_repo_with_empty_dirs() throws Executio
127127

128128
// Check files
129129
checkAllFiles(project);
130-
isPresent(project.get(), EMPTY_DIR, false);
130+
isFolderPresent(project.get(), EMPTY_DIR, false);
131131

132132
// Check branches
133133
List<Branch> branches = checkBranches(project);

src/test/java/fr/yodamad/svn2git/e2e/WeirdRepoTests.java

+29-3
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public void test_migration_with_space_in_trunk_name() throws ExecutionException,
8585
isPresent(project.get(), REVISION, false);
8686

8787
// Check branches
88-
List<Branch> branches = checkBranches(project, 3);
88+
List<Branch> branches = checkBranches(project, 4);
8989

9090
// Check tags
9191
checkTags(project, 0);
@@ -110,7 +110,7 @@ public void test_migration_with_space_in_branch_name() throws ExecutionException
110110
isPresent(project.get(), REVISION, false);
111111

112112
// Check branches
113-
List<Branch> branches = checkBranches(project, 3);
113+
List<Branch> branches = checkBranches(project, 4);
114114
assertThat(branches.stream().anyMatch(b -> b.getName().equals("branch_with_space"))).isTrue();
115115

116116
// Check tags
@@ -139,10 +139,36 @@ public void test_migration_with_space_in_tag_name() throws ExecutionException, I
139139
checkBranches(project, 1);
140140

141141
// Check tags
142-
List<Tag> tags = checkTags(project, 1);
142+
List<Tag> tags = checkTags(project, 2);
143143
assertThat(tags.stream().anyMatch(b -> b.getName().equals("tag%20with%20space"))).isTrue();
144144
}
145145

146+
@Test
147+
public void test_migration_with_parenthesis_in_names() throws ExecutionException, InterruptedException, GitLabApiException {
148+
Migration migration = initWeirdMigration(applicationProperties);
149+
migration.setSvnHistory("all");
150+
migration.setTrunk("trunk");
151+
migration.setTags("*");
152+
migration.setBranches("*");
153+
154+
startAndCheck(migration);
155+
156+
// Check project
157+
Optional<Project> project = checkProject();
158+
159+
// Check files
160+
isMissing(project.get(), ROOT_ANOTHER_BIN);
161+
isPresent(project.get(), FILE_BIN, false);
162+
isPresent(project.get(), REVISION, false);
163+
164+
// Check branches
165+
List<Branch> branches = checkBranches(project, 4);
166+
assertThat(branches.stream().anyMatch(b -> b.getName().equals("branch_with_(parenthesis)"))).isTrue();
167+
168+
// Check tags
169+
List<Tag> tags = checkTags(project, 2);
170+
assertThat(tags.stream().anyMatch(b -> b.getName().equals("tag%20with%20(parenthesis)"))).isTrue();
171+
}
146172

147173
private void startAndCheck(Migration migration) throws ExecutionException, InterruptedException {
148174
Migration saved = migrationRepository.save(migration);

src/test/java/fr/yodamad/svn2git/utils/Checks.java

+10
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,16 @@ public static void isPresent(Project project, String filename, boolean checkSize
7575
}
7676
}
7777

78+
public static void isFolderPresent(Project project, String filename, boolean checkSize) throws GitLabApiException {
79+
List<TreeItem> master = gitLabApi.getRepositoryApi().getTree(project.getId(), filename, "master", true);
80+
assertThat(master.size()).isGreaterThan(0);
81+
}
82+
83+
public static void isFolderMissing(Project project, String filename, boolean checkSize) throws GitLabApiException {
84+
List<TreeItem> master = gitLabApi.getRepositoryApi().getTree(project.getId(), filename, "master", true);
85+
assertThat(master.size()).isEqualTo(0);
86+
}
87+
7888
public static void isMissing(Project project, String filename) {
7989
Optional<RepositoryFile> file = gitLabApi.getRepositoryFileApi().getOptionalFileInfo(project.getId(), filename, "master");
8090
assertThat(file.isPresent()).isFalse();

0 commit comments

Comments
 (0)