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
;
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/",
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()) {
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();
68 public void create() throws IOException
{
69 if (gitDir
.exists()) {
70 throw new IllegalStateException("Repository already exists: "
77 new File(objectsDir
, "pack").mkdir();
78 new File(objectsDir
, "info").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");
90 initializeWindowCache();
93 private void initializeWindowCache() {
94 // FIXME these should be configurable...
95 windows
= new WindowCache(256 * 1024 * 1024, 4);
98 public File
getDirectory() {
102 public File
getObjectsDirectory() {
106 public RepositoryConfig
getConfig() {
110 public WindowCache
getWindowCache() {
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();
124 if (p
.hasObject(objectId
))
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
);
141 return new UnpackedObjectLoader(this, id
);
142 } catch (FileNotFoundException fnfe
) {
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
);
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
);
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
{
195 if (ObjectId
.isId(revstr
)) {
196 id
= new ObjectId(revstr
);
200 final Ref r
= readRef(revstr
, false);
202 id
= r
.getObjectId();
209 public void close() throws IOException
{
213 public void closePacks() throws IOException
{
214 final Iterator i
= packs
.iterator();
215 while (i
.hasNext()) {
216 final PackFile pr
= (PackFile
) i
.next();
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")) {
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()
236 for (int k
= 0; k
< list
.length
; k
++) {
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();
251 final ObjectLoader o
= p
.get(objectId
);
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.
265 private void writeSymref(final String name
, final String target
)
267 final File s
= new File(gitDir
, name
);
268 final File t
= File
.createTempFile("srf", null, gitDir
);
269 FileWriter w
= new FileWriter(t
);
276 if (!t
.renameTo(s
)) {
277 s
.getParentFile().mkdirs();
278 if (!t
.renameTo(s
)) {
280 throw new ObjectWritingException("Unable to"
281 + " write symref " + name
+ " to point to "
293 private Ref
readRef(final String revstr
, final boolean missingOk
)
295 for (int k
= 0; k
< refSearchPaths
.length
; k
++) {
296 final Ref r
= readRefBasic(refSearchPaths
[k
] + revstr
);
297 if (missingOk
|| r
.getObjectId() != null) {
304 private Ref
readRefBasic(String name
) throws IOException
{
307 final File f
= new File(getDirectory(), name
);
309 return new Ref(f
, null);
312 final BufferedReader br
= new BufferedReader(new FileReader(f
));
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
);
327 } while (depth
++ < 5);
328 throw new IOException("Exceed maximum ref depth. Circular reference?");
331 public String
toString() {
332 return "Repository[" + getDirectory() + "]";