From 321e2685302cb6c7f976409bd18cf92b4981a6fb Mon Sep 17 00:00:00 2001 From: Sebastiaan de Schaetzen Date: Fri, 27 Feb 2026 10:05:41 +0100 Subject: [PATCH] Add Rollback Selected button to staging page Rollback restores selected unstaged files to their HEAD state using git checkout. The button shares the form with Stage Selected, distinguished by the submit button's name/value pair. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../webgit/controller/RepoController.java | 8 ++++++-- .../be/seeseepuff/webgit/service/GitService.java | 13 +++++++++++++ src/main/resources/templates/changes.html | 3 ++- .../webgit/controller/RepoControllerTest.java | 12 ++++++++++++ .../seeseepuff/webgit/service/GitServiceTest.java | 12 ++++++++++++ 5 files changed, 45 insertions(+), 3 deletions(-) diff --git a/src/main/java/be/seeseepuff/webgit/controller/RepoController.java b/src/main/java/be/seeseepuff/webgit/controller/RepoController.java index 9e8570c..69516de 100644 --- a/src/main/java/be/seeseepuff/webgit/controller/RepoController.java +++ b/src/main/java/be/seeseepuff/webgit/controller/RepoController.java @@ -146,9 +146,13 @@ public class RepoController } @PostMapping("/repo/{name}/stage") - public String stage(@PathVariable String name, @RequestParam List files) throws IOException, GitAPIException + public String stage(@PathVariable String name, @RequestParam List files, + @RequestParam(defaultValue = "Stage Selected") String action) throws IOException, GitAPIException { - gitService.stageFiles(name, files); + if ("Rollback Selected".equals(action)) + gitService.rollbackFiles(name, files); + else + gitService.stageFiles(name, files); return "redirect:/repo/" + name + "/changes"; } diff --git a/src/main/java/be/seeseepuff/webgit/service/GitService.java b/src/main/java/be/seeseepuff/webgit/service/GitService.java index b6f5890..510794a 100644 --- a/src/main/java/be/seeseepuff/webgit/service/GitService.java +++ b/src/main/java/be/seeseepuff/webgit/service/GitService.java @@ -266,6 +266,19 @@ public class GitService } } + public void rollbackFiles(String name, List files) throws IOException, GitAPIException + { + try (Git git = openRepository(name)) + { + var checkout = git.checkout().setStartPoint("HEAD"); + for (String file : files) + { + checkout.addPath(file); + } + checkout.call(); + } + } + public void commit(String name, String message) throws IOException, GitAPIException { try (Git git = openRepository(name)) diff --git a/src/main/resources/templates/changes.html b/src/main/resources/templates/changes.html index b167e87..8c25014 100644 --- a/src/main/resources/templates/changes.html +++ b/src/main/resources/templates/changes.html @@ -19,7 +19,8 @@
- + +

No modified files.

diff --git a/src/test/java/be/seeseepuff/webgit/controller/RepoControllerTest.java b/src/test/java/be/seeseepuff/webgit/controller/RepoControllerTest.java index d9bf246..b6c477a 100644 --- a/src/test/java/be/seeseepuff/webgit/controller/RepoControllerTest.java +++ b/src/test/java/be/seeseepuff/webgit/controller/RepoControllerTest.java @@ -139,6 +139,18 @@ class RepoControllerTest verify(gitService).stageFiles("myrepo", List.of("a.txt", "b.txt")); } + @Test + void rollbackRedirectsToChanges() throws Exception + { + mockMvc.perform(post("/repo/myrepo/stage") + .param("files", "a.txt") + .param("action", "Rollback Selected")) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("/repo/myrepo/changes")); + + verify(gitService).rollbackFiles("myrepo", List.of("a.txt")); + } + @Test void unstageRedirectsToChanges() throws Exception { diff --git a/src/test/java/be/seeseepuff/webgit/service/GitServiceTest.java b/src/test/java/be/seeseepuff/webgit/service/GitServiceTest.java index 7e11665..96e8927 100644 --- a/src/test/java/be/seeseepuff/webgit/service/GitServiceTest.java +++ b/src/test/java/be/seeseepuff/webgit/service/GitServiceTest.java @@ -451,4 +451,16 @@ class GitServiceTest String diff = gitService.getWorkingTreeDiff("myrepo", "README.md"); assertTrue(diff.isEmpty()); } + + @Test + void rollbackFilesRestoresContent() throws GitAPIException, IOException + { + gitService.cloneRepository(bareRemote.toUri().toString(), "myrepo"); + Path readme = worktreePath.resolve("myrepo").resolve("README.md"); + Files.writeString(readme, "modified"); + + gitService.rollbackFiles("myrepo", List.of("README.md")); + + assertEquals("# Test", Files.readString(readme)); + } }