Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
c90eae4
Git Clone: Add basic functionality of cloning repository
Pythonen Mar 23, 2022
97c1fee
Git Clone: Select target folder and clone the repository
Pythonen Mar 24, 2022
713abee
Git Clone: Open cloned folder
Pythonen Mar 24, 2022
d8735bc
Git Clone: Add error handling
Pythonen Mar 24, 2022
4a201b7
Git Clone: Implementation as a module
Pythonen Mar 24, 2022
3aed56c
Git Clone: Uncomment dependency from Package.swift
Pythonen Mar 25, 2022
715d465
Merge branch 'main' into git-clone
Pythonen Mar 26, 2022
e6a1c17
Git Clone: Switch to use NSSavePanel
Pythonen Mar 26, 2022
5e0b088
Git Clone: Remove .git only if the url has it.
Pythonen Mar 27, 2022
14f4a78
Merge branch 'main' into git-clone
Pythonen Mar 28, 2022
fe462ea
Git Clone: Reposition UI
Pythonen Mar 28, 2022
e94ceec
Git Clone: Change clone view to be sheet
Pythonen Mar 28, 2022
015eafe
Merge branch 'main' into git-clone
Pythonen Mar 29, 2022
12387b5
Git Clone: Validate url and more error handling
Pythonen Mar 29, 2022
5cfdd7a
Merge branch 'main' into git-clone
Pythonen Apr 1, 2022
8fea833
Git Clone: Add automatic pasting
Pythonen Apr 1, 2022
65eef65
Merge branch 'main' into git-clone
Pythonen Apr 12, 2022
07661da
GitClone: Fix some force unwrappings and make vars private
Pythonen Apr 13, 2022
10653c1
Merge branch 'main' into git-clone
Pythonen Apr 13, 2022
7db99f1
GitClone: Make shellClient constant
Pythonen Apr 13, 2022
5774bbb
GitClone: Fix indentation and make logic private func
Pythonen Apr 13, 2022
781e45b
Merge branch 'main' into git-clone
Pythonen Apr 14, 2022
02369ca
GitClone: Add CheckoutBranchModal
Pythonen Apr 14, 2022
4af1524
GitClone: Make checkout button defaultAction
Pythonen Apr 15, 2022
e5fa03b
Merge branch 'main' into git-clone
Pythonen Apr 15, 2022
5f1479a
GitClone: Add possibility to checkout to remote branches
Pythonen Apr 15, 2022
1721415
GitClone: Make the modal wider
Pythonen Apr 15, 2022
217a2b7
GitClone: Open checkout branch modal only if there's multiple branches
Pythonen Apr 17, 2022
1b5be61
Merge branch 'main' into git-clone
Pythonen Apr 17, 2022
13ac174
Merge branch 'main' into git-clone
Pythonen Apr 18, 2022
45010c6
GitClone: Refactor extension to separate file.
Pythonen Apr 18, 2022
e1f4746
Merge branch 'main' into git-clone
Pythonen Apr 18, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CodeEdit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
5CAD1B972806B57D0059A74E /* Breadcrumbs in Frameworks */ = {isa = PBXBuildFile; productRef = 5CAD1B962806B57D0059A74E /* Breadcrumbs */; };
5CF38A5E27E48E6C0096A0F7 /* CodeFile in Frameworks */ = {isa = PBXBuildFile; productRef = 5CF38A5D27E48E6C0096A0F7 /* CodeFile */; };
5CFA753B27E896B60002F01B /* GitClient in Frameworks */ = {isa = PBXBuildFile; productRef = 5CFA753A27E896B60002F01B /* GitClient */; };
B34B213227ECDC3A006033A9 /* GitClone in Frameworks */ = {isa = PBXBuildFile; productRef = B34B213127ECDC3A006033A9 /* GitClone */; };
64B64EDE27F7B79400C400F1 /* About in Frameworks */ = {isa = PBXBuildFile; productRef = 64B64EDD27F7B79400C400F1 /* About */; };
B34B213227ECDC3A006033A9 /* GitClone in Frameworks */ = {isa = PBXBuildFile; productRef = B34B213127ECDC3A006033A9 /* GitClone */; };
B658FB3427DA9E1000EA4DBD /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B658FB3327DA9E1000EA4DBD /* Assets.xcassets */; };
Expand Down Expand Up @@ -1069,6 +1070,10 @@
isa = XCSwiftPackageProductDependency;
productName = GitClient;
};
B34B213127ECDC3A006033A9 /* GitClone */ = {
isa = XCSwiftPackageProductDependency;
productName = GitClone;
};
64B64EDD27F7B79400C400F1 /* About */ = {
isa = XCSwiftPackageProductDependency;
productName = About;
Expand Down
2 changes: 1 addition & 1 deletion CodeEdit/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,6 @@
</dict>
</array>
<key>GitHash</key>
<string>67a2618bab3900fa3dd96764fb0232183a95fb88</string>
<string>13ac174726e9ecffc846676d833be1cac8fd953c</string>
</dict>
</plist>
4 changes: 2 additions & 2 deletions CodeEditModules/Modules/GitClient/src/Interface.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import Foundation

public struct GitClient {
public var getCurrentBranchName: () throws -> String
public var getBranches: () throws -> [String]
public var getBranches: (Bool) throws -> [String]
public var checkoutBranch: (String) throws -> Void
public var pull: () throws -> Void
public var cloneRepository: (String) throws -> Void
Expand All @@ -22,7 +22,7 @@ public struct GitClient {

init(
getCurrentBranchName: @escaping () throws -> String,
getBranches: @escaping () throws -> [String],
getBranches: @escaping (Bool) throws -> [String],
checkoutBranch: @escaping (String) throws -> Void,
pull: @escaping () throws -> Void,
cloneRepository: @escaping (String) throws -> Void,
Expand Down
16 changes: 11 additions & 5 deletions CodeEditModules/Modules/GitClient/src/Live.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,15 @@ public extension GitClient {
directoryURL: URL,
shellClient: ShellClient
) -> GitClient {
func getBranches() throws -> [String] {
try shellClient.run(
func getBranches(_ allBranches: Bool = false) throws -> [String] {
if allBranches == true {
return try shellClient.run(
"cd \(directoryURL.relativePath);git branch -a --format \"%(refname:short)\""
)
.components(separatedBy: "\n")
.filter { $0 != "" }
}
return try shellClient.run(
"cd \(directoryURL.relativePath);git branch --format \"%(refname:short)\""
)
.components(separatedBy: "\n")
Expand All @@ -40,7 +47,7 @@ public extension GitClient {
)
if output.contains("fatal: not a git repository") {
throw GitClientError.notGitRepository
} else if !output.contains("Switched to branch") {
} else if !output.contains("Switched to branch") && !output.contains("Switched to a new branch") {
throw GitClientError.outputError(output)
}
}
Expand All @@ -50,7 +57,6 @@ public extension GitClient {
throw GitClientError.outputError(output)
}
}

func getCommitHistory(entries: Int?, fileLocalPath: String?) throws -> [Commit] {
var entriesString = ""
let fileLocalPath = fileLocalPath ?? ""
Expand All @@ -75,7 +81,7 @@ public extension GitClient {

return GitClient(
getCurrentBranchName: getCurrentBranchName,
getBranches: getBranches,
getBranches: getBranches(_:),
checkoutBranch: checkoutBranch(name:),
pull: {
let output = try shellClient.run(
Expand Down
50 changes: 50 additions & 0 deletions CodeEditModules/Modules/GitClone/src/CheckoutBranch.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//
// CheckoutBranch.swift
//
//
// Created by Aleksi Puttonen on 18.4.2022.
//

import Foundation
import GitClient
import SwiftUI

public extension CheckoutBranchView {
func getBranches() -> [String] {
guard let url = URL(string: repoPath) else {
return [""]
}
do {
let branches = try GitClient.default(directoryURL: url,
shellClient: shellClient).getBranches(true)
return branches
} catch {
return [""]
}
}
func checkoutBranch() {
var parsedBranch = selectedBranch
if selectedBranch.contains("origin/") || selectedBranch.contains("upstream/") {
parsedBranch = selectedBranch.components(separatedBy: "/")[1]
}
do {
if let url = URL(string: repoPath) {
try GitClient.default(directoryURL: url,
shellClient: shellClient).checkoutBranch(parsedBranch)
isPresented = false
}
} catch {
guard let error = error as? GitClient.GitClientError else { return }
let alert = NSAlert()
alert.alertStyle = .critical
alert.addButton(withTitle: "Ok")
switch error {
case .notGitRepository:
alert.messageText = "Not a git repository"
case let .outputError(message):
alert.messageText = message
}
alert.runModal()
}
}
}
76 changes: 76 additions & 0 deletions CodeEditModules/Modules/GitClone/src/CheckoutBranchView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//
// CheckoutBranchView.swift
//
//
// Created by Aleksi Puttonen on 14.4.2022.
//

import Foundation
import SwiftUI
import GitClient
import ShellClient

public struct CheckoutBranchView: View {
internal let shellClient: ShellClient
@Binding internal var isPresented: Bool
@Binding internal var repoPath: String
// TODO: This has to be derived from git
@State internal var selectedBranch = "main"
public init(isPresented: Binding<Bool>,
repoPath: Binding<String>,
shellClient: ShellClient) {
self.shellClient = shellClient
self._isPresented = isPresented
self._repoPath = repoPath
}
public var body: some View {
VStack(spacing: 8) {
HStack {
Image(nsImage: NSApp.applicationIconImage)
.resizable()
.frame(width: 64, height: 64)
.padding(.bottom, 50)
VStack(alignment: .leading) {
Text("Checkout branch")
.bold()
.padding(.bottom, 2)
Text("Select a branch to checkout")
.font(.system(size: 11))
.foregroundColor(.secondary)
.alignmentGuide(.trailing) { context in
context[.trailing]
}
Menu {
ForEach(getBranches().filter {!$0.contains("HEAD")}, id: \.self) { branch in
Button {
guard selectedBranch != branch else { return }
selectedBranch = branch
} label: {
Text(branch)
}.disabled(selectedBranch == branch)
}
} label: {
Text(selectedBranch)
}
HStack {
Button("Cancel") {
isPresented = false
}
Button("Checkout") {
checkoutBranch()
}
.keyboardShortcut(.defaultAction)
}
.alignmentGuide(.trailing) { context in
context[.trailing]
}
.offset(x: 145)
}
}
.padding(.top, 20)
.padding(.horizontal, 20)
.padding(.bottom, 16)
.frame(width: 400)
}
}
}
25 changes: 22 additions & 3 deletions CodeEditModules/Modules/GitClone/src/GitCloneView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,17 @@ import ShellClient
public struct GitCloneView: View {
private let shellClient: ShellClient
@Binding private var isPresented: Bool
@Binding private var showCheckout: Bool
@Binding private var repoPath: String
@State private var repoUrlStr = ""
@State private var repoPath = "~/"
public init(shellClient: ShellClient, isPresented: Binding<Bool>) {
public init(shellClient: ShellClient,
isPresented: Binding<Bool>,
showCheckout: Binding<Bool>,
repoPath: Binding<String>) {
self.shellClient = shellClient
self._isPresented = isPresented
self._showCheckout = showCheckout
self._repoPath = repoPath
}
public var body: some View {
VStack(spacing: 8) {
Expand Down Expand Up @@ -154,8 +160,8 @@ extension GitCloneView {
attributes: nil)
try GitClient.default(directoryURL: dirUrl,
shellClient: shellClient).cloneRepository(repoUrlStr)
// TODO: Maybe add possibility to checkout to certain branch straight after cloning
isPresented = false
checkBranches(dirUrl: dirUrl)
} catch {
guard let error = error as? GitClient.GitClientError else {
return showAlert(alertMsg: "Error", infoText: error.localizedDescription)
Expand All @@ -168,4 +174,17 @@ extension GitCloneView {
}
}
}
private func checkBranches(dirUrl: URL) {
// Check if repo has only one branch, and if so, don't show the checkout page
do {
let branches = try GitClient.default(directoryURL: dirUrl,
shellClient: shellClient).getBranches(true)
let filtered = branches.filter {!$0.contains("HEAD")}
if filtered.count > 1 {
showCheckout = true
}
} catch {
return
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ internal struct StatusBarBranchPicker: View {

internal var body: some View {
Menu {
ForEach((try? model.gitClient.getBranches()) ?? [], id: \.self) { branch in
ForEach((try? model.gitClient.getBranches(false)) ?? [], id: \.self) { branch in
Button {
do {
guard model.selectedBranch != branch else { return }
Expand Down
12 changes: 11 additions & 1 deletion CodeEditModules/Modules/WelcomeModule/src/WelcomeView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import GitClone
public struct WelcomeView: View {
@Environment(\.colorScheme) var colorScheme
@State var showGitClone = false
@State var showCheckoutBranch = false
@State private var repoPath = "~/"
@State var isHovering: Bool = false
@State var isHoveringClose: Bool = false
@StateObject private var prefs: AppPreferencesModel = .shared
Expand Down Expand Up @@ -201,7 +203,15 @@ public struct WelcomeView: View {
}
}
.sheet(isPresented: $showGitClone) {
GitCloneView(shellClient: .live, isPresented: $showGitClone)
GitCloneView(shellClient: .live,
isPresented: $showGitClone,
showCheckout: $showCheckoutBranch,
repoPath: $repoPath)
}
.sheet(isPresented: $showCheckoutBranch) {
CheckoutBranchView(isPresented: $showCheckoutBranch,
repoPath: $repoPath,
shellClient: .live)
}
}

Expand Down