This is an automated email from the ASF dual-hosted git repository.

luigidemasi pushed a commit to branch test/133-local-git-fixtures
in repository https://gitbox.apache.org/repos/asf/camel-kit.git

commit 2294f7effacb425322c9b77c604c473689e8ffa8
Author: Luigi De Masi <[email protected]>
AuthorDate: Thu Oct 29 11:45:55 2026 +0100

    fix: recover Git caches with invalid repository metadata
    
    Re-clone cached document repositories when opening their .git directory
    fails, with a regression for an interrupted clone that left an empty .git.
    Use local file transport for GitRepoFetcher fixtures, keep explicit signing
    overrides, and remove redundant peeled-tag filtering. Clarify that the
    shallow assertion checks history from HEAD.
    
    Follow-up to #133.
    
    Co-authored-by: OpenAI Codex <[email protected]>
---
 knowledge/CHANGELOG.md                             |  5 +++
 .../kit/knowledge/indexer/GitRepoFetcher.java      |  6 +--
 .../kit/knowledge/indexer/GitRepoFetcherTest.java  | 52 +++++++++++-----------
 3 files changed, 33 insertions(+), 30 deletions(-)

diff --git a/knowledge/CHANGELOG.md b/knowledge/CHANGELOG.md
index e839ec8c..75507626 100644
--- a/knowledge/CHANGELOG.md
+++ b/knowledge/CHANGELOG.md
@@ -8,6 +8,11 @@ and this project adheres to [Semantic 
Versioning](https://semver.org/spec/v2.0.0
 
 ## [Unreleased]
 
+### Fixed
+
+- Recover cached document repositories with an invalid `.git` directory by 
re-cloning
+  them instead of failing every indexing attempt.
+
 ### Monorepo migration
 
 - Move Kit and Knowledge into sibling `kit/` and `knowledge/` directories with 
a shared,
diff --git 
a/knowledge/indexer/src/main/java/org/apache/camel/kit/knowledge/indexer/GitRepoFetcher.java
 
b/knowledge/indexer/src/main/java/org/apache/camel/kit/knowledge/indexer/GitRepoFetcher.java
index 1119b41d..16fb4b8e 100644
--- 
a/knowledge/indexer/src/main/java/org/apache/camel/kit/knowledge/indexer/GitRepoFetcher.java
+++ 
b/knowledge/indexer/src/main/java/org/apache/camel/kit/knowledge/indexer/GitRepoFetcher.java
@@ -38,6 +38,7 @@ public class GitRepoFetcher {
      *
      * <ul>
      * <li>If repo exists with .git → fetch + hard reset to latest (fast, only 
downloads delta)</li>
+     * <li>If opening or updating the existing repo fails → delete and 
re-clone</li>
      * <li>If repo exists without .git (corrupted) → delete and re-clone</li>
      * <li>If repo doesn't exist → fresh shallow clone</li>
      * </ul>
@@ -92,7 +93,7 @@ public class GitRepoFetcher {
      * Lists all tags from a remote Git repository without cloning it. Uses 
JGit's {@code ls-remote --tags} equivalent.
      *
      * @param  repoUrl the remote repository URL
-     * @return         tag names without {@code refs/tags/} prefix, excluding 
dereferenced {@code ^{}} entries
+     * @return         tag names without {@code refs/tags/} prefix
      */
     public static List<String> listRemoteTags(String repoUrl) throws 
IOException {
         try {
@@ -103,7 +104,6 @@ public class GitRepoFetcher {
 
             return refs.stream()
                     .map(ref -> ref.getName().replaceFirst("^refs/tags/", ""))
-                    .filter(name -> !name.endsWith("^{}"))
                     .toList();
         } catch (GitAPIException e) {
             throw new IOException("Failed to list tags from " + repoUrl, e);
@@ -135,7 +135,7 @@ public class GitRepoFetcher {
             long elapsed = (System.currentTimeMillis() - start) / 1000;
             LOG.info("  Updated: {} ({}s)", localName, elapsed);
 
-        } catch (GitAPIException e) {
+        } catch (GitAPIException | IOException e) {
             // Update failed — fall back to fresh clone
             LOG.warn("  Update failed for {}: {} — re-cloning", localName, 
e.getMessage());
             deleteDirectory(repoDir);
diff --git 
a/knowledge/indexer/src/test/java/org/apache/camel/kit/knowledge/indexer/GitRepoFetcherTest.java
 
b/knowledge/indexer/src/test/java/org/apache/camel/kit/knowledge/indexer/GitRepoFetcherTest.java
index 9d8043d9..a6307446 100644
--- 
a/knowledge/indexer/src/test/java/org/apache/camel/kit/knowledge/indexer/GitRepoFetcherTest.java
+++ 
b/knowledge/indexer/src/test/java/org/apache/camel/kit/knowledge/indexer/GitRepoFetcherTest.java
@@ -1,7 +1,6 @@
 package org.apache.camel.kit.knowledge.indexer;
 
 import java.io.IOException;
-import java.net.InetSocketAddress;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.List;
@@ -10,8 +9,6 @@ import org.eclipse.jgit.api.Git;
 import org.eclipse.jgit.api.errors.GitAPIException;
 import org.eclipse.jgit.lib.PersonIdent;
 import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.transport.Daemon;
-import org.eclipse.jgit.transport.resolver.FileResolver;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -29,7 +26,6 @@ class GitRepoFetcherTest {
     Path tempDir;
 
     private Git upstream;
-    private Daemon daemon;
     private String repoUrl;
     private GitRepoFetcher fetcher;
     private RevCommit latest;
@@ -37,11 +33,6 @@ class GitRepoFetcherTest {
     @BeforeEach
     void createLocalRepository() throws Exception {
         upstream = 
Git.init().setDirectory(tempDir.resolve("upstream").toFile()).setInitialBranch("main").call();
-        var config = upstream.getRepository().getConfig();
-        config.setBoolean("commit", null, "gpgSign", false);
-        config.setBoolean("tag", null, "gpgSign", false);
-        config.setBoolean("tag", null, "forceSignAnnotated", false);
-        config.save();
         writeUpstream("document.md", "first version");
         writeUpstream("notes.txt", "not a Markdown document");
         writeUpstream("nested/other.md", "nested document");
@@ -51,25 +42,14 @@ class GitRepoFetcherTest {
         latest = commit("second version");
         tag("v2");
 
-        // The local file transport cannot exercise the shallow-clone protocol.
-        daemon = new Daemon(new InetSocketAddress("127.0.0.1", 0));
-        daemon.setRepositoryResolver(new FileResolver<>(tempDir.toFile(), 
true));
-        daemon.setTimeout(10);
-        daemon.start();
-        repoUrl = "git://127.0.0.1:" + daemon.getAddress().getPort() + 
"/upstream";
+        repoUrl = tempDir.resolve("upstream").toUri().toString();
         fetcher = new GitRepoFetcher(tempDir.resolve("cache"));
     }
 
     @AfterEach
-    void closeLocalRepository() throws Exception {
-        try {
-            if (daemon != null) {
-                daemon.stopAndWait();
-            }
-        } finally {
-            if (upstream != null) {
-                upstream.close();
-            }
+    void closeLocalRepository() {
+        if (upstream != null) {
+            upstream.close();
         }
     }
 
@@ -84,7 +64,7 @@ class GitRepoFetcherTest {
             
assertTrue(clone.getRepository().getObjectDatabase().getShallowCommits().contains(latest.getId()));
             var history = clone.log().call().iterator();
             assertEquals(latest.getId(), history.next().getId());
-            assertFalse(history.hasNext(), "The clone must not contain the 
first commit's history");
+            assertFalse(history.hasNext(), "History from HEAD must stop at the 
shallow boundary");
         }
     }
 
@@ -145,8 +125,9 @@ class GitRepoFetcherTest {
         Path repoDir = fetcher.fetchRepo(repoUrl, "v1", "documents", true);
         Path sentinel = repoDir.resolve(".git/retained-on-cache-hit");
         Files.writeString(sentinel, "cached repository");
-        daemon.stopAndWait();
-        assertFalse(daemon.isRunning());
+        upstream.close();
+        upstream = null;
+        Files.move(tempDir.resolve("upstream"), 
tempDir.resolve("offline-upstream"));
 
         assertEquals(repoDir, fetcher.fetchRepo(repoUrl, "v1", "documents", 
true));
 
@@ -184,6 +165,23 @@ class GitRepoFetcherTest {
         assertFalse(Files.exists(partial));
     }
 
+    @Test
+    void recoversCloneWithInvalidGitDirectory() throws Exception {
+        Path repoDir = tempDir.resolve("cache/documents");
+        Files.createDirectories(repoDir.resolve(".git"));
+        Path partial = repoDir.resolve("partial.md");
+        Files.writeString(partial, "interrupted clone");
+
+        assertEquals(repoDir, fetcher.fetchRepo(repoUrl, "main", "documents"));
+
+        assertEquals("second version", 
Files.readString(repoDir.resolve("document.md")));
+        assertFalse(Files.exists(partial));
+        try (Git clone = Git.open(repoDir.toFile())) {
+            assertEquals(latest.getId(), 
clone.getRepository().resolve("HEAD"));
+            assertTrue(clone.status().call().isClean());
+        }
+    }
+
     @Test
     void reclonesFromSuppliedUrlAfterUpdateFails() throws Exception {
         Path repoDir = fetcher.fetchRepo(repoUrl, "main", "documents");

Reply via email to