diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/repo/GitProjectRepo.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/repo/GitProjectRepo.java index 54c5a57b86..d9d190883c 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/repo/GitProjectRepo.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/repo/GitProjectRepo.java @@ -14,6 +14,7 @@ import org.apache.commons.io.IOUtils; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.ResetCommand; import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.storage.file.FileRepositoryBuilder; @@ -83,6 +84,8 @@ public class GitProjectRepo implements ProjectRepo { return; } repo.create(); + // Set default branch to "main" for new projects + repo.updateRef(Constants.HEAD).link("refs/heads/main"); } @Override diff --git a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/application/WLGitBridgeIntegrationTest.java b/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/application/WLGitBridgeIntegrationTest.java index e017476ca9..f3eb95b693 100644 --- a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/application/WLGitBridgeIntegrationTest.java +++ b/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/application/WLGitBridgeIntegrationTest.java @@ -449,6 +449,17 @@ public class WLGitBridgeIntegrationTest { .build()); } }); + put( + "masterBranchSurvivesSwap", + new HashMap() { + { + put( + "state", + new SnapshotAPIStateBuilder( + getResourceAsStream("/masterBranchSurvivesSwap/state/state.json")) + .build()); + } + }); } }; @@ -522,6 +533,13 @@ public class WLGitBridgeIntegrationTest { assertEquals(0, runtime.exec("git pull", null, dir).waitFor()); } + private String gitBranch(File dir) throws IOException, InterruptedException { + Process proc = + runtime.exec(new String[] {"git", "rev-parse", "--abbrev-ref", "HEAD"}, null, dir); + assertEquals(0, proc.waitFor()); + return IOUtils.toString(proc.getInputStream(), StandardCharsets.UTF_8).trim(); + } + @Test public void canCloneARepository() throws IOException, GitAPIException, InterruptedException { server = new MockSnapshotServer(3857, getResource("/canCloneARepository").toFile()); @@ -807,7 +825,7 @@ public class WLGitBridgeIntegrationTest { "remote: hint: hello world.png (rename to: hello_world.png)", "remote: hint: an image.jpg (rename to: an_image.jpg)", "To " + gitRemoteUrl(port), - "! [remote rejected] master -> master (invalid files)", + "! [remote rejected] main -> main (invalid files)", "error: failed to push some refs to '" + gitRemoteUrl(port) + "'"); } @@ -835,7 +853,7 @@ public class WLGitBridgeIntegrationTest { "remote: hint: project: no main file", "remote: hint: The project would have no (editable) main .tex file.", "To " + gitRemoteUrl(port), - "! [remote rejected] master -> master (invalid project)", + "! [remote rejected] main -> main (invalid project)", "error: failed to push some refs to '" + gitRemoteUrl(port) + "'"); } @@ -864,7 +882,7 @@ public class WLGitBridgeIntegrationTest { "remote: hint: There was an internal error with the Overleaf server.", "remote: hint: Please contact Overleaf.", "To " + gitRemoteUrl(port), - "! [remote rejected] master -> master (Overleaf error)", + "! [remote rejected] main -> main (Overleaf error)", "error: failed to push some refs to '" + gitRemoteUrl(port) + "'"); } @@ -896,7 +914,7 @@ public class WLGitBridgeIntegrationTest { "remote: hint: You have 1 invalid files in your Overleaf project:", "remote: hint: file1.exe (invalid file extension)", "To " + gitRemoteUrl(port), - "! [remote rejected] master -> master (invalid files)", + "! [remote rejected] main -> main (invalid files)", "error: failed to push some refs to '" + gitRemoteUrl(port) + "'"); } @@ -1012,13 +1030,59 @@ public class WLGitBridgeIntegrationTest { assertFalse(testProj2ServerDir.exists()); } + @Test + public void masterBranchSurvivesSwap() throws Exception { + int mockServerPort = 4010; + server = + new MockSnapshotServer(mockServerPort, getResource("/masterBranchSurvivesSwap").toFile()); + server.start(); + server.setState(states.get("masterBranchSurvivesSwap").get("state")); + wlgb = + new GitBridgeApp( + new String[] { + makeConfigFile(0, mockServerPort, new SwapJobConfig(1, 0, 0, 250, null, true)) + }); + wlgb.run(); + File rootGitDir = new File(wlgb.config.getRootGitDirectory()); + File proj1ServerDir = new File(rootGitDir, PROJECT_ID1); + File proj2ServerDir = new File(rootGitDir, PROJECT_ID2); + + // Clone project 1 — created with default branch "main" + File proj1Dir = gitClone(PROJECT_ID1, wlgb.getPort(), dir); + assertEquals("main", gitBranch(proj1Dir)); + assertTrue(proj1ServerDir.exists()); + + // Simulate a legacy repo: rewrite server-side HEAD to master + assertEquals( + 0, + runtime + .exec(new String[] {"git", "branch", "-m", "main", "master"}, null, proj1ServerDir) + .waitFor()); + + // Clone project 2 — triggers eviction of project 1 + gitClone(PROJECT_ID2, wlgb.getPort(), dir); + while (proj1ServerDir.exists()) + ; + assertFalse(proj1ServerDir.exists()); + assertTrue(proj2ServerDir.exists()); + + // Re-clone project 1 — triggers restore from swap + FileUtils.deleteDirectory(proj1Dir); + File proj1DirRestored = gitClone(PROJECT_ID1, wlgb.getPort(), dir); + while (proj2ServerDir.exists()) + ; + + // Verify the restored repo still uses "master" + assertEquals("master", gitBranch(proj1DirRestored)); + } + private static List expectedPushSubmodule(int port) { return Arrays.asList( "remote: hint: Your Git repository contains a reference we cannot resolve.", "remote: hint: If your project contains a Git submodule,", "remote: hint: please remove it and try again.", "To " + gitRemoteUrl(port), - "! [remote rejected] master -> master (invalid git repo)", + "! [remote rejected] main -> main (invalid git repo)", "error: failed to push some refs to '" + gitRemoteUrl(port) + "'"); } diff --git a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/repo/GitProjectRepoTest.java b/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/repo/GitProjectRepoTest.java index 7222203223..480792b736 100644 --- a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/repo/GitProjectRepoTest.java +++ b/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/repo/GitProjectRepoTest.java @@ -13,6 +13,7 @@ import java.nio.file.Paths; import java.util.*; import java.util.function.Supplier; import org.apache.commons.io.FileUtils; +import org.eclipse.jgit.lib.Constants; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -169,4 +170,28 @@ public class GitProjectRepoTest { withoutIncoming.deleteIncomingPacks(); assertTrue(FileUtil.directoryDeepEquals(actual, expected)); } + + @Test + public void initRepoSetsDefaultBranchToMain() throws IOException { + FSGitRepoStore store = + new FSGitRepoStore(tmpFolder.newFolder("newrepos").getAbsolutePath(), Optional.empty()); + GitProjectRepo newRepo = GitProjectRepo.fromName("testproject"); + newRepo.initRepo(store); + assertEquals("refs/heads/main", newRepo.getJGitRepository().getFullBranch()); + } + + @Test + public void initRepoDoesNotRewriteExistingMasterRepoToMain() throws IOException { + FSGitRepoStore store = + new FSGitRepoStore(tmpFolder.newFolder("legacyrepos").getAbsolutePath(), Optional.empty()); + GitProjectRepo newRepo = GitProjectRepo.fromName("testproject"); + newRepo.initRepo(store); + newRepo.getJGitRepository().updateRef(Constants.HEAD).link("refs/heads/master"); + assertEquals("refs/heads/master", newRepo.getJGitRepository().getFullBranch()); + + GitProjectRepo existingRepo = GitProjectRepo.fromName("testproject"); + existingRepo.initRepo(store); + + assertEquals("refs/heads/master", existingRepo.getJGitRepository().getFullBranch()); + } } diff --git a/services/git-bridge/src/test/resources/uk/ac/ic/wlgitbridge/WLGitBridgeIntegrationTest/masterBranchSurvivesSwap/state/111111111111111111111111/foo/bar/test.tex b/services/git-bridge/src/test/resources/uk/ac/ic/wlgitbridge/WLGitBridgeIntegrationTest/masterBranchSurvivesSwap/state/111111111111111111111111/foo/bar/test.tex new file mode 100644 index 0000000000..046794f19a --- /dev/null +++ b/services/git-bridge/src/test/resources/uk/ac/ic/wlgitbridge/WLGitBridgeIntegrationTest/masterBranchSurvivesSwap/state/111111111111111111111111/foo/bar/test.tex @@ -0,0 +1 @@ +This text is from another file. \ No newline at end of file diff --git a/services/git-bridge/src/test/resources/uk/ac/ic/wlgitbridge/WLGitBridgeIntegrationTest/masterBranchSurvivesSwap/state/111111111111111111111111/main.tex b/services/git-bridge/src/test/resources/uk/ac/ic/wlgitbridge/WLGitBridgeIntegrationTest/masterBranchSurvivesSwap/state/111111111111111111111111/main.tex new file mode 100644 index 0000000000..d95f3ad14d --- /dev/null +++ b/services/git-bridge/src/test/resources/uk/ac/ic/wlgitbridge/WLGitBridgeIntegrationTest/masterBranchSurvivesSwap/state/111111111111111111111111/main.tex @@ -0,0 +1 @@ +content diff --git a/services/git-bridge/src/test/resources/uk/ac/ic/wlgitbridge/WLGitBridgeIntegrationTest/masterBranchSurvivesSwap/state/111111111111111111111111/overleaf-white-410.png b/services/git-bridge/src/test/resources/uk/ac/ic/wlgitbridge/WLGitBridgeIntegrationTest/masterBranchSurvivesSwap/state/111111111111111111111111/overleaf-white-410.png new file mode 100644 index 0000000000..6a23d10c15 Binary files /dev/null and b/services/git-bridge/src/test/resources/uk/ac/ic/wlgitbridge/WLGitBridgeIntegrationTest/masterBranchSurvivesSwap/state/111111111111111111111111/overleaf-white-410.png differ diff --git a/services/git-bridge/src/test/resources/uk/ac/ic/wlgitbridge/WLGitBridgeIntegrationTest/masterBranchSurvivesSwap/state/222222222222222222222222/editor-versions-a7e4de19d015c3e7477e3f7eaa6c418e.png b/services/git-bridge/src/test/resources/uk/ac/ic/wlgitbridge/WLGitBridgeIntegrationTest/masterBranchSurvivesSwap/state/222222222222222222222222/editor-versions-a7e4de19d015c3e7477e3f7eaa6c418e.png new file mode 100644 index 0000000000..7fa339be7a Binary files /dev/null and b/services/git-bridge/src/test/resources/uk/ac/ic/wlgitbridge/WLGitBridgeIntegrationTest/masterBranchSurvivesSwap/state/222222222222222222222222/editor-versions-a7e4de19d015c3e7477e3f7eaa6c418e.png differ diff --git a/services/git-bridge/src/test/resources/uk/ac/ic/wlgitbridge/WLGitBridgeIntegrationTest/masterBranchSurvivesSwap/state/222222222222222222222222/foo/bar/test.tex b/services/git-bridge/src/test/resources/uk/ac/ic/wlgitbridge/WLGitBridgeIntegrationTest/masterBranchSurvivesSwap/state/222222222222222222222222/foo/bar/test.tex new file mode 100644 index 0000000000..1c05c0183f --- /dev/null +++ b/services/git-bridge/src/test/resources/uk/ac/ic/wlgitbridge/WLGitBridgeIntegrationTest/masterBranchSurvivesSwap/state/222222222222222222222222/foo/bar/test.tex @@ -0,0 +1 @@ +a different one \ No newline at end of file diff --git a/services/git-bridge/src/test/resources/uk/ac/ic/wlgitbridge/WLGitBridgeIntegrationTest/masterBranchSurvivesSwap/state/222222222222222222222222/main.tex b/services/git-bridge/src/test/resources/uk/ac/ic/wlgitbridge/WLGitBridgeIntegrationTest/masterBranchSurvivesSwap/state/222222222222222222222222/main.tex new file mode 100644 index 0000000000..802d4bbd2e --- /dev/null +++ b/services/git-bridge/src/test/resources/uk/ac/ic/wlgitbridge/WLGitBridgeIntegrationTest/masterBranchSurvivesSwap/state/222222222222222222222222/main.tex @@ -0,0 +1 @@ +different content diff --git a/services/git-bridge/src/test/resources/uk/ac/ic/wlgitbridge/WLGitBridgeIntegrationTest/masterBranchSurvivesSwap/state/state.json b/services/git-bridge/src/test/resources/uk/ac/ic/wlgitbridge/WLGitBridgeIntegrationTest/masterBranchSurvivesSwap/state/state.json new file mode 100644 index 0000000000..68d8959b7d --- /dev/null +++ b/services/git-bridge/src/test/resources/uk/ac/ic/wlgitbridge/WLGitBridgeIntegrationTest/masterBranchSurvivesSwap/state/state.json @@ -0,0 +1,90 @@ +[ + { + "project": "111111111111111111111111", + "getDoc": { + "versionID": 1, + "createdAt": "2014-11-30T18:40:58.123Z", + "email": "jdleesmiller+1@gmail.com", + "name": "John+1" + }, + "getSavedVers": [ + { + "versionID": 1, + "comment": "added more info on doc GET and error details\n", + "email": "jdleesmiller+1@gmail.com", + "name": "John+1", + "createdAt": "2014-11-30T18:47:01.456Z" + } + ], + "getForVers": [ + { + "versionID": 1, + "srcs": [ + { + "content": "content\n", + "path": "main.tex" + }, + { + "content": "This text is from another file.", + "path": "foo/bar/test.tex" + } + ], + "atts": [ + { + "url": "http://127.0.0.1:4010/state/111111111111111111111111/overleaf-white-410.png", + "path": "overleaf-white-410.png" + } + ] + } + ], + "push": "success", + "postback": { + "type": "success", + "versionID": 2 + } + }, + { + "project": "222222222222222222222222", + "getDoc": { + "versionID": 1, + "createdAt": "2014-11-30T18:40:58.123Z", + "email": "jdleesmiller+1@gmail.com", + "name": "John+1" + }, + "getSavedVers": [ + { + "versionID": 1, + "comment": "added more info on doc GET and error details", + "email": "jdleesmiller+1@gmail.com", + "name": "John+1", + "createdAt": "2014-11-30T18:47:01.456Z" + } + ], + "getForVers": [ + { + "versionID": 1, + "srcs": [ + { + "content": "different content\n", + "path": "main.tex" + }, + { + "content": "a different one", + "path": "foo/bar/test.tex" + } + ], + "atts": [ + { + "url": "http://127.0.0.1:4010/state/222222222222222222222222/editor-versions-a7e4de19d015c3e7477e3f7eaa6c418e.png", + "path": "editor-versions-a7e4de19d015c3e7477e3f7eaa6c418e.png" + } + ] + } + ], + "push": "success", + "postback": { + "type": "success", + "versionID": 2 + } + } +]