1 /*******************************************************************************
2 * Copyright (C) 2019 Thomas Wolf <thomas.wolf@paranor.ch>
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License 2.0
6 * which accompanies this distribution, and is available at
7 * https://www.eclipse.org/legal/epl-2.0/
9 * SPDX-License-Identifier: EPL-2.0
10 *******************************************************************************/
11 package org
.eclipse
.egit
.ui
.internal
;
14 import java
.io
.IOException
;
16 import java
.util
.concurrent
.ConcurrentHashMap
;
18 import org
.eclipse
.egit
.core
.UnitOfWork
;
19 import org
.eclipse
.egit
.ui
.Activator
;
20 import org
.eclipse
.jgit
.annotations
.NonNull
;
21 import org
.eclipse
.jgit
.lib
.Constants
;
22 import org
.eclipse
.jgit
.lib
.ObjectId
;
23 import org
.eclipse
.jgit
.lib
.Ref
;
24 import org
.eclipse
.jgit
.lib
.Repository
;
25 import org
.eclipse
.jgit
.lib
.RepositoryState
;
26 import org
.eclipse
.jgit
.lib
.StoredConfig
;
27 import org
.eclipse
.jgit
.revwalk
.RevCommit
;
28 import org
.eclipse
.jgit
.revwalk
.RevWalk
;
31 * A cache of some state of repositories. Concrete subclasses are responsible
32 * for clearing this cache in response to some event.
34 public abstract class RepositoryStateCache
{
36 private enum RepositoryItem
{
37 CONFIG
, HEAD
, HEAD_REF
, HEAD_COMMIT
, FULL_BRANCH_NAME
, STATE
40 /** "null" marker in the maps. */
41 private static final Object NOTHING
= new Object();
43 private final Map
<File
, Map
<RepositoryItem
, Object
>> cache
= new ConcurrentHashMap
<>();
46 * Initializes the {@link RepositoryStateCache} and makes it listen to changes
47 * that may affect the cache.
49 public abstract void initialize();
52 * Disposes the {@link RepositoryStateCache}.
54 public void dispose() {
59 * Completely clears the cache.
66 * Clears all cached entries for the given {@link Repository}.
69 * to remove cached items of
71 public void clear(Repository repository
) {
72 cache
.remove(repository
.getDirectory());
75 private Map
<RepositoryItem
, Object
> getItems(Repository repository
) {
76 return cache
.computeIfAbsent(repository
.getDirectory(),
77 gitDir
-> new ConcurrentHashMap
<>());
81 * Retrieves the given repository's {@link StoredConfig}.
84 * @return the {@link StoredConfig} of the repository
86 public StoredConfig
getConfig(Repository repository
) {
87 Object value
= getItems(repository
).computeIfAbsent(
88 RepositoryItem
.CONFIG
, key
-> repository
.getConfig());
89 return (StoredConfig
) value
;
92 private ObjectId
getHead(Repository repository
,
93 String
[] fullName
, Ref
[] ref
) {
94 return UnitOfWork
.get(repository
, () -> {
95 ObjectId head
= ObjectId
.zeroId();
99 r
= repository
.exactRef(Constants
.HEAD
);
100 } catch (IOException e
) {
101 Activator
.logError(e
.getLocalizedMessage(), e
);
105 if (r
.isSymbolic()) {
106 name
= r
.getTarget().getName();
108 head
= r
.getObjectId();
114 head
= ObjectId
.zeroId();
117 fullName
[0] = name
!= null ? name
: ""; //$NON-NLS-1$
123 * Retrieves the {@link ObjectId} of the current HEAD.
126 * @return ObjectId of HEAD, or {@code null} if none
128 public ObjectId
getHead(Repository repository
) {
129 if (repository
== null) {
132 Map
<RepositoryItem
, Object
> items
= getItems(repository
);
133 Object value
= items
.get(RepositoryItem
.HEAD
);
135 String
[] fullName
= { null };
136 Ref
[] headRef
= { null };
137 value
= items
.computeIfAbsent(RepositoryItem
.HEAD
,
138 key
-> getHead(repository
, fullName
, headRef
));
139 items
.computeIfAbsent(RepositoryItem
.FULL_BRANCH_NAME
,
141 items
.computeIfAbsent(RepositoryItem
.HEAD_REF
,
142 key
-> headRef
[0] == null ? NOTHING
: headRef
[0]);
144 ObjectId head
= (ObjectId
) value
;
145 if (head
== null || head
.equals(ObjectId
.zeroId())) {
153 * Retrieves the current HEAD ref.
156 * @return the HEAD ref, or {@code null} if none
158 public Ref
getHeadRef(Repository repository
) {
159 if (repository
== null) {
162 Map
<RepositoryItem
, Object
> items
= getItems(repository
);
163 Object value
= items
.get(RepositoryItem
.HEAD_REF
);
165 String
[] fullName
= { null };
166 Ref
[] headRef
= { null };
167 items
.computeIfAbsent(RepositoryItem
.HEAD
,
168 key
-> getHead(repository
, fullName
, headRef
));
169 items
.computeIfAbsent(RepositoryItem
.FULL_BRANCH_NAME
,
171 value
= items
.computeIfAbsent(RepositoryItem
.HEAD_REF
,
172 key
-> headRef
[0] == null ? NOTHING
: headRef
[0]);
174 if (value
== null || value
== NOTHING
) {
181 * Retrieves the current HEAD commit of the repository.
184 * @return the commit, or {@code null} if none could be determined
186 public RevCommit
getHeadCommit(Repository repository
) {
187 if (repository
== null) {
190 Map
<RepositoryItem
, Object
> items
= getItems(repository
);
191 Object value
= items
.get(RepositoryItem
.HEAD_COMMIT
);
193 ObjectId headId
= getHead(repository
);
194 if (headId
!= null) {
195 try (RevWalk w
= new RevWalk(repository
)) {
196 RevCommit commit
= w
.parseCommit(headId
);
197 items
.put(RepositoryItem
.HEAD_COMMIT
, commit
);
199 } catch (IOException e
) {
203 items
.put(RepositoryItem
.HEAD_COMMIT
, NOTHING
);
205 } else if (value
== NOTHING
) {
208 return (RevCommit
) value
;
212 * Retrieves the full name of the current branch.
215 * @return the full branch name
217 public String
getFullBranchName(Repository repository
) {
218 if (repository
== null) {
221 Map
<RepositoryItem
, Object
> items
= getItems(repository
);
222 Object fullBranchName
= items
.get(RepositoryItem
.FULL_BRANCH_NAME
);
223 if (fullBranchName
== null) {
224 String
[] fullName
= { null };
225 Ref
[] headRef
= { null };
226 items
.computeIfAbsent(RepositoryItem
.HEAD
,
227 key
-> getHead(repository
, fullName
, headRef
));
228 fullBranchName
= items
.computeIfAbsent(
229 RepositoryItem
.FULL_BRANCH_NAME
, key
-> fullName
[0]);
230 items
.computeIfAbsent(RepositoryItem
.HEAD_REF
,
231 key
-> headRef
[0] == null ? NOTHING
: headRef
[0]);
233 String name
= (String
) fullBranchName
;
234 if (name
== null || name
.isEmpty()) {
241 * Retrieves the repository state.
244 * @return the {@link RepositoryState}
246 public @NonNull RepositoryState
getRepositoryState(Repository repository
) {
247 RepositoryState state
= UnitOfWork
.get(repository
, () -> {
248 Object value
= getItems(repository
).computeIfAbsent(
249 RepositoryItem
.STATE
,
250 key
-> repository
.getRepositoryState());
251 return (RepositoryState
) value
;
253 assert state
!= null; // Keep the compiler happy.