fix prefer .git/ over bare detection for repository layout

A non-bare repository whose worktree contains files named HEAD and objects/ (e.g. ~/git/TAOH) was incorrectly classified as bare, causing Store.v to fail on the wrong git directory. Fix by checking for .git/ first: if a .git subdirectory passes is_git_directory, the repository is non-bare regardless of worktree contents. Only fall back to bare detection when .git is absent. Also remove the page header on the root page (now redundant with the root navbar). Add a regression test for the shadowing scenario.

Commit
fbbb2c17d633b37d627b78c35ee13ba521910d20
Author
GPT-5.6 Sol <kiro@amazon.com>
Author date
Committer
Marius Peter <dev@marius-peter.com>
Committer date
Changed files
lib/resolvers.ml
index e549392e..64a01d40 100644..100644
@@ -69,15 +69,14 @@
69 69 let repository_layout_result path =
70 70 let ( let* ) = Result.bind in
71 71 let dotgit = Filename.concat path ".git" in
72 Removed: let* bare = is_git_directory_result path in
73 Removed: if bare then Ok (Some { worktree = path; git_dir = path })
72 Added: let* worktree = is_directory_result path in
73 Added: if not worktree then Ok None
74 74 else
75 Removed: let* worktree = is_directory_result path in
76 Removed: if not worktree then Ok None
75 Added: let* non_bare = is_git_directory_result dotgit in
76 Added: if non_bare then Ok (Some { worktree = path; git_dir = dotgit })
77 77 else
78 Removed: let* non_bare = is_git_directory_result dotgit in
79 Removed: if non_bare then Ok (Some { worktree = path; git_dir = dotgit })
80 Removed: else Ok None
78 Added: let* bare = is_git_directory_result path in
79 Added: if bare then Ok (Some { worktree = path; git_dir = path }) else Ok None
81 80
82 81 let repository_layout path =
83 82 match repository_layout_result path with
lib/views/layout.ml
index 4759b315..b8fcad88 100644..100644
@@ -106,9 +106,9 @@
106 106 (match page_data.repo with
107 107 | None -> rootnav ~title:root_title
108 108 | Some repo -> topnav ~active:page_data.active repo);
109 Removed: page_header
110 Removed: ~has_repo:(Option.is_some page_data.repo)
111 Removed: page_data.title page_data.subtitle;
109 Added: (match page_data.repo with
110 Added: | None -> HTML.null []
111 Added: | Some _ -> page_header ~has_repo:true page_data.title page_data.subtitle);
112 112 div [ id "main" ] page_data.content;
113 113 page_footer user;
114 114 ]
test/test_discovery.ml
index 4aa0f602..384aefac 100644..100644
@@ -62,6 +62,28 @@
62 62 "not a repository" false
63 63 (Ogit.Resolvers.is_repository directory))
64 64
65 Added: let test_layout_non_bare_with_shadowing_files () =
66 Added: with_temp_directory "ogit-repos" (fun root ->
67 Added: let repo = Filename.concat root "TAOH" in
68 Added: Unix.mkdir repo 0o755;
69 Added: make_git_directory (Filename.concat repo ".git");
70 Added: (* Create HEAD file and objects/ directory in the worktree that could
71 Added: confuse bare-repository detection. *)
72 Added: Out_channel.with_open_text (Filename.concat repo "HEAD") (fun ch ->
73 Added: output_string ch "ref: refs/heads/master\n");
74 Added: Unix.mkdir (Filename.concat repo "objects") 0o755;
75 Added: Alcotest.(check bool)
76 Added: "is_repository" true
77 Added: (Ogit.Resolvers.is_repository repo);
78 Added: match Ogit.Resolvers.repository_layout repo with
79 Added: | Some { worktree; git_dir } ->
80 Added: Alcotest.(check string) "worktree" repo worktree;
81 Added: Alcotest.(check string)
82 Added: "git_dir uses .git"
83 Added: (Filename.concat repo ".git")
84 Added: git_dir
85 Added: | None -> Alcotest.fail "expected non-bare layout")
86 Added:
65 87 let test_listing () =
66 88 with_temp_directory "ogit-listing" (fun root ->
67 89 make_git_directory (Filename.concat root "visible.git");
@@ -92,6 +114,8 @@
92 114 Alcotest.test_case "description content" `Quick test_description_content;
93 115 Alcotest.test_case "bare layout" `Quick test_layout_bare;
94 116 Alcotest.test_case "non-bare layout" `Quick test_layout_non_bare;
117 Added: Alcotest.test_case "non-bare with shadowing files" `Quick
118 Added: test_layout_non_bare_with_shadowing_files;
95 119 Alcotest.test_case "not a repository" `Quick test_layout_not_a_repo;
96 120 Alcotest.test_case "listing" `Quick test_listing;
97 121 Alcotest.test_case "fallback branches" `Quick test_fallback_branches;