You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Repository.java 56KB

Rewrite reference handling to be abstract and accurate This commit actually does three major changes to the way references are handled within JGit. Unfortunately they were easier to do as a single massive commit than to break them up into smaller units. Disambiguate symbolic references: --------------------------------- Reporting a symbolic reference such as HEAD as though it were any other normal reference like refs/heads/master causes subtle programming errors. We have been bitten by this error on several occasions, as have some downstream applications written by myself. Instead of reporting HEAD as a reference whose name differs from its "original name", report it as an actual SymbolicRef object that the application can test the type and examine the target of. With this change, Ref is now an abstract type with different subclasses for the different types. In the classical example of "HEAD" being a symbolic reference to branch "refs/heads/master", the Repository.getAllRefs() method will now return: Map<String, Ref> all = repository.getAllRefs(); SymbolicRef HEAD = (SymbolicRef) all.get("HEAD"); ObjectIdRef master = (ObjectIdRef) all.get("refs/heads/master"); assertSame(master, HEAD.getTarget()); assertSame(master.getObjectId(), HEAD.getObjectId()); assertEquals("HEAD", HEAD.getName()); assertEquals("refs/heads/master", master.getName()); A nice side-effect of this change is the storage type of the symbolic reference is no longer ambiguous with the storge type of the underlying reference it targets. In the above example, if master was only available in the packed-refs file, then the following is also true: assertSame(Ref.Storage.LOOSE, HEAD.getStorage()); assertSame(Ref.Storage.PACKED, master.getStorage()); (Prior to this change we returned the ambiguous storage of LOOSE_PACKED for HEAD, which was confusing since it wasn't actually true on disk). Another nice side-effect of this change is all intermediate symbolic references are preserved, and are therefore visible to the application when they walk the target chain. We can now correctly inspect chains of symbolic references. As a result of this change the Ref.getOrigName() method has been removed from the API. Applications should identify a symbolic reference by testing for isSymbolic() and not by using an arcane string comparsion between properties. Abstract the RefDatabase storage: --------------------------------- RefDatabase is now abstract, similar to ObjectDatabase, and a new concrete implementation called RefDirectory is used for the traditional on-disk storage layout. In the future we plan to support additional implementations, such as a pure in-memory RefDatabase for unit testing purposes. Optimize RefDirectory: ---------------------- The implementation of the in-memory reference cache, reading, and update routines has been completely rewritten. Much of the code was heavily borrowed or cribbed from the prior implementation, so copyright notices have been left intact as much as possible. The RefDirectory cache no longer confuses symbolic references with normal references. This permits the cache to resolve the value of a symbolic reference as late as possible, ensuring it is always current, without needing to maintain reverse pointers. The cache is now 2 sorted RefLists, rather than 3 HashMaps. Using sorted lists allows the implementation to reduce the in-memory footprint when storing many refs. Using specialized types for the elements allows the code to avoid additional map lookups for auxiliary stat information. To improve scan time during getRefs(), the lists are returned via a copy-on-write contract. Most callers of getRefs() do not modify the returned collections, so the copy-on-write semantics improves access on repositories with a large number of packed references. Iterator traversals of the returned Map<String,Ref> are performed using a simple merge-join of the two cache lists, ensuring we can perform the entire traversal in linear time as a function of the number of references: O(PackedRefs + LooseRefs). Scans of the loose reference space to update the cache run in O(LooseRefs log LooseRefs) time, as the directory contents are sorted before being merged against the in-memory cache. Since the majority of stable references are kept packed, there typically are only a handful of reference names to be sorted, so the sorting cost should not be very high. Locking is reduced during getRefs() by taking advantage of the copy-on-write semantics of the improved cache data structure. This permits concurrent readers to pull back references without blocking each other. If there is contention updating the cache during a scan, one or more updates are simply skipped and will get picked up again in a future scan. Writing to the $GIT_DIR/packed-refs during reference delete is now fully atomic. The file is locked, reparsed fresh, and written back out if a change is necessary. This avoids all race conditions with concurrent external updates of the packed-refs file. The RefLogWriter class has been fully folded into RefDirectory and is therefore deleted. Maintaining the reference's log is the responsiblity of the database implementation, and not all implementations will use java.io for access. Future work still remains to be done to abstract the ReflogReader class away from local disk IO. Change-Id: I26b9287c45a4b2d2be35ba2849daa316f5eec85d Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Redo event listeners to be more generic Replace the old crude event listener system with a much more generic implementation, patterned after the event dispatch techniques used in Google Web Toolkit 1.5 and later. Each event delivers to an interface that defines a single method, and the event itself is what performs the delivery in a type-safe way through its own dispatch method. Listeners are registered in a generic listener list, indexed by the interface they implement and wish to receive an event for. Delivery of events is performed by looping through all listeners implementing the event's corresponding listener interface, and using the event's own dispatch method to deliver the event. This is the classical "double dispatch" pattern for event delivery. Listeners can be unregistered by invoking remove() on their registration handle. This change therefore requires application code to track the handle if it wishes to remove the listener at a later point in time. Event delivery is now exposed as a generic public method on the Repository class, making it easier for any type of message to be sent out to any type of listener that has registered, without needing to pre-arrange for type-safe fireFoo() methods. New event types can be added in the future simply by defining a new RepositoryEvent subclass and a corresponding RepositoryListener interface that it dispatches to. By always adding new events through a new interface, we never need to worry about defining an Adapter to provide default no-op implementations of new event methods. Change-Id: I651417b3098b9afc93d91085e9f0b2265df8fc81 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Redo event listeners to be more generic Replace the old crude event listener system with a much more generic implementation, patterned after the event dispatch techniques used in Google Web Toolkit 1.5 and later. Each event delivers to an interface that defines a single method, and the event itself is what performs the delivery in a type-safe way through its own dispatch method. Listeners are registered in a generic listener list, indexed by the interface they implement and wish to receive an event for. Delivery of events is performed by looping through all listeners implementing the event's corresponding listener interface, and using the event's own dispatch method to deliver the event. This is the classical "double dispatch" pattern for event delivery. Listeners can be unregistered by invoking remove() on their registration handle. This change therefore requires application code to track the handle if it wishes to remove the listener at a later point in time. Event delivery is now exposed as a generic public method on the Repository class, making it easier for any type of message to be sent out to any type of listener that has registered, without needing to pre-arrange for type-safe fireFoo() methods. New event types can be added in the future simply by defining a new RepositoryEvent subclass and a corresponding RepositoryListener interface that it dispatches to. By always adding new events through a new interface, we never need to worry about defining an Adapter to provide default no-op implementations of new event methods. Change-Id: I651417b3098b9afc93d91085e9f0b2265df8fc81 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Redo event listeners to be more generic Replace the old crude event listener system with a much more generic implementation, patterned after the event dispatch techniques used in Google Web Toolkit 1.5 and later. Each event delivers to an interface that defines a single method, and the event itself is what performs the delivery in a type-safe way through its own dispatch method. Listeners are registered in a generic listener list, indexed by the interface they implement and wish to receive an event for. Delivery of events is performed by looping through all listeners implementing the event's corresponding listener interface, and using the event's own dispatch method to deliver the event. This is the classical "double dispatch" pattern for event delivery. Listeners can be unregistered by invoking remove() on their registration handle. This change therefore requires application code to track the handle if it wishes to remove the listener at a later point in time. Event delivery is now exposed as a generic public method on the Repository class, making it easier for any type of message to be sent out to any type of listener that has registered, without needing to pre-arrange for type-safe fireFoo() methods. New event types can be added in the future simply by defining a new RepositoryEvent subclass and a corresponding RepositoryListener interface that it dispatches to. By always adding new events through a new interface, we never need to worry about defining an Adapter to provide default no-op implementations of new event methods. Change-Id: I651417b3098b9afc93d91085e9f0b2265df8fc81 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Redo event listeners to be more generic Replace the old crude event listener system with a much more generic implementation, patterned after the event dispatch techniques used in Google Web Toolkit 1.5 and later. Each event delivers to an interface that defines a single method, and the event itself is what performs the delivery in a type-safe way through its own dispatch method. Listeners are registered in a generic listener list, indexed by the interface they implement and wish to receive an event for. Delivery of events is performed by looping through all listeners implementing the event's corresponding listener interface, and using the event's own dispatch method to deliver the event. This is the classical "double dispatch" pattern for event delivery. Listeners can be unregistered by invoking remove() on their registration handle. This change therefore requires application code to track the handle if it wishes to remove the listener at a later point in time. Event delivery is now exposed as a generic public method on the Repository class, making it easier for any type of message to be sent out to any type of listener that has registered, without needing to pre-arrange for type-safe fireFoo() methods. New event types can be added in the future simply by defining a new RepositoryEvent subclass and a corresponding RepositoryListener interface that it dispatches to. By always adding new events through a new interface, we never need to worry about defining an Adapter to provide default no-op implementations of new event methods. Change-Id: I651417b3098b9afc93d91085e9f0b2265df8fc81 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Redo event listeners to be more generic Replace the old crude event listener system with a much more generic implementation, patterned after the event dispatch techniques used in Google Web Toolkit 1.5 and later. Each event delivers to an interface that defines a single method, and the event itself is what performs the delivery in a type-safe way through its own dispatch method. Listeners are registered in a generic listener list, indexed by the interface they implement and wish to receive an event for. Delivery of events is performed by looping through all listeners implementing the event's corresponding listener interface, and using the event's own dispatch method to deliver the event. This is the classical "double dispatch" pattern for event delivery. Listeners can be unregistered by invoking remove() on their registration handle. This change therefore requires application code to track the handle if it wishes to remove the listener at a later point in time. Event delivery is now exposed as a generic public method on the Repository class, making it easier for any type of message to be sent out to any type of listener that has registered, without needing to pre-arrange for type-safe fireFoo() methods. New event types can be added in the future simply by defining a new RepositoryEvent subclass and a corresponding RepositoryListener interface that it dispatches to. By always adding new events through a new interface, we never need to worry about defining an Adapter to provide default no-op implementations of new event methods. Change-Id: I651417b3098b9afc93d91085e9f0b2265df8fc81 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Redo event listeners to be more generic Replace the old crude event listener system with a much more generic implementation, patterned after the event dispatch techniques used in Google Web Toolkit 1.5 and later. Each event delivers to an interface that defines a single method, and the event itself is what performs the delivery in a type-safe way through its own dispatch method. Listeners are registered in a generic listener list, indexed by the interface they implement and wish to receive an event for. Delivery of events is performed by looping through all listeners implementing the event's corresponding listener interface, and using the event's own dispatch method to deliver the event. This is the classical "double dispatch" pattern for event delivery. Listeners can be unregistered by invoking remove() on their registration handle. This change therefore requires application code to track the handle if it wishes to remove the listener at a later point in time. Event delivery is now exposed as a generic public method on the Repository class, making it easier for any type of message to be sent out to any type of listener that has registered, without needing to pre-arrange for type-safe fireFoo() methods. New event types can be added in the future simply by defining a new RepositoryEvent subclass and a corresponding RepositoryListener interface that it dispatches to. By always adding new events through a new interface, we never need to worry about defining an Adapter to provide default no-op implementations of new event methods. Change-Id: I651417b3098b9afc93d91085e9f0b2265df8fc81 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Redo event listeners to be more generic Replace the old crude event listener system with a much more generic implementation, patterned after the event dispatch techniques used in Google Web Toolkit 1.5 and later. Each event delivers to an interface that defines a single method, and the event itself is what performs the delivery in a type-safe way through its own dispatch method. Listeners are registered in a generic listener list, indexed by the interface they implement and wish to receive an event for. Delivery of events is performed by looping through all listeners implementing the event's corresponding listener interface, and using the event's own dispatch method to deliver the event. This is the classical "double dispatch" pattern for event delivery. Listeners can be unregistered by invoking remove() on their registration handle. This change therefore requires application code to track the handle if it wishes to remove the listener at a later point in time. Event delivery is now exposed as a generic public method on the Repository class, making it easier for any type of message to be sent out to any type of listener that has registered, without needing to pre-arrange for type-safe fireFoo() methods. New event types can be added in the future simply by defining a new RepositoryEvent subclass and a corresponding RepositoryListener interface that it dispatches to. By always adding new events through a new interface, we never need to worry about defining an Adapter to provide default no-op implementations of new event methods. Change-Id: I651417b3098b9afc93d91085e9f0b2265df8fc81 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Redo event listeners to be more generic Replace the old crude event listener system with a much more generic implementation, patterned after the event dispatch techniques used in Google Web Toolkit 1.5 and later. Each event delivers to an interface that defines a single method, and the event itself is what performs the delivery in a type-safe way through its own dispatch method. Listeners are registered in a generic listener list, indexed by the interface they implement and wish to receive an event for. Delivery of events is performed by looping through all listeners implementing the event's corresponding listener interface, and using the event's own dispatch method to deliver the event. This is the classical "double dispatch" pattern for event delivery. Listeners can be unregistered by invoking remove() on their registration handle. This change therefore requires application code to track the handle if it wishes to remove the listener at a later point in time. Event delivery is now exposed as a generic public method on the Repository class, making it easier for any type of message to be sent out to any type of listener that has registered, without needing to pre-arrange for type-safe fireFoo() methods. New event types can be added in the future simply by defining a new RepositoryEvent subclass and a corresponding RepositoryListener interface that it dispatches to. By always adding new events through a new interface, we never need to worry about defining an Adapter to provide default no-op implementations of new event methods. Change-Id: I651417b3098b9afc93d91085e9f0b2265df8fc81 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Rewrite reference handling to be abstract and accurate This commit actually does three major changes to the way references are handled within JGit. Unfortunately they were easier to do as a single massive commit than to break them up into smaller units. Disambiguate symbolic references: --------------------------------- Reporting a symbolic reference such as HEAD as though it were any other normal reference like refs/heads/master causes subtle programming errors. We have been bitten by this error on several occasions, as have some downstream applications written by myself. Instead of reporting HEAD as a reference whose name differs from its "original name", report it as an actual SymbolicRef object that the application can test the type and examine the target of. With this change, Ref is now an abstract type with different subclasses for the different types. In the classical example of "HEAD" being a symbolic reference to branch "refs/heads/master", the Repository.getAllRefs() method will now return: Map<String, Ref> all = repository.getAllRefs(); SymbolicRef HEAD = (SymbolicRef) all.get("HEAD"); ObjectIdRef master = (ObjectIdRef) all.get("refs/heads/master"); assertSame(master, HEAD.getTarget()); assertSame(master.getObjectId(), HEAD.getObjectId()); assertEquals("HEAD", HEAD.getName()); assertEquals("refs/heads/master", master.getName()); A nice side-effect of this change is the storage type of the symbolic reference is no longer ambiguous with the storge type of the underlying reference it targets. In the above example, if master was only available in the packed-refs file, then the following is also true: assertSame(Ref.Storage.LOOSE, HEAD.getStorage()); assertSame(Ref.Storage.PACKED, master.getStorage()); (Prior to this change we returned the ambiguous storage of LOOSE_PACKED for HEAD, which was confusing since it wasn't actually true on disk). Another nice side-effect of this change is all intermediate symbolic references are preserved, and are therefore visible to the application when they walk the target chain. We can now correctly inspect chains of symbolic references. As a result of this change the Ref.getOrigName() method has been removed from the API. Applications should identify a symbolic reference by testing for isSymbolic() and not by using an arcane string comparsion between properties. Abstract the RefDatabase storage: --------------------------------- RefDatabase is now abstract, similar to ObjectDatabase, and a new concrete implementation called RefDirectory is used for the traditional on-disk storage layout. In the future we plan to support additional implementations, such as a pure in-memory RefDatabase for unit testing purposes. Optimize RefDirectory: ---------------------- The implementation of the in-memory reference cache, reading, and update routines has been completely rewritten. Much of the code was heavily borrowed or cribbed from the prior implementation, so copyright notices have been left intact as much as possible. The RefDirectory cache no longer confuses symbolic references with normal references. This permits the cache to resolve the value of a symbolic reference as late as possible, ensuring it is always current, without needing to maintain reverse pointers. The cache is now 2 sorted RefLists, rather than 3 HashMaps. Using sorted lists allows the implementation to reduce the in-memory footprint when storing many refs. Using specialized types for the elements allows the code to avoid additional map lookups for auxiliary stat information. To improve scan time during getRefs(), the lists are returned via a copy-on-write contract. Most callers of getRefs() do not modify the returned collections, so the copy-on-write semantics improves access on repositories with a large number of packed references. Iterator traversals of the returned Map<String,Ref> are performed using a simple merge-join of the two cache lists, ensuring we can perform the entire traversal in linear time as a function of the number of references: O(PackedRefs + LooseRefs). Scans of the loose reference space to update the cache run in O(LooseRefs log LooseRefs) time, as the directory contents are sorted before being merged against the in-memory cache. Since the majority of stable references are kept packed, there typically are only a handful of reference names to be sorted, so the sorting cost should not be very high. Locking is reduced during getRefs() by taking advantage of the copy-on-write semantics of the improved cache data structure. This permits concurrent readers to pull back references without blocking each other. If there is contention updating the cache during a scan, one or more updates are simply skipped and will get picked up again in a future scan. Writing to the $GIT_DIR/packed-refs during reference delete is now fully atomic. The file is locked, reparsed fresh, and written back out if a change is necessary. This avoids all race conditions with concurrent external updates of the packed-refs file. The RefLogWriter class has been fully folded into RefDirectory and is therefore deleted. Maintaining the reference's log is the responsiblity of the database implementation, and not all implementations will use java.io for access. Future work still remains to be done to abstract the ReflogReader class away from local disk IO. Change-Id: I26b9287c45a4b2d2be35ba2849daa316f5eec85d Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Rewrite reference handling to be abstract and accurate This commit actually does three major changes to the way references are handled within JGit. Unfortunately they were easier to do as a single massive commit than to break them up into smaller units. Disambiguate symbolic references: --------------------------------- Reporting a symbolic reference such as HEAD as though it were any other normal reference like refs/heads/master causes subtle programming errors. We have been bitten by this error on several occasions, as have some downstream applications written by myself. Instead of reporting HEAD as a reference whose name differs from its "original name", report it as an actual SymbolicRef object that the application can test the type and examine the target of. With this change, Ref is now an abstract type with different subclasses for the different types. In the classical example of "HEAD" being a symbolic reference to branch "refs/heads/master", the Repository.getAllRefs() method will now return: Map<String, Ref> all = repository.getAllRefs(); SymbolicRef HEAD = (SymbolicRef) all.get("HEAD"); ObjectIdRef master = (ObjectIdRef) all.get("refs/heads/master"); assertSame(master, HEAD.getTarget()); assertSame(master.getObjectId(), HEAD.getObjectId()); assertEquals("HEAD", HEAD.getName()); assertEquals("refs/heads/master", master.getName()); A nice side-effect of this change is the storage type of the symbolic reference is no longer ambiguous with the storge type of the underlying reference it targets. In the above example, if master was only available in the packed-refs file, then the following is also true: assertSame(Ref.Storage.LOOSE, HEAD.getStorage()); assertSame(Ref.Storage.PACKED, master.getStorage()); (Prior to this change we returned the ambiguous storage of LOOSE_PACKED for HEAD, which was confusing since it wasn't actually true on disk). Another nice side-effect of this change is all intermediate symbolic references are preserved, and are therefore visible to the application when they walk the target chain. We can now correctly inspect chains of symbolic references. As a result of this change the Ref.getOrigName() method has been removed from the API. Applications should identify a symbolic reference by testing for isSymbolic() and not by using an arcane string comparsion between properties. Abstract the RefDatabase storage: --------------------------------- RefDatabase is now abstract, similar to ObjectDatabase, and a new concrete implementation called RefDirectory is used for the traditional on-disk storage layout. In the future we plan to support additional implementations, such as a pure in-memory RefDatabase for unit testing purposes. Optimize RefDirectory: ---------------------- The implementation of the in-memory reference cache, reading, and update routines has been completely rewritten. Much of the code was heavily borrowed or cribbed from the prior implementation, so copyright notices have been left intact as much as possible. The RefDirectory cache no longer confuses symbolic references with normal references. This permits the cache to resolve the value of a symbolic reference as late as possible, ensuring it is always current, without needing to maintain reverse pointers. The cache is now 2 sorted RefLists, rather than 3 HashMaps. Using sorted lists allows the implementation to reduce the in-memory footprint when storing many refs. Using specialized types for the elements allows the code to avoid additional map lookups for auxiliary stat information. To improve scan time during getRefs(), the lists are returned via a copy-on-write contract. Most callers of getRefs() do not modify the returned collections, so the copy-on-write semantics improves access on repositories with a large number of packed references. Iterator traversals of the returned Map<String,Ref> are performed using a simple merge-join of the two cache lists, ensuring we can perform the entire traversal in linear time as a function of the number of references: O(PackedRefs + LooseRefs). Scans of the loose reference space to update the cache run in O(LooseRefs log LooseRefs) time, as the directory contents are sorted before being merged against the in-memory cache. Since the majority of stable references are kept packed, there typically are only a handful of reference names to be sorted, so the sorting cost should not be very high. Locking is reduced during getRefs() by taking advantage of the copy-on-write semantics of the improved cache data structure. This permits concurrent readers to pull back references without blocking each other. If there is contention updating the cache during a scan, one or more updates are simply skipped and will get picked up again in a future scan. Writing to the $GIT_DIR/packed-refs during reference delete is now fully atomic. The file is locked, reparsed fresh, and written back out if a change is necessary. This avoids all race conditions with concurrent external updates of the packed-refs file. The RefLogWriter class has been fully folded into RefDirectory and is therefore deleted. Maintaining the reference's log is the responsiblity of the database implementation, and not all implementations will use java.io for access. Future work still remains to be done to abstract the ReflogReader class away from local disk IO. Change-Id: I26b9287c45a4b2d2be35ba2849daa316f5eec85d Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Rewrite reference handling to be abstract and accurate This commit actually does three major changes to the way references are handled within JGit. Unfortunately they were easier to do as a single massive commit than to break them up into smaller units. Disambiguate symbolic references: --------------------------------- Reporting a symbolic reference such as HEAD as though it were any other normal reference like refs/heads/master causes subtle programming errors. We have been bitten by this error on several occasions, as have some downstream applications written by myself. Instead of reporting HEAD as a reference whose name differs from its "original name", report it as an actual SymbolicRef object that the application can test the type and examine the target of. With this change, Ref is now an abstract type with different subclasses for the different types. In the classical example of "HEAD" being a symbolic reference to branch "refs/heads/master", the Repository.getAllRefs() method will now return: Map<String, Ref> all = repository.getAllRefs(); SymbolicRef HEAD = (SymbolicRef) all.get("HEAD"); ObjectIdRef master = (ObjectIdRef) all.get("refs/heads/master"); assertSame(master, HEAD.getTarget()); assertSame(master.getObjectId(), HEAD.getObjectId()); assertEquals("HEAD", HEAD.getName()); assertEquals("refs/heads/master", master.getName()); A nice side-effect of this change is the storage type of the symbolic reference is no longer ambiguous with the storge type of the underlying reference it targets. In the above example, if master was only available in the packed-refs file, then the following is also true: assertSame(Ref.Storage.LOOSE, HEAD.getStorage()); assertSame(Ref.Storage.PACKED, master.getStorage()); (Prior to this change we returned the ambiguous storage of LOOSE_PACKED for HEAD, which was confusing since it wasn't actually true on disk). Another nice side-effect of this change is all intermediate symbolic references are preserved, and are therefore visible to the application when they walk the target chain. We can now correctly inspect chains of symbolic references. As a result of this change the Ref.getOrigName() method has been removed from the API. Applications should identify a symbolic reference by testing for isSymbolic() and not by using an arcane string comparsion between properties. Abstract the RefDatabase storage: --------------------------------- RefDatabase is now abstract, similar to ObjectDatabase, and a new concrete implementation called RefDirectory is used for the traditional on-disk storage layout. In the future we plan to support additional implementations, such as a pure in-memory RefDatabase for unit testing purposes. Optimize RefDirectory: ---------------------- The implementation of the in-memory reference cache, reading, and update routines has been completely rewritten. Much of the code was heavily borrowed or cribbed from the prior implementation, so copyright notices have been left intact as much as possible. The RefDirectory cache no longer confuses symbolic references with normal references. This permits the cache to resolve the value of a symbolic reference as late as possible, ensuring it is always current, without needing to maintain reverse pointers. The cache is now 2 sorted RefLists, rather than 3 HashMaps. Using sorted lists allows the implementation to reduce the in-memory footprint when storing many refs. Using specialized types for the elements allows the code to avoid additional map lookups for auxiliary stat information. To improve scan time during getRefs(), the lists are returned via a copy-on-write contract. Most callers of getRefs() do not modify the returned collections, so the copy-on-write semantics improves access on repositories with a large number of packed references. Iterator traversals of the returned Map<String,Ref> are performed using a simple merge-join of the two cache lists, ensuring we can perform the entire traversal in linear time as a function of the number of references: O(PackedRefs + LooseRefs). Scans of the loose reference space to update the cache run in O(LooseRefs log LooseRefs) time, as the directory contents are sorted before being merged against the in-memory cache. Since the majority of stable references are kept packed, there typically are only a handful of reference names to be sorted, so the sorting cost should not be very high. Locking is reduced during getRefs() by taking advantage of the copy-on-write semantics of the improved cache data structure. This permits concurrent readers to pull back references without blocking each other. If there is contention updating the cache during a scan, one or more updates are simply skipped and will get picked up again in a future scan. Writing to the $GIT_DIR/packed-refs during reference delete is now fully atomic. The file is locked, reparsed fresh, and written back out if a change is necessary. This avoids all race conditions with concurrent external updates of the packed-refs file. The RefLogWriter class has been fully folded into RefDirectory and is therefore deleted. Maintaining the reference's log is the responsiblity of the database implementation, and not all implementations will use java.io for access. Future work still remains to be done to abstract the ReflogReader class away from local disk IO. Change-Id: I26b9287c45a4b2d2be35ba2849daa316f5eec85d Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Rewrite reference handling to be abstract and accurate This commit actually does three major changes to the way references are handled within JGit. Unfortunately they were easier to do as a single massive commit than to break them up into smaller units. Disambiguate symbolic references: --------------------------------- Reporting a symbolic reference such as HEAD as though it were any other normal reference like refs/heads/master causes subtle programming errors. We have been bitten by this error on several occasions, as have some downstream applications written by myself. Instead of reporting HEAD as a reference whose name differs from its "original name", report it as an actual SymbolicRef object that the application can test the type and examine the target of. With this change, Ref is now an abstract type with different subclasses for the different types. In the classical example of "HEAD" being a symbolic reference to branch "refs/heads/master", the Repository.getAllRefs() method will now return: Map<String, Ref> all = repository.getAllRefs(); SymbolicRef HEAD = (SymbolicRef) all.get("HEAD"); ObjectIdRef master = (ObjectIdRef) all.get("refs/heads/master"); assertSame(master, HEAD.getTarget()); assertSame(master.getObjectId(), HEAD.getObjectId()); assertEquals("HEAD", HEAD.getName()); assertEquals("refs/heads/master", master.getName()); A nice side-effect of this change is the storage type of the symbolic reference is no longer ambiguous with the storge type of the underlying reference it targets. In the above example, if master was only available in the packed-refs file, then the following is also true: assertSame(Ref.Storage.LOOSE, HEAD.getStorage()); assertSame(Ref.Storage.PACKED, master.getStorage()); (Prior to this change we returned the ambiguous storage of LOOSE_PACKED for HEAD, which was confusing since it wasn't actually true on disk). Another nice side-effect of this change is all intermediate symbolic references are preserved, and are therefore visible to the application when they walk the target chain. We can now correctly inspect chains of symbolic references. As a result of this change the Ref.getOrigName() method has been removed from the API. Applications should identify a symbolic reference by testing for isSymbolic() and not by using an arcane string comparsion between properties. Abstract the RefDatabase storage: --------------------------------- RefDatabase is now abstract, similar to ObjectDatabase, and a new concrete implementation called RefDirectory is used for the traditional on-disk storage layout. In the future we plan to support additional implementations, such as a pure in-memory RefDatabase for unit testing purposes. Optimize RefDirectory: ---------------------- The implementation of the in-memory reference cache, reading, and update routines has been completely rewritten. Much of the code was heavily borrowed or cribbed from the prior implementation, so copyright notices have been left intact as much as possible. The RefDirectory cache no longer confuses symbolic references with normal references. This permits the cache to resolve the value of a symbolic reference as late as possible, ensuring it is always current, without needing to maintain reverse pointers. The cache is now 2 sorted RefLists, rather than 3 HashMaps. Using sorted lists allows the implementation to reduce the in-memory footprint when storing many refs. Using specialized types for the elements allows the code to avoid additional map lookups for auxiliary stat information. To improve scan time during getRefs(), the lists are returned via a copy-on-write contract. Most callers of getRefs() do not modify the returned collections, so the copy-on-write semantics improves access on repositories with a large number of packed references. Iterator traversals of the returned Map<String,Ref> are performed using a simple merge-join of the two cache lists, ensuring we can perform the entire traversal in linear time as a function of the number of references: O(PackedRefs + LooseRefs). Scans of the loose reference space to update the cache run in O(LooseRefs log LooseRefs) time, as the directory contents are sorted before being merged against the in-memory cache. Since the majority of stable references are kept packed, there typically are only a handful of reference names to be sorted, so the sorting cost should not be very high. Locking is reduced during getRefs() by taking advantage of the copy-on-write semantics of the improved cache data structure. This permits concurrent readers to pull back references without blocking each other. If there is contention updating the cache during a scan, one or more updates are simply skipped and will get picked up again in a future scan. Writing to the $GIT_DIR/packed-refs during reference delete is now fully atomic. The file is locked, reparsed fresh, and written back out if a change is necessary. This avoids all race conditions with concurrent external updates of the packed-refs file. The RefLogWriter class has been fully folded into RefDirectory and is therefore deleted. Maintaining the reference's log is the responsiblity of the database implementation, and not all implementations will use java.io for access. Future work still remains to be done to abstract the ReflogReader class away from local disk IO. Change-Id: I26b9287c45a4b2d2be35ba2849daa316f5eec85d Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Rewrite reference handling to be abstract and accurate This commit actually does three major changes to the way references are handled within JGit. Unfortunately they were easier to do as a single massive commit than to break them up into smaller units. Disambiguate symbolic references: --------------------------------- Reporting a symbolic reference such as HEAD as though it were any other normal reference like refs/heads/master causes subtle programming errors. We have been bitten by this error on several occasions, as have some downstream applications written by myself. Instead of reporting HEAD as a reference whose name differs from its "original name", report it as an actual SymbolicRef object that the application can test the type and examine the target of. With this change, Ref is now an abstract type with different subclasses for the different types. In the classical example of "HEAD" being a symbolic reference to branch "refs/heads/master", the Repository.getAllRefs() method will now return: Map<String, Ref> all = repository.getAllRefs(); SymbolicRef HEAD = (SymbolicRef) all.get("HEAD"); ObjectIdRef master = (ObjectIdRef) all.get("refs/heads/master"); assertSame(master, HEAD.getTarget()); assertSame(master.getObjectId(), HEAD.getObjectId()); assertEquals("HEAD", HEAD.getName()); assertEquals("refs/heads/master", master.getName()); A nice side-effect of this change is the storage type of the symbolic reference is no longer ambiguous with the storge type of the underlying reference it targets. In the above example, if master was only available in the packed-refs file, then the following is also true: assertSame(Ref.Storage.LOOSE, HEAD.getStorage()); assertSame(Ref.Storage.PACKED, master.getStorage()); (Prior to this change we returned the ambiguous storage of LOOSE_PACKED for HEAD, which was confusing since it wasn't actually true on disk). Another nice side-effect of this change is all intermediate symbolic references are preserved, and are therefore visible to the application when they walk the target chain. We can now correctly inspect chains of symbolic references. As a result of this change the Ref.getOrigName() method has been removed from the API. Applications should identify a symbolic reference by testing for isSymbolic() and not by using an arcane string comparsion between properties. Abstract the RefDatabase storage: --------------------------------- RefDatabase is now abstract, similar to ObjectDatabase, and a new concrete implementation called RefDirectory is used for the traditional on-disk storage layout. In the future we plan to support additional implementations, such as a pure in-memory RefDatabase for unit testing purposes. Optimize RefDirectory: ---------------------- The implementation of the in-memory reference cache, reading, and update routines has been completely rewritten. Much of the code was heavily borrowed or cribbed from the prior implementation, so copyright notices have been left intact as much as possible. The RefDirectory cache no longer confuses symbolic references with normal references. This permits the cache to resolve the value of a symbolic reference as late as possible, ensuring it is always current, without needing to maintain reverse pointers. The cache is now 2 sorted RefLists, rather than 3 HashMaps. Using sorted lists allows the implementation to reduce the in-memory footprint when storing many refs. Using specialized types for the elements allows the code to avoid additional map lookups for auxiliary stat information. To improve scan time during getRefs(), the lists are returned via a copy-on-write contract. Most callers of getRefs() do not modify the returned collections, so the copy-on-write semantics improves access on repositories with a large number of packed references. Iterator traversals of the returned Map<String,Ref> are performed using a simple merge-join of the two cache lists, ensuring we can perform the entire traversal in linear time as a function of the number of references: O(PackedRefs + LooseRefs). Scans of the loose reference space to update the cache run in O(LooseRefs log LooseRefs) time, as the directory contents are sorted before being merged against the in-memory cache. Since the majority of stable references are kept packed, there typically are only a handful of reference names to be sorted, so the sorting cost should not be very high. Locking is reduced during getRefs() by taking advantage of the copy-on-write semantics of the improved cache data structure. This permits concurrent readers to pull back references without blocking each other. If there is contention updating the cache during a scan, one or more updates are simply skipped and will get picked up again in a future scan. Writing to the $GIT_DIR/packed-refs during reference delete is now fully atomic. The file is locked, reparsed fresh, and written back out if a change is necessary. This avoids all race conditions with concurrent external updates of the packed-refs file. The RefLogWriter class has been fully folded into RefDirectory and is therefore deleted. Maintaining the reference's log is the responsiblity of the database implementation, and not all implementations will use java.io for access. Future work still remains to be done to abstract the ReflogReader class away from local disk IO. Change-Id: I26b9287c45a4b2d2be35ba2849daa316f5eec85d Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Rewrite reference handling to be abstract and accurate This commit actually does three major changes to the way references are handled within JGit. Unfortunately they were easier to do as a single massive commit than to break them up into smaller units. Disambiguate symbolic references: --------------------------------- Reporting a symbolic reference such as HEAD as though it were any other normal reference like refs/heads/master causes subtle programming errors. We have been bitten by this error on several occasions, as have some downstream applications written by myself. Instead of reporting HEAD as a reference whose name differs from its "original name", report it as an actual SymbolicRef object that the application can test the type and examine the target of. With this change, Ref is now an abstract type with different subclasses for the different types. In the classical example of "HEAD" being a symbolic reference to branch "refs/heads/master", the Repository.getAllRefs() method will now return: Map<String, Ref> all = repository.getAllRefs(); SymbolicRef HEAD = (SymbolicRef) all.get("HEAD"); ObjectIdRef master = (ObjectIdRef) all.get("refs/heads/master"); assertSame(master, HEAD.getTarget()); assertSame(master.getObjectId(), HEAD.getObjectId()); assertEquals("HEAD", HEAD.getName()); assertEquals("refs/heads/master", master.getName()); A nice side-effect of this change is the storage type of the symbolic reference is no longer ambiguous with the storge type of the underlying reference it targets. In the above example, if master was only available in the packed-refs file, then the following is also true: assertSame(Ref.Storage.LOOSE, HEAD.getStorage()); assertSame(Ref.Storage.PACKED, master.getStorage()); (Prior to this change we returned the ambiguous storage of LOOSE_PACKED for HEAD, which was confusing since it wasn't actually true on disk). Another nice side-effect of this change is all intermediate symbolic references are preserved, and are therefore visible to the application when they walk the target chain. We can now correctly inspect chains of symbolic references. As a result of this change the Ref.getOrigName() method has been removed from the API. Applications should identify a symbolic reference by testing for isSymbolic() and not by using an arcane string comparsion between properties. Abstract the RefDatabase storage: --------------------------------- RefDatabase is now abstract, similar to ObjectDatabase, and a new concrete implementation called RefDirectory is used for the traditional on-disk storage layout. In the future we plan to support additional implementations, such as a pure in-memory RefDatabase for unit testing purposes. Optimize RefDirectory: ---------------------- The implementation of the in-memory reference cache, reading, and update routines has been completely rewritten. Much of the code was heavily borrowed or cribbed from the prior implementation, so copyright notices have been left intact as much as possible. The RefDirectory cache no longer confuses symbolic references with normal references. This permits the cache to resolve the value of a symbolic reference as late as possible, ensuring it is always current, without needing to maintain reverse pointers. The cache is now 2 sorted RefLists, rather than 3 HashMaps. Using sorted lists allows the implementation to reduce the in-memory footprint when storing many refs. Using specialized types for the elements allows the code to avoid additional map lookups for auxiliary stat information. To improve scan time during getRefs(), the lists are returned via a copy-on-write contract. Most callers of getRefs() do not modify the returned collections, so the copy-on-write semantics improves access on repositories with a large number of packed references. Iterator traversals of the returned Map<String,Ref> are performed using a simple merge-join of the two cache lists, ensuring we can perform the entire traversal in linear time as a function of the number of references: O(PackedRefs + LooseRefs). Scans of the loose reference space to update the cache run in O(LooseRefs log LooseRefs) time, as the directory contents are sorted before being merged against the in-memory cache. Since the majority of stable references are kept packed, there typically are only a handful of reference names to be sorted, so the sorting cost should not be very high. Locking is reduced during getRefs() by taking advantage of the copy-on-write semantics of the improved cache data structure. This permits concurrent readers to pull back references without blocking each other. If there is contention updating the cache during a scan, one or more updates are simply skipped and will get picked up again in a future scan. Writing to the $GIT_DIR/packed-refs during reference delete is now fully atomic. The file is locked, reparsed fresh, and written back out if a change is necessary. This avoids all race conditions with concurrent external updates of the packed-refs file. The RefLogWriter class has been fully folded into RefDirectory and is therefore deleted. Maintaining the reference's log is the responsiblity of the database implementation, and not all implementations will use java.io for access. Future work still remains to be done to abstract the ReflogReader class away from local disk IO. Change-Id: I26b9287c45a4b2d2be35ba2849daa316f5eec85d Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Rewrite reference handling to be abstract and accurate This commit actually does three major changes to the way references are handled within JGit. Unfortunately they were easier to do as a single massive commit than to break them up into smaller units. Disambiguate symbolic references: --------------------------------- Reporting a symbolic reference such as HEAD as though it were any other normal reference like refs/heads/master causes subtle programming errors. We have been bitten by this error on several occasions, as have some downstream applications written by myself. Instead of reporting HEAD as a reference whose name differs from its "original name", report it as an actual SymbolicRef object that the application can test the type and examine the target of. With this change, Ref is now an abstract type with different subclasses for the different types. In the classical example of "HEAD" being a symbolic reference to branch "refs/heads/master", the Repository.getAllRefs() method will now return: Map<String, Ref> all = repository.getAllRefs(); SymbolicRef HEAD = (SymbolicRef) all.get("HEAD"); ObjectIdRef master = (ObjectIdRef) all.get("refs/heads/master"); assertSame(master, HEAD.getTarget()); assertSame(master.getObjectId(), HEAD.getObjectId()); assertEquals("HEAD", HEAD.getName()); assertEquals("refs/heads/master", master.getName()); A nice side-effect of this change is the storage type of the symbolic reference is no longer ambiguous with the storge type of the underlying reference it targets. In the above example, if master was only available in the packed-refs file, then the following is also true: assertSame(Ref.Storage.LOOSE, HEAD.getStorage()); assertSame(Ref.Storage.PACKED, master.getStorage()); (Prior to this change we returned the ambiguous storage of LOOSE_PACKED for HEAD, which was confusing since it wasn't actually true on disk). Another nice side-effect of this change is all intermediate symbolic references are preserved, and are therefore visible to the application when they walk the target chain. We can now correctly inspect chains of symbolic references. As a result of this change the Ref.getOrigName() method has been removed from the API. Applications should identify a symbolic reference by testing for isSymbolic() and not by using an arcane string comparsion between properties. Abstract the RefDatabase storage: --------------------------------- RefDatabase is now abstract, similar to ObjectDatabase, and a new concrete implementation called RefDirectory is used for the traditional on-disk storage layout. In the future we plan to support additional implementations, such as a pure in-memory RefDatabase for unit testing purposes. Optimize RefDirectory: ---------------------- The implementation of the in-memory reference cache, reading, and update routines has been completely rewritten. Much of the code was heavily borrowed or cribbed from the prior implementation, so copyright notices have been left intact as much as possible. The RefDirectory cache no longer confuses symbolic references with normal references. This permits the cache to resolve the value of a symbolic reference as late as possible, ensuring it is always current, without needing to maintain reverse pointers. The cache is now 2 sorted RefLists, rather than 3 HashMaps. Using sorted lists allows the implementation to reduce the in-memory footprint when storing many refs. Using specialized types for the elements allows the code to avoid additional map lookups for auxiliary stat information. To improve scan time during getRefs(), the lists are returned via a copy-on-write contract. Most callers of getRefs() do not modify the returned collections, so the copy-on-write semantics improves access on repositories with a large number of packed references. Iterator traversals of the returned Map<String,Ref> are performed using a simple merge-join of the two cache lists, ensuring we can perform the entire traversal in linear time as a function of the number of references: O(PackedRefs + LooseRefs). Scans of the loose reference space to update the cache run in O(LooseRefs log LooseRefs) time, as the directory contents are sorted before being merged against the in-memory cache. Since the majority of stable references are kept packed, there typically are only a handful of reference names to be sorted, so the sorting cost should not be very high. Locking is reduced during getRefs() by taking advantage of the copy-on-write semantics of the improved cache data structure. This permits concurrent readers to pull back references without blocking each other. If there is contention updating the cache during a scan, one or more updates are simply skipped and will get picked up again in a future scan. Writing to the $GIT_DIR/packed-refs during reference delete is now fully atomic. The file is locked, reparsed fresh, and written back out if a change is necessary. This avoids all race conditions with concurrent external updates of the packed-refs file. The RefLogWriter class has been fully folded into RefDirectory and is therefore deleted. Maintaining the reference's log is the responsiblity of the database implementation, and not all implementations will use java.io for access. Future work still remains to be done to abstract the ReflogReader class away from local disk IO. Change-Id: I26b9287c45a4b2d2be35ba2849daa316f5eec85d Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Rewrite reference handling to be abstract and accurate This commit actually does three major changes to the way references are handled within JGit. Unfortunately they were easier to do as a single massive commit than to break them up into smaller units. Disambiguate symbolic references: --------------------------------- Reporting a symbolic reference such as HEAD as though it were any other normal reference like refs/heads/master causes subtle programming errors. We have been bitten by this error on several occasions, as have some downstream applications written by myself. Instead of reporting HEAD as a reference whose name differs from its "original name", report it as an actual SymbolicRef object that the application can test the type and examine the target of. With this change, Ref is now an abstract type with different subclasses for the different types. In the classical example of "HEAD" being a symbolic reference to branch "refs/heads/master", the Repository.getAllRefs() method will now return: Map<String, Ref> all = repository.getAllRefs(); SymbolicRef HEAD = (SymbolicRef) all.get("HEAD"); ObjectIdRef master = (ObjectIdRef) all.get("refs/heads/master"); assertSame(master, HEAD.getTarget()); assertSame(master.getObjectId(), HEAD.getObjectId()); assertEquals("HEAD", HEAD.getName()); assertEquals("refs/heads/master", master.getName()); A nice side-effect of this change is the storage type of the symbolic reference is no longer ambiguous with the storge type of the underlying reference it targets. In the above example, if master was only available in the packed-refs file, then the following is also true: assertSame(Ref.Storage.LOOSE, HEAD.getStorage()); assertSame(Ref.Storage.PACKED, master.getStorage()); (Prior to this change we returned the ambiguous storage of LOOSE_PACKED for HEAD, which was confusing since it wasn't actually true on disk). Another nice side-effect of this change is all intermediate symbolic references are preserved, and are therefore visible to the application when they walk the target chain. We can now correctly inspect chains of symbolic references. As a result of this change the Ref.getOrigName() method has been removed from the API. Applications should identify a symbolic reference by testing for isSymbolic() and not by using an arcane string comparsion between properties. Abstract the RefDatabase storage: --------------------------------- RefDatabase is now abstract, similar to ObjectDatabase, and a new concrete implementation called RefDirectory is used for the traditional on-disk storage layout. In the future we plan to support additional implementations, such as a pure in-memory RefDatabase for unit testing purposes. Optimize RefDirectory: ---------------------- The implementation of the in-memory reference cache, reading, and update routines has been completely rewritten. Much of the code was heavily borrowed or cribbed from the prior implementation, so copyright notices have been left intact as much as possible. The RefDirectory cache no longer confuses symbolic references with normal references. This permits the cache to resolve the value of a symbolic reference as late as possible, ensuring it is always current, without needing to maintain reverse pointers. The cache is now 2 sorted RefLists, rather than 3 HashMaps. Using sorted lists allows the implementation to reduce the in-memory footprint when storing many refs. Using specialized types for the elements allows the code to avoid additional map lookups for auxiliary stat information. To improve scan time during getRefs(), the lists are returned via a copy-on-write contract. Most callers of getRefs() do not modify the returned collections, so the copy-on-write semantics improves access on repositories with a large number of packed references. Iterator traversals of the returned Map<String,Ref> are performed using a simple merge-join of the two cache lists, ensuring we can perform the entire traversal in linear time as a function of the number of references: O(PackedRefs + LooseRefs). Scans of the loose reference space to update the cache run in O(LooseRefs log LooseRefs) time, as the directory contents are sorted before being merged against the in-memory cache. Since the majority of stable references are kept packed, there typically are only a handful of reference names to be sorted, so the sorting cost should not be very high. Locking is reduced during getRefs() by taking advantage of the copy-on-write semantics of the improved cache data structure. This permits concurrent readers to pull back references without blocking each other. If there is contention updating the cache during a scan, one or more updates are simply skipped and will get picked up again in a future scan. Writing to the $GIT_DIR/packed-refs during reference delete is now fully atomic. The file is locked, reparsed fresh, and written back out if a change is necessary. This avoids all race conditions with concurrent external updates of the packed-refs file. The RefLogWriter class has been fully folded into RefDirectory and is therefore deleted. Maintaining the reference's log is the responsiblity of the database implementation, and not all implementations will use java.io for access. Future work still remains to be done to abstract the ReflogReader class away from local disk IO. Change-Id: I26b9287c45a4b2d2be35ba2849daa316f5eec85d Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Rewrite reference handling to be abstract and accurate This commit actually does three major changes to the way references are handled within JGit. Unfortunately they were easier to do as a single massive commit than to break them up into smaller units. Disambiguate symbolic references: --------------------------------- Reporting a symbolic reference such as HEAD as though it were any other normal reference like refs/heads/master causes subtle programming errors. We have been bitten by this error on several occasions, as have some downstream applications written by myself. Instead of reporting HEAD as a reference whose name differs from its "original name", report it as an actual SymbolicRef object that the application can test the type and examine the target of. With this change, Ref is now an abstract type with different subclasses for the different types. In the classical example of "HEAD" being a symbolic reference to branch "refs/heads/master", the Repository.getAllRefs() method will now return: Map<String, Ref> all = repository.getAllRefs(); SymbolicRef HEAD = (SymbolicRef) all.get("HEAD"); ObjectIdRef master = (ObjectIdRef) all.get("refs/heads/master"); assertSame(master, HEAD.getTarget()); assertSame(master.getObjectId(), HEAD.getObjectId()); assertEquals("HEAD", HEAD.getName()); assertEquals("refs/heads/master", master.getName()); A nice side-effect of this change is the storage type of the symbolic reference is no longer ambiguous with the storge type of the underlying reference it targets. In the above example, if master was only available in the packed-refs file, then the following is also true: assertSame(Ref.Storage.LOOSE, HEAD.getStorage()); assertSame(Ref.Storage.PACKED, master.getStorage()); (Prior to this change we returned the ambiguous storage of LOOSE_PACKED for HEAD, which was confusing since it wasn't actually true on disk). Another nice side-effect of this change is all intermediate symbolic references are preserved, and are therefore visible to the application when they walk the target chain. We can now correctly inspect chains of symbolic references. As a result of this change the Ref.getOrigName() method has been removed from the API. Applications should identify a symbolic reference by testing for isSymbolic() and not by using an arcane string comparsion between properties. Abstract the RefDatabase storage: --------------------------------- RefDatabase is now abstract, similar to ObjectDatabase, and a new concrete implementation called RefDirectory is used for the traditional on-disk storage layout. In the future we plan to support additional implementations, such as a pure in-memory RefDatabase for unit testing purposes. Optimize RefDirectory: ---------------------- The implementation of the in-memory reference cache, reading, and update routines has been completely rewritten. Much of the code was heavily borrowed or cribbed from the prior implementation, so copyright notices have been left intact as much as possible. The RefDirectory cache no longer confuses symbolic references with normal references. This permits the cache to resolve the value of a symbolic reference as late as possible, ensuring it is always current, without needing to maintain reverse pointers. The cache is now 2 sorted RefLists, rather than 3 HashMaps. Using sorted lists allows the implementation to reduce the in-memory footprint when storing many refs. Using specialized types for the elements allows the code to avoid additional map lookups for auxiliary stat information. To improve scan time during getRefs(), the lists are returned via a copy-on-write contract. Most callers of getRefs() do not modify the returned collections, so the copy-on-write semantics improves access on repositories with a large number of packed references. Iterator traversals of the returned Map<String,Ref> are performed using a simple merge-join of the two cache lists, ensuring we can perform the entire traversal in linear time as a function of the number of references: O(PackedRefs + LooseRefs). Scans of the loose reference space to update the cache run in O(LooseRefs log LooseRefs) time, as the directory contents are sorted before being merged against the in-memory cache. Since the majority of stable references are kept packed, there typically are only a handful of reference names to be sorted, so the sorting cost should not be very high. Locking is reduced during getRefs() by taking advantage of the copy-on-write semantics of the improved cache data structure. This permits concurrent readers to pull back references without blocking each other. If there is contention updating the cache during a scan, one or more updates are simply skipped and will get picked up again in a future scan. Writing to the $GIT_DIR/packed-refs during reference delete is now fully atomic. The file is locked, reparsed fresh, and written back out if a change is necessary. This avoids all race conditions with concurrent external updates of the packed-refs file. The RefLogWriter class has been fully folded into RefDirectory and is therefore deleted. Maintaining the reference's log is the responsiblity of the database implementation, and not all implementations will use java.io for access. Future work still remains to be done to abstract the ReflogReader class away from local disk IO. Change-Id: I26b9287c45a4b2d2be35ba2849daa316f5eec85d Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Rewrite reference handling to be abstract and accurate This commit actually does three major changes to the way references are handled within JGit. Unfortunately they were easier to do as a single massive commit than to break them up into smaller units. Disambiguate symbolic references: --------------------------------- Reporting a symbolic reference such as HEAD as though it were any other normal reference like refs/heads/master causes subtle programming errors. We have been bitten by this error on several occasions, as have some downstream applications written by myself. Instead of reporting HEAD as a reference whose name differs from its "original name", report it as an actual SymbolicRef object that the application can test the type and examine the target of. With this change, Ref is now an abstract type with different subclasses for the different types. In the classical example of "HEAD" being a symbolic reference to branch "refs/heads/master", the Repository.getAllRefs() method will now return: Map<String, Ref> all = repository.getAllRefs(); SymbolicRef HEAD = (SymbolicRef) all.get("HEAD"); ObjectIdRef master = (ObjectIdRef) all.get("refs/heads/master"); assertSame(master, HEAD.getTarget()); assertSame(master.getObjectId(), HEAD.getObjectId()); assertEquals("HEAD", HEAD.getName()); assertEquals("refs/heads/master", master.getName()); A nice side-effect of this change is the storage type of the symbolic reference is no longer ambiguous with the storge type of the underlying reference it targets. In the above example, if master was only available in the packed-refs file, then the following is also true: assertSame(Ref.Storage.LOOSE, HEAD.getStorage()); assertSame(Ref.Storage.PACKED, master.getStorage()); (Prior to this change we returned the ambiguous storage of LOOSE_PACKED for HEAD, which was confusing since it wasn't actually true on disk). Another nice side-effect of this change is all intermediate symbolic references are preserved, and are therefore visible to the application when they walk the target chain. We can now correctly inspect chains of symbolic references. As a result of this change the Ref.getOrigName() method has been removed from the API. Applications should identify a symbolic reference by testing for isSymbolic() and not by using an arcane string comparsion between properties. Abstract the RefDatabase storage: --------------------------------- RefDatabase is now abstract, similar to ObjectDatabase, and a new concrete implementation called RefDirectory is used for the traditional on-disk storage layout. In the future we plan to support additional implementations, such as a pure in-memory RefDatabase for unit testing purposes. Optimize RefDirectory: ---------------------- The implementation of the in-memory reference cache, reading, and update routines has been completely rewritten. Much of the code was heavily borrowed or cribbed from the prior implementation, so copyright notices have been left intact as much as possible. The RefDirectory cache no longer confuses symbolic references with normal references. This permits the cache to resolve the value of a symbolic reference as late as possible, ensuring it is always current, without needing to maintain reverse pointers. The cache is now 2 sorted RefLists, rather than 3 HashMaps. Using sorted lists allows the implementation to reduce the in-memory footprint when storing many refs. Using specialized types for the elements allows the code to avoid additional map lookups for auxiliary stat information. To improve scan time during getRefs(), the lists are returned via a copy-on-write contract. Most callers of getRefs() do not modify the returned collections, so the copy-on-write semantics improves access on repositories with a large number of packed references. Iterator traversals of the returned Map<String,Ref> are performed using a simple merge-join of the two cache lists, ensuring we can perform the entire traversal in linear time as a function of the number of references: O(PackedRefs + LooseRefs). Scans of the loose reference space to update the cache run in O(LooseRefs log LooseRefs) time, as the directory contents are sorted before being merged against the in-memory cache. Since the majority of stable references are kept packed, there typically are only a handful of reference names to be sorted, so the sorting cost should not be very high. Locking is reduced during getRefs() by taking advantage of the copy-on-write semantics of the improved cache data structure. This permits concurrent readers to pull back references without blocking each other. If there is contention updating the cache during a scan, one or more updates are simply skipped and will get picked up again in a future scan. Writing to the $GIT_DIR/packed-refs during reference delete is now fully atomic. The file is locked, reparsed fresh, and written back out if a change is necessary. This avoids all race conditions with concurrent external updates of the packed-refs file. The RefLogWriter class has been fully folded into RefDirectory and is therefore deleted. Maintaining the reference's log is the responsiblity of the database implementation, and not all implementations will use java.io for access. Future work still remains to be done to abstract the ReflogReader class away from local disk IO. Change-Id: I26b9287c45a4b2d2be35ba2849daa316f5eec85d Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Rewrite reference handling to be abstract and accurate This commit actually does three major changes to the way references are handled within JGit. Unfortunately they were easier to do as a single massive commit than to break them up into smaller units. Disambiguate symbolic references: --------------------------------- Reporting a symbolic reference such as HEAD as though it were any other normal reference like refs/heads/master causes subtle programming errors. We have been bitten by this error on several occasions, as have some downstream applications written by myself. Instead of reporting HEAD as a reference whose name differs from its "original name", report it as an actual SymbolicRef object that the application can test the type and examine the target of. With this change, Ref is now an abstract type with different subclasses for the different types. In the classical example of "HEAD" being a symbolic reference to branch "refs/heads/master", the Repository.getAllRefs() method will now return: Map<String, Ref> all = repository.getAllRefs(); SymbolicRef HEAD = (SymbolicRef) all.get("HEAD"); ObjectIdRef master = (ObjectIdRef) all.get("refs/heads/master"); assertSame(master, HEAD.getTarget()); assertSame(master.getObjectId(), HEAD.getObjectId()); assertEquals("HEAD", HEAD.getName()); assertEquals("refs/heads/master", master.getName()); A nice side-effect of this change is the storage type of the symbolic reference is no longer ambiguous with the storge type of the underlying reference it targets. In the above example, if master was only available in the packed-refs file, then the following is also true: assertSame(Ref.Storage.LOOSE, HEAD.getStorage()); assertSame(Ref.Storage.PACKED, master.getStorage()); (Prior to this change we returned the ambiguous storage of LOOSE_PACKED for HEAD, which was confusing since it wasn't actually true on disk). Another nice side-effect of this change is all intermediate symbolic references are preserved, and are therefore visible to the application when they walk the target chain. We can now correctly inspect chains of symbolic references. As a result of this change the Ref.getOrigName() method has been removed from the API. Applications should identify a symbolic reference by testing for isSymbolic() and not by using an arcane string comparsion between properties. Abstract the RefDatabase storage: --------------------------------- RefDatabase is now abstract, similar to ObjectDatabase, and a new concrete implementation called RefDirectory is used for the traditional on-disk storage layout. In the future we plan to support additional implementations, such as a pure in-memory RefDatabase for unit testing purposes. Optimize RefDirectory: ---------------------- The implementation of the in-memory reference cache, reading, and update routines has been completely rewritten. Much of the code was heavily borrowed or cribbed from the prior implementation, so copyright notices have been left intact as much as possible. The RefDirectory cache no longer confuses symbolic references with normal references. This permits the cache to resolve the value of a symbolic reference as late as possible, ensuring it is always current, without needing to maintain reverse pointers. The cache is now 2 sorted RefLists, rather than 3 HashMaps. Using sorted lists allows the implementation to reduce the in-memory footprint when storing many refs. Using specialized types for the elements allows the code to avoid additional map lookups for auxiliary stat information. To improve scan time during getRefs(), the lists are returned via a copy-on-write contract. Most callers of getRefs() do not modify the returned collections, so the copy-on-write semantics improves access on repositories with a large number of packed references. Iterator traversals of the returned Map<String,Ref> are performed using a simple merge-join of the two cache lists, ensuring we can perform the entire traversal in linear time as a function of the number of references: O(PackedRefs + LooseRefs). Scans of the loose reference space to update the cache run in O(LooseRefs log LooseRefs) time, as the directory contents are sorted before being merged against the in-memory cache. Since the majority of stable references are kept packed, there typically are only a handful of reference names to be sorted, so the sorting cost should not be very high. Locking is reduced during getRefs() by taking advantage of the copy-on-write semantics of the improved cache data structure. This permits concurrent readers to pull back references without blocking each other. If there is contention updating the cache during a scan, one or more updates are simply skipped and will get picked up again in a future scan. Writing to the $GIT_DIR/packed-refs during reference delete is now fully atomic. The file is locked, reparsed fresh, and written back out if a change is necessary. This avoids all race conditions with concurrent external updates of the packed-refs file. The RefLogWriter class has been fully folded into RefDirectory and is therefore deleted. Maintaining the reference's log is the responsiblity of the database implementation, and not all implementations will use java.io for access. Future work still remains to be done to abstract the ReflogReader class away from local disk IO. Change-Id: I26b9287c45a4b2d2be35ba2849daa316f5eec85d Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Rewrite reference handling to be abstract and accurate This commit actually does three major changes to the way references are handled within JGit. Unfortunately they were easier to do as a single massive commit than to break them up into smaller units. Disambiguate symbolic references: --------------------------------- Reporting a symbolic reference such as HEAD as though it were any other normal reference like refs/heads/master causes subtle programming errors. We have been bitten by this error on several occasions, as have some downstream applications written by myself. Instead of reporting HEAD as a reference whose name differs from its "original name", report it as an actual SymbolicRef object that the application can test the type and examine the target of. With this change, Ref is now an abstract type with different subclasses for the different types. In the classical example of "HEAD" being a symbolic reference to branch "refs/heads/master", the Repository.getAllRefs() method will now return: Map<String, Ref> all = repository.getAllRefs(); SymbolicRef HEAD = (SymbolicRef) all.get("HEAD"); ObjectIdRef master = (ObjectIdRef) all.get("refs/heads/master"); assertSame(master, HEAD.getTarget()); assertSame(master.getObjectId(), HEAD.getObjectId()); assertEquals("HEAD", HEAD.getName()); assertEquals("refs/heads/master", master.getName()); A nice side-effect of this change is the storage type of the symbolic reference is no longer ambiguous with the storge type of the underlying reference it targets. In the above example, if master was only available in the packed-refs file, then the following is also true: assertSame(Ref.Storage.LOOSE, HEAD.getStorage()); assertSame(Ref.Storage.PACKED, master.getStorage()); (Prior to this change we returned the ambiguous storage of LOOSE_PACKED for HEAD, which was confusing since it wasn't actually true on disk). Another nice side-effect of this change is all intermediate symbolic references are preserved, and are therefore visible to the application when they walk the target chain. We can now correctly inspect chains of symbolic references. As a result of this change the Ref.getOrigName() method has been removed from the API. Applications should identify a symbolic reference by testing for isSymbolic() and not by using an arcane string comparsion between properties. Abstract the RefDatabase storage: --------------------------------- RefDatabase is now abstract, similar to ObjectDatabase, and a new concrete implementation called RefDirectory is used for the traditional on-disk storage layout. In the future we plan to support additional implementations, such as a pure in-memory RefDatabase for unit testing purposes. Optimize RefDirectory: ---------------------- The implementation of the in-memory reference cache, reading, and update routines has been completely rewritten. Much of the code was heavily borrowed or cribbed from the prior implementation, so copyright notices have been left intact as much as possible. The RefDirectory cache no longer confuses symbolic references with normal references. This permits the cache to resolve the value of a symbolic reference as late as possible, ensuring it is always current, without needing to maintain reverse pointers. The cache is now 2 sorted RefLists, rather than 3 HashMaps. Using sorted lists allows the implementation to reduce the in-memory footprint when storing many refs. Using specialized types for the elements allows the code to avoid additional map lookups for auxiliary stat information. To improve scan time during getRefs(), the lists are returned via a copy-on-write contract. Most callers of getRefs() do not modify the returned collections, so the copy-on-write semantics improves access on repositories with a large number of packed references. Iterator traversals of the returned Map<String,Ref> are performed using a simple merge-join of the two cache lists, ensuring we can perform the entire traversal in linear time as a function of the number of references: O(PackedRefs + LooseRefs). Scans of the loose reference space to update the cache run in O(LooseRefs log LooseRefs) time, as the directory contents are sorted before being merged against the in-memory cache. Since the majority of stable references are kept packed, there typically are only a handful of reference names to be sorted, so the sorting cost should not be very high. Locking is reduced during getRefs() by taking advantage of the copy-on-write semantics of the improved cache data structure. This permits concurrent readers to pull back references without blocking each other. If there is contention updating the cache during a scan, one or more updates are simply skipped and will get picked up again in a future scan. Writing to the $GIT_DIR/packed-refs during reference delete is now fully atomic. The file is locked, reparsed fresh, and written back out if a change is necessary. This avoids all race conditions with concurrent external updates of the packed-refs file. The RefLogWriter class has been fully folded into RefDirectory and is therefore deleted. Maintaining the reference's log is the responsiblity of the database implementation, and not all implementations will use java.io for access. Future work still remains to be done to abstract the ReflogReader class away from local disk IO. Change-Id: I26b9287c45a4b2d2be35ba2849daa316f5eec85d Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Rewrite reference handling to be abstract and accurate This commit actually does three major changes to the way references are handled within JGit. Unfortunately they were easier to do as a single massive commit than to break them up into smaller units. Disambiguate symbolic references: --------------------------------- Reporting a symbolic reference such as HEAD as though it were any other normal reference like refs/heads/master causes subtle programming errors. We have been bitten by this error on several occasions, as have some downstream applications written by myself. Instead of reporting HEAD as a reference whose name differs from its "original name", report it as an actual SymbolicRef object that the application can test the type and examine the target of. With this change, Ref is now an abstract type with different subclasses for the different types. In the classical example of "HEAD" being a symbolic reference to branch "refs/heads/master", the Repository.getAllRefs() method will now return: Map<String, Ref> all = repository.getAllRefs(); SymbolicRef HEAD = (SymbolicRef) all.get("HEAD"); ObjectIdRef master = (ObjectIdRef) all.get("refs/heads/master"); assertSame(master, HEAD.getTarget()); assertSame(master.getObjectId(), HEAD.getObjectId()); assertEquals("HEAD", HEAD.getName()); assertEquals("refs/heads/master", master.getName()); A nice side-effect of this change is the storage type of the symbolic reference is no longer ambiguous with the storge type of the underlying reference it targets. In the above example, if master was only available in the packed-refs file, then the following is also true: assertSame(Ref.Storage.LOOSE, HEAD.getStorage()); assertSame(Ref.Storage.PACKED, master.getStorage()); (Prior to this change we returned the ambiguous storage of LOOSE_PACKED for HEAD, which was confusing since it wasn't actually true on disk). Another nice side-effect of this change is all intermediate symbolic references are preserved, and are therefore visible to the application when they walk the target chain. We can now correctly inspect chains of symbolic references. As a result of this change the Ref.getOrigName() method has been removed from the API. Applications should identify a symbolic reference by testing for isSymbolic() and not by using an arcane string comparsion between properties. Abstract the RefDatabase storage: --------------------------------- RefDatabase is now abstract, similar to ObjectDatabase, and a new concrete implementation called RefDirectory is used for the traditional on-disk storage layout. In the future we plan to support additional implementations, such as a pure in-memory RefDatabase for unit testing purposes. Optimize RefDirectory: ---------------------- The implementation of the in-memory reference cache, reading, and update routines has been completely rewritten. Much of the code was heavily borrowed or cribbed from the prior implementation, so copyright notices have been left intact as much as possible. The RefDirectory cache no longer confuses symbolic references with normal references. This permits the cache to resolve the value of a symbolic reference as late as possible, ensuring it is always current, without needing to maintain reverse pointers. The cache is now 2 sorted RefLists, rather than 3 HashMaps. Using sorted lists allows the implementation to reduce the in-memory footprint when storing many refs. Using specialized types for the elements allows the code to avoid additional map lookups for auxiliary stat information. To improve scan time during getRefs(), the lists are returned via a copy-on-write contract. Most callers of getRefs() do not modify the returned collections, so the copy-on-write semantics improves access on repositories with a large number of packed references. Iterator traversals of the returned Map<String,Ref> are performed using a simple merge-join of the two cache lists, ensuring we can perform the entire traversal in linear time as a function of the number of references: O(PackedRefs + LooseRefs). Scans of the loose reference space to update the cache run in O(LooseRefs log LooseRefs) time, as the directory contents are sorted before being merged against the in-memory cache. Since the majority of stable references are kept packed, there typically are only a handful of reference names to be sorted, so the sorting cost should not be very high. Locking is reduced during getRefs() by taking advantage of the copy-on-write semantics of the improved cache data structure. This permits concurrent readers to pull back references without blocking each other. If there is contention updating the cache during a scan, one or more updates are simply skipped and will get picked up again in a future scan. Writing to the $GIT_DIR/packed-refs during reference delete is now fully atomic. The file is locked, reparsed fresh, and written back out if a change is necessary. This avoids all race conditions with concurrent external updates of the packed-refs file. The RefLogWriter class has been fully folded into RefDirectory and is therefore deleted. Maintaining the reference's log is the responsiblity of the database implementation, and not all implementations will use java.io for access. Future work still remains to be done to abstract the ReflogReader class away from local disk IO. Change-Id: I26b9287c45a4b2d2be35ba2849daa316f5eec85d Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Rewrite reference handling to be abstract and accurate This commit actually does three major changes to the way references are handled within JGit. Unfortunately they were easier to do as a single massive commit than to break them up into smaller units. Disambiguate symbolic references: --------------------------------- Reporting a symbolic reference such as HEAD as though it were any other normal reference like refs/heads/master causes subtle programming errors. We have been bitten by this error on several occasions, as have some downstream applications written by myself. Instead of reporting HEAD as a reference whose name differs from its "original name", report it as an actual SymbolicRef object that the application can test the type and examine the target of. With this change, Ref is now an abstract type with different subclasses for the different types. In the classical example of "HEAD" being a symbolic reference to branch "refs/heads/master", the Repository.getAllRefs() method will now return: Map<String, Ref> all = repository.getAllRefs(); SymbolicRef HEAD = (SymbolicRef) all.get("HEAD"); ObjectIdRef master = (ObjectIdRef) all.get("refs/heads/master"); assertSame(master, HEAD.getTarget()); assertSame(master.getObjectId(), HEAD.getObjectId()); assertEquals("HEAD", HEAD.getName()); assertEquals("refs/heads/master", master.getName()); A nice side-effect of this change is the storage type of the symbolic reference is no longer ambiguous with the storge type of the underlying reference it targets. In the above example, if master was only available in the packed-refs file, then the following is also true: assertSame(Ref.Storage.LOOSE, HEAD.getStorage()); assertSame(Ref.Storage.PACKED, master.getStorage()); (Prior to this change we returned the ambiguous storage of LOOSE_PACKED for HEAD, which was confusing since it wasn't actually true on disk). Another nice side-effect of this change is all intermediate symbolic references are preserved, and are therefore visible to the application when they walk the target chain. We can now correctly inspect chains of symbolic references. As a result of this change the Ref.getOrigName() method has been removed from the API. Applications should identify a symbolic reference by testing for isSymbolic() and not by using an arcane string comparsion between properties. Abstract the RefDatabase storage: --------------------------------- RefDatabase is now abstract, similar to ObjectDatabase, and a new concrete implementation called RefDirectory is used for the traditional on-disk storage layout. In the future we plan to support additional implementations, such as a pure in-memory RefDatabase for unit testing purposes. Optimize RefDirectory: ---------------------- The implementation of the in-memory reference cache, reading, and update routines has been completely rewritten. Much of the code was heavily borrowed or cribbed from the prior implementation, so copyright notices have been left intact as much as possible. The RefDirectory cache no longer confuses symbolic references with normal references. This permits the cache to resolve the value of a symbolic reference as late as possible, ensuring it is always current, without needing to maintain reverse pointers. The cache is now 2 sorted RefLists, rather than 3 HashMaps. Using sorted lists allows the implementation to reduce the in-memory footprint when storing many refs. Using specialized types for the elements allows the code to avoid additional map lookups for auxiliary stat information. To improve scan time during getRefs(), the lists are returned via a copy-on-write contract. Most callers of getRefs() do not modify the returned collections, so the copy-on-write semantics improves access on repositories with a large number of packed references. Iterator traversals of the returned Map<String,Ref> are performed using a simple merge-join of the two cache lists, ensuring we can perform the entire traversal in linear time as a function of the number of references: O(PackedRefs + LooseRefs). Scans of the loose reference space to update the cache run in O(LooseRefs log LooseRefs) time, as the directory contents are sorted before being merged against the in-memory cache. Since the majority of stable references are kept packed, there typically are only a handful of reference names to be sorted, so the sorting cost should not be very high. Locking is reduced during getRefs() by taking advantage of the copy-on-write semantics of the improved cache data structure. This permits concurrent readers to pull back references without blocking each other. If there is contention updating the cache during a scan, one or more updates are simply skipped and will get picked up again in a future scan. Writing to the $GIT_DIR/packed-refs during reference delete is now fully atomic. The file is locked, reparsed fresh, and written back out if a change is necessary. This avoids all race conditions with concurrent external updates of the packed-refs file. The RefLogWriter class has been fully folded into RefDirectory and is therefore deleted. Maintaining the reference's log is the responsiblity of the database implementation, and not all implementations will use java.io for access. Future work still remains to be done to abstract the ReflogReader class away from local disk IO. Change-Id: I26b9287c45a4b2d2be35ba2849daa316f5eec85d Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Rewrite reference handling to be abstract and accurate This commit actually does three major changes to the way references are handled within JGit. Unfortunately they were easier to do as a single massive commit than to break them up into smaller units. Disambiguate symbolic references: --------------------------------- Reporting a symbolic reference such as HEAD as though it were any other normal reference like refs/heads/master causes subtle programming errors. We have been bitten by this error on several occasions, as have some downstream applications written by myself. Instead of reporting HEAD as a reference whose name differs from its "original name", report it as an actual SymbolicRef object that the application can test the type and examine the target of. With this change, Ref is now an abstract type with different subclasses for the different types. In the classical example of "HEAD" being a symbolic reference to branch "refs/heads/master", the Repository.getAllRefs() method will now return: Map<String, Ref> all = repository.getAllRefs(); SymbolicRef HEAD = (SymbolicRef) all.get("HEAD"); ObjectIdRef master = (ObjectIdRef) all.get("refs/heads/master"); assertSame(master, HEAD.getTarget()); assertSame(master.getObjectId(), HEAD.getObjectId()); assertEquals("HEAD", HEAD.getName()); assertEquals("refs/heads/master", master.getName()); A nice side-effect of this change is the storage type of the symbolic reference is no longer ambiguous with the storge type of the underlying reference it targets. In the above example, if master was only available in the packed-refs file, then the following is also true: assertSame(Ref.Storage.LOOSE, HEAD.getStorage()); assertSame(Ref.Storage.PACKED, master.getStorage()); (Prior to this change we returned the ambiguous storage of LOOSE_PACKED for HEAD, which was confusing since it wasn't actually true on disk). Another nice side-effect of this change is all intermediate symbolic references are preserved, and are therefore visible to the application when they walk the target chain. We can now correctly inspect chains of symbolic references. As a result of this change the Ref.getOrigName() method has been removed from the API. Applications should identify a symbolic reference by testing for isSymbolic() and not by using an arcane string comparsion between properties. Abstract the RefDatabase storage: --------------------------------- RefDatabase is now abstract, similar to ObjectDatabase, and a new concrete implementation called RefDirectory is used for the traditional on-disk storage layout. In the future we plan to support additional implementations, such as a pure in-memory RefDatabase for unit testing purposes. Optimize RefDirectory: ---------------------- The implementation of the in-memory reference cache, reading, and update routines has been completely rewritten. Much of the code was heavily borrowed or cribbed from the prior implementation, so copyright notices have been left intact as much as possible. The RefDirectory cache no longer confuses symbolic references with normal references. This permits the cache to resolve the value of a symbolic reference as late as possible, ensuring it is always current, without needing to maintain reverse pointers. The cache is now 2 sorted RefLists, rather than 3 HashMaps. Using sorted lists allows the implementation to reduce the in-memory footprint when storing many refs. Using specialized types for the elements allows the code to avoid additional map lookups for auxiliary stat information. To improve scan time during getRefs(), the lists are returned via a copy-on-write contract. Most callers of getRefs() do not modify the returned collections, so the copy-on-write semantics improves access on repositories with a large number of packed references. Iterator traversals of the returned Map<String,Ref> are performed using a simple merge-join of the two cache lists, ensuring we can perform the entire traversal in linear time as a function of the number of references: O(PackedRefs + LooseRefs). Scans of the loose reference space to update the cache run in O(LooseRefs log LooseRefs) time, as the directory contents are sorted before being merged against the in-memory cache. Since the majority of stable references are kept packed, there typically are only a handful of reference names to be sorted, so the sorting cost should not be very high. Locking is reduced during getRefs() by taking advantage of the copy-on-write semantics of the improved cache data structure. This permits concurrent readers to pull back references without blocking each other. If there is contention updating the cache during a scan, one or more updates are simply skipped and will get picked up again in a future scan. Writing to the $GIT_DIR/packed-refs during reference delete is now fully atomic. The file is locked, reparsed fresh, and written back out if a change is necessary. This avoids all race conditions with concurrent external updates of the packed-refs file. The RefLogWriter class has been fully folded into RefDirectory and is therefore deleted. Maintaining the reference's log is the responsiblity of the database implementation, and not all implementations will use java.io for access. Future work still remains to be done to abstract the ReflogReader class away from local disk IO. Change-Id: I26b9287c45a4b2d2be35ba2849daa316f5eec85d Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Rewrite reference handling to be abstract and accurate This commit actually does three major changes to the way references are handled within JGit. Unfortunately they were easier to do as a single massive commit than to break them up into smaller units. Disambiguate symbolic references: --------------------------------- Reporting a symbolic reference such as HEAD as though it were any other normal reference like refs/heads/master causes subtle programming errors. We have been bitten by this error on several occasions, as have some downstream applications written by myself. Instead of reporting HEAD as a reference whose name differs from its "original name", report it as an actual SymbolicRef object that the application can test the type and examine the target of. With this change, Ref is now an abstract type with different subclasses for the different types. In the classical example of "HEAD" being a symbolic reference to branch "refs/heads/master", the Repository.getAllRefs() method will now return: Map<String, Ref> all = repository.getAllRefs(); SymbolicRef HEAD = (SymbolicRef) all.get("HEAD"); ObjectIdRef master = (ObjectIdRef) all.get("refs/heads/master"); assertSame(master, HEAD.getTarget()); assertSame(master.getObjectId(), HEAD.getObjectId()); assertEquals("HEAD", HEAD.getName()); assertEquals("refs/heads/master", master.getName()); A nice side-effect of this change is the storage type of the symbolic reference is no longer ambiguous with the storge type of the underlying reference it targets. In the above example, if master was only available in the packed-refs file, then the following is also true: assertSame(Ref.Storage.LOOSE, HEAD.getStorage()); assertSame(Ref.Storage.PACKED, master.getStorage()); (Prior to this change we returned the ambiguous storage of LOOSE_PACKED for HEAD, which was confusing since it wasn't actually true on disk). Another nice side-effect of this change is all intermediate symbolic references are preserved, and are therefore visible to the application when they walk the target chain. We can now correctly inspect chains of symbolic references. As a result of this change the Ref.getOrigName() method has been removed from the API. Applications should identify a symbolic reference by testing for isSymbolic() and not by using an arcane string comparsion between properties. Abstract the RefDatabase storage: --------------------------------- RefDatabase is now abstract, similar to ObjectDatabase, and a new concrete implementation called RefDirectory is used for the traditional on-disk storage layout. In the future we plan to support additional implementations, such as a pure in-memory RefDatabase for unit testing purposes. Optimize RefDirectory: ---------------------- The implementation of the in-memory reference cache, reading, and update routines has been completely rewritten. Much of the code was heavily borrowed or cribbed from the prior implementation, so copyright notices have been left intact as much as possible. The RefDirectory cache no longer confuses symbolic references with normal references. This permits the cache to resolve the value of a symbolic reference as late as possible, ensuring it is always current, without needing to maintain reverse pointers. The cache is now 2 sorted RefLists, rather than 3 HashMaps. Using sorted lists allows the implementation to reduce the in-memory footprint when storing many refs. Using specialized types for the elements allows the code to avoid additional map lookups for auxiliary stat information. To improve scan time during getRefs(), the lists are returned via a copy-on-write contract. Most callers of getRefs() do not modify the returned collections, so the copy-on-write semantics improves access on repositories with a large number of packed references. Iterator traversals of the returned Map<String,Ref> are performed using a simple merge-join of the two cache lists, ensuring we can perform the entire traversal in linear time as a function of the number of references: O(PackedRefs + LooseRefs). Scans of the loose reference space to update the cache run in O(LooseRefs log LooseRefs) time, as the directory contents are sorted before being merged against the in-memory cache. Since the majority of stable references are kept packed, there typically are only a handful of reference names to be sorted, so the sorting cost should not be very high. Locking is reduced during getRefs() by taking advantage of the copy-on-write semantics of the improved cache data structure. This permits concurrent readers to pull back references without blocking each other. If there is contention updating the cache during a scan, one or more updates are simply skipped and will get picked up again in a future scan. Writing to the $GIT_DIR/packed-refs during reference delete is now fully atomic. The file is locked, reparsed fresh, and written back out if a change is necessary. This avoids all race conditions with concurrent external updates of the packed-refs file. The RefLogWriter class has been fully folded into RefDirectory and is therefore deleted. Maintaining the reference's log is the responsiblity of the database implementation, and not all implementations will use java.io for access. Future work still remains to be done to abstract the ReflogReader class away from local disk IO. Change-Id: I26b9287c45a4b2d2be35ba2849daa316f5eec85d Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Rewrite reference handling to be abstract and accurate This commit actually does three major changes to the way references are handled within JGit. Unfortunately they were easier to do as a single massive commit than to break them up into smaller units. Disambiguate symbolic references: --------------------------------- Reporting a symbolic reference such as HEAD as though it were any other normal reference like refs/heads/master causes subtle programming errors. We have been bitten by this error on several occasions, as have some downstream applications written by myself. Instead of reporting HEAD as a reference whose name differs from its "original name", report it as an actual SymbolicRef object that the application can test the type and examine the target of. With this change, Ref is now an abstract type with different subclasses for the different types. In the classical example of "HEAD" being a symbolic reference to branch "refs/heads/master", the Repository.getAllRefs() method will now return: Map<String, Ref> all = repository.getAllRefs(); SymbolicRef HEAD = (SymbolicRef) all.get("HEAD"); ObjectIdRef master = (ObjectIdRef) all.get("refs/heads/master"); assertSame(master, HEAD.getTarget()); assertSame(master.getObjectId(), HEAD.getObjectId()); assertEquals("HEAD", HEAD.getName()); assertEquals("refs/heads/master", master.getName()); A nice side-effect of this change is the storage type of the symbolic reference is no longer ambiguous with the storge type of the underlying reference it targets. In the above example, if master was only available in the packed-refs file, then the following is also true: assertSame(Ref.Storage.LOOSE, HEAD.getStorage()); assertSame(Ref.Storage.PACKED, master.getStorage()); (Prior to this change we returned the ambiguous storage of LOOSE_PACKED for HEAD, which was confusing since it wasn't actually true on disk). Another nice side-effect of this change is all intermediate symbolic references are preserved, and are therefore visible to the application when they walk the target chain. We can now correctly inspect chains of symbolic references. As a result of this change the Ref.getOrigName() method has been removed from the API. Applications should identify a symbolic reference by testing for isSymbolic() and not by using an arcane string comparsion between properties. Abstract the RefDatabase storage: --------------------------------- RefDatabase is now abstract, similar to ObjectDatabase, and a new concrete implementation called RefDirectory is used for the traditional on-disk storage layout. In the future we plan to support additional implementations, such as a pure in-memory RefDatabase for unit testing purposes. Optimize RefDirectory: ---------------------- The implementation of the in-memory reference cache, reading, and update routines has been completely rewritten. Much of the code was heavily borrowed or cribbed from the prior implementation, so copyright notices have been left intact as much as possible. The RefDirectory cache no longer confuses symbolic references with normal references. This permits the cache to resolve the value of a symbolic reference as late as possible, ensuring it is always current, without needing to maintain reverse pointers. The cache is now 2 sorted RefLists, rather than 3 HashMaps. Using sorted lists allows the implementation to reduce the in-memory footprint when storing many refs. Using specialized types for the elements allows the code to avoid additional map lookups for auxiliary stat information. To improve scan time during getRefs(), the lists are returned via a copy-on-write contract. Most callers of getRefs() do not modify the returned collections, so the copy-on-write semantics improves access on repositories with a large number of packed references. Iterator traversals of the returned Map<String,Ref> are performed using a simple merge-join of the two cache lists, ensuring we can perform the entire traversal in linear time as a function of the number of references: O(PackedRefs + LooseRefs). Scans of the loose reference space to update the cache run in O(LooseRefs log LooseRefs) time, as the directory contents are sorted before being merged against the in-memory cache. Since the majority of stable references are kept packed, there typically are only a handful of reference names to be sorted, so the sorting cost should not be very high. Locking is reduced during getRefs() by taking advantage of the copy-on-write semantics of the improved cache data structure. This permits concurrent readers to pull back references without blocking each other. If there is contention updating the cache during a scan, one or more updates are simply skipped and will get picked up again in a future scan. Writing to the $GIT_DIR/packed-refs during reference delete is now fully atomic. The file is locked, reparsed fresh, and written back out if a change is necessary. This avoids all race conditions with concurrent external updates of the packed-refs file. The RefLogWriter class has been fully folded into RefDirectory and is therefore deleted. Maintaining the reference's log is the responsiblity of the database implementation, and not all implementations will use java.io for access. Future work still remains to be done to abstract the ReflogReader class away from local disk IO. Change-Id: I26b9287c45a4b2d2be35ba2849daa316f5eec85d Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Rewrite reference handling to be abstract and accurate This commit actually does three major changes to the way references are handled within JGit. Unfortunately they were easier to do as a single massive commit than to break them up into smaller units. Disambiguate symbolic references: --------------------------------- Reporting a symbolic reference such as HEAD as though it were any other normal reference like refs/heads/master causes subtle programming errors. We have been bitten by this error on several occasions, as have some downstream applications written by myself. Instead of reporting HEAD as a reference whose name differs from its "original name", report it as an actual SymbolicRef object that the application can test the type and examine the target of. With this change, Ref is now an abstract type with different subclasses for the different types. In the classical example of "HEAD" being a symbolic reference to branch "refs/heads/master", the Repository.getAllRefs() method will now return: Map<String, Ref> all = repository.getAllRefs(); SymbolicRef HEAD = (SymbolicRef) all.get("HEAD"); ObjectIdRef master = (ObjectIdRef) all.get("refs/heads/master"); assertSame(master, HEAD.getTarget()); assertSame(master.getObjectId(), HEAD.getObjectId()); assertEquals("HEAD", HEAD.getName()); assertEquals("refs/heads/master", master.getName()); A nice side-effect of this change is the storage type of the symbolic reference is no longer ambiguous with the storge type of the underlying reference it targets. In the above example, if master was only available in the packed-refs file, then the following is also true: assertSame(Ref.Storage.LOOSE, HEAD.getStorage()); assertSame(Ref.Storage.PACKED, master.getStorage()); (Prior to this change we returned the ambiguous storage of LOOSE_PACKED for HEAD, which was confusing since it wasn't actually true on disk). Another nice side-effect of this change is all intermediate symbolic references are preserved, and are therefore visible to the application when they walk the target chain. We can now correctly inspect chains of symbolic references. As a result of this change the Ref.getOrigName() method has been removed from the API. Applications should identify a symbolic reference by testing for isSymbolic() and not by using an arcane string comparsion between properties. Abstract the RefDatabase storage: --------------------------------- RefDatabase is now abstract, similar to ObjectDatabase, and a new concrete implementation called RefDirectory is used for the traditional on-disk storage layout. In the future we plan to support additional implementations, such as a pure in-memory RefDatabase for unit testing purposes. Optimize RefDirectory: ---------------------- The implementation of the in-memory reference cache, reading, and update routines has been completely rewritten. Much of the code was heavily borrowed or cribbed from the prior implementation, so copyright notices have been left intact as much as possible. The RefDirectory cache no longer confuses symbolic references with normal references. This permits the cache to resolve the value of a symbolic reference as late as possible, ensuring it is always current, without needing to maintain reverse pointers. The cache is now 2 sorted RefLists, rather than 3 HashMaps. Using sorted lists allows the implementation to reduce the in-memory footprint when storing many refs. Using specialized types for the elements allows the code to avoid additional map lookups for auxiliary stat information. To improve scan time during getRefs(), the lists are returned via a copy-on-write contract. Most callers of getRefs() do not modify the returned collections, so the copy-on-write semantics improves access on repositories with a large number of packed references. Iterator traversals of the returned Map<String,Ref> are performed using a simple merge-join of the two cache lists, ensuring we can perform the entire traversal in linear time as a function of the number of references: O(PackedRefs + LooseRefs). Scans of the loose reference space to update the cache run in O(LooseRefs log LooseRefs) time, as the directory contents are sorted before being merged against the in-memory cache. Since the majority of stable references are kept packed, there typically are only a handful of reference names to be sorted, so the sorting cost should not be very high. Locking is reduced during getRefs() by taking advantage of the copy-on-write semantics of the improved cache data structure. This permits concurrent readers to pull back references without blocking each other. If there is contention updating the cache during a scan, one or more updates are simply skipped and will get picked up again in a future scan. Writing to the $GIT_DIR/packed-refs during reference delete is now fully atomic. The file is locked, reparsed fresh, and written back out if a change is necessary. This avoids all race conditions with concurrent external updates of the packed-refs file. The RefLogWriter class has been fully folded into RefDirectory and is therefore deleted. Maintaining the reference's log is the responsiblity of the database implementation, and not all implementations will use java.io for access. Future work still remains to be done to abstract the ReflogReader class away from local disk IO. Change-Id: I26b9287c45a4b2d2be35ba2849daa316f5eec85d Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Rewrite reference handling to be abstract and accurate This commit actually does three major changes to the way references are handled within JGit. Unfortunately they were easier to do as a single massive commit than to break them up into smaller units. Disambiguate symbolic references: --------------------------------- Reporting a symbolic reference such as HEAD as though it were any other normal reference like refs/heads/master causes subtle programming errors. We have been bitten by this error on several occasions, as have some downstream applications written by myself. Instead of reporting HEAD as a reference whose name differs from its "original name", report it as an actual SymbolicRef object that the application can test the type and examine the target of. With this change, Ref is now an abstract type with different subclasses for the different types. In the classical example of "HEAD" being a symbolic reference to branch "refs/heads/master", the Repository.getAllRefs() method will now return: Map<String, Ref> all = repository.getAllRefs(); SymbolicRef HEAD = (SymbolicRef) all.get("HEAD"); ObjectIdRef master = (ObjectIdRef) all.get("refs/heads/master"); assertSame(master, HEAD.getTarget()); assertSame(master.getObjectId(), HEAD.getObjectId()); assertEquals("HEAD", HEAD.getName()); assertEquals("refs/heads/master", master.getName()); A nice side-effect of this change is the storage type of the symbolic reference is no longer ambiguous with the storge type of the underlying reference it targets. In the above example, if master was only available in the packed-refs file, then the following is also true: assertSame(Ref.Storage.LOOSE, HEAD.getStorage()); assertSame(Ref.Storage.PACKED, master.getStorage()); (Prior to this change we returned the ambiguous storage of LOOSE_PACKED for HEAD, which was confusing since it wasn't actually true on disk). Another nice side-effect of this change is all intermediate symbolic references are preserved, and are therefore visible to the application when they walk the target chain. We can now correctly inspect chains of symbolic references. As a result of this change the Ref.getOrigName() method has been removed from the API. Applications should identify a symbolic reference by testing for isSymbolic() and not by using an arcane string comparsion between properties. Abstract the RefDatabase storage: --------------------------------- RefDatabase is now abstract, similar to ObjectDatabase, and a new concrete implementation called RefDirectory is used for the traditional on-disk storage layout. In the future we plan to support additional implementations, such as a pure in-memory RefDatabase for unit testing purposes. Optimize RefDirectory: ---------------------- The implementation of the in-memory reference cache, reading, and update routines has been completely rewritten. Much of the code was heavily borrowed or cribbed from the prior implementation, so copyright notices have been left intact as much as possible. The RefDirectory cache no longer confuses symbolic references with normal references. This permits the cache to resolve the value of a symbolic reference as late as possible, ensuring it is always current, without needing to maintain reverse pointers. The cache is now 2 sorted RefLists, rather than 3 HashMaps. Using sorted lists allows the implementation to reduce the in-memory footprint when storing many refs. Using specialized types for the elements allows the code to avoid additional map lookups for auxiliary stat information. To improve scan time during getRefs(), the lists are returned via a copy-on-write contract. Most callers of getRefs() do not modify the returned collections, so the copy-on-write semantics improves access on repositories with a large number of packed references. Iterator traversals of the returned Map<String,Ref> are performed using a simple merge-join of the two cache lists, ensuring we can perform the entire traversal in linear time as a function of the number of references: O(PackedRefs + LooseRefs). Scans of the loose reference space to update the cache run in O(LooseRefs log LooseRefs) time, as the directory contents are sorted before being merged against the in-memory cache. Since the majority of stable references are kept packed, there typically are only a handful of reference names to be sorted, so the sorting cost should not be very high. Locking is reduced during getRefs() by taking advantage of the copy-on-write semantics of the improved cache data structure. This permits concurrent readers to pull back references without blocking each other. If there is contention updating the cache during a scan, one or more updates are simply skipped and will get picked up again in a future scan. Writing to the $GIT_DIR/packed-refs during reference delete is now fully atomic. The file is locked, reparsed fresh, and written back out if a change is necessary. This avoids all race conditions with concurrent external updates of the packed-refs file. The RefLogWriter class has been fully folded into RefDirectory and is therefore deleted. Maintaining the reference's log is the responsiblity of the database implementation, and not all implementations will use java.io for access. Future work still remains to be done to abstract the ReflogReader class away from local disk IO. Change-Id: I26b9287c45a4b2d2be35ba2849daa316f5eec85d Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Rewrite reference handling to be abstract and accurate This commit actually does three major changes to the way references are handled within JGit. Unfortunately they were easier to do as a single massive commit than to break them up into smaller units. Disambiguate symbolic references: --------------------------------- Reporting a symbolic reference such as HEAD as though it were any other normal reference like refs/heads/master causes subtle programming errors. We have been bitten by this error on several occasions, as have some downstream applications written by myself. Instead of reporting HEAD as a reference whose name differs from its "original name", report it as an actual SymbolicRef object that the application can test the type and examine the target of. With this change, Ref is now an abstract type with different subclasses for the different types. In the classical example of "HEAD" being a symbolic reference to branch "refs/heads/master", the Repository.getAllRefs() method will now return: Map<String, Ref> all = repository.getAllRefs(); SymbolicRef HEAD = (SymbolicRef) all.get("HEAD"); ObjectIdRef master = (ObjectIdRef) all.get("refs/heads/master"); assertSame(master, HEAD.getTarget()); assertSame(master.getObjectId(), HEAD.getObjectId()); assertEquals("HEAD", HEAD.getName()); assertEquals("refs/heads/master", master.getName()); A nice side-effect of this change is the storage type of the symbolic reference is no longer ambiguous with the storge type of the underlying reference it targets. In the above example, if master was only available in the packed-refs file, then the following is also true: assertSame(Ref.Storage.LOOSE, HEAD.getStorage()); assertSame(Ref.Storage.PACKED, master.getStorage()); (Prior to this change we returned the ambiguous storage of LOOSE_PACKED for HEAD, which was confusing since it wasn't actually true on disk). Another nice side-effect of this change is all intermediate symbolic references are preserved, and are therefore visible to the application when they walk the target chain. We can now correctly inspect chains of symbolic references. As a result of this change the Ref.getOrigName() method has been removed from the API. Applications should identify a symbolic reference by testing for isSymbolic() and not by using an arcane string comparsion between properties. Abstract the RefDatabase storage: --------------------------------- RefDatabase is now abstract, similar to ObjectDatabase, and a new concrete implementation called RefDirectory is used for the traditional on-disk storage layout. In the future we plan to support additional implementations, such as a pure in-memory RefDatabase for unit testing purposes. Optimize RefDirectory: ---------------------- The implementation of the in-memory reference cache, reading, and update routines has been completely rewritten. Much of the code was heavily borrowed or cribbed from the prior implementation, so copyright notices have been left intact as much as possible. The RefDirectory cache no longer confuses symbolic references with normal references. This permits the cache to resolve the value of a symbolic reference as late as possible, ensuring it is always current, without needing to maintain reverse pointers. The cache is now 2 sorted RefLists, rather than 3 HashMaps. Using sorted lists allows the implementation to reduce the in-memory footprint when storing many refs. Using specialized types for the elements allows the code to avoid additional map lookups for auxiliary stat information. To improve scan time during getRefs(), the lists are returned via a copy-on-write contract. Most callers of getRefs() do not modify the returned collections, so the copy-on-write semantics improves access on repositories with a large number of packed references. Iterator traversals of the returned Map<String,Ref> are performed using a simple merge-join of the two cache lists, ensuring we can perform the entire traversal in linear time as a function of the number of references: O(PackedRefs + LooseRefs). Scans of the loose reference space to update the cache run in O(LooseRefs log LooseRefs) time, as the directory contents are sorted before being merged against the in-memory cache. Since the majority of stable references are kept packed, there typically are only a handful of reference names to be sorted, so the sorting cost should not be very high. Locking is reduced during getRefs() by taking advantage of the copy-on-write semantics of the improved cache data structure. This permits concurrent readers to pull back references without blocking each other. If there is contention updating the cache during a scan, one or more updates are simply skipped and will get picked up again in a future scan. Writing to the $GIT_DIR/packed-refs during reference delete is now fully atomic. The file is locked, reparsed fresh, and written back out if a change is necessary. This avoids all race conditions with concurrent external updates of the packed-refs file. The RefLogWriter class has been fully folded into RefDirectory and is therefore deleted. Maintaining the reference's log is the responsiblity of the database implementation, and not all implementations will use java.io for access. Future work still remains to be done to abstract the ReflogReader class away from local disk IO. Change-Id: I26b9287c45a4b2d2be35ba2849daa316f5eec85d Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Rewrite reference handling to be abstract and accurate This commit actually does three major changes to the way references are handled within JGit. Unfortunately they were easier to do as a single massive commit than to break them up into smaller units. Disambiguate symbolic references: --------------------------------- Reporting a symbolic reference such as HEAD as though it were any other normal reference like refs/heads/master causes subtle programming errors. We have been bitten by this error on several occasions, as have some downstream applications written by myself. Instead of reporting HEAD as a reference whose name differs from its "original name", report it as an actual SymbolicRef object that the application can test the type and examine the target of. With this change, Ref is now an abstract type with different subclasses for the different types. In the classical example of "HEAD" being a symbolic reference to branch "refs/heads/master", the Repository.getAllRefs() method will now return: Map<String, Ref> all = repository.getAllRefs(); SymbolicRef HEAD = (SymbolicRef) all.get("HEAD"); ObjectIdRef master = (ObjectIdRef) all.get("refs/heads/master"); assertSame(master, HEAD.getTarget()); assertSame(master.getObjectId(), HEAD.getObjectId()); assertEquals("HEAD", HEAD.getName()); assertEquals("refs/heads/master", master.getName()); A nice side-effect of this change is the storage type of the symbolic reference is no longer ambiguous with the storge type of the underlying reference it targets. In the above example, if master was only available in the packed-refs file, then the following is also true: assertSame(Ref.Storage.LOOSE, HEAD.getStorage()); assertSame(Ref.Storage.PACKED, master.getStorage()); (Prior to this change we returned the ambiguous storage of LOOSE_PACKED for HEAD, which was confusing since it wasn't actually true on disk). Another nice side-effect of this change is all intermediate symbolic references are preserved, and are therefore visible to the application when they walk the target chain. We can now correctly inspect chains of symbolic references. As a result of this change the Ref.getOrigName() method has been removed from the API. Applications should identify a symbolic reference by testing for isSymbolic() and not by using an arcane string comparsion between properties. Abstract the RefDatabase storage: --------------------------------- RefDatabase is now abstract, similar to ObjectDatabase, and a new concrete implementation called RefDirectory is used for the traditional on-disk storage layout. In the future we plan to support additional implementations, such as a pure in-memory RefDatabase for unit testing purposes. Optimize RefDirectory: ---------------------- The implementation of the in-memory reference cache, reading, and update routines has been completely rewritten. Much of the code was heavily borrowed or cribbed from the prior implementation, so copyright notices have been left intact as much as possible. The RefDirectory cache no longer confuses symbolic references with normal references. This permits the cache to resolve the value of a symbolic reference as late as possible, ensuring it is always current, without needing to maintain reverse pointers. The cache is now 2 sorted RefLists, rather than 3 HashMaps. Using sorted lists allows the implementation to reduce the in-memory footprint when storing many refs. Using specialized types for the elements allows the code to avoid additional map lookups for auxiliary stat information. To improve scan time during getRefs(), the lists are returned via a copy-on-write contract. Most callers of getRefs() do not modify the returned collections, so the copy-on-write semantics improves access on repositories with a large number of packed references. Iterator traversals of the returned Map<String,Ref> are performed using a simple merge-join of the two cache lists, ensuring we can perform the entire traversal in linear time as a function of the number of references: O(PackedRefs + LooseRefs). Scans of the loose reference space to update the cache run in O(LooseRefs log LooseRefs) time, as the directory contents are sorted before being merged against the in-memory cache. Since the majority of stable references are kept packed, there typically are only a handful of reference names to be sorted, so the sorting cost should not be very high. Locking is reduced during getRefs() by taking advantage of the copy-on-write semantics of the improved cache data structure. This permits concurrent readers to pull back references without blocking each other. If there is contention updating the cache during a scan, one or more updates are simply skipped and will get picked up again in a future scan. Writing to the $GIT_DIR/packed-refs during reference delete is now fully atomic. The file is locked, reparsed fresh, and written back out if a change is necessary. This avoids all race conditions with concurrent external updates of the packed-refs file. The RefLogWriter class has been fully folded into RefDirectory and is therefore deleted. Maintaining the reference's log is the responsiblity of the database implementation, and not all implementations will use java.io for access. Future work still remains to be done to abstract the ReflogReader class away from local disk IO. Change-Id: I26b9287c45a4b2d2be35ba2849daa316f5eec85d Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Rewrite reference handling to be abstract and accurate This commit actually does three major changes to the way references are handled within JGit. Unfortunately they were easier to do as a single massive commit than to break them up into smaller units. Disambiguate symbolic references: --------------------------------- Reporting a symbolic reference such as HEAD as though it were any other normal reference like refs/heads/master causes subtle programming errors. We have been bitten by this error on several occasions, as have some downstream applications written by myself. Instead of reporting HEAD as a reference whose name differs from its "original name", report it as an actual SymbolicRef object that the application can test the type and examine the target of. With this change, Ref is now an abstract type with different subclasses for the different types. In the classical example of "HEAD" being a symbolic reference to branch "refs/heads/master", the Repository.getAllRefs() method will now return: Map<String, Ref> all = repository.getAllRefs(); SymbolicRef HEAD = (SymbolicRef) all.get("HEAD"); ObjectIdRef master = (ObjectIdRef) all.get("refs/heads/master"); assertSame(master, HEAD.getTarget()); assertSame(master.getObjectId(), HEAD.getObjectId()); assertEquals("HEAD", HEAD.getName()); assertEquals("refs/heads/master", master.getName()); A nice side-effect of this change is the storage type of the symbolic reference is no longer ambiguous with the storge type of the underlying reference it targets. In the above example, if master was only available in the packed-refs file, then the following is also true: assertSame(Ref.Storage.LOOSE, HEAD.getStorage()); assertSame(Ref.Storage.PACKED, master.getStorage()); (Prior to this change we returned the ambiguous storage of LOOSE_PACKED for HEAD, which was confusing since it wasn't actually true on disk). Another nice side-effect of this change is all intermediate symbolic references are preserved, and are therefore visible to the application when they walk the target chain. We can now correctly inspect chains of symbolic references. As a result of this change the Ref.getOrigName() method has been removed from the API. Applications should identify a symbolic reference by testing for isSymbolic() and not by using an arcane string comparsion between properties. Abstract the RefDatabase storage: --------------------------------- RefDatabase is now abstract, similar to ObjectDatabase, and a new concrete implementation called RefDirectory is used for the traditional on-disk storage layout. In the future we plan to support additional implementations, such as a pure in-memory RefDatabase for unit testing purposes. Optimize RefDirectory: ---------------------- The implementation of the in-memory reference cache, reading, and update routines has been completely rewritten. Much of the code was heavily borrowed or cribbed from the prior implementation, so copyright notices have been left intact as much as possible. The RefDirectory cache no longer confuses symbolic references with normal references. This permits the cache to resolve the value of a symbolic reference as late as possible, ensuring it is always current, without needing to maintain reverse pointers. The cache is now 2 sorted RefLists, rather than 3 HashMaps. Using sorted lists allows the implementation to reduce the in-memory footprint when storing many refs. Using specialized types for the elements allows the code to avoid additional map lookups for auxiliary stat information. To improve scan time during getRefs(), the lists are returned via a copy-on-write contract. Most callers of getRefs() do not modify the returned collections, so the copy-on-write semantics improves access on repositories with a large number of packed references. Iterator traversals of the returned Map<String,Ref> are performed using a simple merge-join of the two cache lists, ensuring we can perform the entire traversal in linear time as a function of the number of references: O(PackedRefs + LooseRefs). Scans of the loose reference space to update the cache run in O(LooseRefs log LooseRefs) time, as the directory contents are sorted before being merged against the in-memory cache. Since the majority of stable references are kept packed, there typically are only a handful of reference names to be sorted, so the sorting cost should not be very high. Locking is reduced during getRefs() by taking advantage of the copy-on-write semantics of the improved cache data structure. This permits concurrent readers to pull back references without blocking each other. If there is contention updating the cache during a scan, one or more updates are simply skipped and will get picked up again in a future scan. Writing to the $GIT_DIR/packed-refs during reference delete is now fully atomic. The file is locked, reparsed fresh, and written back out if a change is necessary. This avoids all race conditions with concurrent external updates of the packed-refs file. The RefLogWriter class has been fully folded into RefDirectory and is therefore deleted. Maintaining the reference's log is the responsiblity of the database implementation, and not all implementations will use java.io for access. Future work still remains to be done to abstract the ReflogReader class away from local disk IO. Change-Id: I26b9287c45a4b2d2be35ba2849daa316f5eec85d Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831
  1. /*
  2. * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
  3. * Copyright (C) 2008-2010, Google Inc.
  4. * Copyright (C) 2006-2010, Robin Rosenberg <robin.rosenberg@dewire.com>
  5. * Copyright (C) 2006-2012, Shawn O. Pearce <spearce@spearce.org>
  6. * Copyright (C) 2012, Daniel Megert <daniel_megert@ch.ibm.com>
  7. * and other copyright owners as documented in the project's IP log.
  8. *
  9. * This program and the accompanying materials are made available
  10. * under the terms of the Eclipse Distribution License v1.0 which
  11. * accompanies this distribution, is reproduced below, and is
  12. * available at http://www.eclipse.org/org/documents/edl-v10.php
  13. *
  14. * All rights reserved.
  15. *
  16. * Redistribution and use in source and binary forms, with or
  17. * without modification, are permitted provided that the following
  18. * conditions are met:
  19. *
  20. * - Redistributions of source code must retain the above copyright
  21. * notice, this list of conditions and the following disclaimer.
  22. *
  23. * - Redistributions in binary form must reproduce the above
  24. * copyright notice, this list of conditions and the following
  25. * disclaimer in the documentation and/or other materials provided
  26. * with the distribution.
  27. *
  28. * - Neither the name of the Eclipse Foundation, Inc. nor the
  29. * names of its contributors may be used to endorse or promote
  30. * products derived from this software without specific prior
  31. * written permission.
  32. *
  33. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  34. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  35. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  36. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  37. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  38. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  39. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  40. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  41. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  42. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  43. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  44. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  45. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  46. */
  47. package org.eclipse.jgit.lib;
  48. import java.io.BufferedOutputStream;
  49. import java.io.File;
  50. import java.io.FileNotFoundException;
  51. import java.io.FileOutputStream;
  52. import java.io.IOException;
  53. import java.net.URISyntaxException;
  54. import java.text.MessageFormat;
  55. import java.util.Collection;
  56. import java.util.Collections;
  57. import java.util.HashMap;
  58. import java.util.HashSet;
  59. import java.util.LinkedList;
  60. import java.util.List;
  61. import java.util.Map;
  62. import java.util.Set;
  63. import java.util.concurrent.atomic.AtomicInteger;
  64. import java.util.concurrent.atomic.AtomicLong;
  65. import org.eclipse.jgit.annotations.NonNull;
  66. import org.eclipse.jgit.annotations.Nullable;
  67. import org.eclipse.jgit.attributes.AttributesNodeProvider;
  68. import org.eclipse.jgit.dircache.DirCache;
  69. import org.eclipse.jgit.errors.AmbiguousObjectException;
  70. import org.eclipse.jgit.errors.CorruptObjectException;
  71. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  72. import org.eclipse.jgit.errors.MissingObjectException;
  73. import org.eclipse.jgit.errors.NoWorkTreeException;
  74. import org.eclipse.jgit.errors.RevisionSyntaxException;
  75. import org.eclipse.jgit.events.IndexChangedEvent;
  76. import org.eclipse.jgit.events.IndexChangedListener;
  77. import org.eclipse.jgit.events.ListenerList;
  78. import org.eclipse.jgit.events.RepositoryEvent;
  79. import org.eclipse.jgit.internal.JGitText;
  80. import org.eclipse.jgit.revwalk.RevBlob;
  81. import org.eclipse.jgit.revwalk.RevCommit;
  82. import org.eclipse.jgit.revwalk.RevObject;
  83. import org.eclipse.jgit.revwalk.RevTree;
  84. import org.eclipse.jgit.revwalk.RevWalk;
  85. import org.eclipse.jgit.transport.RefSpec;
  86. import org.eclipse.jgit.transport.RemoteConfig;
  87. import org.eclipse.jgit.treewalk.TreeWalk;
  88. import org.eclipse.jgit.util.FS;
  89. import org.eclipse.jgit.util.FileUtils;
  90. import org.eclipse.jgit.util.IO;
  91. import org.eclipse.jgit.util.RawParseUtils;
  92. import org.eclipse.jgit.util.SystemReader;
  93. import org.eclipse.jgit.util.io.SafeBufferedOutputStream;
  94. /**
  95. * Represents a Git repository.
  96. * <p>
  97. * A repository holds all objects and refs used for managing source code (could
  98. * be any type of file, but source code is what SCM's are typically used for).
  99. * <p>
  100. * This class is thread-safe.
  101. */
  102. public abstract class Repository implements AutoCloseable {
  103. private static final ListenerList globalListeners = new ListenerList();
  104. /** @return the global listener list observing all events in this JVM. */
  105. public static ListenerList getGlobalListenerList() {
  106. return globalListeners;
  107. }
  108. /** Use counter */
  109. final AtomicInteger useCnt = new AtomicInteger(1);
  110. final AtomicLong closedAt = new AtomicLong();
  111. /** Metadata directory holding the repository's critical files. */
  112. private final File gitDir;
  113. /** File abstraction used to resolve paths. */
  114. private final FS fs;
  115. private final ListenerList myListeners = new ListenerList();
  116. /** If not bare, the top level directory of the working files. */
  117. private final File workTree;
  118. /** If not bare, the index file caching the working file states. */
  119. private final File indexFile;
  120. /**
  121. * Initialize a new repository instance.
  122. *
  123. * @param options
  124. * options to configure the repository.
  125. */
  126. protected Repository(final BaseRepositoryBuilder options) {
  127. gitDir = options.getGitDir();
  128. fs = options.getFS();
  129. workTree = options.getWorkTree();
  130. indexFile = options.getIndexFile();
  131. }
  132. /** @return listeners observing only events on this repository. */
  133. @NonNull
  134. public ListenerList getListenerList() {
  135. return myListeners;
  136. }
  137. /**
  138. * Fire an event to all registered listeners.
  139. * <p>
  140. * The source repository of the event is automatically set to this
  141. * repository, before the event is delivered to any listeners.
  142. *
  143. * @param event
  144. * the event to deliver.
  145. */
  146. public void fireEvent(RepositoryEvent<?> event) {
  147. event.setRepository(this);
  148. myListeners.dispatch(event);
  149. globalListeners.dispatch(event);
  150. }
  151. /**
  152. * Create a new Git repository.
  153. * <p>
  154. * Repository with working tree is created using this method. This method is
  155. * the same as {@code create(false)}.
  156. *
  157. * @throws IOException
  158. * @see #create(boolean)
  159. */
  160. public void create() throws IOException {
  161. create(false);
  162. }
  163. /**
  164. * Create a new Git repository initializing the necessary files and
  165. * directories.
  166. *
  167. * @param bare
  168. * if true, a bare repository (a repository without a working
  169. * directory) is created.
  170. * @throws IOException
  171. * in case of IO problem
  172. */
  173. public abstract void create(boolean bare) throws IOException;
  174. /**
  175. * @return local metadata directory; {@code null} if repository isn't local.
  176. */
  177. /*
  178. * TODO This method should be annotated as Nullable, because in some
  179. * specific configurations metadata is not located in the local file system
  180. * (for example in memory databases). In "usual" repositories this
  181. * annotation would only cause compiler errors at places where the actual
  182. * directory can never be null.
  183. */
  184. public File getDirectory() {
  185. return gitDir;
  186. }
  187. /**
  188. * @return the object database which stores this repository's data.
  189. */
  190. @NonNull
  191. public abstract ObjectDatabase getObjectDatabase();
  192. /** @return a new inserter to create objects in {@link #getObjectDatabase()} */
  193. @NonNull
  194. public ObjectInserter newObjectInserter() {
  195. return getObjectDatabase().newInserter();
  196. }
  197. /** @return a new reader to read objects from {@link #getObjectDatabase()} */
  198. @NonNull
  199. public ObjectReader newObjectReader() {
  200. return getObjectDatabase().newReader();
  201. }
  202. /** @return the reference database which stores the reference namespace. */
  203. @NonNull
  204. public abstract RefDatabase getRefDatabase();
  205. /**
  206. * @return the configuration of this repository
  207. */
  208. @NonNull
  209. public abstract StoredConfig getConfig();
  210. /**
  211. * @return a new {@link AttributesNodeProvider}. This
  212. * {@link AttributesNodeProvider} is lazy loaded only once. It means
  213. * that it will not be updated after loading. Prefer creating new
  214. * instance for each use.
  215. * @since 4.2
  216. */
  217. @NonNull
  218. public abstract AttributesNodeProvider createAttributesNodeProvider();
  219. /**
  220. * @return the used file system abstraction, or or {@code null} if
  221. * repository isn't local.
  222. */
  223. /*
  224. * TODO This method should be annotated as Nullable, because in some
  225. * specific configurations metadata is not located in the local file system
  226. * (for example in memory databases). In "usual" repositories this
  227. * annotation would only cause compiler errors at places where the actual
  228. * directory can never be null.
  229. */
  230. public FS getFS() {
  231. return fs;
  232. }
  233. /**
  234. * @param objectId
  235. * @return true if the specified object is stored in this repo or any of the
  236. * known shared repositories.
  237. */
  238. public boolean hasObject(AnyObjectId objectId) {
  239. try {
  240. return getObjectDatabase().has(objectId);
  241. } catch (IOException e) {
  242. // Legacy API, assume error means "no"
  243. return false;
  244. }
  245. }
  246. /**
  247. * Open an object from this repository.
  248. * <p>
  249. * This is a one-shot call interface which may be faster than allocating a
  250. * {@link #newObjectReader()} to perform the lookup.
  251. *
  252. * @param objectId
  253. * identity of the object to open.
  254. * @return a {@link ObjectLoader} for accessing the object.
  255. * @throws MissingObjectException
  256. * the object does not exist.
  257. * @throws IOException
  258. * the object store cannot be accessed.
  259. */
  260. @NonNull
  261. public ObjectLoader open(final AnyObjectId objectId)
  262. throws MissingObjectException, IOException {
  263. return getObjectDatabase().open(objectId);
  264. }
  265. /**
  266. * Open an object from this repository.
  267. * <p>
  268. * This is a one-shot call interface which may be faster than allocating a
  269. * {@link #newObjectReader()} to perform the lookup.
  270. *
  271. * @param objectId
  272. * identity of the object to open.
  273. * @param typeHint
  274. * hint about the type of object being requested, e.g.
  275. * {@link Constants#OBJ_BLOB}; {@link ObjectReader#OBJ_ANY} if
  276. * the object type is not known, or does not matter to the
  277. * caller.
  278. * @return a {@link ObjectLoader} for accessing the object.
  279. * @throws MissingObjectException
  280. * the object does not exist.
  281. * @throws IncorrectObjectTypeException
  282. * typeHint was not OBJ_ANY, and the object's actual type does
  283. * not match typeHint.
  284. * @throws IOException
  285. * the object store cannot be accessed.
  286. */
  287. @NonNull
  288. public ObjectLoader open(AnyObjectId objectId, int typeHint)
  289. throws MissingObjectException, IncorrectObjectTypeException,
  290. IOException {
  291. return getObjectDatabase().open(objectId, typeHint);
  292. }
  293. /**
  294. * Create a command to update, create or delete a ref in this repository.
  295. *
  296. * @param ref
  297. * name of the ref the caller wants to modify.
  298. * @return an update command. The caller must finish populating this command
  299. * and then invoke one of the update methods to actually make a
  300. * change.
  301. * @throws IOException
  302. * a symbolic ref was passed in and could not be resolved back
  303. * to the base ref, as the symbolic ref could not be read.
  304. */
  305. @NonNull
  306. public RefUpdate updateRef(final String ref) throws IOException {
  307. return updateRef(ref, false);
  308. }
  309. /**
  310. * Create a command to update, create or delete a ref in this repository.
  311. *
  312. * @param ref
  313. * name of the ref the caller wants to modify.
  314. * @param detach
  315. * true to create a detached head
  316. * @return an update command. The caller must finish populating this command
  317. * and then invoke one of the update methods to actually make a
  318. * change.
  319. * @throws IOException
  320. * a symbolic ref was passed in and could not be resolved back
  321. * to the base ref, as the symbolic ref could not be read.
  322. */
  323. @NonNull
  324. public RefUpdate updateRef(final String ref, final boolean detach) throws IOException {
  325. return getRefDatabase().newUpdate(ref, detach);
  326. }
  327. /**
  328. * Create a command to rename a ref in this repository
  329. *
  330. * @param fromRef
  331. * name of ref to rename from
  332. * @param toRef
  333. * name of ref to rename to
  334. * @return an update command that knows how to rename a branch to another.
  335. * @throws IOException
  336. * the rename could not be performed.
  337. *
  338. */
  339. @NonNull
  340. public RefRename renameRef(final String fromRef, final String toRef) throws IOException {
  341. return getRefDatabase().newRename(fromRef, toRef);
  342. }
  343. /**
  344. * Parse a git revision string and return an object id.
  345. *
  346. * Combinations of these operators are supported:
  347. * <ul>
  348. * <li><b>HEAD</b>, <b>MERGE_HEAD</b>, <b>FETCH_HEAD</b></li>
  349. * <li><b>SHA-1</b>: a complete or abbreviated SHA-1</li>
  350. * <li><b>refs/...</b>: a complete reference name</li>
  351. * <li><b>short-name</b>: a short reference name under {@code refs/heads},
  352. * {@code refs/tags}, or {@code refs/remotes} namespace</li>
  353. * <li><b>tag-NN-gABBREV</b>: output from describe, parsed by treating
  354. * {@code ABBREV} as an abbreviated SHA-1.</li>
  355. * <li><i>id</i><b>^</b>: first parent of commit <i>id</i>, this is the same
  356. * as {@code id^1}</li>
  357. * <li><i>id</i><b>^0</b>: ensure <i>id</i> is a commit</li>
  358. * <li><i>id</i><b>^n</b>: n-th parent of commit <i>id</i></li>
  359. * <li><i>id</i><b>~n</b>: n-th historical ancestor of <i>id</i>, by first
  360. * parent. {@code id~3} is equivalent to {@code id^1^1^1} or {@code id^^^}.</li>
  361. * <li><i>id</i><b>:path</b>: Lookup path under tree named by <i>id</i></li>
  362. * <li><i>id</i><b>^{commit}</b>: ensure <i>id</i> is a commit</li>
  363. * <li><i>id</i><b>^{tree}</b>: ensure <i>id</i> is a tree</li>
  364. * <li><i>id</i><b>^{tag}</b>: ensure <i>id</i> is a tag</li>
  365. * <li><i>id</i><b>^{blob}</b>: ensure <i>id</i> is a blob</li>
  366. * </ul>
  367. *
  368. * <p>
  369. * The following operators are specified by Git conventions, but are not
  370. * supported by this method:
  371. * <ul>
  372. * <li><b>ref@{n}</b>: n-th version of ref as given by its reflog</li>
  373. * <li><b>ref@{time}</b>: value of ref at the designated time</li>
  374. * </ul>
  375. *
  376. * @param revstr
  377. * A git object references expression
  378. * @return an ObjectId or {@code null} if revstr can't be resolved to any
  379. * ObjectId
  380. * @throws AmbiguousObjectException
  381. * {@code revstr} contains an abbreviated ObjectId and this
  382. * repository contains more than one object which match to the
  383. * input abbreviation.
  384. * @throws IncorrectObjectTypeException
  385. * the id parsed does not meet the type required to finish
  386. * applying the operators in the expression.
  387. * @throws RevisionSyntaxException
  388. * the expression is not supported by this implementation, or
  389. * does not meet the standard syntax.
  390. * @throws IOException
  391. * on serious errors
  392. */
  393. @Nullable
  394. public ObjectId resolve(final String revstr)
  395. throws AmbiguousObjectException, IncorrectObjectTypeException,
  396. RevisionSyntaxException, IOException {
  397. try (RevWalk rw = new RevWalk(this)) {
  398. Object resolved = resolve(rw, revstr);
  399. if (resolved instanceof String) {
  400. final Ref ref = getRef((String)resolved);
  401. return ref != null ? ref.getLeaf().getObjectId() : null;
  402. } else {
  403. return (ObjectId) resolved;
  404. }
  405. }
  406. }
  407. /**
  408. * Simplify an expression, but unlike {@link #resolve(String)} it will not
  409. * resolve a branch passed or resulting from the expression, such as @{-}.
  410. * Thus this method can be used to process an expression to a method that
  411. * expects a branch or revision id.
  412. *
  413. * @param revstr
  414. * @return object id or ref name from resolved expression or {@code null} if
  415. * given expression cannot be resolved
  416. * @throws AmbiguousObjectException
  417. * @throws IOException
  418. */
  419. @Nullable
  420. public String simplify(final String revstr)
  421. throws AmbiguousObjectException, IOException {
  422. try (RevWalk rw = new RevWalk(this)) {
  423. Object resolved = resolve(rw, revstr);
  424. if (resolved != null)
  425. if (resolved instanceof String)
  426. return (String) resolved;
  427. else
  428. return ((AnyObjectId) resolved).getName();
  429. return null;
  430. }
  431. }
  432. @Nullable
  433. private Object resolve(final RevWalk rw, final String revstr)
  434. throws IOException {
  435. char[] revChars = revstr.toCharArray();
  436. RevObject rev = null;
  437. String name = null;
  438. int done = 0;
  439. for (int i = 0; i < revChars.length; ++i) {
  440. switch (revChars[i]) {
  441. case '^':
  442. if (rev == null) {
  443. if (name == null)
  444. if (done == 0)
  445. name = new String(revChars, done, i);
  446. else {
  447. done = i + 1;
  448. break;
  449. }
  450. rev = parseSimple(rw, name);
  451. name = null;
  452. if (rev == null)
  453. return null;
  454. }
  455. if (i + 1 < revChars.length) {
  456. switch (revChars[i + 1]) {
  457. case '0':
  458. case '1':
  459. case '2':
  460. case '3':
  461. case '4':
  462. case '5':
  463. case '6':
  464. case '7':
  465. case '8':
  466. case '9':
  467. int j;
  468. rev = rw.parseCommit(rev);
  469. for (j = i + 1; j < revChars.length; ++j) {
  470. if (!Character.isDigit(revChars[j]))
  471. break;
  472. }
  473. String parentnum = new String(revChars, i + 1, j - i
  474. - 1);
  475. int pnum;
  476. try {
  477. pnum = Integer.parseInt(parentnum);
  478. } catch (NumberFormatException e) {
  479. throw new RevisionSyntaxException(
  480. JGitText.get().invalidCommitParentNumber,
  481. revstr);
  482. }
  483. if (pnum != 0) {
  484. RevCommit commit = (RevCommit) rev;
  485. if (pnum > commit.getParentCount())
  486. rev = null;
  487. else
  488. rev = commit.getParent(pnum - 1);
  489. }
  490. i = j - 1;
  491. done = j;
  492. break;
  493. case '{':
  494. int k;
  495. String item = null;
  496. for (k = i + 2; k < revChars.length; ++k) {
  497. if (revChars[k] == '}') {
  498. item = new String(revChars, i + 2, k - i - 2);
  499. break;
  500. }
  501. }
  502. i = k;
  503. if (item != null)
  504. if (item.equals("tree")) { //$NON-NLS-1$
  505. rev = rw.parseTree(rev);
  506. } else if (item.equals("commit")) { //$NON-NLS-1$
  507. rev = rw.parseCommit(rev);
  508. } else if (item.equals("blob")) { //$NON-NLS-1$
  509. rev = rw.peel(rev);
  510. if (!(rev instanceof RevBlob))
  511. throw new IncorrectObjectTypeException(rev,
  512. Constants.TYPE_BLOB);
  513. } else if (item.equals("")) { //$NON-NLS-1$
  514. rev = rw.peel(rev);
  515. } else
  516. throw new RevisionSyntaxException(revstr);
  517. else
  518. throw new RevisionSyntaxException(revstr);
  519. done = k;
  520. break;
  521. default:
  522. rev = rw.peel(rev);
  523. if (rev instanceof RevCommit) {
  524. RevCommit commit = ((RevCommit) rev);
  525. if (commit.getParentCount() == 0)
  526. rev = null;
  527. else
  528. rev = commit.getParent(0);
  529. } else
  530. throw new IncorrectObjectTypeException(rev,
  531. Constants.TYPE_COMMIT);
  532. }
  533. } else {
  534. rev = rw.peel(rev);
  535. if (rev instanceof RevCommit) {
  536. RevCommit commit = ((RevCommit) rev);
  537. if (commit.getParentCount() == 0)
  538. rev = null;
  539. else
  540. rev = commit.getParent(0);
  541. } else
  542. throw new IncorrectObjectTypeException(rev,
  543. Constants.TYPE_COMMIT);
  544. }
  545. done = i + 1;
  546. break;
  547. case '~':
  548. if (rev == null) {
  549. if (name == null)
  550. if (done == 0)
  551. name = new String(revChars, done, i);
  552. else {
  553. done = i + 1;
  554. break;
  555. }
  556. rev = parseSimple(rw, name);
  557. name = null;
  558. if (rev == null)
  559. return null;
  560. }
  561. rev = rw.peel(rev);
  562. if (!(rev instanceof RevCommit))
  563. throw new IncorrectObjectTypeException(rev,
  564. Constants.TYPE_COMMIT);
  565. int l;
  566. for (l = i + 1; l < revChars.length; ++l) {
  567. if (!Character.isDigit(revChars[l]))
  568. break;
  569. }
  570. int dist;
  571. if (l - i > 1) {
  572. String distnum = new String(revChars, i + 1, l - i - 1);
  573. try {
  574. dist = Integer.parseInt(distnum);
  575. } catch (NumberFormatException e) {
  576. throw new RevisionSyntaxException(
  577. JGitText.get().invalidAncestryLength, revstr);
  578. }
  579. } else
  580. dist = 1;
  581. while (dist > 0) {
  582. RevCommit commit = (RevCommit) rev;
  583. if (commit.getParentCount() == 0) {
  584. rev = null;
  585. break;
  586. }
  587. commit = commit.getParent(0);
  588. rw.parseHeaders(commit);
  589. rev = commit;
  590. --dist;
  591. }
  592. i = l - 1;
  593. done = l;
  594. break;
  595. case '@':
  596. if (rev != null)
  597. throw new RevisionSyntaxException(revstr);
  598. if (i + 1 < revChars.length && revChars[i + 1] != '{')
  599. continue;
  600. int m;
  601. String time = null;
  602. for (m = i + 2; m < revChars.length; ++m) {
  603. if (revChars[m] == '}') {
  604. time = new String(revChars, i + 2, m - i - 2);
  605. break;
  606. }
  607. }
  608. if (time != null) {
  609. if (time.equals("upstream")) { //$NON-NLS-1$
  610. if (name == null)
  611. name = new String(revChars, done, i);
  612. if (name.equals("")) //$NON-NLS-1$
  613. // Currently checked out branch, HEAD if
  614. // detached
  615. name = Constants.HEAD;
  616. if (!Repository.isValidRefName("x/" + name)) //$NON-NLS-1$
  617. throw new RevisionSyntaxException(revstr);
  618. Ref ref = getRef(name);
  619. name = null;
  620. if (ref == null)
  621. return null;
  622. if (ref.isSymbolic())
  623. ref = ref.getLeaf();
  624. name = ref.getName();
  625. RemoteConfig remoteConfig;
  626. try {
  627. remoteConfig = new RemoteConfig(getConfig(),
  628. "origin"); //$NON-NLS-1$
  629. } catch (URISyntaxException e) {
  630. throw new RevisionSyntaxException(revstr);
  631. }
  632. String remoteBranchName = getConfig()
  633. .getString(
  634. ConfigConstants.CONFIG_BRANCH_SECTION,
  635. Repository.shortenRefName(ref.getName()),
  636. ConfigConstants.CONFIG_KEY_MERGE);
  637. List<RefSpec> fetchRefSpecs = remoteConfig
  638. .getFetchRefSpecs();
  639. for (RefSpec refSpec : fetchRefSpecs) {
  640. if (refSpec.matchSource(remoteBranchName)) {
  641. RefSpec expandFromSource = refSpec
  642. .expandFromSource(remoteBranchName);
  643. name = expandFromSource.getDestination();
  644. break;
  645. }
  646. }
  647. if (name == null)
  648. throw new RevisionSyntaxException(revstr);
  649. } else if (time.matches("^-\\d+$")) { //$NON-NLS-1$
  650. if (name != null)
  651. throw new RevisionSyntaxException(revstr);
  652. else {
  653. String previousCheckout = resolveReflogCheckout(-Integer
  654. .parseInt(time));
  655. if (ObjectId.isId(previousCheckout))
  656. rev = parseSimple(rw, previousCheckout);
  657. else
  658. name = previousCheckout;
  659. }
  660. } else {
  661. if (name == null)
  662. name = new String(revChars, done, i);
  663. if (name.equals("")) //$NON-NLS-1$
  664. name = Constants.HEAD;
  665. if (!Repository.isValidRefName("x/" + name)) //$NON-NLS-1$
  666. throw new RevisionSyntaxException(revstr);
  667. Ref ref = getRef(name);
  668. name = null;
  669. if (ref == null)
  670. return null;
  671. // @{n} means current branch, not HEAD@{1} unless
  672. // detached
  673. if (ref.isSymbolic())
  674. ref = ref.getLeaf();
  675. rev = resolveReflog(rw, ref, time);
  676. }
  677. i = m;
  678. } else
  679. throw new RevisionSyntaxException(revstr);
  680. break;
  681. case ':': {
  682. RevTree tree;
  683. if (rev == null) {
  684. if (name == null)
  685. name = new String(revChars, done, i);
  686. if (name.equals("")) //$NON-NLS-1$
  687. name = Constants.HEAD;
  688. rev = parseSimple(rw, name);
  689. name = null;
  690. }
  691. if (rev == null)
  692. return null;
  693. tree = rw.parseTree(rev);
  694. if (i == revChars.length - 1)
  695. return tree.copy();
  696. TreeWalk tw = TreeWalk.forPath(rw.getObjectReader(),
  697. new String(revChars, i + 1, revChars.length - i - 1),
  698. tree);
  699. return tw != null ? tw.getObjectId(0) : null;
  700. }
  701. default:
  702. if (rev != null)
  703. throw new RevisionSyntaxException(revstr);
  704. }
  705. }
  706. if (rev != null)
  707. return rev.copy();
  708. if (name != null)
  709. return name;
  710. if (done == revstr.length())
  711. return null;
  712. name = revstr.substring(done);
  713. if (!Repository.isValidRefName("x/" + name)) //$NON-NLS-1$
  714. throw new RevisionSyntaxException(revstr);
  715. if (getRef(name) != null)
  716. return name;
  717. return resolveSimple(name);
  718. }
  719. private static boolean isHex(char c) {
  720. return ('0' <= c && c <= '9') //
  721. || ('a' <= c && c <= 'f') //
  722. || ('A' <= c && c <= 'F');
  723. }
  724. private static boolean isAllHex(String str, int ptr) {
  725. while (ptr < str.length()) {
  726. if (!isHex(str.charAt(ptr++)))
  727. return false;
  728. }
  729. return true;
  730. }
  731. @Nullable
  732. private RevObject parseSimple(RevWalk rw, String revstr) throws IOException {
  733. ObjectId id = resolveSimple(revstr);
  734. return id != null ? rw.parseAny(id) : null;
  735. }
  736. @Nullable
  737. private ObjectId resolveSimple(final String revstr) throws IOException {
  738. if (ObjectId.isId(revstr))
  739. return ObjectId.fromString(revstr);
  740. if (Repository.isValidRefName("x/" + revstr)) { //$NON-NLS-1$
  741. Ref r = getRefDatabase().getRef(revstr);
  742. if (r != null)
  743. return r.getObjectId();
  744. }
  745. if (AbbreviatedObjectId.isId(revstr))
  746. return resolveAbbreviation(revstr);
  747. int dashg = revstr.indexOf("-g"); //$NON-NLS-1$
  748. if ((dashg + 5) < revstr.length() && 0 <= dashg
  749. && isHex(revstr.charAt(dashg + 2))
  750. && isHex(revstr.charAt(dashg + 3))
  751. && isAllHex(revstr, dashg + 4)) {
  752. // Possibly output from git describe?
  753. String s = revstr.substring(dashg + 2);
  754. if (AbbreviatedObjectId.isId(s))
  755. return resolveAbbreviation(s);
  756. }
  757. return null;
  758. }
  759. @Nullable
  760. private String resolveReflogCheckout(int checkoutNo)
  761. throws IOException {
  762. ReflogReader reader = getReflogReader(Constants.HEAD);
  763. if (reader == null) {
  764. return null;
  765. }
  766. List<ReflogEntry> reflogEntries = reader.getReverseEntries();
  767. for (ReflogEntry entry : reflogEntries) {
  768. CheckoutEntry checkout = entry.parseCheckout();
  769. if (checkout != null)
  770. if (checkoutNo-- == 1)
  771. return checkout.getFromBranch();
  772. }
  773. return null;
  774. }
  775. private RevCommit resolveReflog(RevWalk rw, Ref ref, String time)
  776. throws IOException {
  777. int number;
  778. try {
  779. number = Integer.parseInt(time);
  780. } catch (NumberFormatException nfe) {
  781. throw new RevisionSyntaxException(MessageFormat.format(
  782. JGitText.get().invalidReflogRevision, time));
  783. }
  784. assert number >= 0;
  785. ReflogReader reader = getReflogReader(ref.getName());
  786. if (reader == null) {
  787. throw new RevisionSyntaxException(
  788. MessageFormat.format(JGitText.get().reflogEntryNotFound,
  789. Integer.valueOf(number), ref.getName()));
  790. }
  791. ReflogEntry entry = reader.getReverseEntry(number);
  792. if (entry == null)
  793. throw new RevisionSyntaxException(MessageFormat.format(
  794. JGitText.get().reflogEntryNotFound,
  795. Integer.valueOf(number), ref.getName()));
  796. return rw.parseCommit(entry.getNewId());
  797. }
  798. @Nullable
  799. private ObjectId resolveAbbreviation(final String revstr) throws IOException,
  800. AmbiguousObjectException {
  801. AbbreviatedObjectId id = AbbreviatedObjectId.fromString(revstr);
  802. try (ObjectReader reader = newObjectReader()) {
  803. Collection<ObjectId> matches = reader.resolve(id);
  804. if (matches.size() == 0)
  805. return null;
  806. else if (matches.size() == 1)
  807. return matches.iterator().next();
  808. else
  809. throw new AmbiguousObjectException(id, matches);
  810. }
  811. }
  812. /** Increment the use counter by one, requiring a matched {@link #close()}. */
  813. public void incrementOpen() {
  814. useCnt.incrementAndGet();
  815. }
  816. /** Decrement the use count, and maybe close resources. */
  817. public void close() {
  818. if (useCnt.decrementAndGet() == 0) {
  819. if (RepositoryCache.isCached(this)) {
  820. closedAt.set(System.currentTimeMillis());
  821. } else {
  822. doClose();
  823. }
  824. }
  825. }
  826. /**
  827. * Invoked when the use count drops to zero during {@link #close()}.
  828. * <p>
  829. * The default implementation closes the object and ref databases.
  830. */
  831. protected void doClose() {
  832. getObjectDatabase().close();
  833. getRefDatabase().close();
  834. }
  835. @NonNull
  836. @SuppressWarnings("nls")
  837. public String toString() {
  838. String desc;
  839. File directory = getDirectory();
  840. if (directory != null)
  841. desc = directory.getPath();
  842. else
  843. desc = getClass().getSimpleName() + "-" //$NON-NLS-1$
  844. + System.identityHashCode(this);
  845. return "Repository[" + desc + "]"; //$NON-NLS-1$
  846. }
  847. /**
  848. * Get the name of the reference that {@code HEAD} points to.
  849. * <p>
  850. * This is essentially the same as doing:
  851. *
  852. * <pre>
  853. * return exactRef(Constants.HEAD).getTarget().getName()
  854. * </pre>
  855. *
  856. * Except when HEAD is detached, in which case this method returns the
  857. * current ObjectId in hexadecimal string format.
  858. *
  859. * @return name of current branch (for example {@code refs/heads/master}),
  860. * an ObjectId in hex format if the current branch is detached, or
  861. * {@code null} if the repository is corrupt and has no HEAD
  862. * reference.
  863. * @throws IOException
  864. */
  865. @Nullable
  866. public String getFullBranch() throws IOException {
  867. Ref head = exactRef(Constants.HEAD);
  868. if (head == null) {
  869. return null;
  870. }
  871. if (head.isSymbolic()) {
  872. return head.getTarget().getName();
  873. }
  874. ObjectId objectId = head.getObjectId();
  875. if (objectId != null) {
  876. return objectId.name();
  877. }
  878. return null;
  879. }
  880. /**
  881. * Get the short name of the current branch that {@code HEAD} points to.
  882. * <p>
  883. * This is essentially the same as {@link #getFullBranch()}, except the
  884. * leading prefix {@code refs/heads/} is removed from the reference before
  885. * it is returned to the caller.
  886. *
  887. * @return name of current branch (for example {@code master}), an ObjectId
  888. * in hex format if the current branch is detached, or {@code null}
  889. * if the repository is corrupt and has no HEAD reference.
  890. * @throws IOException
  891. */
  892. @Nullable
  893. public String getBranch() throws IOException {
  894. String name = getFullBranch();
  895. if (name != null)
  896. return shortenRefName(name);
  897. return null;
  898. }
  899. /**
  900. * Objects known to exist but not expressed by {@link #getAllRefs()}.
  901. * <p>
  902. * When a repository borrows objects from another repository, it can
  903. * advertise that it safely has that other repository's references, without
  904. * exposing any other details about the other repository. This may help
  905. * a client trying to push changes avoid pushing more than it needs to.
  906. *
  907. * @return unmodifiable collection of other known objects.
  908. */
  909. @NonNull
  910. public Set<ObjectId> getAdditionalHaves() {
  911. return Collections.emptySet();
  912. }
  913. /**
  914. * Get a ref by name.
  915. *
  916. * @param name
  917. * the name of the ref to lookup. May be a short-hand form, e.g.
  918. * "master" which is is automatically expanded to
  919. * "refs/heads/master" if "refs/heads/master" already exists.
  920. * @return the Ref with the given name, or {@code null} if it does not exist
  921. * @throws IOException
  922. * @deprecated Use {@link #exactRef(String)} or {@link #findRef(String)}
  923. * instead.
  924. */
  925. @Deprecated
  926. @Nullable
  927. public Ref getRef(final String name) throws IOException {
  928. return findRef(name);
  929. }
  930. /**
  931. * Get a ref by name.
  932. *
  933. * @param name
  934. * the name of the ref to lookup. Must not be a short-hand
  935. * form; e.g., "master" is not automatically expanded to
  936. * "refs/heads/master".
  937. * @return the Ref with the given name, or {@code null} if it does not exist
  938. * @throws IOException
  939. * @since 4.2
  940. */
  941. @Nullable
  942. public Ref exactRef(String name) throws IOException {
  943. return getRefDatabase().exactRef(name);
  944. }
  945. /**
  946. * Search for a ref by (possibly abbreviated) name.
  947. *
  948. * @param name
  949. * the name of the ref to lookup. May be a short-hand form, e.g.
  950. * "master" which is is automatically expanded to
  951. * "refs/heads/master" if "refs/heads/master" already exists.
  952. * @return the Ref with the given name, or {@code null} if it does not exist
  953. * @throws IOException
  954. * @since 4.2
  955. */
  956. @Nullable
  957. public Ref findRef(String name) throws IOException {
  958. return getRefDatabase().getRef(name);
  959. }
  960. /**
  961. * @return mutable map of all known refs (heads, tags, remotes).
  962. */
  963. @NonNull
  964. public Map<String, Ref> getAllRefs() {
  965. try {
  966. return getRefDatabase().getRefs(RefDatabase.ALL);
  967. } catch (IOException e) {
  968. return new HashMap<String, Ref>();
  969. }
  970. }
  971. /**
  972. * @return mutable map of all tags; key is short tag name ("v1.0") and value
  973. * of the entry contains the ref with the full tag name
  974. * ("refs/tags/v1.0").
  975. */
  976. @NonNull
  977. public Map<String, Ref> getTags() {
  978. try {
  979. return getRefDatabase().getRefs(Constants.R_TAGS);
  980. } catch (IOException e) {
  981. return new HashMap<String, Ref>();
  982. }
  983. }
  984. /**
  985. * Peel a possibly unpeeled reference to an annotated tag.
  986. * <p>
  987. * If the ref cannot be peeled (as it does not refer to an annotated tag)
  988. * the peeled id stays null, but {@link Ref#isPeeled()} will be true.
  989. *
  990. * @param ref
  991. * The ref to peel
  992. * @return <code>ref</code> if <code>ref.isPeeled()</code> is true; else a
  993. * new Ref object representing the same data as Ref, but isPeeled()
  994. * will be true and getPeeledObjectId will contain the peeled object
  995. * (or null).
  996. */
  997. @NonNull
  998. public Ref peel(final Ref ref) {
  999. try {
  1000. return getRefDatabase().peel(ref);
  1001. } catch (IOException e) {
  1002. // Historical accident; if the reference cannot be peeled due
  1003. // to some sort of repository access problem we claim that the
  1004. // same as if the reference was not an annotated tag.
  1005. return ref;
  1006. }
  1007. }
  1008. /**
  1009. * @return a map with all objects referenced by a peeled ref.
  1010. */
  1011. @NonNull
  1012. public Map<AnyObjectId, Set<Ref>> getAllRefsByPeeledObjectId() {
  1013. Map<String, Ref> allRefs = getAllRefs();
  1014. Map<AnyObjectId, Set<Ref>> ret = new HashMap<AnyObjectId, Set<Ref>>(allRefs.size());
  1015. for (Ref ref : allRefs.values()) {
  1016. ref = peel(ref);
  1017. AnyObjectId target = ref.getPeeledObjectId();
  1018. if (target == null)
  1019. target = ref.getObjectId();
  1020. // We assume most Sets here are singletons
  1021. Set<Ref> oset = ret.put(target, Collections.singleton(ref));
  1022. if (oset != null) {
  1023. // that was not the case (rare)
  1024. if (oset.size() == 1) {
  1025. // Was a read-only singleton, we must copy to a new Set
  1026. oset = new HashSet<Ref>(oset);
  1027. }
  1028. ret.put(target, oset);
  1029. oset.add(ref);
  1030. }
  1031. }
  1032. return ret;
  1033. }
  1034. /**
  1035. * @return the index file location or {@code null} if repository isn't
  1036. * local.
  1037. * @throws NoWorkTreeException
  1038. * if this is bare, which implies it has no working directory.
  1039. * See {@link #isBare()}.
  1040. */
  1041. @NonNull
  1042. public File getIndexFile() throws NoWorkTreeException {
  1043. if (isBare())
  1044. throw new NoWorkTreeException();
  1045. return indexFile;
  1046. }
  1047. /**
  1048. * Create a new in-core index representation and read an index from disk.
  1049. * <p>
  1050. * The new index will be read before it is returned to the caller. Read
  1051. * failures are reported as exceptions and therefore prevent the method from
  1052. * returning a partially populated index.
  1053. *
  1054. * @return a cache representing the contents of the specified index file (if
  1055. * it exists) or an empty cache if the file does not exist.
  1056. * @throws NoWorkTreeException
  1057. * if this is bare, which implies it has no working directory.
  1058. * See {@link #isBare()}.
  1059. * @throws IOException
  1060. * the index file is present but could not be read.
  1061. * @throws CorruptObjectException
  1062. * the index file is using a format or extension that this
  1063. * library does not support.
  1064. */
  1065. @NonNull
  1066. public DirCache readDirCache() throws NoWorkTreeException,
  1067. CorruptObjectException, IOException {
  1068. return DirCache.read(this);
  1069. }
  1070. /**
  1071. * Create a new in-core index representation, lock it, and read from disk.
  1072. * <p>
  1073. * The new index will be locked and then read before it is returned to the
  1074. * caller. Read failures are reported as exceptions and therefore prevent
  1075. * the method from returning a partially populated index.
  1076. *
  1077. * @return a cache representing the contents of the specified index file (if
  1078. * it exists) or an empty cache if the file does not exist.
  1079. * @throws NoWorkTreeException
  1080. * if this is bare, which implies it has no working directory.
  1081. * See {@link #isBare()}.
  1082. * @throws IOException
  1083. * the index file is present but could not be read, or the lock
  1084. * could not be obtained.
  1085. * @throws CorruptObjectException
  1086. * the index file is using a format or extension that this
  1087. * library does not support.
  1088. */
  1089. @NonNull
  1090. public DirCache lockDirCache() throws NoWorkTreeException,
  1091. CorruptObjectException, IOException {
  1092. // we want DirCache to inform us so that we can inform registered
  1093. // listeners about index changes
  1094. IndexChangedListener l = new IndexChangedListener() {
  1095. public void onIndexChanged(IndexChangedEvent event) {
  1096. notifyIndexChanged();
  1097. }
  1098. };
  1099. return DirCache.lock(this, l);
  1100. }
  1101. static byte[] gitInternalSlash(byte[] bytes) {
  1102. if (File.separatorChar == '/')
  1103. return bytes;
  1104. for (int i=0; i<bytes.length; ++i)
  1105. if (bytes[i] == File.separatorChar)
  1106. bytes[i] = '/';
  1107. return bytes;
  1108. }
  1109. /**
  1110. * @return an important state
  1111. */
  1112. @NonNull
  1113. public RepositoryState getRepositoryState() {
  1114. if (isBare() || getDirectory() == null)
  1115. return RepositoryState.BARE;
  1116. // Pre Git-1.6 logic
  1117. if (new File(getWorkTree(), ".dotest").exists()) //$NON-NLS-1$
  1118. return RepositoryState.REBASING;
  1119. if (new File(getDirectory(), ".dotest-merge").exists()) //$NON-NLS-1$
  1120. return RepositoryState.REBASING_INTERACTIVE;
  1121. // From 1.6 onwards
  1122. if (new File(getDirectory(),"rebase-apply/rebasing").exists()) //$NON-NLS-1$
  1123. return RepositoryState.REBASING_REBASING;
  1124. if (new File(getDirectory(),"rebase-apply/applying").exists()) //$NON-NLS-1$
  1125. return RepositoryState.APPLY;
  1126. if (new File(getDirectory(),"rebase-apply").exists()) //$NON-NLS-1$
  1127. return RepositoryState.REBASING;
  1128. if (new File(getDirectory(),"rebase-merge/interactive").exists()) //$NON-NLS-1$
  1129. return RepositoryState.REBASING_INTERACTIVE;
  1130. if (new File(getDirectory(),"rebase-merge").exists()) //$NON-NLS-1$
  1131. return RepositoryState.REBASING_MERGE;
  1132. // Both versions
  1133. if (new File(getDirectory(), Constants.MERGE_HEAD).exists()) {
  1134. // we are merging - now check whether we have unmerged paths
  1135. try {
  1136. if (!readDirCache().hasUnmergedPaths()) {
  1137. // no unmerged paths -> return the MERGING_RESOLVED state
  1138. return RepositoryState.MERGING_RESOLVED;
  1139. }
  1140. } catch (IOException e) {
  1141. // Can't decide whether unmerged paths exists. Return
  1142. // MERGING state to be on the safe side (in state MERGING
  1143. // you are not allow to do anything)
  1144. }
  1145. return RepositoryState.MERGING;
  1146. }
  1147. if (new File(getDirectory(), "BISECT_LOG").exists()) //$NON-NLS-1$
  1148. return RepositoryState.BISECTING;
  1149. if (new File(getDirectory(), Constants.CHERRY_PICK_HEAD).exists()) {
  1150. try {
  1151. if (!readDirCache().hasUnmergedPaths()) {
  1152. // no unmerged paths
  1153. return RepositoryState.CHERRY_PICKING_RESOLVED;
  1154. }
  1155. } catch (IOException e) {
  1156. // fall through to CHERRY_PICKING
  1157. }
  1158. return RepositoryState.CHERRY_PICKING;
  1159. }
  1160. if (new File(getDirectory(), Constants.REVERT_HEAD).exists()) {
  1161. try {
  1162. if (!readDirCache().hasUnmergedPaths()) {
  1163. // no unmerged paths
  1164. return RepositoryState.REVERTING_RESOLVED;
  1165. }
  1166. } catch (IOException e) {
  1167. // fall through to REVERTING
  1168. }
  1169. return RepositoryState.REVERTING;
  1170. }
  1171. return RepositoryState.SAFE;
  1172. }
  1173. /**
  1174. * Check validity of a ref name. It must not contain character that has
  1175. * a special meaning in a Git object reference expression. Some other
  1176. * dangerous characters are also excluded.
  1177. *
  1178. * For portability reasons '\' is excluded
  1179. *
  1180. * @param refName
  1181. *
  1182. * @return true if refName is a valid ref name
  1183. */
  1184. public static boolean isValidRefName(final String refName) {
  1185. final int len = refName.length();
  1186. if (len == 0)
  1187. return false;
  1188. if (refName.endsWith(".lock")) //$NON-NLS-1$
  1189. return false;
  1190. // Refs may be stored as loose files so invalid paths
  1191. // on the local system must also be invalid refs.
  1192. try {
  1193. SystemReader.getInstance().checkPath(refName);
  1194. } catch (CorruptObjectException e) {
  1195. return false;
  1196. }
  1197. int components = 1;
  1198. char p = '\0';
  1199. for (int i = 0; i < len; i++) {
  1200. final char c = refName.charAt(i);
  1201. if (c <= ' ')
  1202. return false;
  1203. switch (c) {
  1204. case '.':
  1205. switch (p) {
  1206. case '\0': case '/': case '.':
  1207. return false;
  1208. }
  1209. if (i == len -1)
  1210. return false;
  1211. break;
  1212. case '/':
  1213. if (i == 0 || i == len - 1)
  1214. return false;
  1215. if (p == '/')
  1216. return false;
  1217. components++;
  1218. break;
  1219. case '{':
  1220. if (p == '@')
  1221. return false;
  1222. break;
  1223. case '~': case '^': case ':':
  1224. case '?': case '[': case '*':
  1225. case '\\':
  1226. case '\u007F':
  1227. return false;
  1228. }
  1229. p = c;
  1230. }
  1231. return components > 1;
  1232. }
  1233. /**
  1234. * Strip work dir and return normalized repository path.
  1235. *
  1236. * @param workDir Work dir
  1237. * @param file File whose path shall be stripped of its workdir
  1238. * @return normalized repository relative path or the empty
  1239. * string if the file is not relative to the work directory.
  1240. */
  1241. @NonNull
  1242. public static String stripWorkDir(File workDir, File file) {
  1243. final String filePath = file.getPath();
  1244. final String workDirPath = workDir.getPath();
  1245. if (filePath.length() <= workDirPath.length() ||
  1246. filePath.charAt(workDirPath.length()) != File.separatorChar ||
  1247. !filePath.startsWith(workDirPath)) {
  1248. File absWd = workDir.isAbsolute() ? workDir : workDir.getAbsoluteFile();
  1249. File absFile = file.isAbsolute() ? file : file.getAbsoluteFile();
  1250. if (absWd == workDir && absFile == file)
  1251. return ""; //$NON-NLS-1$
  1252. return stripWorkDir(absWd, absFile);
  1253. }
  1254. String relName = filePath.substring(workDirPath.length() + 1);
  1255. if (File.separatorChar != '/')
  1256. relName = relName.replace(File.separatorChar, '/');
  1257. return relName;
  1258. }
  1259. /**
  1260. * @return true if this is bare, which implies it has no working directory.
  1261. */
  1262. public boolean isBare() {
  1263. return workTree == null;
  1264. }
  1265. /**
  1266. * @return the root directory of the working tree, where files are checked
  1267. * out for viewing and editing.
  1268. * @throws NoWorkTreeException
  1269. * if this is bare, which implies it has no working directory.
  1270. * See {@link #isBare()}.
  1271. */
  1272. @NonNull
  1273. public File getWorkTree() throws NoWorkTreeException {
  1274. if (isBare())
  1275. throw new NoWorkTreeException();
  1276. return workTree;
  1277. }
  1278. /**
  1279. * Force a scan for changed refs.
  1280. *
  1281. * @throws IOException
  1282. */
  1283. public abstract void scanForRepoChanges() throws IOException;
  1284. /**
  1285. * Notify that the index changed
  1286. */
  1287. public abstract void notifyIndexChanged();
  1288. /**
  1289. * @param refName
  1290. *
  1291. * @return a more user friendly ref name
  1292. */
  1293. @NonNull
  1294. public static String shortenRefName(String refName) {
  1295. if (refName.startsWith(Constants.R_HEADS))
  1296. return refName.substring(Constants.R_HEADS.length());
  1297. if (refName.startsWith(Constants.R_TAGS))
  1298. return refName.substring(Constants.R_TAGS.length());
  1299. if (refName.startsWith(Constants.R_REMOTES))
  1300. return refName.substring(Constants.R_REMOTES.length());
  1301. return refName;
  1302. }
  1303. /**
  1304. * @param refName
  1305. * @return the remote branch name part of <code>refName</code>, i.e. without
  1306. * the <code>refs/remotes/&lt;remote&gt;</code> prefix, if
  1307. * <code>refName</code> represents a remote tracking branch;
  1308. * otherwise {@code null}.
  1309. * @since 3.4
  1310. */
  1311. @Nullable
  1312. public String shortenRemoteBranchName(String refName) {
  1313. for (String remote : getRemoteNames()) {
  1314. String remotePrefix = Constants.R_REMOTES + remote + "/"; //$NON-NLS-1$
  1315. if (refName.startsWith(remotePrefix))
  1316. return refName.substring(remotePrefix.length());
  1317. }
  1318. return null;
  1319. }
  1320. /**
  1321. * @param refName
  1322. * @return the remote name part of <code>refName</code>, i.e. without the
  1323. * <code>refs/remotes/&lt;remote&gt;</code> prefix, if
  1324. * <code>refName</code> represents a remote tracking branch;
  1325. * otherwise {@code null}.
  1326. * @since 3.4
  1327. */
  1328. @Nullable
  1329. public String getRemoteName(String refName) {
  1330. for (String remote : getRemoteNames()) {
  1331. String remotePrefix = Constants.R_REMOTES + remote + "/"; //$NON-NLS-1$
  1332. if (refName.startsWith(remotePrefix))
  1333. return remote;
  1334. }
  1335. return null;
  1336. }
  1337. /**
  1338. * @param refName
  1339. * @return a {@link ReflogReader} for the supplied refname, or {@code null}
  1340. * if the named ref does not exist.
  1341. * @throws IOException
  1342. * the ref could not be accessed.
  1343. * @since 3.0
  1344. */
  1345. @Nullable
  1346. public abstract ReflogReader getReflogReader(String refName)
  1347. throws IOException;
  1348. /**
  1349. * Return the information stored in the file $GIT_DIR/MERGE_MSG. In this
  1350. * file operations triggering a merge will store a template for the commit
  1351. * message of the merge commit.
  1352. *
  1353. * @return a String containing the content of the MERGE_MSG file or
  1354. * {@code null} if this file doesn't exist
  1355. * @throws IOException
  1356. * @throws NoWorkTreeException
  1357. * if this is bare, which implies it has no working directory.
  1358. * See {@link #isBare()}.
  1359. */
  1360. @Nullable
  1361. public String readMergeCommitMsg() throws IOException, NoWorkTreeException {
  1362. return readCommitMsgFile(Constants.MERGE_MSG);
  1363. }
  1364. /**
  1365. * Write new content to the file $GIT_DIR/MERGE_MSG. In this file operations
  1366. * triggering a merge will store a template for the commit message of the
  1367. * merge commit. If <code>null</code> is specified as message the file will
  1368. * be deleted.
  1369. *
  1370. * @param msg
  1371. * the message which should be written or <code>null</code> to
  1372. * delete the file
  1373. *
  1374. * @throws IOException
  1375. */
  1376. public void writeMergeCommitMsg(String msg) throws IOException {
  1377. File mergeMsgFile = new File(gitDir, Constants.MERGE_MSG);
  1378. writeCommitMsg(mergeMsgFile, msg);
  1379. }
  1380. /**
  1381. * Return the information stored in the file $GIT_DIR/COMMIT_EDITMSG. In
  1382. * this file hooks triggered by an operation may read or modify the current
  1383. * commit message.
  1384. *
  1385. * @return a String containing the content of the COMMIT_EDITMSG file or
  1386. * {@code null} if this file doesn't exist
  1387. * @throws IOException
  1388. * @throws NoWorkTreeException
  1389. * if this is bare, which implies it has no working directory.
  1390. * See {@link #isBare()}.
  1391. * @since 4.0
  1392. */
  1393. @Nullable
  1394. public String readCommitEditMsg() throws IOException, NoWorkTreeException {
  1395. return readCommitMsgFile(Constants.COMMIT_EDITMSG);
  1396. }
  1397. /**
  1398. * Write new content to the file $GIT_DIR/COMMIT_EDITMSG. In this file hooks
  1399. * triggered by an operation may read or modify the current commit message.
  1400. * If {@code null} is specified as message the file will be deleted.
  1401. *
  1402. * @param msg
  1403. * the message which should be written or {@code null} to delete
  1404. * the file
  1405. *
  1406. * @throws IOException
  1407. * @since 4.0
  1408. */
  1409. public void writeCommitEditMsg(String msg) throws IOException {
  1410. File commiEditMsgFile = new File(gitDir, Constants.COMMIT_EDITMSG);
  1411. writeCommitMsg(commiEditMsgFile, msg);
  1412. }
  1413. /**
  1414. * Return the information stored in the file $GIT_DIR/MERGE_HEAD. In this
  1415. * file operations triggering a merge will store the IDs of all heads which
  1416. * should be merged together with HEAD.
  1417. *
  1418. * @return a list of commits which IDs are listed in the MERGE_HEAD file or
  1419. * {@code null} if this file doesn't exist. Also if the file exists
  1420. * but is empty {@code null} will be returned
  1421. * @throws IOException
  1422. * @throws NoWorkTreeException
  1423. * if this is bare, which implies it has no working directory.
  1424. * See {@link #isBare()}.
  1425. */
  1426. @Nullable
  1427. public List<ObjectId> readMergeHeads() throws IOException, NoWorkTreeException {
  1428. if (isBare() || getDirectory() == null)
  1429. throw new NoWorkTreeException();
  1430. byte[] raw = readGitDirectoryFile(Constants.MERGE_HEAD);
  1431. if (raw == null)
  1432. return null;
  1433. LinkedList<ObjectId> heads = new LinkedList<ObjectId>();
  1434. for (int p = 0; p < raw.length;) {
  1435. heads.add(ObjectId.fromString(raw, p));
  1436. p = RawParseUtils
  1437. .nextLF(raw, p + Constants.OBJECT_ID_STRING_LENGTH);
  1438. }
  1439. return heads;
  1440. }
  1441. /**
  1442. * Write new merge-heads into $GIT_DIR/MERGE_HEAD. In this file operations
  1443. * triggering a merge will store the IDs of all heads which should be merged
  1444. * together with HEAD. If <code>null</code> is specified as list of commits
  1445. * the file will be deleted
  1446. *
  1447. * @param heads
  1448. * a list of commits which IDs should be written to
  1449. * $GIT_DIR/MERGE_HEAD or <code>null</code> to delete the file
  1450. * @throws IOException
  1451. */
  1452. public void writeMergeHeads(List<? extends ObjectId> heads) throws IOException {
  1453. writeHeadsFile(heads, Constants.MERGE_HEAD);
  1454. }
  1455. /**
  1456. * Return the information stored in the file $GIT_DIR/CHERRY_PICK_HEAD.
  1457. *
  1458. * @return object id from CHERRY_PICK_HEAD file or {@code null} if this file
  1459. * doesn't exist. Also if the file exists but is empty {@code null}
  1460. * will be returned
  1461. * @throws IOException
  1462. * @throws NoWorkTreeException
  1463. * if this is bare, which implies it has no working directory.
  1464. * See {@link #isBare()}.
  1465. */
  1466. @Nullable
  1467. public ObjectId readCherryPickHead() throws IOException,
  1468. NoWorkTreeException {
  1469. if (isBare() || getDirectory() == null)
  1470. throw new NoWorkTreeException();
  1471. byte[] raw = readGitDirectoryFile(Constants.CHERRY_PICK_HEAD);
  1472. if (raw == null)
  1473. return null;
  1474. return ObjectId.fromString(raw, 0);
  1475. }
  1476. /**
  1477. * Return the information stored in the file $GIT_DIR/REVERT_HEAD.
  1478. *
  1479. * @return object id from REVERT_HEAD file or {@code null} if this file
  1480. * doesn't exist. Also if the file exists but is empty {@code null}
  1481. * will be returned
  1482. * @throws IOException
  1483. * @throws NoWorkTreeException
  1484. * if this is bare, which implies it has no working directory.
  1485. * See {@link #isBare()}.
  1486. */
  1487. @Nullable
  1488. public ObjectId readRevertHead() throws IOException, NoWorkTreeException {
  1489. if (isBare() || getDirectory() == null)
  1490. throw new NoWorkTreeException();
  1491. byte[] raw = readGitDirectoryFile(Constants.REVERT_HEAD);
  1492. if (raw == null)
  1493. return null;
  1494. return ObjectId.fromString(raw, 0);
  1495. }
  1496. /**
  1497. * Write cherry pick commit into $GIT_DIR/CHERRY_PICK_HEAD. This is used in
  1498. * case of conflicts to store the cherry which was tried to be picked.
  1499. *
  1500. * @param head
  1501. * an object id of the cherry commit or <code>null</code> to
  1502. * delete the file
  1503. * @throws IOException
  1504. */
  1505. public void writeCherryPickHead(ObjectId head) throws IOException {
  1506. List<ObjectId> heads = (head != null) ? Collections.singletonList(head)
  1507. : null;
  1508. writeHeadsFile(heads, Constants.CHERRY_PICK_HEAD);
  1509. }
  1510. /**
  1511. * Write revert commit into $GIT_DIR/REVERT_HEAD. This is used in case of
  1512. * conflicts to store the revert which was tried to be picked.
  1513. *
  1514. * @param head
  1515. * an object id of the revert commit or <code>null</code> to
  1516. * delete the file
  1517. * @throws IOException
  1518. */
  1519. public void writeRevertHead(ObjectId head) throws IOException {
  1520. List<ObjectId> heads = (head != null) ? Collections.singletonList(head)
  1521. : null;
  1522. writeHeadsFile(heads, Constants.REVERT_HEAD);
  1523. }
  1524. /**
  1525. * Write original HEAD commit into $GIT_DIR/ORIG_HEAD.
  1526. *
  1527. * @param head
  1528. * an object id of the original HEAD commit or <code>null</code>
  1529. * to delete the file
  1530. * @throws IOException
  1531. */
  1532. public void writeOrigHead(ObjectId head) throws IOException {
  1533. List<ObjectId> heads = head != null ? Collections.singletonList(head)
  1534. : null;
  1535. writeHeadsFile(heads, Constants.ORIG_HEAD);
  1536. }
  1537. /**
  1538. * Return the information stored in the file $GIT_DIR/ORIG_HEAD.
  1539. *
  1540. * @return object id from ORIG_HEAD file or {@code null} if this file
  1541. * doesn't exist. Also if the file exists but is empty {@code null}
  1542. * will be returned
  1543. * @throws IOException
  1544. * @throws NoWorkTreeException
  1545. * if this is bare, which implies it has no working directory.
  1546. * See {@link #isBare()}.
  1547. */
  1548. @Nullable
  1549. public ObjectId readOrigHead() throws IOException, NoWorkTreeException {
  1550. if (isBare() || getDirectory() == null)
  1551. throw new NoWorkTreeException();
  1552. byte[] raw = readGitDirectoryFile(Constants.ORIG_HEAD);
  1553. return raw != null ? ObjectId.fromString(raw, 0) : null;
  1554. }
  1555. /**
  1556. * Return the information stored in the file $GIT_DIR/SQUASH_MSG. In this
  1557. * file operations triggering a squashed merge will store a template for the
  1558. * commit message of the squash commit.
  1559. *
  1560. * @return a String containing the content of the SQUASH_MSG file or
  1561. * {@code null} if this file doesn't exist
  1562. * @throws IOException
  1563. * @throws NoWorkTreeException
  1564. * if this is bare, which implies it has no working directory.
  1565. * See {@link #isBare()}.
  1566. */
  1567. @Nullable
  1568. public String readSquashCommitMsg() throws IOException {
  1569. return readCommitMsgFile(Constants.SQUASH_MSG);
  1570. }
  1571. /**
  1572. * Write new content to the file $GIT_DIR/SQUASH_MSG. In this file
  1573. * operations triggering a squashed merge will store a template for the
  1574. * commit message of the squash commit. If <code>null</code> is specified as
  1575. * message the file will be deleted.
  1576. *
  1577. * @param msg
  1578. * the message which should be written or <code>null</code> to
  1579. * delete the file
  1580. *
  1581. * @throws IOException
  1582. */
  1583. public void writeSquashCommitMsg(String msg) throws IOException {
  1584. File squashMsgFile = new File(gitDir, Constants.SQUASH_MSG);
  1585. writeCommitMsg(squashMsgFile, msg);
  1586. }
  1587. @Nullable
  1588. private String readCommitMsgFile(String msgFilename) throws IOException {
  1589. if (isBare() || getDirectory() == null)
  1590. throw new NoWorkTreeException();
  1591. File mergeMsgFile = new File(getDirectory(), msgFilename);
  1592. try {
  1593. return RawParseUtils.decode(IO.readFully(mergeMsgFile));
  1594. } catch (FileNotFoundException e) {
  1595. if (mergeMsgFile.exists()) {
  1596. throw e;
  1597. }
  1598. // the file has disappeared in the meantime ignore it
  1599. return null;
  1600. }
  1601. }
  1602. private void writeCommitMsg(File msgFile, String msg) throws IOException {
  1603. if (msg != null) {
  1604. FileOutputStream fos = new FileOutputStream(msgFile);
  1605. try {
  1606. fos.write(msg.getBytes(Constants.CHARACTER_ENCODING));
  1607. } finally {
  1608. fos.close();
  1609. }
  1610. } else {
  1611. FileUtils.delete(msgFile, FileUtils.SKIP_MISSING);
  1612. }
  1613. }
  1614. /**
  1615. * Read a file from the git directory.
  1616. *
  1617. * @param filename
  1618. * @return the raw contents or {@code null} if the file doesn't exist or is
  1619. * empty
  1620. * @throws IOException
  1621. */
  1622. @Nullable
  1623. private byte[] readGitDirectoryFile(String filename) throws IOException {
  1624. File file = new File(getDirectory(), filename);
  1625. try {
  1626. byte[] raw = IO.readFully(file);
  1627. return raw.length > 0 ? raw : null;
  1628. } catch (FileNotFoundException notFound) {
  1629. if (file.exists()) {
  1630. throw notFound;
  1631. }
  1632. return null;
  1633. }
  1634. }
  1635. /**
  1636. * Write the given heads to a file in the git directory.
  1637. *
  1638. * @param heads
  1639. * a list of object ids to write or null if the file should be
  1640. * deleted.
  1641. * @param filename
  1642. * @throws FileNotFoundException
  1643. * @throws IOException
  1644. */
  1645. private void writeHeadsFile(List<? extends ObjectId> heads, String filename)
  1646. throws FileNotFoundException, IOException {
  1647. File headsFile = new File(getDirectory(), filename);
  1648. if (heads != null) {
  1649. BufferedOutputStream bos = new SafeBufferedOutputStream(
  1650. new FileOutputStream(headsFile));
  1651. try {
  1652. for (ObjectId id : heads) {
  1653. id.copyTo(bos);
  1654. bos.write('\n');
  1655. }
  1656. } finally {
  1657. bos.close();
  1658. }
  1659. } else {
  1660. FileUtils.delete(headsFile, FileUtils.SKIP_MISSING);
  1661. }
  1662. }
  1663. /**
  1664. * Read a file formatted like the git-rebase-todo file. The "done" file is
  1665. * also formatted like the git-rebase-todo file. These files can be found in
  1666. * .git/rebase-merge/ or .git/rebase-append/ folders.
  1667. *
  1668. * @param path
  1669. * path to the file relative to the repository's git-dir. E.g.
  1670. * "rebase-merge/git-rebase-todo" or "rebase-append/done"
  1671. * @param includeComments
  1672. * <code>true</code> if also comments should be reported
  1673. * @return the list of steps
  1674. * @throws IOException
  1675. * @since 3.2
  1676. */
  1677. @NonNull
  1678. public List<RebaseTodoLine> readRebaseTodo(String path,
  1679. boolean includeComments)
  1680. throws IOException {
  1681. return new RebaseTodoFile(this).readRebaseTodo(path, includeComments);
  1682. }
  1683. /**
  1684. * Write a file formatted like a git-rebase-todo file.
  1685. *
  1686. * @param path
  1687. * path to the file relative to the repository's git-dir. E.g.
  1688. * "rebase-merge/git-rebase-todo" or "rebase-append/done"
  1689. * @param steps
  1690. * the steps to be written
  1691. * @param append
  1692. * whether to append to an existing file or to write a new file
  1693. * @throws IOException
  1694. * @since 3.2
  1695. */
  1696. public void writeRebaseTodoFile(String path, List<RebaseTodoLine> steps,
  1697. boolean append)
  1698. throws IOException {
  1699. new RebaseTodoFile(this).writeRebaseTodoFile(path, steps, append);
  1700. }
  1701. /**
  1702. * @return the names of all known remotes
  1703. * @since 3.4
  1704. */
  1705. @NonNull
  1706. public Set<String> getRemoteNames() {
  1707. return getConfig()
  1708. .getSubsections(ConfigConstants.CONFIG_REMOTE_SECTION);
  1709. }
  1710. }