Don't load an object if we just need its existance.
[egit/egit-new.git] / org.spearce.jgit / src / org / spearce / jgit / lib / Repository.java
blob89fbef5d08fafe90c992ba86d37fecb33c66566a
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 Lesser General Public
6 * License, version 2.1, 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 * Lesser General Public License for more details.
13 * You should have received a copy of the GNU Lesser 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.util.ArrayList;
27 import java.util.Iterator;
28 import java.util.List;
30 import org.spearce.jgit.errors.IncorrectObjectTypeException;
31 import org.spearce.jgit.errors.ObjectWritingException;
33 public class Repository {
34 private static final String[] refSearchPaths = { "", "refs/", "refs/tags/",
35 "refs/heads/", };
37 private final File gitDir;
39 private final File objectsDir;
41 private final File refsDir;
43 private final List packs;
45 private final RepositoryConfig config;
47 private WindowCache windows;
49 public Repository(final File d) throws IOException {
50 gitDir = d.getAbsoluteFile();
51 objectsDir = new File(gitDir, "objects");
52 refsDir = new File(gitDir, "refs");
53 packs = new ArrayList();
54 config = new RepositoryConfig(this);
55 if (objectsDir.exists()) {
56 getConfig().load();
57 final String repositoryFormatVersion = getConfig().getString(
58 "core", "repositoryFormatVersion");
59 if (!"0".equals(repositoryFormatVersion)) {
60 throw new IOException("Unknown repository format \""
61 + repositoryFormatVersion + "\"; expected \"0\".");
63 initializeWindowCache();
64 scanForPacks();
68 public void create() throws IOException {
69 if (gitDir.exists()) {
70 throw new IllegalStateException("Repository already exists: "
71 + gitDir);
74 gitDir.mkdirs();
76 objectsDir.mkdirs();
77 new File(objectsDir, "pack").mkdir();
78 new File(objectsDir, "info").mkdir();
80 refsDir.mkdir();
81 new File(refsDir, "heads").mkdir();
82 new File(refsDir, "tags").mkdir();
84 new File(gitDir, "branches").mkdir();
85 new File(gitDir, "remotes").mkdir();
86 writeSymref("HEAD", "refs/heads/master");
88 getConfig().create();
89 getConfig().save();
90 initializeWindowCache();
93 private void initializeWindowCache() {
94 // FIXME these should be configurable...
95 windows = new WindowCache(256 * 1024 * 1024, 4);
98 public File getDirectory() {
99 return gitDir;
102 public File getObjectsDirectory() {
103 return objectsDir;
106 public RepositoryConfig getConfig() {
107 return config;
110 public WindowCache getWindowCache() {
111 return windows;
114 public File toFile(final ObjectId objectId) {
115 final String n = objectId.toString();
116 return new File(new File(objectsDir, n.substring(0, 2)), n.substring(2));
119 public boolean hasObject(final ObjectId objectId) {
120 final Iterator i = packs.iterator();
121 while (i.hasNext()) {
122 final PackFile p = (PackFile) i.next();
123 try {
124 if (p.hasObject(objectId))
125 return true;
126 } catch (IOException ioe) {
127 // This shouldn't happen unless the pack was corrupted
128 // after we opened it. We'll ignore the error as though
129 // the object does not exist in this pack.
133 return toFile(objectId).isFile();
136 public ObjectLoader openObject(final ObjectId id) throws IOException {
137 final ObjectLoader packed = objectInPack(id);
138 if (packed != null)
139 return packed;
140 try {
141 return new UnpackedObjectLoader(this, id);
142 } catch (FileNotFoundException fnfe) {
143 return null;
147 public ObjectLoader openBlob(final ObjectId id) throws IOException {
148 return openObject(id);
151 public ObjectLoader openTree(final ObjectId id) throws IOException {
152 return openObject(id);
155 public Commit mapCommit(final String revstr) throws IOException {
156 final ObjectId id = resolve(revstr);
157 return id != null ? mapCommit(id) : null;
160 public Commit mapCommit(final ObjectId id) throws IOException {
161 final ObjectLoader or = openObject(id);
162 if (or == null)
163 return null;
164 final byte[] raw = or.getBytes();
165 if (Constants.TYPE_COMMIT.equals(or.getType()))
166 return new Commit(this, id, raw);
167 throw new IncorrectObjectTypeException(id, Constants.TYPE_COMMIT);
170 public Tree mapTree(final String revstr) throws IOException {
171 final ObjectId id = resolve(revstr);
172 return id != null ? mapTree(id) : null;
175 public Tree mapTree(final ObjectId id) throws IOException {
176 final ObjectLoader or = openObject(id);
177 if (or == null)
178 return null;
179 final byte[] raw = or.getBytes();
180 if (Constants.TYPE_TREE.equals(or.getType()))
181 return new Tree(this, id, raw);
182 if (Constants.TYPE_COMMIT.equals(or.getType()))
183 return mapTree(ObjectId.fromString(raw, 5));
184 throw new IncorrectObjectTypeException(id, Constants.TYPE_TREE);
187 public RefLock lockRef(final String ref) throws IOException {
188 final RefLock l = new RefLock(readRef(ref, true));
189 return l.lock() ? l : null;
192 public ObjectId resolve(final String revstr) throws IOException {
193 ObjectId id = null;
195 if (ObjectId.isId(revstr)) {
196 id = new ObjectId(revstr);
199 if (id == null) {
200 final Ref r = readRef(revstr, false);
201 if (r != null) {
202 id = r.getObjectId();
206 return id;
209 public void close() throws IOException {
210 closePacks();
213 public void closePacks() throws IOException {
214 final Iterator i = packs.iterator();
215 while (i.hasNext()) {
216 final PackFile pr = (PackFile) i.next();
217 pr.close();
219 packs.clear();
222 public void scanForPacks() {
223 final File packDir = new File(objectsDir, "pack");
224 final File[] list = packDir.listFiles(new FileFilter() {
225 public boolean accept(final File f) {
226 final String n = f.getName();
227 if (!n.endsWith(".pack")) {
228 return false;
230 final String nBase = n.substring(0, n.lastIndexOf('.'));
231 final File idx = new File(packDir, nBase + ".idx");
232 return f.isFile() && f.canRead() && idx.isFile()
233 && idx.canRead();
236 for (int k = 0; k < list.length; k++) {
237 try {
238 packs.add(new PackFile(this, list[k]));
239 } catch (IOException ioe) {
240 // Whoops. That's not a pack!
246 private ObjectLoader objectInPack(final ObjectId objectId) {
247 final Iterator i = packs.iterator();
248 while (i.hasNext()) {
249 final PackFile p = (PackFile) i.next();
250 try {
251 final ObjectLoader o = p.get(objectId);
252 if (o != null) {
253 return o;
255 } catch (IOException ioe) {
256 // This shouldn't happen unless the pack was corrupted after we
257 // opened it. We'll ignore the error as though the object does
258 // not exist in this pack.
262 return null;
265 private void writeSymref(final String name, final String target)
266 throws IOException {
267 final File s = new File(gitDir, name);
268 final File t = File.createTempFile("srf", null, gitDir);
269 FileWriter w = new FileWriter(t);
270 try {
271 w.write("ref: ");
272 w.write(target);
273 w.write('\n');
274 w.close();
275 w = null;
276 if (!t.renameTo(s)) {
277 s.getParentFile().mkdirs();
278 if (!t.renameTo(s)) {
279 t.delete();
280 throw new ObjectWritingException("Unable to"
281 + " write symref " + name + " to point to "
282 + target);
285 } finally {
286 if (w != null) {
287 w.close();
288 t.delete();
293 private Ref readRef(final String revstr, final boolean missingOk)
294 throws IOException {
295 for (int k = 0; k < refSearchPaths.length; k++) {
296 final Ref r = readRefBasic(refSearchPaths[k] + revstr);
297 if (missingOk || r.getObjectId() != null) {
298 return r;
301 return null;
304 private Ref readRefBasic(String name) throws IOException {
305 int depth = 0;
306 REF_READING: do {
307 final File f = new File(getDirectory(), name);
308 if (!f.isFile()) {
309 return new Ref(f, null);
312 final BufferedReader br = new BufferedReader(new FileReader(f));
313 try {
314 final String line = br.readLine();
315 if (line == null || line.length() == 0) {
316 return new Ref(f, null);
317 } else if (line.startsWith("ref: ")) {
318 name = line.substring("ref: ".length());
319 continue REF_READING;
320 } else if (ObjectId.isId(line)) {
321 return new Ref(f, new ObjectId(line));
323 throw new IOException("Not a ref: " + name + ": " + line);
324 } finally {
325 br.close();
327 } while (depth++ < 5);
328 throw new IOException("Exceed maximum ref depth. Circular reference?");
331 public String toString() {
332 return "Repository[" + getDirectory() + "]";