Show patch name in history view
[egit.git] / org.spearce.jgit / src / org / spearce / jgit / lib / Repository.java
blobb4d6473e78aea2a3ebcdd08dce4683d1b537f32b
1 /*
2 * Copyright (C) 2006 Shawn Pearce <spearce@spearce.org>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License, version 2, as published by the Free Software Foundation.
8 * This library 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 GNU
11 * General Public License for more details.
13 * You should have received a copy of the GNU General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
17 package org.spearce.jgit.lib;
19 import java.io.BufferedReader;
20 import java.io.File;
21 import java.io.FileFilter;
22 import java.io.FileNotFoundException;
23 import java.io.FileReader;
24 import java.io.FileWriter;
25 import java.io.IOException;
26 import java.lang.ref.Reference;
27 import java.lang.ref.SoftReference;
28 import java.util.ArrayList;
29 import java.util.Collection;
30 import java.util.HashMap;
31 import java.util.Map;
32 import java.util.WeakHashMap;
34 import org.spearce.jgit.errors.IncorrectObjectTypeException;
35 import org.spearce.jgit.errors.ObjectWritingException;
37 public class Repository {
38 private static final String[] refSearchPaths = { "", "refs/", "refs/tags/",
39 "refs/heads/", };
41 private final File gitDir;
43 private final File[] objectsDirs;
45 private final File refsDir;
47 private final RepositoryConfig config;
49 private PackFile[] packs;
51 private WindowCache windows;
53 private Map treeCache = new WeakHashMap(30000);
54 private Map commitCache = new WeakHashMap(30000);
56 public Repository(final File d) throws IOException {
57 gitDir = d.getAbsoluteFile();
58 try {
59 objectsDirs = (File[])readObjectsDirs(new File(gitDir, "objects"), new ArrayList()).toArray(new File[0]);
60 } catch (IOException e) {
61 IOException ex = new IOException("Cannot find all object dirs for " + gitDir);
62 ex.initCause(e);
63 throw ex;
65 refsDir = new File(gitDir, "refs");
66 packs = new PackFile[0];
67 config = new RepositoryConfig(this);
68 if (objectsDirs[0].exists()) {
69 getConfig().load();
70 final String repositoryFormatVersion = getConfig().getString(
71 "core", "repositoryFormatVersion");
72 if (!"0".equals(repositoryFormatVersion)) {
73 throw new IOException("Unknown repository format \""
74 + repositoryFormatVersion + "\"; expected \"0\".");
76 initializeWindowCache();
77 scanForPacks();
81 private Collection readObjectsDirs(File objectsDir, Collection ret) throws IOException {
82 ret.add(objectsDir);
83 File alternatesFile = new File(objectsDir,"info/alternates");
84 if (alternatesFile.exists()) {
85 BufferedReader ar = new BufferedReader(new FileReader(alternatesFile));
86 for (String alt=ar.readLine(); alt!=null; alt=ar.readLine()) {
87 readObjectsDirs(new File(alt), ret);
89 ar.close();
91 return ret;
94 public void create() throws IOException {
95 if (gitDir.exists()) {
96 throw new IllegalStateException("Repository already exists: "
97 + gitDir);
100 gitDir.mkdirs();
102 objectsDirs[0].mkdirs();
103 new File(objectsDirs[0], "pack").mkdir();
104 new File(objectsDirs[0], "info").mkdir();
106 refsDir.mkdir();
107 new File(refsDir, "heads").mkdir();
108 new File(refsDir, "tags").mkdir();
110 new File(gitDir, "branches").mkdir();
111 new File(gitDir, "remotes").mkdir();
112 writeSymref("HEAD", "refs/heads/master");
114 getConfig().create();
115 getConfig().save();
116 initializeWindowCache();
119 private void initializeWindowCache() {
120 // FIXME these should be configurable...
121 windows = new WindowCache(256 * 1024 * 1024, 4);
124 public File getDirectory() {
125 return gitDir;
128 public File getObjectsDirectory() {
129 return objectsDirs[0];
132 public RepositoryConfig getConfig() {
133 return config;
136 public WindowCache getWindowCache() {
137 return windows;
140 public File toFile(final ObjectId objectId) {
141 final String n = objectId.toString();
142 String d=n.substring(0, 2);
143 String f=n.substring(2);
144 for (int i=0; i<objectsDirs.length; ++i) {
145 File ret = new File(new File(objectsDirs[i], d), f);
146 if (ret.exists())
147 return ret;
149 return new File(new File(objectsDirs[0], d), f);
152 public boolean hasObject(final ObjectId objectId) {
153 int k = packs.length;
154 if (k > 0) {
155 final byte[] tmp = new byte[Constants.OBJECT_ID_LENGTH];
156 do {
157 try {
158 if (packs[--k].hasObject(objectId, tmp))
159 return true;
160 } catch (IOException ioe) {
161 // This shouldn't happen unless the pack was corrupted
162 // after we opened it. We'll ignore the error as though
163 // the object does not exist in this pack.
166 } while (k > 0);
168 return toFile(objectId).isFile();
171 public ObjectLoader openObject(final ObjectId id) throws IOException {
172 int k = packs.length;
173 if (k > 0) {
174 final byte[] tmp = new byte[Constants.OBJECT_ID_LENGTH];
175 do {
176 try {
177 final ObjectLoader ol = packs[--k].get(id, tmp);
178 if (ol != null)
179 return ol;
180 } catch (IOException ioe) {
181 // This shouldn't happen unless the pack was corrupted
182 // after we opened it or the VM runs out of memory. This is
183 // a know problem with memory mapped I/O in java and have
184 // been noticed with JDK < 1.6. Tell the gc that now is a good
185 // time to collect and try once more.
186 try {
187 System.gc();
188 final ObjectLoader ol = packs[k].get(id, tmp);
189 if (ol != null)
190 return ol;
191 } catch (IOException ioe2) {
192 ioe2.printStackTrace();
193 ioe.printStackTrace();
194 // Still fails.. that's BAD, maybe the pack has
195 // been corrupted after all, or the gc didn't manage
196 // to release enough previously mmaped areas.
199 } while (k > 0);
201 try {
202 return new UnpackedObjectLoader(this, id);
203 } catch (FileNotFoundException fnfe) {
204 return null;
208 public ObjectLoader openBlob(final ObjectId id) throws IOException {
209 return openObject(id);
212 public ObjectLoader openTree(final ObjectId id) throws IOException {
213 return openObject(id);
216 public Commit mapCommit(final String revstr) throws IOException {
217 final ObjectId id = resolve(revstr);
218 return id != null ? mapCommit(id) : null;
221 public Commit mapCommit(final ObjectId id) throws IOException {
222 // System.out.println("commitcache.size="+commitCache.size());
223 Reference retr = (Reference)commitCache.get(id);
224 if (retr != null) {
225 Commit ret = (Commit)retr.get();
226 if (ret != null)
227 return ret;
228 System.out.println("Found a null id, size was "+commitCache.size());
231 final ObjectLoader or = openObject(id);
232 if (or == null)
233 return null;
234 final byte[] raw = or.getBytes();
235 if (Constants.TYPE_COMMIT.equals(or.getType())) {
236 Commit ret = new Commit(this, id, raw);
237 // The key must not be the referenced strongly
238 // by the value in WeakHashMaps
239 commitCache.put(id, new SoftReference(ret));
240 return ret;
242 throw new IncorrectObjectTypeException(id, Constants.TYPE_COMMIT);
245 public Tree mapTree(final String revstr) throws IOException {
246 final ObjectId id = resolve(revstr);
247 return id != null ? mapTree(id) : null;
250 public Tree mapTree(final ObjectId id) throws IOException {
251 Reference wret = (Reference)treeCache.get(id);
252 if (wret != null) {
253 Tree ret = (Tree)wret.get();
254 if (ret != null)
255 return ret;
258 final ObjectLoader or = openObject(id);
259 if (or == null)
260 return null;
261 final byte[] raw = or.getBytes();
262 if (Constants.TYPE_TREE.equals(or.getType())) {
263 Tree ret = new Tree(this, id, raw);
264 treeCache.put(id, new SoftReference(ret));
265 return ret;
267 if (Constants.TYPE_COMMIT.equals(or.getType()))
268 return mapTree(ObjectId.fromString(raw, 5));
269 throw new IncorrectObjectTypeException(id, Constants.TYPE_TREE);
272 public Tag mapTag(String revstr) throws IOException {
273 final ObjectId id = resolve(revstr);
274 return id != null ? mapTag(id) : null;
277 public Tag mapTag(final ObjectId id) throws IOException {
278 final ObjectLoader or = openObject(id);
279 if (or == null)
280 return null;
281 final byte[] raw = or.getBytes();
282 if (Constants.TYPE_TAG.equals(or.getType()))
283 return new Tag(this, id, raw);
284 throw new IncorrectObjectTypeException(id, Constants.TYPE_TAG);
287 public RefLock lockRef(final String ref) throws IOException {
288 final RefLock l = new RefLock(readRef(ref, true));
289 return l.lock() ? l : null;
292 public ObjectId resolve(final String revstr) throws IOException {
293 ObjectId id = null;
295 if (ObjectId.isId(revstr)) {
296 id = new ObjectId(revstr);
299 if (id == null) {
300 final Ref r = readRef(revstr, false);
301 if (r != null) {
302 id = r.getObjectId();
306 return id;
309 public void close() throws IOException {
310 closePacks();
313 public void closePacks() throws IOException {
314 for (int k = packs.length - 1; k >= 0; k--) {
315 packs[k].close();
317 packs = new PackFile[0];
320 public void scanForPacks() {
321 final ArrayList p = new ArrayList();
322 for (int i=0; i<objectsDirs.length; ++i)
323 scanForPacks(new File(objectsDirs[i], "pack"), p);
324 final PackFile[] arr = new PackFile[p.size()];
325 p.toArray(arr);
326 packs = arr;
329 public void scanForPacks(final File packDir, Collection packList) {
330 final File[] list = packDir.listFiles(new FileFilter() {
331 public boolean accept(final File f) {
332 final String n = f.getName();
333 if (!n.endsWith(".pack")) {
334 return false;
336 final String nBase = n.substring(0, n.lastIndexOf('.'));
337 final File idx = new File(packDir, nBase + ".idx");
338 return f.isFile() && f.canRead() && idx.isFile()
339 && idx.canRead();
342 for (int k = 0; k < list.length; k++) {
343 try {
344 packList.add(new PackFile(this, list[k]));
345 } catch (IOException ioe) {
346 // Whoops. That's not a pack!
352 private void writeSymref(final String name, final String target)
353 throws IOException {
354 final File s = new File(gitDir, name);
355 final File t = File.createTempFile("srf", null, gitDir);
356 FileWriter w = new FileWriter(t);
357 try {
358 w.write("ref: ");
359 w.write(target);
360 w.write('\n');
361 w.close();
362 w = null;
363 if (!t.renameTo(s)) {
364 s.getParentFile().mkdirs();
365 if (!t.renameTo(s)) {
366 t.delete();
367 throw new ObjectWritingException("Unable to"
368 + " write symref " + name + " to point to "
369 + target);
372 } finally {
373 if (w != null) {
374 w.close();
375 t.delete();
380 private Ref readRef(final String revstr, final boolean missingOk)
381 throws IOException {
382 for (int k = 0; k < refSearchPaths.length; k++) {
383 final Ref r = readRefBasic(refSearchPaths[k] + revstr);
384 if (missingOk || r.getObjectId() != null) {
385 return r;
388 return null;
391 private Ref readRefBasic(String name) throws IOException {
392 int depth = 0;
393 REF_READING: do {
394 final File f = new File(getDirectory(), name);
395 if (!f.isFile()) {
396 return new Ref(f, null);
399 final BufferedReader br = new BufferedReader(new FileReader(f));
400 try {
401 final String line = br.readLine();
402 if (line == null || line.length() == 0) {
403 return new Ref(f, null);
404 } else if (line.startsWith("ref: ")) {
405 name = line.substring("ref: ".length());
406 continue REF_READING;
407 } else if (ObjectId.isId(line)) {
408 return new Ref(f, new ObjectId(line));
410 throw new IOException("Not a ref: " + name + ": " + line);
411 } finally {
412 br.close();
414 } while (depth++ < 5);
415 throw new IOException("Exceed maximum ref depth. Circular reference?");
418 public String toString() {
419 return "Repository[" + getDirectory() + "]";
422 public String getPatch() throws IOException {
423 final File ptr = new File(getDirectory(),"patches/"+getBranch()+"/current");
424 final BufferedReader br = new BufferedReader(new FileReader(ptr));
425 try {
426 return br.readLine();
427 } finally {
428 br.close();
432 public String getBranch() throws IOException {
433 final File ptr = new File(getDirectory(),"HEAD");
434 final BufferedReader br = new BufferedReader(new FileReader(ptr));
435 String ref;
436 try {
437 ref = br.readLine();
438 } finally {
439 br.close();
441 if (ref.startsWith("ref: "))
442 ref = ref.substring(5);
443 if (ref.startsWith("refs/heads/"))
444 ref = ref.substring(11);
445 return ref;
448 public Collection getBranches() {
449 return listFilesRecursively(new File(refsDir, "heads"), null);
452 public Collection getTags() {
453 return listFilesRecursively(new File(refsDir, "tags"), null);
457 * @return true if HEAD points to a StGit patch.
459 public boolean isStGitMode() {
460 try {
461 File file = new File(getDirectory(), "HEAD");
462 BufferedReader reader = new BufferedReader(new FileReader(file));
463 String string = reader.readLine();
464 if (!string.startsWith("ref: refs/heads/"))
465 return false;
466 String branch = string.substring("ref: refs/heads/".length());
467 File currentPatch = new File(new File(new File(getDirectory(),
468 "patches"), branch), "current");
469 if (!currentPatch.exists())
470 return false;
471 if (currentPatch.length() == 0)
472 return false;
473 return true;
475 } catch (IOException e) {
476 e.printStackTrace();
477 return false;
481 public static class StGitPatch {
482 public StGitPatch(String patchName, ObjectId id) {
483 name = patchName;
484 gitId = id;
486 public ObjectId getGitId() {
487 return gitId;
489 public String getName() {
490 return name;
492 private String name;
493 private ObjectId gitId;
497 * @return applied patches in a map indexed on current commit id
498 * @throws IOException
500 public Map getAppliedPatches() throws IOException {
501 Map ret = new HashMap();
502 if (isStGitMode()) {
503 File patchDir = new File(new File(getDirectory(),"patches"),getBranch());
504 BufferedReader apr = new BufferedReader(new FileReader(new File(patchDir,"applied")));
505 for (String patchName=apr.readLine(); patchName!=null; patchName=apr.readLine()) {
506 File topFile = new File(new File(new File(patchDir,"patches"), patchName), "top");
507 BufferedReader tfr = new BufferedReader(new FileReader(topFile));
508 String objectId = tfr.readLine();
509 ObjectId id = new ObjectId(objectId);
510 ret.put(id, new StGitPatch(patchName, id));
511 tfr.close();
513 apr.close();
515 return ret;
518 private Collection listFilesRecursively(File root, File start) {
519 if (start == null)
520 start = root;
521 Collection ret = new ArrayList();
522 File[] files = start.listFiles();
523 for (int i = 0; i < files.length; ++i) {
524 if (files[i].isDirectory())
525 ret.addAll(listFilesRecursively(root, files[i]));
526 else if (files[i].length() == 41) {
527 String name = files[i].toString().substring(
528 root.toString().length() + 1);
529 ret.add(name);
532 return ret;