Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

git pull: write conflicts #891

Merged
merged 3 commits into from
Aug 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions Code.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,8 @@
947BF349262453040015DAEB /* SearchManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 947BF348262453040015DAEB /* SearchManager.swift */; };
94801F93266FB5E400B29D80 /* TerminalInstance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94801F92266FB5E400B29D80 /* TerminalInstance.swift */; };
94801F95266FBC3500B29D80 /* ViewRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94801F94266FBC3500B29D80 /* ViewRepresentable.swift */; };
948A5FE92A87B09500303C12 /* NSError+init.swift in Sources */ = {isa = PBXBuildFile; fileRef = 948A5FE82A87B09500303C12 /* NSError+init.swift */; };
948A5FEA2A87B09500303C12 /* NSError+init.swift in Sources */ = {isa = PBXBuildFile; fileRef = 948A5FE82A87B09500303C12 /* NSError+init.swift */; };
948D12002583F20D008F877A /* extraCommandsDictionary.plist in Resources */ = {isa = PBXBuildFile; fileRef = 948D11FE2583F20C008F877A /* extraCommandsDictionary.plist */; };
948D12012583F20D008F877A /* commandDictionary.plist in Resources */ = {isa = PBXBuildFile; fileRef = 948D11FF2583F20C008F877A /* commandDictionary.plist */; };
948D12222583F2A5008F877A /* Executor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 948D12212583F2A5008F877A /* Executor.swift */; };
Expand Down Expand Up @@ -1573,6 +1575,7 @@
947BF348262453040015DAEB /* SearchManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchManager.swift; sourceTree = "<group>"; };
94801F92266FB5E400B29D80 /* TerminalInstance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalInstance.swift; sourceTree = "<group>"; };
94801F94266FBC3500B29D80 /* ViewRepresentable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewRepresentable.swift; sourceTree = "<group>"; };
948A5FE82A87B09500303C12 /* NSError+init.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSError+init.swift"; sourceTree = "<group>"; };
948D11FE2583F20C008F877A /* extraCommandsDictionary.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = extraCommandsDictionary.plist; sourceTree = "<group>"; };
948D11FF2583F20C008F877A /* commandDictionary.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = commandDictionary.plist; sourceTree = "<group>"; };
948D12202583F24E008F877A /* terminal.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = terminal.bundle; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1997,6 +2000,7 @@
94CF58DC265E4E0F00CB6A4B /* URL+isDirectory.swift */,
947A9D8827D3C562007680C3 /* UIApplication+getSafeArea.swift */,
94FE0C93281E50F200C05529 /* UserDefaults+Hosts.swift */,
948A5FE82A87B09500303C12 /* NSError+init.swift */,
);
path = Extensions;
sourceTree = "<group>";
Expand Down Expand Up @@ -3054,6 +3058,7 @@
94A045EF280480E800182275 /* RemoteListSection.swift in Sources */,
9419696D280316C7008AAEB2 /* DirectoryFolderMonitor.swift in Sources */,
94795C462931489C0057C12F /* ActivityBar.swift in Sources */,
948A5FEA2A87B09500303C12 /* NSError+init.swift in Sources */,
9FD5BCEA29238B6C00F20C4B /* TerminalExtension.swift in Sources */,
9419696F280316C7008AAEB2 /* FileSystemProvider.swift in Sources */,
941E53B728787D3100561C7E /* MenuButtonView.swift in Sources */,
Expand Down Expand Up @@ -3209,6 +3214,7 @@
94A045EE280480E800182275 /* RemoteListSection.swift in Sources */,
94A777A8257ABEBE008FE7B2 /* DirectoryFolderMonitor.swift in Sources */,
94795C452931489C0057C12F /* ActivityBar.swift in Sources */,
948A5FE92A87B09500303C12 /* NSError+init.swift in Sources */,
9FD5BCE929238B6C00F20C4B /* TerminalExtension.swift in Sources */,
941969252802AB2C008AAEB2 /* FileSystemProvider.swift in Sources */,
941E53B628787D3100561C7E /* MenuButtonView.swift in Sources */,
Expand Down
19 changes: 7 additions & 12 deletions CodeApp/Containers/SourceControlContainer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,21 +45,16 @@ struct SourceControlContainer: View {
App.notificationManager.showWarningMessage(
"errors.source_control.empty_commit_message")
} else {
serviceProvider.commit(
message: App.commitMessage,
error: {
App.notificationManager.showErrorMessage(
$0.localizedDescription)
}
) {
App.git_status()
DispatchQueue.main.async {
App.commitMessage = ""
App.monacoInstance.invalidateDecorations()
}
do {
try await serviceProvider.commit(message: App.commitMessage)
App.notificationManager.showInformationMessage(
"source_control.commit_succeeded")
} catch {
App.notificationManager.showErrorMessage(error.localizedDescription)
}
App.git_status()
App.commitMessage = ""
App.monacoInstance.invalidateDecorations()
}
}

Expand Down
14 changes: 14 additions & 0 deletions CodeApp/Extensions/NSError+init.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
// NSError+init.swift
// Code
//
// Created by Ken Chung on 12/08/2023.
//

import Foundation

extension NSError {
convenience init(descriptionKey: String) {
self.init(domain: "", code: -1, userInfo: [NSLocalizedDescriptionKey: descriptionKey])
}
}
4 changes: 1 addition & 3 deletions CodeApp/Managers/FileSystem/GitServiceProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,7 @@ protocol GitServiceProvider {
from: URL, to: URL, progress: Progress?, error: @escaping (NSError) -> Void,
completionHandler: @escaping () -> Void
)
func commit(
message: String, error: @escaping (NSError) -> Void, completionHandler: @escaping () -> Void
)
func commit(message: String) async throws
func unstage(paths: [String]) throws
func stage(paths: [String]) throws
func fetch(error: @escaping (NSError) -> Void, completionHandler: @escaping () -> Void)
Expand Down
146 changes: 68 additions & 78 deletions CodeApp/Managers/FileSystem/Local/LocalGitServiceProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,22 @@
import Foundation
import SwiftGit2

extension Diff.Status {
extension Diff.Status: CaseIterable {
public static var allCases: [SwiftGit2.Diff.Status] {
[
.conflicted, .current, .ignored, .indexDeleted, .indexModified, .indexNew,
.indexRenamed, .indexTypeChange, .workTreeDeleted, .workTreeNew, .workTreeRenamed,
.workTreeModified, .workTreeUnreadable, .workTreeTypeChange,
]
}

var allIncludedCases: [Diff.Status] {
return Diff.Status.allCases.compactMap {
if self.contains($0) { return $0 }
return nil
}
}

func str() -> String {
switch self {
case .conflicted:
Expand Down Expand Up @@ -63,7 +78,6 @@ class LocalGitServiceProvider: GitServiceProvider {
private var signature: Signature? = nil
private var credential: Credentials? = nil
private var contentCache = NSCache<NSString, NSString>()
private var newAndIgnored = [URL: Diff.Status]()

public var hasRepository: Bool {
return repository != nil
Expand All @@ -80,7 +94,6 @@ class LocalGitServiceProvider: GitServiceProvider {

func loadDirectory(url: URL) {
contentCache.removeAllObjects()
newAndIgnored.removeAll()

if url.absoluteString.contains("com.apple.filesystems.smbclientd") {
return
Expand Down Expand Up @@ -223,53 +236,35 @@ class LocalGitServiceProvider: GitServiceProvider {
case let .success(entries):
var indexedGroup = [(URL, Diff.Status)]()
var workingGroup = [(URL, Diff.Status)]()
self.newAndIgnored = [:]

for i in entries {
let status = i.status

if status == .ignored || status == .workTreeNew || status == .indexNew {
var path: String? = nil
path = i.headToIndex?.newFile?.path
path = i.indexToWorkDir?.newFile?.path
if let path = path {
let url = self.workingURL.appendingPathComponent(path)
self.newAndIgnored[url] = status
}
}

switch status {
case let x where x.rawValue == 258 || x.rawValue == 257:
guard let path = i.headToIndex?.newFile?.path else {
continue
}
let url = self.workingURL.appendingPathComponent(path)
if x.rawValue != 132 {
indexedGroup.append((url, .indexModified))
}
workingGroup.append((url, .workTreeModified))
case let x where x.rawValue == 514:
let headToIndexURL: URL? = {
guard let path = i.headToIndex?.newFile?.path else {
continue
return nil
}
let url = self.workingURL.appendingPathComponent(path)
workingGroup.append((url, .workTreeDeleted))
case .indexDeleted, .indexRenamed, .indexModified, .indexDeleted,
.indexTypeChange, .indexNew:
guard let path = i.headToIndex?.newFile?.path else {
continue
}
let url = self.workingURL.appendingPathComponent(path)
indexedGroup.append((url, status))
case .workTreeNew, .workTreeDeleted, .workTreeRenamed, .workTreeModified,
.workTreeUnreadable, .workTreeTypeChange:
return self.workingURL.appendingPathComponent(path)
}()
let indexToWorkURL: URL? = {
guard let path = i.indexToWorkDir?.newFile?.path else {
continue
return nil
}
return self.workingURL.appendingPathComponent(path)
}()

status.allIncludedCases.forEach { includedCase in
if [
.indexDeleted, .indexRenamed, .indexModified, .indexDeleted,
.indexTypeChange, .indexNew,
].contains(includedCase) {
indexedGroup.append((headToIndexURL!, includedCase))
} else if [
.workTreeNew, .workTreeDeleted, .workTreeRenamed, .workTreeModified,
.workTreeUnreadable, .workTreeTypeChange, .conflicted,
].contains(includedCase) {
workingGroup.append((indexToWorkURL!, includedCase))
}
let url = self.workingURL.appendingPathComponent(path)
workingGroup.append((url, status))
default:
continue
}
}
completionHandler(indexedGroup, workingGroup, self.branch())
Expand Down Expand Up @@ -360,32 +355,37 @@ class LocalGitServiceProvider: GitServiceProvider {
}
}

func commit(
message: String, error: @escaping (NSError) -> Void, completionHandler: @escaping () -> Void
) {
workerQueue.async {
guard self.repository != nil else {
let _error = NSError(
domain: "", code: 401,
userInfo: [NSLocalizedDescriptionKey: "Repository doesn't exist"])
error(_error)
return
}
guard self.signature != nil else {
let _error = NSError(
domain: "", code: 401,
userInfo: [NSLocalizedDescriptionKey: "Signature is not configured"])
error(_error)
return
}
let result = self.repository!.commit(message: message, signature: self.signature!)
switch result {
case .success:
self.contentCache.removeAllObjects()
completionHandler()
return
case let .failure(_error):
error(_error)
func commit(message: String) async throws {
return try await withCheckedThrowingContinuation { continuation in
workerQueue.async {
do {
guard let repository = self.repository else {
throw NSError(descriptionKey: "Repository doesn't exist")
}
guard let signature = self.signature else {
throw NSError(descriptionKey: "Signature is not configured")
}

let isMerging = repository.repositoryState().contains(.merge)
if isMerging {
let head = try repository.HEAD().get()
let oids = try [head.oid] + repository.enumerateMergeHeadEntries()
let parents = try oids.map { oid in
try repository.commit(oid).get()
}
let treeOid = try repository.writeIndexAsTree()

_ = try repository.commit(
tree: treeOid, parents: parents, message: message, signature: signature
).get()
} else {
_ = try repository.commit(message: message, signature: signature).get()
}
self.contentCache.removeAllObjects()
continuation.resume()
} catch {
continuation.resume(throwing: error)
}
}
}
}
Expand Down Expand Up @@ -929,16 +929,6 @@ class LocalGitServiceProvider: GitServiceProvider {
completionHandler(cached as String)
return
}
if let url = URL(string: path), self.newAndIgnored.keys.contains(url) {
let _error = NSError(
domain: "", code: 401,
userInfo: [
NSLocalizedDescriptionKey:
"The requested file is not indexed and cannot be found on disk."
])
error(_error)
return
}
self.load()
guard self.repository != nil else {
let _error = NSError(
Expand Down
2 changes: 1 addition & 1 deletion CodeApp/Managers/WebViewBase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ class WebViewBase: KBWebViewBase {
let newMethod = class_getInstanceMethod(
WebViewBase.self, #selector(WebViewBase.getCustomInputAccessoryView))
class_addMethod(
newClass.self, #selector(getter: UIResponder.inputAccessoryView),
newClass.self, #selector(getter:UIResponder.inputAccessoryView),
method_getImplementation(newMethod!), method_getTypeEncoding(newMethod!))

objc_registerClassPair(newClass!)
Expand Down
34 changes: 2 additions & 32 deletions CodeApp/Views/FileDisplayName.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,46 +14,16 @@ struct FileDisplayName: View {
let gitStatus: Diff.Status?
let name: String

func statusToDisplay(status: Diff.Status) -> String {
switch status {
case .workTreeModified, .indexModified:
return "M"
case .workTreeNew:
return "U"
case .workTreeDeleted, .indexDeleted:
return "D"
case .indexNew:
return "A"
default:
return ""
}
}

func statusToColor(status: Diff.Status) -> Color {
switch status {
case .workTreeModified, .indexModified:
return Color.init("git.modified")
case .workTreeNew:
return Color.init("git.untracked")
case .workTreeDeleted, .indexDeleted:
return Color.init("git.deleted")
case .indexNew:
return Color.init("git.added")
default:
return Color.init(id: "list.inactiveSelectionForeground")
}
}

var body: some View {
HStack {
if let status = gitStatus {
Group {
Text(name)
Spacer()
Text(statusToDisplay(status: status))
Text(status.symbol)
}
.font(.subheadline)
.foregroundColor(statusToColor(status: status))
.foregroundColor(status.backgroundColor)
} else {
Text(name)
.font(.subheadline)
Expand Down
12 changes: 7 additions & 5 deletions CodeApp/Views/SourceControlEntry.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ struct SourceControlEntry: View {
status: status, itemUrl: itemUrl, onUnstage: onUnstage,
onRevert: onRevert, onStage: onStage)
Text(status.symbol)
.foregroundColor(Color.init("git.added"))
.foregroundColor(status.backgroundColor)
.font(.system(size: 14, weight: .semibold))
.padding(.horizontal, 5)
} else {
Expand Down Expand Up @@ -81,8 +81,8 @@ private struct Controls: View {
try? onUnstage(itemUrl.absoluteString)
}
.hoverEffect(.highlight)
case .workTreeModified, .workTreeNew, .workTreeDeleted:
if status != .workTreeNew {
case .workTreeModified, .workTreeNew, .workTreeDeleted, .conflicted:
if status != .workTreeNew, status != .conflicted {
Image(systemName: "arrow.uturn.backward")
.font(.system(size: 12))
.padding(2)
Expand Down Expand Up @@ -114,12 +114,12 @@ extension Diff.Status {
return Color.init("git.modified")
case .workTreeNew:
return Color.init("git.untracked")
case .workTreeDeleted, .indexDeleted:
case .workTreeDeleted, .indexDeleted, .conflicted:
return Color.init("git.deleted")
case .indexNew:
return Color.init("git.added")
default:
return Color.init("BW")
return Color.init(id: "list.inactiveSelectionForeground")
}
}

Expand All @@ -133,6 +133,8 @@ extension Diff.Status {
return "D"
case .indexNew:
return "A"
case .conflicted:
return "C"
default:
return "X"
}
Expand Down
Loading