diff --git a/gradle/changelog/import_export.yaml b/gradle/changelog/import_export.yaml new file mode 100644 index 0000000000..1d7ba899b0 --- /dev/null +++ b/gradle/changelog/import_export.yaml @@ -0,0 +1,2 @@ +- type: fixed + description: Various issues for repository dump export and import without metadata diff --git a/scm-core/src/main/java/sonia/scm/util/Archives.java b/scm-core/src/main/java/sonia/scm/util/Archives.java index 841c08b442..734c827fd4 100644 --- a/scm-core/src/main/java/sonia/scm/util/Archives.java +++ b/scm-core/src/main/java/sonia/scm/util/Archives.java @@ -142,12 +142,14 @@ public final class Archives { } } - @SuppressWarnings("java:S1075") // We use / here because we build tar entries, not files + @SuppressWarnings("java:S1075") // We use / here because we build tar entries, not files private void bundleFileOrDir(String path, Path fileOrDir, TarArchiveOutputStream taos) { try { String filePath = path + "/" + fileOrDir.getFileName().toString(); if (Files.isDirectory(fileOrDir)) { createTarEntryForFiles(filePath, fileOrDir, taos); + } else if (Files.isSymbolicLink(fileOrDir)) { + createArchiveEntryForLink(filePath, fileOrDir, taos); } else { createArchiveEntryForFile(filePath, fileOrDir, taos); } @@ -159,6 +161,14 @@ public final class Archives { } } + private static void createArchiveEntryForLink(String filePath, Path path, TarArchiveOutputStream taos) throws IOException { + String linkTarget = Files.readSymbolicLink(path).toString(); + TarArchiveEntry entry = new TarArchiveEntry(filePath, TarArchiveEntry.LF_SYMLINK); + entry.setLinkName(linkTarget); + taos.putArchiveEntry(entry); + taos.closeArchiveEntry(); + } + private void createArchiveEntryForFile(String filePath, Path path, TarArchiveOutputStream taos) throws IOException { TarArchiveEntry entry = new TarArchiveEntry(filePath); entry.setSize(path.toFile().length()); @@ -189,7 +199,17 @@ public final class Archives { while ((entry = tais.getNextTarEntry()) != null) { Path filePath = targetPath.resolve(entry.getName()); createDirectoriesIfNestedFile(filePath); - Files.copy(tais, filePath, StandardCopyOption.REPLACE_EXISTING); + if (entry.isDirectory()) { + Files.createDirectories(filePath); + } else if (entry.isSymbolicLink() || entry.isLink()) { + String linkTarget = entry.getLinkName(); + if (linkTarget == null || linkTarget.isEmpty()) { + throw new ArchiveException("Symbolic link entry '" + entry.getName() + "' has no target", null); + } + Files.createSymbolicLink(filePath, Path.of(linkTarget)); + } else { + Files.copy(tais, filePath, StandardCopyOption.REPLACE_EXISTING); + } } } } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgUnbundleCommand.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgUnbundleCommand.java index 1e05b81682..debb20ca18 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgUnbundleCommand.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgUnbundleCommand.java @@ -23,6 +23,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.repository.RepositoryHookEvent; import sonia.scm.repository.api.UnbundleResponse; +import sonia.scm.util.IOUtil; import java.io.IOException; import java.nio.file.Files; @@ -57,6 +58,10 @@ public class HgUnbundleCommand implements UnbundleCommand { if (!Files.exists(repositoryDir)) { Files.createDirectories(repositoryDir); } + Path hgDir = repositoryDir.resolve(".hg"); + if (Files.exists(hgDir)) { + IOUtil.delete(hgDir.toFile()); + } unbundleRepositoryFromRequest(request, repositoryDir); fireHookEvent(request); diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryExportResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryExportResource.java index 6a3be4e977..584fa1dcfe 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryExportResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryExportResource.java @@ -474,7 +474,7 @@ public class RepositoryExportResource { }; return createResponse(repository, fileExtension, compressed, output); } - } catch (IOException e) { + } catch (Exception e) { notificationHandler.handleFailedExport(repository); throw new ExportFailedException(entity(repository).build(), "repository export failed", e); }