2 * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
3 * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
4 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
8 * Redistribution and use in source and binary forms, with or
9 * without modification, are permitted provided that the following
12 * - Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * - Redistributions in binary form must reproduce the above
16 * copyright notice, this list of conditions and the following
17 * disclaimer in the documentation and/or other materials provided
18 * with the distribution.
20 * - Neither the name of the Git Development Community nor the
21 * names of its contributors may be used to endorse or promote
22 * products derived from this software without specific prior
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
26 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
27 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
28 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
30 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
32 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
33 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
34 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
35 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
37 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40 package org
.spearce
.jgit
.lib
;
42 import java
.io
.BufferedReader
;
44 import java
.io
.FileNotFoundException
;
45 import java
.io
.FileReader
;
46 import java
.io
.FilenameFilter
;
47 import java
.io
.IOException
;
48 import java
.util
.ArrayList
;
49 import java
.util
.Collection
;
50 import java
.util
.HashMap
;
51 import java
.util
.LinkedList
;
54 import org
.spearce
.jgit
.errors
.IncorrectObjectTypeException
;
55 import org
.spearce
.jgit
.errors
.RevisionSyntaxException
;
56 import org
.spearce
.jgit
.stgit
.StGitPatch
;
57 import org
.spearce
.jgit
.util
.FS
;
60 * Represents a Git repository. A repository holds all objects and refs used for
61 * managing source code (could by any type of file, but source code is what
62 * SCM's are typically used for).
64 * In Git terms all data is stored in GIT_DIR, typically a directory called
65 * .git. A work tree is maintained unless the repository is a bare repository.
66 * Typically the .git directory is located at the root of the work dir.
71 * <li>objects/ - objects</li>
72 * <li>refs/ - tags and heads</li>
73 * <li>config - configuration</li>
74 * <li>info/ - more configurations</li>
79 * This implementation only handles a subtly undocumented subset of git features.
82 public class Repository
{
83 private final File gitDir
;
85 private final File
[] objectsDirs
;
87 private final RepositoryConfig config
;
89 private final RefDatabase refs
;
91 private PackFile
[] packs
;
93 private GitIndex index
;
96 * Construct a representation of a Git repository.
99 * GIT_DIR (the location of the repository metadata).
100 * @throws IOException
101 * the repository appears to already exist but cannot be
104 public Repository(final File d
) throws IOException
{
105 gitDir
= d
.getAbsoluteFile();
107 objectsDirs
= readObjectsDirs(FS
.resolve(gitDir
, "objects"),
108 new ArrayList
<File
>()).toArray(new File
[0]);
109 } catch (IOException e
) {
110 IOException ex
= new IOException("Cannot find all object dirs for " + gitDir
);
114 refs
= new RefDatabase(this);
115 packs
= new PackFile
[0];
116 config
= new RepositoryConfig(this);
118 final boolean isExisting
= objectsDirs
[0].exists();
121 final String repositoryFormatVersion
= getConfig().getString(
122 "core", null, "repositoryFormatVersion");
123 if (!"0".equals(repositoryFormatVersion
)) {
124 throw new IOException("Unknown repository format \""
125 + repositoryFormatVersion
+ "\"; expected \"0\".");
128 getConfig().create();
134 private Collection
<File
> readObjectsDirs(File objectsDir
, Collection
<File
> ret
) throws IOException
{
136 final File altFile
= FS
.resolve(objectsDir
, "info/alternates");
137 if (altFile
.exists()) {
138 BufferedReader ar
= new BufferedReader(new FileReader(altFile
));
139 for (String alt
=ar
.readLine(); alt
!=null; alt
=ar
.readLine()) {
140 readObjectsDirs(FS
.resolve(objectsDir
, alt
), ret
);
148 * Create a new Git repository initializing the necessary files and
151 * @throws IOException
153 public void create() throws IOException
{
154 if (gitDir
.exists()) {
155 throw new IllegalStateException("Repository already exists: "
162 objectsDirs
[0].mkdirs();
163 new File(objectsDirs
[0], "pack").mkdir();
164 new File(objectsDirs
[0], "info").mkdir();
166 new File(gitDir
, "branches").mkdir();
167 new File(gitDir
, "remotes").mkdir();
168 final String master
= Constants
.HEADS_PREFIX
+ "/" + Constants
.MASTER
;
169 refs
.link(Constants
.HEAD
, master
);
171 getConfig().create();
178 public File
getDirectory() {
183 * @return the directory containg the objects owned by this repository.
185 public File
getObjectsDirectory() {
186 return objectsDirs
[0];
190 * @return the configuration of this repository
192 public RepositoryConfig
getConfig() {
197 * Construct a filename where the loose object having a specified SHA-1
198 * should be stored. If the object is stored in a shared repository the path
199 * to the alternative repo will be returned. If the object is not yet store
200 * a usable path in this repo will be returned. It is assumed that callers
201 * will look for objects in a pack first.
204 * @return suggested file name
206 public File
toFile(final AnyObjectId objectId
) {
207 final String n
= objectId
.toString();
208 String d
=n
.substring(0, 2);
209 String f
=n
.substring(2);
210 for (int i
=0; i
<objectsDirs
.length
; ++i
) {
211 File ret
= new File(new File(objectsDirs
[i
], d
), f
);
215 return new File(new File(objectsDirs
[0], d
), f
);
220 * @return true if the specified object is stored in this repo or any of the
221 * known shared repositories.
223 public boolean hasObject(final AnyObjectId objectId
) {
224 int k
= packs
.length
;
227 if (packs
[--k
].hasObject(objectId
))
231 return toFile(objectId
).isFile();
236 * SHA-1 of an object.
238 * @return a {@link ObjectLoader} for accessing the data of the named
239 * object, or null if the object does not exist.
240 * @throws IOException
242 public ObjectLoader
openObject(final AnyObjectId id
)
244 return openObject(new WindowCursor(),id
);
249 * temporary working space associated with the calling thread.
251 * SHA-1 of an object.
253 * @return a {@link ObjectLoader} for accessing the data of the named
254 * object, or null if the object does not exist.
255 * @throws IOException
257 public ObjectLoader
openObject(final WindowCursor curs
, final AnyObjectId id
)
259 int k
= packs
.length
;
263 final ObjectLoader ol
= packs
[--k
].get(curs
, id
);
266 } catch (IOException ioe
) {
267 // This shouldn't happen unless the pack was corrupted
268 // after we opened it or the VM runs out of memory. This is
269 // a know problem with memory mapped I/O in java and have
270 // been noticed with JDK < 1.6. Tell the gc that now is a good
271 // time to collect and try once more.
275 final ObjectLoader ol
= packs
[k
].get(curs
, id
);
278 } catch (IOException ioe2
) {
279 ioe2
.printStackTrace();
280 ioe
.printStackTrace();
281 // Still fails.. that's BAD, maybe the pack has
282 // been corrupted after all, or the gc didn't manage
283 // to release enough previously mmaped areas.
289 return new UnpackedObjectLoader(this, id
.toObjectId());
290 } catch (FileNotFoundException fnfe
) {
296 * Open object in all packs containing specified object.
299 * id of object to search for
301 * temporary working space associated with the calling thread.
302 * @return collection of loaders for this object, from all packs containing
304 * @throws IOException
306 public Collection
<PackedObjectLoader
> openObjectInAllPacks(
307 final AnyObjectId objectId
, final WindowCursor curs
)
309 Collection
<PackedObjectLoader
> result
= new LinkedList
<PackedObjectLoader
>();
310 openObjectInAllPacks(objectId
, result
, curs
);
315 * Open object in all packs containing specified object.
318 * id of object to search for
319 * @param resultLoaders
320 * result collection of loaders for this object, filled with
321 * loaders from all packs containing specified object
323 * temporary working space associated with the calling thread.
324 * @throws IOException
326 void openObjectInAllPacks(final AnyObjectId objectId
,
327 final Collection
<PackedObjectLoader
> resultLoaders
,
328 final WindowCursor curs
) throws IOException
{
329 for (PackFile pack
: packs
) {
330 final PackedObjectLoader loader
= pack
.get(curs
, objectId
);
332 resultLoaders
.add(loader
);
339 * @return an {@link ObjectLoader} for accessing the data of a named blob
340 * @throws IOException
342 public ObjectLoader
openBlob(final ObjectId id
) throws IOException
{
343 return openObject(id
);
349 * @return an {@link ObjectLoader} for accessing the data of a named tree
350 * @throws IOException
352 public ObjectLoader
openTree(final ObjectId id
) throws IOException
{
353 return openObject(id
);
357 * Access a Commit object using a symbolic reference. This reference may
358 * be a SHA-1 or ref in combination with a number of symbols translating
359 * from one ref or SHA1-1 to another, such as HEAD^ etc.
361 * @param revstr a reference to a git commit object
362 * @return a Commit named by the specified string
363 * @throws IOException for I/O error or unexpected object type.
365 * @see #resolve(String)
367 public Commit
mapCommit(final String revstr
) throws IOException
{
368 final ObjectId id
= resolve(revstr
);
369 return id
!= null ?
mapCommit(id
) : null;
373 * Access any type of Git object by id and
376 * SHA-1 of object to read
377 * @param refName optional, only relevant for simple tags
378 * @return The Git object if found or null
379 * @throws IOException
381 public Object
mapObject(final ObjectId id
, final String refName
) throws IOException
{
382 final ObjectLoader or
= openObject(id
);
383 final byte[] raw
= or
.getBytes();
384 if (or
.getType() == Constants
.OBJ_TREE
)
385 return makeTree(id
, raw
);
386 if (or
.getType() == Constants
.OBJ_COMMIT
)
387 return makeCommit(id
, raw
);
388 if (or
.getType() == Constants
.OBJ_TAG
)
389 return makeTag(id
, refName
, raw
);
390 if (or
.getType() == Constants
.OBJ_BLOB
)
396 * Access a Commit by SHA'1 id.
398 * @return Commit or null
399 * @throws IOException for I/O error or unexpected object type.
401 public Commit
mapCommit(final ObjectId id
) throws IOException
{
402 final ObjectLoader or
= openObject(id
);
405 final byte[] raw
= or
.getBytes();
406 if (Constants
.OBJ_COMMIT
== or
.getType())
407 return new Commit(this, id
, raw
);
408 throw new IncorrectObjectTypeException(id
, Constants
.TYPE_COMMIT
);
411 private Commit
makeCommit(final ObjectId id
, final byte[] raw
) {
412 Commit ret
= new Commit(this, id
, raw
);
417 * Access a Tree object using a symbolic reference. This reference may
418 * be a SHA-1 or ref in combination with a number of symbols translating
419 * from one ref or SHA1-1 to another, such as HEAD^{tree} etc.
421 * @param revstr a reference to a git commit object
422 * @return a Tree named by the specified string
423 * @throws IOException
425 * @see #resolve(String)
427 public Tree
mapTree(final String revstr
) throws IOException
{
428 final ObjectId id
= resolve(revstr
);
429 return id
!= null ?
mapTree(id
) : null;
433 * Access a Tree by SHA'1 id.
435 * @return Tree or null
436 * @throws IOException for I/O error or unexpected object type.
438 public Tree
mapTree(final ObjectId id
) throws IOException
{
439 final ObjectLoader or
= openObject(id
);
442 final byte[] raw
= or
.getBytes();
443 if (Constants
.OBJ_TREE
== or
.getType()) {
444 return new Tree(this, id
, raw
);
446 if (Constants
.OBJ_COMMIT
== or
.getType())
447 return mapTree(ObjectId
.fromString(raw
, 5));
448 throw new IncorrectObjectTypeException(id
, Constants
.TYPE_TREE
);
451 private Tree
makeTree(final ObjectId id
, final byte[] raw
) throws IOException
{
452 Tree ret
= new Tree(this, id
, raw
);
456 private Tag
makeTag(final ObjectId id
, final String refName
, final byte[] raw
) {
457 Tag ret
= new Tag(this, id
, refName
, raw
);
462 * Access a tag by symbolic name.
465 * @return a Tag or null
466 * @throws IOException on I/O error or unexpected type
468 public Tag
mapTag(String revstr
) throws IOException
{
469 final ObjectId id
= resolve(revstr
);
470 return id
!= null ?
mapTag(revstr
, id
) : null;
474 * Access a Tag by SHA'1 id
477 * @return Commit or null
478 * @throws IOException for I/O error or unexpected object type.
480 public Tag
mapTag(final String refName
, final ObjectId id
) throws IOException
{
481 final ObjectLoader or
= openObject(id
);
484 final byte[] raw
= or
.getBytes();
485 if (Constants
.OBJ_TAG
== or
.getType())
486 return new Tag(this, id
, refName
, raw
);
487 return new Tag(this, id
, refName
, null);
491 * Create a command to update (or create) a ref in this repository.
494 * name of the ref the caller wants to modify.
495 * @return an update command. The caller must finish populating this command
496 * and then invoke one of the update methods to actually make a
498 * @throws IOException
499 * a symbolic ref was passed in and could not be resolved back
500 * to the base ref, as the symbolic ref could not be read.
502 public RefUpdate
updateRef(final String ref
) throws IOException
{
503 return refs
.newUpdate(ref
);
507 * Parse a git revision string and return an object id.
509 * Currently supported is combinations of these.
511 * <li>SHA-1 - a SHA-1</li>
512 * <li>refs/... - a ref name</li>
513 * <li>ref^n - nth parent reference</li>
514 * <li>ref~n - distance via parent reference</li>
515 * <li>ref@{n} - nth version of ref</li>
516 * <li>ref^{tree} - tree references by ref</li>
517 * <li>ref^{commit} - commit references by ref</li>
522 * <li>timestamps in reflogs, ref@{full or relative timestamp}</li>
523 * <li>abbreviated SHA-1's</li>
526 * @param revstr A git object references expression
527 * @return an ObjectId
528 * @throws IOException on serious errors
530 public ObjectId
resolve(final String revstr
) throws IOException
{
531 char[] rev
= revstr
.toCharArray();
533 ObjectId refId
= null;
534 for (int i
= 0; i
< rev
.length
; ++i
) {
538 String refstr
= new String(rev
,0,i
);
539 refId
= resolveSimple(refstr
);
543 if (i
+ 1 < rev
.length
) {
544 switch (rev
[i
+ 1]) {
556 ref
= mapObject(refId
, null);
557 if (!(ref
instanceof Commit
))
558 throw new IncorrectObjectTypeException(refId
, Constants
.TYPE_COMMIT
);
559 for (j
=i
+1; j
<rev
.length
; ++j
) {
560 if (!Character
.isDigit(rev
[j
]))
563 String parentnum
= new String(rev
, i
+1, j
-i
-1);
564 int pnum
= Integer
.parseInt(parentnum
);
566 refId
= ((Commit
)ref
).getParentIds()[pnum
- 1];
572 for (k
=i
+2; k
<rev
.length
; ++k
) {
574 item
= new String(rev
, i
+2, k
-i
-2);
580 if (item
.equals("tree")) {
581 ref
= mapObject(refId
, null);
582 while (ref
instanceof Tag
) {
584 refId
= t
.getObjId();
585 ref
= mapObject(refId
, null);
587 if (ref
instanceof Treeish
)
588 refId
= ((Treeish
)ref
).getTreeId();
590 throw new IncorrectObjectTypeException(refId
, Constants
.TYPE_TREE
);
592 else if (item
.equals("commit")) {
593 ref
= mapObject(refId
, null);
594 while (ref
instanceof Tag
) {
596 refId
= t
.getObjId();
597 ref
= mapObject(refId
, null);
599 if (!(ref
instanceof Commit
))
600 throw new IncorrectObjectTypeException(refId
, Constants
.TYPE_COMMIT
);
602 else if (item
.equals("blob")) {
603 ref
= mapObject(refId
, null);
604 while (ref
instanceof Tag
) {
606 refId
= t
.getObjId();
607 ref
= mapObject(refId
, null);
609 if (!(ref
instanceof byte[]))
610 throw new IncorrectObjectTypeException(refId
, Constants
.TYPE_COMMIT
);
612 else if (item
.equals("")) {
613 ref
= mapObject(refId
, null);
614 if (ref
instanceof Tag
)
615 refId
= ((Tag
)ref
).getObjId();
621 throw new RevisionSyntaxException(revstr
);
623 throw new RevisionSyntaxException(revstr
);
626 ref
= mapObject(refId
, null);
627 if (ref
instanceof Commit
)
628 refId
= ((Commit
)ref
).getParentIds()[0];
630 throw new IncorrectObjectTypeException(refId
, Constants
.TYPE_COMMIT
);
634 ref
= mapObject(refId
, null);
635 if (ref
instanceof Commit
)
636 refId
= ((Commit
)ref
).getParentIds()[0];
638 throw new IncorrectObjectTypeException(refId
, Constants
.TYPE_COMMIT
);
643 String refstr
= new String(rev
,0,i
);
644 refId
= resolveSimple(refstr
);
645 ref
= mapCommit(refId
);
648 for (l
= i
+ 1; l
< rev
.length
; ++l
) {
649 if (!Character
.isDigit(rev
[l
]))
652 String distnum
= new String(rev
, i
+1, l
-i
-1);
653 int dist
= Integer
.parseInt(distnum
);
655 refId
= ((Commit
)ref
).getParentIds()[0];
656 ref
= mapCommit(refId
);
664 for (m
=i
+2; m
<rev
.length
; ++m
) {
666 time
= new String(rev
, i
+2, m
-i
-2);
671 throw new RevisionSyntaxException("reflogs not yet supported by revision parser yet", revstr
);
676 throw new RevisionSyntaxException(revstr
);
680 refId
= resolveSimple(revstr
);
684 private ObjectId
resolveSimple(final String revstr
) throws IOException
{
685 if (ObjectId
.isId(revstr
))
686 return ObjectId
.fromString(revstr
);
687 final Ref r
= refs
.readRef(revstr
);
688 return r
!= null ? r
.getObjectId() : null;
692 * Close all resources used by this repository
694 public void close() {
699 for (int k
= packs
.length
- 1; k
>= 0; k
--) {
702 packs
= new PackFile
[0];
706 * Add a single existing pack to the list of available pack files.
709 * path of the pack file to open.
711 * path of the corresponding index file.
712 * @throws IOException
713 * index file could not be opened, read, or is not recognized as
714 * a Git pack file index.
716 public void openPack(final File pack
, final File idx
) throws IOException
{
717 final String p
= pack
.getName();
718 final String i
= idx
.getName();
719 if (p
.length() != 50 || !p
.startsWith("pack-") || !p
.endsWith(".pack"))
720 throw new IllegalArgumentException("Not a valid pack " + pack
);
721 if (i
.length() != 49 || !i
.startsWith("pack-") || !i
.endsWith(".idx"))
722 throw new IllegalArgumentException("Not a valid pack " + idx
);
723 if (!p
.substring(0,45).equals(i
.substring(0,45)))
724 throw new IllegalArgumentException("Pack " + pack
725 + "does not match index " + idx
);
727 final PackFile
[] cur
= packs
;
728 final PackFile
[] arr
= new PackFile
[cur
.length
+ 1];
729 System
.arraycopy(cur
, 0, arr
, 1, cur
.length
);
730 arr
[0] = new PackFile(this, idx
, pack
);
735 * Scan the object dirs, including alternates for packs
738 public void scanForPacks() {
739 final ArrayList
<PackFile
> p
= new ArrayList
<PackFile
>();
740 for (int i
=0; i
<objectsDirs
.length
; ++i
)
741 scanForPacks(new File(objectsDirs
[i
], "pack"), p
);
742 final PackFile
[] arr
= new PackFile
[p
.size()];
747 private void scanForPacks(final File packDir
, Collection
<PackFile
> packList
) {
748 final String
[] idxList
= packDir
.list(new FilenameFilter() {
749 public boolean accept(final File baseDir
, final String n
) {
750 // Must match "pack-[0-9a-f]{40}.idx" to be an index.
751 return n
.length() == 49 && n
.endsWith(".idx")
752 && n
.startsWith("pack-");
755 if (idxList
!= null) {
756 for (final String indexName
: idxList
) {
757 final String n
= indexName
.substring(0, indexName
.length() - 4);
758 final File idxFile
= new File(packDir
, n
+ ".idx");
759 final File packFile
= new File(packDir
, n
+ ".pack");
761 if (!packFile
.isFile()) {
762 // Sometimes C Git's http fetch transport leaves a
763 // .idx file behind and does not download the .pack.
764 // We have to skip over such useless indexes.
770 packList
.add(new PackFile(this, idxFile
, packFile
));
771 } catch (IOException ioe
) {
772 // Whoops. That's not a pack!
774 ioe
.printStackTrace();
781 * Writes a symref (e.g. HEAD) to disk
783 * @param name symref name
784 * @param target pointed to ref
785 * @throws IOException
787 public void writeSymref(final String name
, final String target
)
789 refs
.link(name
, target
);
792 public String
toString() {
793 return "Repository[" + getDirectory() + "]";
797 * @return name of topmost Stacked Git patch.
798 * @throws IOException
800 public String
getPatch() throws IOException
{
801 final File ptr
= new File(getDirectory(),"patches/"+getBranch()+"/applied");
802 final BufferedReader br
= new BufferedReader(new FileReader(ptr
));
806 while ((line
=br
.readLine())!=null) {
816 * @return name of current branch
817 * @throws IOException
819 public String
getFullBranch() throws IOException
{
820 final File ptr
= new File(getDirectory(),"HEAD");
821 final BufferedReader br
= new BufferedReader(new FileReader(ptr
));
828 if (ref
.startsWith("ref: "))
829 ref
= ref
.substring(5);
834 * @return name of current branch.
835 * @throws IOException
837 public String
getBranch() throws IOException
{
839 final File ptr
= new File(getDirectory(), Constants
.HEAD
);
840 final BufferedReader br
= new BufferedReader(new FileReader(ptr
));
847 if (ref
.startsWith("ref: "))
848 ref
= ref
.substring(5);
849 if (ref
.startsWith("refs/heads/"))
850 ref
= ref
.substring(11);
852 } catch (FileNotFoundException e
) {
853 final File ptr
= new File(getDirectory(),"head-name");
854 final BufferedReader br
= new BufferedReader(new FileReader(ptr
));
866 * @return all known refs (heads, tags, remotes).
868 public Map
<String
, Ref
> getAllRefs() {
869 return refs
.getAllRefs();
873 * @return all tags; key is short tag name ("v1.0") and value of the entry
874 * contains the ref with the full tag name ("refs/tags/v1.0").
876 public Map
<String
, Ref
> getTags() {
877 return refs
.getTags();
881 * @return true if HEAD points to a StGit patch.
883 public boolean isStGitMode() {
885 File file
= new File(getDirectory(), "HEAD");
886 BufferedReader reader
= new BufferedReader(new FileReader(file
));
887 String string
= reader
.readLine();
888 if (!string
.startsWith("ref: refs/heads/"))
890 String branch
= string
.substring("ref: refs/heads/".length());
891 File currentPatches
= new File(new File(new File(getDirectory(),
892 "patches"), branch
), "applied");
893 if (!currentPatches
.exists())
895 if (currentPatches
.length() == 0)
899 } catch (IOException e
) {
906 * @return applied patches in a map indexed on current commit id
907 * @throws IOException
909 public Map
<ObjectId
,StGitPatch
> getAppliedPatches() throws IOException
{
910 Map
<ObjectId
,StGitPatch
> ret
= new HashMap
<ObjectId
,StGitPatch
>();
912 File patchDir
= new File(new File(getDirectory(),"patches"),getBranch());
913 BufferedReader apr
= new BufferedReader(new FileReader(new File(patchDir
,"applied")));
914 for (String patchName
=apr
.readLine(); patchName
!=null; patchName
=apr
.readLine()) {
915 File topFile
= new File(new File(new File(patchDir
,"patches"), patchName
), "top");
916 BufferedReader tfr
= new BufferedReader(new FileReader(topFile
));
917 String objectId
= tfr
.readLine();
918 ObjectId id
= ObjectId
.fromString(objectId
);
919 ret
.put(id
, new StGitPatch(patchName
, id
));
927 /** Clean up stale caches */
928 public void refreshFromDisk() {
933 * @return a representation of the index associated with this repo
934 * @throws IOException
936 public GitIndex
getIndex() throws IOException
{
938 index
= new GitIndex(this);
941 index
.rereadIfNecessary();
946 static byte[] gitInternalSlash(byte[] bytes
) {
947 if (File
.separatorChar
== '/')
949 for (int i
=0; i
<bytes
.length
; ++i
)
950 if (bytes
[i
] == File
.separatorChar
)
956 * @return an important state
958 public RepositoryState
getRepositoryState() {
959 if (new File(getWorkDir(), ".dotest").exists())
960 return RepositoryState
.REBASING
;
961 if (new File(gitDir
,".dotest-merge").exists())
962 return RepositoryState
.REBASING_INTERACTIVE
;
963 if (new File(gitDir
,"MERGE_HEAD").exists())
964 return RepositoryState
.MERGING
;
965 if (new File(gitDir
,"BISECT_LOG").exists())
966 return RepositoryState
.BISECTING
;
967 return RepositoryState
.SAFE
;
971 * Check validty of a ref name. It must not contain character that has
972 * a special meaning in a Git object reference expression. Some other
973 * dangerous characters are also excluded.
977 * @return true if refName is a valid ref name
979 public static boolean isValidRefName(final String refName
) {
980 final int len
= refName
.length();
982 for (int i
=0; i
<len
; ++i
) {
983 char c
= refName
.charAt(i
);
1001 case '~': case '^': case ':':
1013 * String work dir and return normalized repository path
1015 * @param wd Work dir
1016 * @param f File whose path shall be stripp off it's workdir
1017 * @return normalized repository relative path
1019 public static String
stripWorkDir(File wd
, File f
) {
1020 String relName
= f
.getPath().substring(wd
.getPath().length() + 1);
1021 relName
= relName
.replace(File
.separatorChar
, '/');
1026 * @return the workdir file, i.e. where the files are checked out
1028 public File
getWorkDir() {
1029 return getDirectory().getParentFile();