comparison src/org/tmatesoft/hg/repo/HgRepository.java @ 493:ba36f66c32b4

Refactor to keep knowledge about repository control files and their location in respect to .hg/ in a single place (facilitate future adoption of shared repositories)
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Thu, 18 Oct 2012 18:36:13 +0200
parents 4a670f76e7d1
children d2f6ab541330
comparison
equal deleted inserted replaced
492:e4eaa23e3442 493:ba36f66c32b4
97 } 97 }
98 98
99 private final File repoDir; // .hg folder 99 private final File repoDir; // .hg folder
100 private final File workingDir; // .hg/../ 100 private final File workingDir; // .hg/../
101 private final String repoLocation; 101 private final String repoLocation;
102 private final PathRewrite normalizePath; // normalized slashes but otherwise regular file names 102 /*
103 private final PathRewrite dataPathHelper; // access to file storage area (usually under .hg/store/data/), with filenames mangled 103 * normalized slashes but otherwise regular file names
104 private final PathRewrite repoPathHelper; // access to system files 104 * the only front-end path rewrite, kept here as rest of the library shall
105 * not bother with names normalization.
106 */
107 private final PathRewrite normalizePath;
105 private final SessionContext sessionContext; 108 private final SessionContext sessionContext;
106 109
107 private HgChangelog changelog; 110 private HgChangelog changelog;
108 private HgManifest manifest; 111 private HgManifest manifest;
109 private HgTags tags; 112 private HgTags tags;
129 132
130 HgRepository(String repositoryPath) { 133 HgRepository(String repositoryPath) {
131 repoDir = null; 134 repoDir = null;
132 workingDir = null; 135 workingDir = null;
133 repoLocation = repositoryPath; 136 repoLocation = repositoryPath;
134 dataPathHelper = repoPathHelper = null;
135 normalizePath = null; 137 normalizePath = null;
136 sessionContext = null; 138 sessionContext = null;
137 impl = null; 139 impl = null;
138 } 140 }
139 141
151 throw new IllegalArgumentException(repoDir.toString()); 153 throw new IllegalArgumentException(repoDir.toString());
152 } 154 }
153 repoLocation = repositoryPath; 155 repoLocation = repositoryPath;
154 sessionContext = ctx; 156 sessionContext = ctx;
155 impl = new org.tmatesoft.hg.internal.Internals(this, repositoryRoot); 157 impl = new org.tmatesoft.hg.internal.Internals(this, repositoryRoot);
156 impl.parseRequires();
157 normalizePath = impl.buildNormalizePathRewrite(); 158 normalizePath = impl.buildNormalizePathRewrite();
158 dataPathHelper = impl.buildDataFilesHelper();
159 repoPathHelper = impl.buildRepositoryFilesHelper();
160 } 159 }
161 160
162 @Override 161 @Override
163 public String toString() { 162 public String toString() {
164 return getClass().getSimpleName() + "[" + getLocation() + (isInvalid() ? "(BAD)" : "") + "]"; 163 return getClass().getSimpleName() + "[" + getLocation() + (isInvalid() ? "(BAD)" : "") + "]";
183 return impl == null || impl.isInvalid(); 182 return impl == null || impl.isInvalid();
184 } 183 }
185 184
186 public HgChangelog getChangelog() { 185 public HgChangelog getChangelog() {
187 if (changelog == null) { 186 if (changelog == null) {
188 CharSequence storagePath = repoPathHelper.rewrite("00changelog.i"); 187 File chlogFile = impl.getFileFromStoreDir("00changelog.i");
189 RevlogStream content = resolve(Path.create(storagePath), true); 188 if (!chlogFile.exists()) {
189 // fake its existence
190 chlogFile = fakeNonExistentFile(chlogFile);
191 }
192 RevlogStream content = new RevlogStream(impl.getDataAccess(), chlogFile);
190 changelog = new HgChangelog(this, content); 193 changelog = new HgChangelog(this, content);
191 } 194 }
192 return changelog; 195 return changelog;
193 } 196 }
194 197
195 public HgManifest getManifest() { 198 public HgManifest getManifest() {
196 if (manifest == null) { 199 if (manifest == null) {
197 RevlogStream content = resolve(Path.create(repoPathHelper.rewrite("00manifest.i")), true); 200 File manifestFile = impl.getFileFromStoreDir("00manifest.i");
201 if (!manifestFile.exists()) {
202 manifestFile = fakeNonExistentFile(manifestFile);
203 }
204 RevlogStream content = new RevlogStream(impl.getDataAccess(), manifestFile);
198 manifest = new HgManifest(this, content, impl.buildFileNameEncodingHelper()); 205 manifest = new HgManifest(this, content, impl.buildFileNameEncodingHelper());
199 } 206 }
200 return manifest; 207 return manifest;
201 } 208 }
202 209
265 return mergeState; 272 return mergeState;
266 } 273 }
267 274
268 public HgDataFile getFileNode(String path) { 275 public HgDataFile getFileNode(String path) {
269 CharSequence nPath = normalizePath.rewrite(path); 276 CharSequence nPath = normalizePath.rewrite(path);
270 CharSequence storagePath = dataPathHelper.rewrite(nPath);
271 RevlogStream content = resolve(Path.create(storagePath), false);
272 Path p = Path.create(nPath); 277 Path p = Path.create(nPath);
273 if (content == null) { 278 return getFileNode(p);
274 return new HgDataFile(this, p);
275 }
276 return new HgDataFile(this, p, content);
277 } 279 }
278 280
279 public HgDataFile getFileNode(Path path) { 281 public HgDataFile getFileNode(Path path) {
280 CharSequence storagePath = dataPathHelper.rewrite(path.toString()); 282 RevlogStream content = resolveStoreFile(path);
281 RevlogStream content = resolve(Path.create(storagePath), false);
282 // XXX no content when no file? or HgDataFile.exists() to detect that?
283 if (content == null) { 283 if (content == null) {
284 return new HgDataFile(this, path); 284 return new HgDataFile(this, path);
285 } 285 }
286 return new HgDataFile(this, path, content); 286 return new HgDataFile(this, path, content);
287 } 287 }
343 return new HgRepoConfig(new ConfigFile(getSessionContext())); // empty config, do not cache, allow to try once again 343 return new HgRepoConfig(new ConfigFile(getSessionContext())); // empty config, do not cache, allow to try once again
344 //throw new HgInvalidControlFileException(m, ex, null); 344 //throw new HgInvalidControlFileException(m, ex, null);
345 } 345 }
346 } 346 }
347 return repoConfig; 347 return repoConfig;
348 }
349
350 // shall be of use only for internal classes
351 /*package-local*/ File getRepositoryRoot() {
352 return repoDir;
353 }
354
355 /*package-local, debug*/String getStoragePath(HgDataFile df) {
356 // may come handy for debug
357 return dataPathHelper.rewrite(df.getPath().toString()).toString();
358 } 348 }
359 349
360 // 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) 350 // 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)
361 // XXX consider passing Path pool or factory to produce (shared) Path instead of Strings 351 // XXX consider passing Path pool or factory to produce (shared) Path instead of Strings
362 /*package-local*/ final HgDirstate loadDirstate(Path.Source pathFactory) throws HgInvalidControlFileException { 352 /*package-local*/ final HgDirstate loadDirstate(Path.Source pathFactory) throws HgInvalidControlFileException {
457 447
458 @Experimental(reason="WORK IN PROGRESS") 448 @Experimental(reason="WORK IN PROGRESS")
459 public HgRepositoryLock getStoreLock() { 449 public HgRepositoryLock getStoreLock() {
460 if (storeLock == null) { 450 if (storeLock == null) {
461 int timeout = getLockTimeout(); 451 int timeout = getLockTimeout();
462 File fl = impl.getFileFromRepoDir(repoPathHelper.rewrite("lock").toString()); 452 File fl = impl.getFileFromStoreDir("lock");
463 synchronized (this) { 453 synchronized (this) {
464 if (storeLock == null) { 454 if (storeLock == null) {
465 storeLock = new HgRepositoryLock(fl, timeout); 455 storeLock = new HgRepositoryLock(fl, timeout);
466 } 456 }
467 } 457 }
489 return sessionContext; 479 return sessionContext;
490 } 480 }
491 481
492 /** 482 /**
493 * Perhaps, should be separate interface, like ContentLookup 483 * Perhaps, should be separate interface, like ContentLookup
494 * path - repository storage path (i.e. one usually with .i or .d) 484 * @param path - normalized file name
495 */ 485 * @return <code>null</code> if path doesn't resolve to a existing file
496 /*package-local*/ RevlogStream resolve(Path path, boolean shallFakeNonExistent) { 486 */
487 /*package-local*/ RevlogStream resolveStoreFile(Path path) {
497 final SoftReference<RevlogStream> ref = streamsCache.get(path); 488 final SoftReference<RevlogStream> ref = streamsCache.get(path);
498 RevlogStream cached = ref == null ? null : ref.get(); 489 RevlogStream cached = ref == null ? null : ref.get();
499 if (cached != null) { 490 if (cached != null) {
500 return cached; 491 return cached;
501 } 492 }
502 File f = new File(repoDir, path.toString()); 493 File f = impl.getFileFromDataDir(path);
503 if (f.exists()) { 494 if (f.exists()) {
504 RevlogStream s = new RevlogStream(impl.getDataAccess(), f); 495 RevlogStream s = new RevlogStream(impl.getDataAccess(), f);
505 if (impl.shallCacheRevlogs()) { 496 if (impl.shallCacheRevlogs()) {
506 streamsCache.put(path, new SoftReference<RevlogStream>(s)); 497 streamsCache.put(path, new SoftReference<RevlogStream>(s));
507 } 498 }
508 return s; 499 return s;
509 } else { 500 }
510 if (shallFakeNonExistent) { 501 return null;
511 try { 502 }
512 File fake = File.createTempFile(f.getName(), null); 503
513 fake.deleteOnExit(); 504 private File fakeNonExistentFile(File expected) throws HgInvalidFileException {
514 return new RevlogStream(impl.getDataAccess(), fake); 505 try {
515 } catch (IOException ex) { 506 File fake = File.createTempFile(expected.getName(), null);
516 getSessionContext().getLog().dump(getClass(), Info, ex, null); 507 fake.deleteOnExit();
517 } 508 return fake;
518 } 509 } catch (IOException ex) {
519 } 510 getSessionContext().getLog().dump(getClass(), Info, ex, null);
520 return null; // XXX empty stream instead? 511 throw new HgInvalidFileException(String.format("Failed to fake existence of file %s", expected), ex);
512 }
521 } 513 }
522 514
523 /*package-local*/ List<Filter> getFiltersFromRepoToWorkingDir(Path p) { 515 /*package-local*/ List<Filter> getFiltersFromRepoToWorkingDir(Path p) {
524 return instantiateFilters(p, new Filter.Options(Filter.Direction.FromRepo)); 516 return instantiateFilters(p, new Filter.Options(Filter.Direction.FromRepo));
525 } 517 }