Auto-fill repository name from URL when left blank

Derive the repository name from the clone URL by extracting the
last path segment and stripping the .git suffix. Applied to the
web controller, GitService, and telnet interface. The telnet
prompt now shows the default name in brackets.

Also fix flaky contextLoads test by using RANDOM_PORT.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-02-27 07:41:33 +01:00
parent c3424362d4
commit 6e98901b9c
7 changed files with 109 additions and 9 deletions

View File

@@ -52,8 +52,10 @@ public class HomeController
}
@PostMapping("/clone")
public String cloneRepo(@RequestParam String url, @RequestParam String name) throws GitAPIException, IOException
public String cloneRepo(@RequestParam String url, @RequestParam(required = false) String name) throws GitAPIException, IOException
{
if (name == null || name.isBlank())
name = gitService.deriveRepositoryName(url);
gitService.cloneRepository(url, name);
return "redirect:/?repo=" + name;
}

View File

@@ -22,8 +22,20 @@ public class GitService
{
private final WebgitProperties properties;
public String deriveRepositoryName(String url)
{
String path = url.replaceAll("[/\\\\]+$", "");
int lastSep = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\'));
String base = lastSep >= 0 ? path.substring(lastSep + 1) : path;
if (base.endsWith(".git"))
base = base.substring(0, base.length() - 4);
return base;
}
public void cloneRepository(String url, String name) throws GitAPIException, IOException
{
if (name == null || name.isBlank())
name = deriveRepositoryName(url);
Path worktree = properties.getWorktreePath().resolve(name);
Path gitDir = properties.getGitDirPath().resolve(name);

View File

@@ -88,11 +88,12 @@ public class TelnetSession implements Runnable
if (url == null || url.isBlank())
return;
out.print("Name: ");
String defaultName = gitService.deriveRepositoryName(url.trim());
out.print("Name [" + defaultName + "]: ");
out.flush();
String name = in.readLine();
if (name == null || name.isBlank())
return;
name = defaultName;
gitService.cloneRepository(url.trim(), name.trim());
out.println("Cloned successfully.");

View File

@@ -2,8 +2,10 @@ package be.seeseepuff.webgit;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;
@SpringBootTest
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestPropertySource(properties = "webgit.telnet.enabled=false")
class WebgitApplicationTests {
@Test

View File

@@ -115,4 +115,31 @@ class HomeControllerTest
verify(gitService).cloneRepository("https://example.com/repo.git", "myrepo");
}
@Test
void cloneWithEmptyNameDerivesFromUrl() throws Exception
{
when(gitService.deriveRepositoryName("https://example.com/repo.git")).thenReturn("repo");
mockMvc.perform(post("/clone")
.param("url", "https://example.com/repo.git")
.param("name", ""))
.andExpect(status().is3xxRedirection())
.andExpect(redirectedUrl("/?repo=repo"));
verify(gitService).cloneRepository("https://example.com/repo.git", "repo");
}
@Test
void cloneWithNoNameParamDerivesFromUrl() throws Exception
{
when(gitService.deriveRepositoryName("https://example.com/project.git")).thenReturn("project");
mockMvc.perform(post("/clone")
.param("url", "https://example.com/project.git"))
.andExpect(status().is3xxRedirection())
.andExpect(redirectedUrl("/?repo=project"));
verify(gitService).cloneRepository("https://example.com/project.git", "project");
}
}

View File

@@ -250,4 +250,53 @@ class GitServiceTest
assertDoesNotThrow(() -> gitService.pull("myrepo"));
}
@Test
void deriveNameFromHttpsUrl()
{
assertEquals("my-project", gitService.deriveRepositoryName("https://github.com/user/my-project.git"));
}
@Test
void deriveNameFromUrlWithoutGitSuffix()
{
assertEquals("repo", gitService.deriveRepositoryName("https://github.com/user/repo"));
}
@Test
void deriveNameFromUrlWithTrailingSlash()
{
assertEquals("repo", gitService.deriveRepositoryName("https://github.com/user/repo/"));
}
@Test
void deriveNameFromSshUrl()
{
assertEquals("project", gitService.deriveRepositoryName("git@github.com:user/project.git"));
}
@Test
void deriveNameFromBareName()
{
assertEquals("foo", gitService.deriveRepositoryName("foo.git"));
}
@Test
void cloneWithBlankNameDerivesFromUrl() throws GitAPIException, IOException
{
gitService.cloneRepository(bareRemote.toUri().toString(), "");
String expectedName = gitService.deriveRepositoryName(bareRemote.toUri().toString());
assertTrue(Files.exists(worktreePath.resolve(expectedName)));
assertTrue(Files.exists(gitDirPath.resolve(expectedName)));
}
@Test
void cloneWithNullNameDerivesFromUrl() throws GitAPIException, IOException
{
gitService.cloneRepository(bareRemote.toUri().toString(), null);
String expectedName = gitService.deriveRepositoryName(bareRemote.toUri().toString());
assertTrue(Files.exists(worktreePath.resolve(expectedName)));
}
}

View File

@@ -72,7 +72,9 @@ class TelnetSessionTest
@Test
void cloneRepository() throws IOException, GitAPIException
{
when(gitService.deriveRepositoryName("https://example.com/repo.git")).thenReturn("repo");
String output = runSession("2\nhttps://example.com/repo.git\nmyrepo\nq\n");
assertTrue(output.contains("Name [repo]:"));
assertTrue(output.contains("Cloned successfully."));
verify(gitService).cloneRepository("https://example.com/repo.git", "myrepo");
}
@@ -85,10 +87,13 @@ class TelnetSessionTest
}
@Test
void cloneRepositoryEmptyName()
void cloneRepositoryEmptyNameUsesDefault() throws IOException, GitAPIException
{
when(gitService.deriveRepositoryName("https://example.com/repo.git")).thenReturn("repo");
String output = runSession("2\nhttps://example.com/repo.git\n\nq\n");
assertFalse(output.contains("Cloned successfully."));
assertTrue(output.contains("Name [repo]:"));
assertTrue(output.contains("Cloned successfully."));
verify(gitService).cloneRepository("https://example.com/repo.git", "repo");
}
@Test
@@ -521,10 +526,12 @@ class TelnetSessionTest
}
@Test
void cloneNullName()
void cloneNullName() throws IOException, GitAPIException
{
// URL is provided but name input stream ends
// URL is provided but name input stream ends — uses default
when(gitService.deriveRepositoryName("https://example.com/repo.git")).thenReturn("repo");
String output = runSession("2\nhttps://example.com/repo.git\n");
assertTrue(output.contains("Goodbye!"));
assertTrue(output.contains("Cloned successfully."));
verify(gitService).cloneRepository("https://example.com/repo.git", "repo");
}
}