Skip to content

Commit 860b85e

Browse files
committed
Initial
0 parents  commit 860b85e

File tree

9 files changed

+515
-0
lines changed

9 files changed

+515
-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+
.swiftpm
4+
Packages
5+
*.xcodeproj
6+
Package.resolved

Dockerfile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
FROM swift
2+
WORKDIR /app
3+
COPY . ./
4+
CMD swift package clean
5+
CMD swift test --parallel

LICENSE

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
2+
Version 2, December 2004
3+
4+
Copyright (C) 2018-2019 Binary Birds
5+
6+
Authors:
7+
8+
Tibor Bodecs <[email protected]>
9+
10+
Everyone is permitted to copy and distribute verbatim or modified
11+
copies of this license document, and changing it is allowed as long
12+
as the name is changed.
13+
14+
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
15+
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
16+
17+
0. You just DO WHAT THE FUCK YOU WANT TO.

Package.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// swift-tools-version:5.2
2+
import PackageDescription
3+
4+
let package = Package(
5+
name: "git-kit",
6+
products: [
7+
.library(name: "GitKit", targets: ["GitKit"]),
8+
.library(name: "GitKitDynamic", type: .dynamic, targets: ["GitKit"])
9+
],
10+
dependencies: [
11+
.package(url: "https://github.com/binarybirds/shell-kit", from: "1.0.0"),
12+
],
13+
targets: [
14+
.target(name: "GitKit", dependencies: [
15+
.product(name: "ShellKit", package: "shell-kit"),
16+
]),
17+
.testTarget(name: "GitKitTests", dependencies: ["GitKit"]),
18+
]
19+
)

README.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# GitKit (🐱)
2+
3+
GitKit is a Swift wrapper around the git command line interface.
4+
5+
## Usage
6+
7+
Some basic examples:
8+
9+
```swift
10+
import GitKit
11+
12+
try Git().run(.cmd(.config, "--global user.name"))
13+
14+
let git = Git(path: "~/example/")
15+
16+
try git.run(.cmd(.initialize))
17+
try git.run(.cmd(.status))
18+
try git.run(.cmd(.branch, "-a"))
19+
try git.run(.cmd(.pull))
20+
21+
try git.run(.clone(url: "https://gitlab.com/binarybirds/shell-kit.git"))
22+
try git.run(.commit(message: "some nasty bug fixed"))
23+
try git.run(.log(1))
24+
try git.run(.tag("1.0.0"))
25+
try git.run(.pull(remote: "origin", branch: "master"))
26+
try git.run(.push(remote: "origin", branch: "master"))
27+
try git.run(.create(branch: "dev"))
28+
try git.run(.checkout(branch: "master"))
29+
try git.run(.merge(branch: "dev"))
30+
31+
try git.run(.raw("log -2"))
32+
try git.run(.raw("rebase -i <hash>"))
33+
34+
```
35+
36+
## Install
37+
38+
Just use the Swift Package Manager as usual:
39+
40+
```swift
41+
.package(url: "https://github.com/binarybirds/git-kit", from: "1.0.0"),
42+
```
43+
44+
Don't forget to add "GitKit" to your target as a dependency:
45+
46+
```swift
47+
.product(name: "GitKit", package: "git-kit"),
48+
```
49+
50+
That's it.
51+
52+
53+
## License
54+
55+
[WTFPL](LICENSE) - Do what the fuck you want to.

Sources/GitKit/Git.swift

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
/**
2+
Git.swift
3+
GitKit
4+
5+
Created by Tibor Bödecs on 2019.01.02.
6+
Copyright Binary Birds. All rights reserved.
7+
*/
8+
9+
import ShellKit
10+
11+
/// a Git wrapper class
12+
public final class Git: Shell {
13+
14+
/// Git aliases to make the API more convenient
15+
public enum Alias {
16+
case cmd(Command, String? = nil)
17+
case addAll
18+
case commit(message: String, Bool = false)
19+
case clone(url: String)
20+
case checkout(branch: String)
21+
case log(Int? = nil)
22+
case push(remote: String? = nil, branch: String? = nil)
23+
case pull(remote: String? = nil, branch: String? = nil)
24+
case merge(branch: String)
25+
case create(branch: String)
26+
case delete(branch: String)
27+
case tag(String)
28+
case raw(String)
29+
30+
private func commandParams() -> [String] {
31+
var params: [String] = []
32+
switch self {
33+
case .cmd(let command, let args):
34+
params = [command.rawValue]
35+
if let args = args {
36+
params.append(args)
37+
}
38+
case .addAll:
39+
params = [Command.add.rawValue, "."]
40+
case .commit(let message, let allowEmpty):
41+
params = [Command.commit.rawValue, "-m", "\"\(message)\""]
42+
if allowEmpty {
43+
params.append("--allow-empty")
44+
}
45+
case .clone(let url):
46+
params = [Command.clone.rawValue, url]
47+
case .checkout(let branch):
48+
params = [Command.checkout.rawValue, branch]
49+
case .log(let n):
50+
params = [Command.log.rawValue]
51+
if let n = n {
52+
params.append("-\(n)")
53+
}
54+
case .push(let remote, let branch):
55+
params = [Command.push.rawValue]
56+
if let remote = remote {
57+
params.append(remote)
58+
}
59+
if let branch = branch {
60+
params.append(branch)
61+
}
62+
case .pull(let remote, let branch):
63+
params = [Command.pull.rawValue]
64+
if let remote = remote {
65+
params.append(remote)
66+
}
67+
if let branch = branch {
68+
params.append(branch)
69+
}
70+
case .merge(let branch):
71+
params = [Command.merge.rawValue, branch]
72+
case .create(let branch):
73+
params = [Command.checkout.rawValue, "-b", branch]
74+
case .delete(let branch):
75+
params = [Command.branch.rawValue, "-D", branch]
76+
case .tag(let name):
77+
params = [Command.tag.rawValue, name]
78+
case .raw(let command):
79+
params.append(command)
80+
}
81+
return params
82+
}
83+
84+
public var rawValue: String {
85+
self.commandParams().joined(separator: " ")
86+
}
87+
}
88+
89+
/// basic git commands
90+
public enum Command: String {
91+
92+
// MARK: - start a working area (see also: git help tutorial)
93+
94+
case config
95+
96+
case clean
97+
/// Clone a repository into a new directory
98+
case clone
99+
/// Create an empty Git repository or reinitialize an existing one
100+
case initialize = "init"
101+
102+
// MARK: - work on the current change (see also: git help everyday)
103+
104+
/// Add file contents to the index
105+
case add
106+
/// Move or rename a file, a directory, or a symlink
107+
case mv
108+
/// Reset current HEAD to the specified state
109+
case reset
110+
/// Remove files from the working tree and from the index
111+
case rm
112+
113+
// MARK: - examine the history and state (see also: git help revisions)
114+
115+
/// Use binary search to find the commit that introduced a bug
116+
case bisect
117+
/// Print lines matching a pattern
118+
case grep
119+
/// Show commit logs
120+
case log
121+
/// Show various types of objects
122+
case show
123+
/// Show the working tree status
124+
case status
125+
126+
// MARK: - grow, mark and tweak your common history
127+
128+
/// List, create, or delete branches
129+
case branch
130+
/// Switch branches or restore working tree files
131+
case checkout
132+
/// Record changes to the repository
133+
case commit
134+
/// Show changes between commits, commit and working tree, etc
135+
case diff
136+
/// Join two or more development histories together
137+
case merge
138+
/// Reapply commits on top of another base tip
139+
case rebase
140+
/// Create, list, delete or verify a tag object signed with GPG
141+
case tag
142+
143+
// MARK: - collaborate (see also: git help workflows)
144+
145+
/// Download objects and refs from another repository
146+
case fetch
147+
/// Fetch from and integrate with another repository or a local branch
148+
case pull
149+
/// Update remote refs along with associated objects
150+
case push
151+
}
152+
153+
// MARK: - private helper methods
154+
155+
/**
156+
This method helps to assemble a Git command string from an alias
157+
158+
If there is a git repo path (working directory) presented, proper directories
159+
will be used & created recursively if a new repository is being initialized.
160+
161+
- Parameters:
162+
- alias: The git alias to be executed
163+
- args: Additional arguments for the Git alias
164+
165+
- Returns: The Git command
166+
*/
167+
private func rawCommand(_ alias: Alias) -> String {
168+
var cmd: [String] = []
169+
// if there is a path let's change directory first
170+
if let path = self.path {
171+
// try to create work dir at given path for init or clone commands
172+
if
173+
alias.rawValue.hasPrefix(Command.initialize.rawValue) ||
174+
alias.rawValue.hasPrefix(Command.clone.rawValue)
175+
{
176+
cmd += ["mkdir", "-p", path, "&&"]
177+
}
178+
cmd += ["cd", path, "&&"]
179+
}
180+
cmd += ["git", alias.rawValue]
181+
182+
let command = cmd.joined(separator: " ")
183+
184+
if self.verbose {
185+
print(command)
186+
}
187+
return command
188+
}
189+
190+
// MARK: - public api
191+
192+
/// work directory, if peresent a directory change will occur before running any Git commands
193+
///
194+
/// NOTE: if the git init command is called with a non-existing path, directories
195+
/// presented in the path string will be created recursively
196+
public var path: String?
197+
198+
// prints git commands constructed from the alias before execution
199+
public var verbose = false
200+
201+
/**
202+
Initializes a new Git object
203+
204+
- Parameters:
205+
- path: The path of the Swift package (work directory)
206+
- type: The type of the shell, default: /bin/sh
207+
- env: Additional environment variables for the shell, default: empty
208+
209+
*/
210+
public init(path: String? = nil, type: String = "/bin/sh", env: [String: String] = [:]) {
211+
self.path = path
212+
213+
super.init(type, env: env)
214+
}
215+
216+
/**
217+
Runs a specific Git alias through the current shell.
218+
219+
- Parameters:
220+
- alias: The git command alias to be executed
221+
222+
- Throws:
223+
`ShellError.outputData` if the command execution succeeded but the output is empty,
224+
otherwise `ShellError.generic(Int, String)` where the first parameter is the exit code,
225+
the second is the error message
226+
227+
- Returns: The output string of the command without trailing newlines
228+
*/
229+
@discardableResult
230+
public func run(_ alias: Alias) throws -> String {
231+
try self.run(self.rawCommand(alias))
232+
}
233+
234+
/**
235+
Async version of the run function
236+
237+
- Parameters:
238+
- alias: The git command alias to be executed
239+
- completion: The completion block with the output and error
240+
241+
The command will be executed on a concurrent dispatch queue.
242+
*/
243+
public func run(_ alias: Alias, completion: @escaping ((String?, Swift.Error?) -> Void)) {
244+
self.run(self.rawCommand(alias), completion: completion)
245+
}
246+
}

0 commit comments

Comments
 (0)