Skip to content
This repository was archived by the owner on Oct 17, 2021. It is now read-only.

Commit 3aef39d

Browse files
committed
Initial commit
0 parents  commit 3aef39d

21 files changed

+1306
-0
lines changed

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
.DS_Store
2+
/.build
3+
/Packages
4+
/*.xcodeproj
5+
xcuserdata/
6+
.swiftpm

LICENSE.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright 2020 Read Evaluate Press, LLC
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a
4+
copy of this software and associated documentation files (the "Software"),
5+
to deal in the Software without restriction, including without limitation
6+
the rights to use, copy, modify, merge, publish, distribute, sublicense,
7+
and/or sell copies of the Software, and to permit persons to whom the
8+
Software is furnished to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in
11+
all copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
14+
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19+
DEALINGS IN THE SOFTWARE.

Package.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// swift-tools-version:5.2
2+
// The swift-tools-version declares the minimum version of Swift required to build this package.
3+
4+
import PackageDescription
5+
6+
let package = Package(
7+
name: "Git",
8+
products: [
9+
// Products define the executables and libraries produced by a package, and make them visible to other packages.
10+
.library(
11+
name: "Git",
12+
targets: ["Git"]),
13+
],
14+
dependencies: [
15+
// Dependencies declare other packages that this package depends on.
16+
// .package(url: /* package url */, from: "1.0.0"),
17+
],
18+
targets: [
19+
.systemLibrary(name: "Clibgit2", pkgConfig: "libgit2", providers: [
20+
.brew(["libgit2"]),
21+
.apt(["libgit2"])
22+
]),
23+
.target(
24+
name: "Git",
25+
dependencies: ["Clibgit2"]),
26+
.testTarget(
27+
name: "GitTests",
28+
dependencies: ["Git"]),
29+
]
30+
)

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Git
2+
3+
A package for working with Git repositories in Swift,
4+
built on top of [libgit2](https://libgit2.org).
5+
6+
> **Warning**:
7+
> This is currently a work-in-progress and shouldn't be used in production.
8+
9+
## Requirements
10+
11+
- Swift 5.2
12+
13+
## License
14+
15+
MIT
16+
17+
## Contact
18+
19+
Mattt ([@mattt](https://twitter.com/mattt))

Sources/Clibgit2/module.modulemap

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module Clibgit2 [system] {
2+
umbrella header "/usr/local/include/git2.h"
3+
link "git2"
4+
export *
5+
}

Sources/Git/Error.swift

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import Clibgit2
2+
3+
/// A libgit error.
4+
public struct Error: Swift.Error {
5+
6+
/**
7+
The error code.
8+
9+
Error codes correspond to `git_error_code` constants, like `GIT_ENOTFOUND`.
10+
*/
11+
public let code: Int // TODO: import raw values declared by libgit2
12+
13+
14+
/// The error message, if any.
15+
public let message: String?
16+
17+
private static var lastErrorMessage: String? {
18+
guard let error = giterr_last() else { return nil }
19+
return String(cString: error.pointee.message)
20+
}
21+
22+
init<Code: FixedWidthInteger>(code: Code, message: String? = Error.lastErrorMessage) {
23+
self.code = Int(code)
24+
self.message = message
25+
}
26+
}
27+
28+
// MARK: -
29+
30+
func wrap(_ fn: () -> Int32) throws {
31+
_ = initializer // FIXME
32+
let result = fn()
33+
guard result == 0 else {
34+
throw Error(code: result)
35+
}
36+
}

Sources/Git/Git.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import Clibgit2
2+
3+
let initializer: () = {
4+
git_libgit2_init()
5+
}()
6+

Sources/Git/Index.swift

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
import Clibgit2
2+
import Foundation
3+
4+
/// A repository index.
5+
public final class Index {
6+
private(set) var pointer: OpaquePointer!
7+
8+
private var managed: Bool = false
9+
10+
init(_ pointer: OpaquePointer) {
11+
self.pointer = pointer
12+
}
13+
14+
deinit {
15+
guard managed else { return }
16+
git_index_free(pointer)
17+
}
18+
19+
// MARK: -
20+
21+
/// The repository for the index.
22+
public var owner: Repository {
23+
return Repository(git_index_owner(pointer))
24+
}
25+
26+
/// The file path to the repository index file.
27+
public var path: String! {
28+
return String(validatingUTF8: git_index_path(pointer))
29+
}
30+
31+
/**
32+
Update the contents of an existing index object in memory
33+
by reading from disk.
34+
35+
- Important: If there are changes on disk,
36+
unwritten in-memory changes are discarded.
37+
38+
- Parameters:
39+
- force: If true, this performs a "hard" read
40+
that discards in-memory changes
41+
and always reloads the on-disk index data.
42+
If there is no on-disk version,
43+
the index will be cleared.
44+
If false,
45+
this does a "soft" read that reloads the index data from disk
46+
only if it has changed since the last time it was loaded.
47+
Purely in-memory index data will be untouched.
48+
*/
49+
public func reload(force: Bool) throws {
50+
try wrap { git_index_read(pointer, force ? 1 : 0)}
51+
}
52+
53+
/**
54+
The index on-disk version.
55+
56+
Valid return values are 2, 3, or 4.
57+
If 3 is returned, an index with version 2 may be written instead,
58+
if the extension data in version 3 is not necessary.
59+
*/
60+
public var version: Int {
61+
return Int(git_index_version(pointer))
62+
}
63+
}
64+
65+
// MARK: -
66+
67+
extension Index {
68+
/// An entry in the index.
69+
public final class Entry: Equatable, Comparable, Hashable {
70+
weak var index: Index?
71+
private(set) var rawValue: git_index_entry
72+
73+
init(rawValue: git_index_entry) {
74+
self.rawValue = rawValue
75+
}
76+
77+
/// The file path of the index entry.
78+
public var path: String {
79+
return String(validatingUTF8: rawValue.path)!
80+
}
81+
82+
/// The size of the index entry.
83+
public var fileSize: Int {
84+
return Int(rawValue.file_size)
85+
}
86+
87+
/// The creation time of the index entry.
88+
public var creationTime: Date {
89+
return Date(timeIntervalSince1970: TimeInterval(rawValue.ctime.seconds))
90+
}
91+
92+
/// The modification time of the index entry
93+
public var modificationTime: Date {
94+
return Date(timeIntervalSince1970: TimeInterval(rawValue.mtime.seconds))
95+
}
96+
97+
/// The blob object for the index entry, if any.
98+
public var blob: Blob? {
99+
let id = Object.ID(rawValue: rawValue.id)
100+
return try? index?.owner.lookup(id)
101+
}
102+
103+
// MARK: - Equatable
104+
105+
public static func == (lhs: Index.Entry, rhs: Index.Entry) -> Bool {
106+
var loid = lhs.rawValue.id, roid = rhs.rawValue.id
107+
return git_oid_cmp(&loid, &roid) == 0
108+
}
109+
110+
// MARK: - Comparable
111+
112+
public static func < (lhs: Index.Entry, rhs: Index.Entry) -> Bool {
113+
return lhs.path < rhs.path
114+
}
115+
116+
// MARK: - Hashable
117+
118+
public func hash(into hasher: inout Hasher) {
119+
hasher.combine(rawValue.uid)
120+
}
121+
}
122+
123+
final class Entries: Sequence, IteratorProtocol {
124+
private weak var index: Index?
125+
private(set) var pointer: OpaquePointer!
126+
127+
init(_ index: Index) throws {
128+
try wrap { git_index_iterator_new(&pointer, index.pointer) }
129+
self.index = index
130+
}
131+
132+
deinit {
133+
git_index_iterator_free(pointer)
134+
}
135+
136+
var underestimatedCount: Int {
137+
guard let index = index else { return 0 }
138+
return git_index_entrycount(index.pointer)
139+
}
140+
141+
// MARK: - Sequence
142+
143+
func next() -> Entry? {
144+
do {
145+
var pointer: UnsafePointer<git_index_entry>?
146+
try wrap { git_index_iterator_next(&pointer, self.pointer) }
147+
let entry = Entry(rawValue: pointer!.pointee)
148+
entry.index = index
149+
return entry
150+
} catch {
151+
return nil
152+
}
153+
}
154+
}
155+
156+
/// Returns a sequence of entries in the index.
157+
public var entries: AnySequence<Entry> {
158+
guard let entries = try? Entries(self) else { return AnySequence(EmptyCollection()) }
159+
return AnySequence(entries)
160+
}
161+
}

0 commit comments

Comments
 (0)