Mercurial > jhg
comparison hg4j/src/main/java/org/tmatesoft/hg/repo/HgRepository.java @ 213:6ec4af642ba8 gradle
Project uses Gradle for build - actual changes
author | Alexander Kitaev <kitaev@gmail.com> |
---|---|
date | Tue, 10 May 2011 10:52:53 +0200 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
212:edb2e2829352 | 213:6ec4af642ba8 |
---|---|
1 /* | |
2 * Copyright (c) 2010-2011 TMate Software Ltd | |
3 * | |
4 * This program is free software; you can redistribute it and/or modify | |
5 * it under the terms of the GNU General Public License as published by | |
6 * the Free Software Foundation; version 2 of the License. | |
7 * | |
8 * This program is distributed in the hope that it will be useful, | |
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 * GNU General Public License for more details. | |
12 * | |
13 * For information on how to redistribute this software under | |
14 * the terms of a license other than GNU General Public License | |
15 * contact TMate Software at support@hg4j.com | |
16 */ | |
17 package org.tmatesoft.hg.repo; | |
18 | |
19 import java.io.File; | |
20 import java.io.IOException; | |
21 import java.lang.ref.SoftReference; | |
22 import java.util.ArrayList; | |
23 import java.util.Collections; | |
24 import java.util.HashMap; | |
25 import java.util.List; | |
26 | |
27 import org.tmatesoft.hg.internal.ConfigFile; | |
28 import org.tmatesoft.hg.internal.DataAccessProvider; | |
29 import org.tmatesoft.hg.internal.Filter; | |
30 import org.tmatesoft.hg.internal.RelativePathRewrite; | |
31 import org.tmatesoft.hg.internal.RequiresFile; | |
32 import org.tmatesoft.hg.internal.RevlogStream; | |
33 import org.tmatesoft.hg.util.FileIterator; | |
34 import org.tmatesoft.hg.util.FileWalker; | |
35 import org.tmatesoft.hg.util.Path; | |
36 import org.tmatesoft.hg.util.PathRewrite; | |
37 | |
38 | |
39 | |
40 /** | |
41 * Shall be as state-less as possible, all the caching happens outside the repo, in commands/walkers | |
42 * | |
43 * @author Artem Tikhomirov | |
44 * @author TMate Software Ltd. | |
45 */ | |
46 public final class HgRepository { | |
47 | |
48 // if new constants added, consider fixing HgInternals#wrongLocalRevision | |
49 public static final int TIP = -3; | |
50 public static final int BAD_REVISION = Integer.MIN_VALUE; | |
51 public static final int WORKING_COPY = -2; | |
52 | |
53 // temp aux marker method | |
54 public static IllegalStateException notImplemented() { | |
55 return new IllegalStateException("Not implemented"); | |
56 } | |
57 | |
58 private final File repoDir; // .hg folder | |
59 private final String repoLocation; | |
60 private final DataAccessProvider dataAccess; | |
61 private final PathRewrite normalizePath; | |
62 private final PathRewrite dataPathHelper; | |
63 private final PathRewrite repoPathHelper; | |
64 | |
65 private HgChangelog changelog; | |
66 private HgManifest manifest; | |
67 private HgTags tags; | |
68 // XXX perhaps, shall enable caching explicitly | |
69 private final HashMap<Path, SoftReference<RevlogStream>> streamsCache = new HashMap<Path, SoftReference<RevlogStream>>(); | |
70 | |
71 private final org.tmatesoft.hg.internal.Internals impl = new org.tmatesoft.hg.internal.Internals(); | |
72 private HgIgnore ignore; | |
73 private ConfigFile configFile; | |
74 | |
75 HgRepository(String repositoryPath) { | |
76 repoDir = null; | |
77 repoLocation = repositoryPath; | |
78 dataAccess = null; | |
79 dataPathHelper = repoPathHelper = null; | |
80 normalizePath = null; | |
81 } | |
82 | |
83 HgRepository(String repositoryPath, File repositoryRoot) { | |
84 assert ".hg".equals(repositoryRoot.getName()) && repositoryRoot.isDirectory(); | |
85 assert repositoryPath != null; | |
86 assert repositoryRoot != null; | |
87 repoDir = repositoryRoot; | |
88 repoLocation = repositoryPath; | |
89 dataAccess = new DataAccessProvider(); | |
90 final boolean runningOnWindows = System.getProperty("os.name").indexOf("Windows") != -1; | |
91 if (runningOnWindows) { | |
92 normalizePath = new PathRewrite() { | |
93 | |
94 public String rewrite(String path) { | |
95 // TODO handle . and .. (although unlikely to face them from GUI client) | |
96 path = path.replace('\\', '/').replace("//", "/"); | |
97 if (path.startsWith("/")) { | |
98 path = path.substring(1); | |
99 } | |
100 return path; | |
101 } | |
102 }; | |
103 } else { | |
104 normalizePath = new PathRewrite.Empty(); // or strip leading slash, perhaps? | |
105 } | |
106 parseRequires(); | |
107 dataPathHelper = impl.buildDataFilesHelper(); | |
108 repoPathHelper = impl.buildRepositoryFilesHelper(); | |
109 } | |
110 | |
111 @Override | |
112 public String toString() { | |
113 return getClass().getSimpleName() + "[" + getLocation() + (isInvalid() ? "(BAD)" : "") + "]"; | |
114 } | |
115 | |
116 public String getLocation() { | |
117 return repoLocation; | |
118 } | |
119 | |
120 public boolean isInvalid() { | |
121 return repoDir == null || !repoDir.exists() || !repoDir.isDirectory(); | |
122 } | |
123 | |
124 public HgChangelog getChangelog() { | |
125 if (this.changelog == null) { | |
126 String storagePath = repoPathHelper.rewrite("00changelog.i"); | |
127 RevlogStream content = resolve(Path.create(storagePath), true); | |
128 this.changelog = new HgChangelog(this, content); | |
129 } | |
130 return this.changelog; | |
131 } | |
132 | |
133 public HgManifest getManifest() { | |
134 if (this.manifest == null) { | |
135 RevlogStream content = resolve(Path.create(repoPathHelper.rewrite("00manifest.i")), true); | |
136 this.manifest = new HgManifest(this, content); | |
137 } | |
138 return this.manifest; | |
139 } | |
140 | |
141 public final HgTags getTags() { | |
142 if (tags == null) { | |
143 tags = new HgTags(); | |
144 try { | |
145 tags.readGlobal(new File(repoDir.getParentFile(), ".hgtags")); | |
146 tags.readLocal(new File(repoDir, "localtags")); | |
147 } catch (IOException ex) { | |
148 ex.printStackTrace(); // FIXME log or othewise report | |
149 } | |
150 } | |
151 return tags; | |
152 } | |
153 | |
154 public HgDataFile getFileNode(String path) { | |
155 String nPath = normalizePath.rewrite(path); | |
156 String storagePath = dataPathHelper.rewrite(nPath); | |
157 RevlogStream content = resolve(Path.create(storagePath), false); | |
158 Path p = Path.create(nPath); | |
159 if (content == null) { | |
160 return new HgDataFile(this, p); | |
161 } | |
162 return new HgDataFile(this, p, content); | |
163 } | |
164 | |
165 public HgDataFile getFileNode(Path path) { | |
166 String storagePath = dataPathHelper.rewrite(path.toString()); | |
167 RevlogStream content = resolve(Path.create(storagePath), false); | |
168 // XXX no content when no file? or HgDataFile.exists() to detect that? | |
169 if (content == null) { | |
170 return new HgDataFile(this, path); | |
171 } | |
172 return new HgDataFile(this, path, content); | |
173 } | |
174 | |
175 /* clients need to rewrite path from their FS to a repository-friendly paths, and, perhaps, vice versa*/ | |
176 public PathRewrite getToRepoPathHelper() { | |
177 return normalizePath; | |
178 } | |
179 | |
180 // local to hide use of io.File. | |
181 /*package-local*/ File getRepositoryRoot() { | |
182 return repoDir; | |
183 } | |
184 | |
185 // XXX package-local, unless there are cases when required from outside (guess, working dir/revision walkers may hide dirstate access and no public visibility needed) | |
186 /*package-local*/ final HgDirstate loadDirstate() { | |
187 return new HgDirstate(getDataAccess(), new File(repoDir, "dirstate")); | |
188 } | |
189 | |
190 // package-local, see comment for loadDirstate | |
191 /*package-local*/ final HgIgnore getIgnore() { | |
192 // TODO read config for additional locations | |
193 if (ignore == null) { | |
194 ignore = new HgIgnore(); | |
195 try { | |
196 File ignoreFile = new File(repoDir.getParentFile(), ".hgignore"); | |
197 ignore.read(ignoreFile); | |
198 } catch (IOException ex) { | |
199 ex.printStackTrace(); // log warn | |
200 } | |
201 } | |
202 return ignore; | |
203 } | |
204 | |
205 /*package-local*/ DataAccessProvider getDataAccess() { | |
206 return dataAccess; | |
207 } | |
208 | |
209 // FIXME not sure repository shall create walkers | |
210 /*package-local*/ FileIterator createWorkingDirWalker() { | |
211 File repoRoot = repoDir.getParentFile(); | |
212 Path.Source pathSrc = new Path.SimpleSource(new PathRewrite.Composite(new RelativePathRewrite(repoRoot), getToRepoPathHelper())); | |
213 // Impl note: simple source is enough as files in the working dir are all unique | |
214 // even if they might get reused (i.e. after FileIterator#reset() and walking once again), | |
215 // path caching is better to be done in the code which knows that path are being reused | |
216 return new FileWalker(repoRoot, pathSrc); | |
217 } | |
218 | |
219 /** | |
220 * Perhaps, should be separate interface, like ContentLookup | |
221 * path - repository storage path (i.e. one usually with .i or .d) | |
222 */ | |
223 /*package-local*/ RevlogStream resolve(Path path, boolean shallFakeNonExistent) { | |
224 final SoftReference<RevlogStream> ref = streamsCache.get(path); | |
225 RevlogStream cached = ref == null ? null : ref.get(); | |
226 if (cached != null) { | |
227 return cached; | |
228 } | |
229 File f = new File(repoDir, path.toString()); | |
230 if (f.exists()) { | |
231 RevlogStream s = new RevlogStream(dataAccess, f); | |
232 streamsCache.put(path, new SoftReference<RevlogStream>(s)); | |
233 return s; | |
234 } else { | |
235 if (shallFakeNonExistent) { | |
236 try { | |
237 File fake = File.createTempFile(f.getName(), null); | |
238 fake.deleteOnExit(); | |
239 return new RevlogStream(dataAccess, fake); | |
240 } catch (IOException ex) { | |
241 ex.printStackTrace(); // FIXME report in debug | |
242 } | |
243 } | |
244 } | |
245 return null; // XXX empty stream instead? | |
246 } | |
247 | |
248 // can't expose internal class, otherwise seems reasonable to have it in API | |
249 /*package-local*/ ConfigFile getConfigFile() { | |
250 if (configFile == null) { | |
251 configFile = impl.newConfigFile(); | |
252 configFile.addLocation(new File(System.getProperty("user.home"), ".hgrc")); | |
253 // last one, overrides anything else | |
254 // <repo>/.hg/hgrc | |
255 configFile.addLocation(new File(getRepositoryRoot(), "hgrc")); | |
256 } | |
257 return configFile; | |
258 } | |
259 | |
260 /*package-local*/ List<Filter> getFiltersFromRepoToWorkingDir(Path p) { | |
261 return instantiateFilters(p, new Filter.Options(Filter.Direction.FromRepo)); | |
262 } | |
263 | |
264 /*package-local*/ List<Filter> getFiltersFromWorkingDirToRepo(Path p) { | |
265 return instantiateFilters(p, new Filter.Options(Filter.Direction.ToRepo)); | |
266 } | |
267 | |
268 private List<Filter> instantiateFilters(Path p, Filter.Options opts) { | |
269 List<Filter.Factory> factories = impl.getFilters(this, getConfigFile()); | |
270 if (factories.isEmpty()) { | |
271 return Collections.emptyList(); | |
272 } | |
273 ArrayList<Filter> rv = new ArrayList<Filter>(factories.size()); | |
274 for (Filter.Factory ff : factories) { | |
275 Filter f = ff.create(p, opts); | |
276 if (f != null) { | |
277 rv.add(f); | |
278 } | |
279 } | |
280 return rv; | |
281 } | |
282 | |
283 private void parseRequires() { | |
284 new RequiresFile().parse(impl, new File(repoDir, "requires")); | |
285 } | |
286 | |
287 } |