diff --git a/CodeEdit.xcodeproj/project.pbxproj b/CodeEdit.xcodeproj/project.pbxproj index 15d4f127d..a86ae8637 100644 --- a/CodeEdit.xcodeproj/project.pbxproj +++ b/CodeEdit.xcodeproj/project.pbxproj @@ -22,12 +22,12 @@ 04BA7C1C2AE2D84100584E1C /* GitClient+Commit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04BA7C1B2AE2D84100584E1C /* GitClient+Commit.swift */; }; 04BA7C1E2AE2D8A000584E1C /* GitClient+Clone.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04BA7C1D2AE2D8A000584E1C /* GitClient+Clone.swift */; }; 04BA7C202AE2D92B00584E1C /* GitClient+Status.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04BA7C1F2AE2D92B00584E1C /* GitClient+Status.swift */; }; - 04BA7C222AE2D95E00584E1C /* GitClient+CommitHistory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04BA7C212AE2D95E00584E1C /* GitClient+CommitHistory.swift */; }; 04BA7C242AE2E7CD00584E1C /* SourceControlNavigatorSyncView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04BA7C232AE2E7CD00584E1C /* SourceControlNavigatorSyncView.swift */; }; 04BA7C272AE2E9F100584E1C /* GitClient+Push.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04BA7C262AE2E9F100584E1C /* GitClient+Push.swift */; }; 04BC1CDE2AD9B4B000A83EA5 /* EditorFileTabCloseButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04BC1CDD2AD9B4B000A83EA5 /* EditorFileTabCloseButton.swift */; }; 04C3255B2801F86400C8DA2D /* ProjectNavigatorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 285FEC6D27FE4B4A00E57D53 /* ProjectNavigatorViewController.swift */; }; 04C3255C2801F86900C8DA2D /* ProjectNavigatorMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 285FEC7127FE4EEF00E57D53 /* ProjectNavigatorMenu.swift */; }; + 0FB8F70E2D650481004AF0B3 /* SwiftGitX in Frameworks */ = {isa = PBXBuildFile; productRef = 0FF8F8872D634CB500C5D113 /* SwiftGitX */; }; 0FD96BCE2BEF42530025A697 /* CodeEditWindowController+Toolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FD96BCD2BEF42530025A697 /* CodeEditWindowController+Toolbar.swift */; }; 201169D72837B2E300F92B46 /* SourceControlNavigatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 201169D62837B2E300F92B46 /* SourceControlNavigatorView.swift */; }; 201169DB2837B34000F92B46 /* GitChangedFileListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 201169DA2837B34000F92B46 /* GitChangedFileListView.swift */; }; @@ -221,7 +221,6 @@ 587B9E9529301D8F00AC7927 /* BitBucketUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587B9E4E29301D8F00AC7927 /* BitBucketUser.swift */; }; 587B9E9629301D8F00AC7927 /* BitBucketRepositories.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587B9E4F29301D8F00AC7927 /* BitBucketRepositories.swift */; }; 587B9E9729301D8F00AC7927 /* BitBucketAccount+Token.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587B9E5029301D8F00AC7927 /* BitBucketAccount+Token.swift */; }; - 587B9E9829301D8F00AC7927 /* GitCommit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587B9E5329301D8F00AC7927 /* GitCommit.swift */; }; 587B9E9929301D8F00AC7927 /* GitChangedFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587B9E5429301D8F00AC7927 /* GitChangedFile.swift */; }; 587B9E9A29301D8F00AC7927 /* GitStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587B9E5529301D8F00AC7927 /* GitStatus.swift */; }; 587FB99029C1246400B519DD /* EditorTabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587FB98F29C1246400B519DD /* EditorTabView.swift */; }; @@ -516,8 +515,6 @@ 9D36E1BF2B5E7D7500443C41 /* GitBranchesGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D36E1BE2B5E7D7500443C41 /* GitBranchesGroup.swift */; }; B6041F4D29D7A4E9000F3454 /* SettingsPageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6041F4C29D7A4E9000F3454 /* SettingsPageView.swift */; }; B6041F5229D7D6D6000F3454 /* SettingsWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6041F5129D7D6D6000F3454 /* SettingsWindow.swift */; }; - B607181D2B0C5BE3009CDAB4 /* GitClient+Stash.swift in Sources */ = {isa = PBXBuildFile; fileRef = B607181C2B0C5BE3009CDAB4 /* GitClient+Stash.swift */; }; - B60718202B0C6CE7009CDAB4 /* GitStashEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = B607181F2B0C6CE7009CDAB4 /* GitStashEntry.swift */; }; B60718312B15A9A3009CDAB4 /* CEOutlineGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = B60718302B15A9A3009CDAB4 /* CEOutlineGroup.swift */; }; B60718372B170638009CDAB4 /* SourceControlRenameBranchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B60718362B170638009CDAB4 /* SourceControlRenameBranchView.swift */; }; B607183F2B17DB07009CDAB4 /* SourceControlNavigatorRepositoryView+contextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = B607183E2B17DB07009CDAB4 /* SourceControlNavigatorRepositoryView+contextMenu.swift */; }; @@ -726,7 +723,6 @@ 04BA7C1B2AE2D84100584E1C /* GitClient+Commit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GitClient+Commit.swift"; sourceTree = ""; }; 04BA7C1D2AE2D8A000584E1C /* GitClient+Clone.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GitClient+Clone.swift"; sourceTree = ""; }; 04BA7C1F2AE2D92B00584E1C /* GitClient+Status.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GitClient+Status.swift"; sourceTree = ""; }; - 04BA7C212AE2D95E00584E1C /* GitClient+CommitHistory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GitClient+CommitHistory.swift"; sourceTree = ""; }; 04BA7C232AE2E7CD00584E1C /* SourceControlNavigatorSyncView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceControlNavigatorSyncView.swift; sourceTree = ""; }; 04BA7C262AE2E9F100584E1C /* GitClient+Push.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GitClient+Push.swift"; sourceTree = ""; }; 04BC1CDD2AD9B4B000A83EA5 /* EditorFileTabCloseButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditorFileTabCloseButton.swift; sourceTree = ""; }; @@ -927,7 +923,6 @@ 587B9E4E29301D8F00AC7927 /* BitBucketUser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BitBucketUser.swift; sourceTree = ""; }; 587B9E4F29301D8F00AC7927 /* BitBucketRepositories.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BitBucketRepositories.swift; sourceTree = ""; }; 587B9E5029301D8F00AC7927 /* BitBucketAccount+Token.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitBucketAccount+Token.swift"; sourceTree = ""; }; - 587B9E5329301D8F00AC7927 /* GitCommit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitCommit.swift; sourceTree = ""; }; 587B9E5429301D8F00AC7927 /* GitChangedFile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitChangedFile.swift; sourceTree = ""; }; 587B9E5529301D8F00AC7927 /* GitStatus.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitStatus.swift; sourceTree = ""; }; 587FB98F29C1246400B519DD /* EditorTabView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EditorTabView.swift; sourceTree = ""; }; @@ -1201,8 +1196,6 @@ 9D36E1BE2B5E7D7500443C41 /* GitBranchesGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GitBranchesGroup.swift; sourceTree = ""; }; B6041F4C29D7A4E9000F3454 /* SettingsPageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsPageView.swift; sourceTree = ""; }; B6041F5129D7D6D6000F3454 /* SettingsWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsWindow.swift; sourceTree = ""; }; - B607181C2B0C5BE3009CDAB4 /* GitClient+Stash.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GitClient+Stash.swift"; sourceTree = ""; }; - B607181F2B0C6CE7009CDAB4 /* GitStashEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GitStashEntry.swift; sourceTree = ""; }; B60718302B15A9A3009CDAB4 /* CEOutlineGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CEOutlineGroup.swift; sourceTree = ""; }; B60718362B170638009CDAB4 /* SourceControlRenameBranchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceControlRenameBranchView.swift; sourceTree = ""; }; B607183E2B17DB07009CDAB4 /* SourceControlNavigatorRepositoryView+contextMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SourceControlNavigatorRepositoryView+contextMenu.swift"; sourceTree = ""; }; @@ -1352,6 +1345,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 0FB8F70E2D650481004AF0B3 /* SwiftGitX in Frameworks */, 6C85BB402C2105ED00EB5DEF /* CodeEditKit in Frameworks */, 6C66C31329D05CDC00DE9ED2 /* GRDB in Frameworks */, 58F2EB1E292FB954004A9BDE /* Sparkle in Frameworks */, @@ -2322,13 +2316,11 @@ 04BA7C182AE2D7C600584E1C /* GitClient+Branches.swift */, 04BA7C1D2AE2D8A000584E1C /* GitClient+Clone.swift */, 04BA7C1B2AE2D84100584E1C /* GitClient+Commit.swift */, - 04BA7C212AE2D95E00584E1C /* GitClient+CommitHistory.swift */, B65B11032B09DB1C002852CF /* GitClient+Fetch.swift */, B69BFDC62B0686910050D9A6 /* GitClient+Initiate.swift */, B65B10EE2B07C454002852CF /* GitClient+Remote.swift */, B65B11002B09D5D4002852CF /* GitClient+Pull.swift */, 04BA7C262AE2E9F100584E1C /* GitClient+Push.swift */, - B607181C2B0C5BE3009CDAB4 /* GitClient+Stash.swift */, 04BA7C1F2AE2D92B00584E1C /* GitClient+Status.swift */, B628B7922B18369800F9775A /* GitClient+Validate.swift */, ); @@ -2341,9 +2333,7 @@ 04BA7C0A2AE2A2D100584E1C /* GitBranch.swift */, 9D36E1BE2B5E7D7500443C41 /* GitBranchesGroup.swift */, 587B9E5429301D8F00AC7927 /* GitChangedFile.swift */, - 587B9E5329301D8F00AC7927 /* GitCommit.swift */, B65B10F12B07D34F002852CF /* GitRemote.swift */, - B607181F2B0C6CE7009CDAB4 /* GitStashEntry.swift */, 587B9E5529301D8F00AC7927 /* GitStatus.swift */, ); path = Models; @@ -3829,6 +3819,7 @@ 6CC00A8A2CBEF150004E8134 /* CodeEditSourceEditor */, 6C73A6D22D4F1E550012D95C /* CodeEditSourceEditor */, 6C9DB9E32D55656300ACD86E /* CodeEditSourceEditor */, + 0FF8F8872D634CB500C5D113 /* SwiftGitX */, ); productName = CodeEdit; productReference = B658FB2C27DA9E0F00EA4DBD /* CodeEdit.app */; @@ -3927,6 +3918,7 @@ 6C4E37FA2C73E00700AEE7B5 /* XCRemoteSwiftPackageReference "SwiftTerm" */, 6CB94D012CA1205100E8651C /* XCRemoteSwiftPackageReference "swift-async-algorithms" */, 6C9DB9E22D55656300ACD86E /* XCRemoteSwiftPackageReference "CodeEditSourceEditor" */, + 0FF8F8862D634CB500C5D113 /* XCRemoteSwiftPackageReference "SwiftGitX" */, ); productRefGroup = B658FB2D27DA9E0F00EA4DBD /* Products */; projectDirPath = ""; @@ -4087,7 +4079,6 @@ 5B698A0F2B2636A700DE9392 /* SearchSettingsIgnoreGlobPatternItemView.swift in Sources */, 587B9E8A29301D8F00AC7927 /* GitHubIssue.swift in Sources */, EC0870F72A455F6400EB8692 /* ProjectNavigatorViewController+NSMenuDelegate.swift in Sources */, - B60718202B0C6CE7009CDAB4 /* GitStashEntry.swift in Sources */, 6CAAF69429BCD78600A1F48A /* (null) in Sources */, 6CDAFDDF2D35DADD002B2D47 /* String+ValidFileName.swift in Sources */, 3026F50F2AC006C80061227E /* InspectorAreaViewModel.swift in Sources */, @@ -4190,10 +4181,8 @@ 587B9E8B29301D8F00AC7927 /* GitHubAccount+deleteReference.swift in Sources */, 6CD0358A2C3461160091E1F4 /* KeyWindowControllerObserver.swift in Sources */, 58798237292E30B90085B254 /* FeedbackView.swift in Sources */, - 587B9E9829301D8F00AC7927 /* GitCommit.swift in Sources */, 6C5228B529A868BD00AC48F6 /* Environment+ContentInsets.swift in Sources */, 587B9E9429301D8F00AC7927 /* BitBucketTokenConfiguration.swift in Sources */, - 04BA7C222AE2D95E00584E1C /* GitClient+CommitHistory.swift in Sources */, 581BFB672926431000D251EC /* WelcomeWindowView.swift in Sources */, 58A5DFA329339F6400D1BD5D /* CommandManager.swift in Sources */, 58798284292ED0FB0085B254 /* TerminalEmulatorView.swift in Sources */, @@ -4476,7 +4465,6 @@ 04BA7C1E2AE2D8A000584E1C /* GitClient+Clone.swift in Sources */, 58F2EB09292FB2B0004A9BDE /* TerminalSettings.swift in Sources */, 6C578D8429CD343800DC73B2 /* ExtensionDetailView.swift in Sources */, - B607181D2B0C5BE3009CDAB4 /* GitClient+Stash.swift in Sources */, B65B10F22B07D34F002852CF /* GitRemote.swift in Sources */, B6A43C5D29FC4AF00027E0E0 /* CreateSSHKeyView.swift in Sources */, B6EA200229DB7F81001BF195 /* View+ConstrainHeightToWindow.swift in Sources */, @@ -5725,6 +5713,14 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ + 0FF8F8862D634CB500C5D113 /* XCRemoteSwiftPackageReference "SwiftGitX" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/ibrahimcetin/SwiftGitX"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 0.1.7; + }; + }; 2816F592280CF50500DD548B /* XCRemoteSwiftPackageReference "CodeEditSymbols" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/CodeEditApp/CodeEditSymbols"; @@ -5864,6 +5860,11 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + 0FF8F8872D634CB500C5D113 /* SwiftGitX */ = { + isa = XCSwiftPackageProductDependency; + package = 0FF8F8862D634CB500C5D113 /* XCRemoteSwiftPackageReference "SwiftGitX" */; + productName = SwiftGitX; + }; 2816F593280CF50500DD548B /* CodeEditSymbols */ = { isa = XCSwiftPackageProductDependency; package = 2816F592280CF50500DD548B /* XCRemoteSwiftPackageReference "CodeEditSymbols" */; diff --git a/CodeEdit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/CodeEdit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 8440b8a74..134f4e65a 100644 --- a/CodeEdit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/CodeEdit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "aef43d6aa0c467418565c574c33495a50d6e24057eb350c17704ab4ae2aead6c", + "originHash" : "d3cab6e96b236b7927ac50c474e65b9a97c4c314fc032fee7184cd5d58e1afc9", "pins" : [ { "identity" : "anycodable", @@ -127,6 +127,15 @@ "version" : "0.13.2" } }, + { + "identity" : "libgit2", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ibrahimcetin/libgit2.git", + "state" : { + "revision" : "6e627a8d345d9de7df6f69c134ec5dfa81e75ade", + "version" : "1.8.0" + } + }, { "identity" : "logstream", "kind" : "remoteSourceControl", @@ -217,6 +226,15 @@ "version" : "509.1.1" } }, + { + "identity" : "swiftgitx", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ibrahimcetin/SwiftGitX", + "state" : { + "revision" : "a0b878c02599cda868cb7920e123b4f0be620fcb", + "version" : "0.1.7" + } + }, { "identity" : "swiftlintplugin", "kind" : "remoteSourceControl", diff --git a/CodeEdit/AppDelegate.swift b/CodeEdit/AppDelegate.swift index 7945d1e71..3bd4d2456 100644 --- a/CodeEdit/AppDelegate.swift +++ b/CodeEdit/AppDelegate.swift @@ -9,6 +9,7 @@ import SwiftUI import CodeEditSymbols import CodeEditSourceEditor import OSLog +import SwiftGitX final class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject { private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "", category: "AppDelegate") @@ -24,6 +25,8 @@ final class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject { Settings.shared.preferences.general.appAppearance.applyAppearance() checkForFilesToOpen() + _ = try? SwiftGitX.initialize() + NSApp.closeWindow(.welcome, .about) DispatchQueue.main.async { @@ -60,7 +63,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject { } func applicationWillTerminate(_ aNotification: Notification) { - + _ = try? SwiftGitX.shutdown() } func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { diff --git a/CodeEdit/Features/InspectorArea/HistoryInspector/HistoryInspectorItemView.swift b/CodeEdit/Features/InspectorArea/HistoryInspector/HistoryInspectorItemView.swift index 96bf256a2..7b88c59b8 100644 --- a/CodeEdit/Features/InspectorArea/HistoryInspector/HistoryInspectorItemView.swift +++ b/CodeEdit/Features/InspectorArea/HistoryInspector/HistoryInspectorItemView.swift @@ -6,11 +6,12 @@ // import SwiftUI +import SwiftGitX struct HistoryInspectorItemView: View { - var commit: GitCommit + var commit: Commit - @Binding var selection: GitCommit? + @Binding var selection: Commit? private var showPopup: Binding { Binding { diff --git a/CodeEdit/Features/InspectorArea/HistoryInspector/HistoryInspectorModel.swift b/CodeEdit/Features/InspectorArea/HistoryInspector/HistoryInspectorModel.swift index bb5ff4b8b..43b870b72 100644 --- a/CodeEdit/Features/InspectorArea/HistoryInspector/HistoryInspectorModel.swift +++ b/CodeEdit/Features/InspectorArea/HistoryInspector/HistoryInspectorModel.swift @@ -6,6 +6,7 @@ // import Foundation +import SwiftGitX final class HistoryInspectorModel: ObservableObject { private(set) var sourceControlManager: SourceControlManager? @@ -17,7 +18,7 @@ final class HistoryInspectorModel: ObservableObject { private(set) var fileURL: String? /// The selected branch from the GitClient - @Published var commitHistory: [GitCommit] = [] + @Published var commitHistory: [Commit] = [] func setWorkspace(sourceControlManager: SourceControlManager?) async { self.sourceControlManager = sourceControlManager @@ -32,19 +33,19 @@ final class HistoryInspectorModel: ObservableObject { } func updateCommitHistory() async { - guard let sourceControlManager, let fileURL else { + guard let repository = sourceControlManager?.repository, let fileURL else { await setCommitHistory([]) return } do { - let commitHistory = try await sourceControlManager - .gitClient - .getCommitHistory( - maxCount: 40, - fileLocalPath: fileURL, - showMergeCommits: Settings.shared.preferences.sourceControl.git.showMergeCommitsPerFileLog - ) + let commitHistory = try Array(repository.log(sorting: .time).prefix(40)) +// .gitClient +// .getCommitHistory( +// maxCount: 40, +// fileLocalPath: fileURL, +// showMergeCommits: Settings.shared.preferences.sourceControl.git.showMergeCommitsPerFileLog +// ) await setCommitHistory(commitHistory) } catch { await setCommitHistory([]) @@ -52,7 +53,7 @@ final class HistoryInspectorModel: ObservableObject { } @MainActor - private func setCommitHistory(_ history: [GitCommit]) { + private func setCommitHistory(_ history: [Commit]) { self.commitHistory = history } } diff --git a/CodeEdit/Features/InspectorArea/HistoryInspector/HistoryInspectorView.swift b/CodeEdit/Features/InspectorArea/HistoryInspector/HistoryInspectorView.swift index 280c00ffb..1eec5f6ea 100644 --- a/CodeEdit/Features/InspectorArea/HistoryInspector/HistoryInspectorView.swift +++ b/CodeEdit/Features/InspectorArea/HistoryInspector/HistoryInspectorView.swift @@ -5,6 +5,7 @@ // Created by Nanashi Li on 2022/03/24. // import SwiftUI +import SwiftGitX struct HistoryInspectorView: View { @AppSettings(\.sourceControl.git.showMergeCommitsPerFileLog) @@ -16,7 +17,7 @@ struct HistoryInspectorView: View { @ObservedObject private var model: HistoryInspectorModel - @State var selection: GitCommit? + @State var selection: Commit? /// Initialize with GitClient /// - Parameter gitClient: a GitClient diff --git a/CodeEdit/Features/InspectorArea/HistoryInspector/HistoryPopoverView.swift b/CodeEdit/Features/InspectorArea/HistoryInspector/HistoryPopoverView.swift index 1913af453..d380aab40 100644 --- a/CodeEdit/Features/InspectorArea/HistoryInspector/HistoryPopoverView.swift +++ b/CodeEdit/Features/InspectorArea/HistoryInspector/HistoryPopoverView.swift @@ -6,12 +6,13 @@ // import SwiftUI +import SwiftGitX struct HistoryPopoverView: View { - private var commit: GitCommit + private var commit: Commit - init(commit: GitCommit) { + init(commit: Commit) { self.commit = commit } @@ -32,7 +33,7 @@ struct HistoryPopoverView: View { .disabled(true) ActionButton("Email \(commit.author)", systemImage: "envelope") { let service = NSSharingService(named: NSSharingService.Name.composeEmail) - service?.recipients = [commit.authorEmail] + service?.recipients = [commit.author.email] service?.perform(withItems: []) } } diff --git a/CodeEdit/Features/NavigatorArea/SourceControlNavigator/History/Views/CommitDetailsHeaderView.swift b/CodeEdit/Features/NavigatorArea/SourceControlNavigator/History/Views/CommitDetailsHeaderView.swift index c5993fa51..1fc6a0850 100644 --- a/CodeEdit/Features/NavigatorArea/SourceControlNavigator/History/Views/CommitDetailsHeaderView.swift +++ b/CodeEdit/Features/NavigatorArea/SourceControlNavigator/History/Views/CommitDetailsHeaderView.swift @@ -6,9 +6,10 @@ // import SwiftUI +import SwiftGitX struct CommitDetailsHeaderView: View { - var commit: GitCommit + var commit: Commit private var defaultAvatar: some View { Image(systemName: "person.crop.circle.fill") @@ -19,9 +20,9 @@ struct CommitDetailsHeaderView: View { } private func commitDetails() -> String { - if commit.committerEmail == "noreply@github.com" { + if commit.committer.email == "noreply@github.com" { return commit.message.trimmingCharacters(in: .whitespacesAndNewlines) - } else if commit.authorEmail != commit.committerEmail { + } else if commit.author.email != commit.committer.email { return commit.message.trimmingCharacters(in: .whitespacesAndNewlines) } else { return "\(commit.message)\n\n\(coAuthDetail())".trimmingCharacters(in: .whitespacesAndNewlines) @@ -29,16 +30,16 @@ struct CommitDetailsHeaderView: View { } private func coAuthDetail() -> String { - if commit.committerEmail == "noreply@github.com" { + if commit.committer.email == "noreply@github.com" { return "" - } else if commit.authorEmail != commit.committerEmail { - return "Co-authored by: \(commit.committer)\n<\(commit.committerEmail)>" + } else if commit.author.email != commit.committer.email { + return "Co-authored by: \(commit.committer)\n<\(commit.committer.email)>" } return "" } private func generateAvatarHash() -> String { - let hash = commit.authorEmail.md5(trim: true, caseSensitive: false) + let hash = commit.author.email.md5(trim: true, caseSensitive: false) return "\(hash)?d=404&s=64" // send 404 if no image available, image size 64x64 (32x32 @2x) } @@ -70,18 +71,18 @@ struct CommitDetailsHeaderView: View { .resizable() .clipShape(Circle()) .frame(width: 32, height: 32) - .help(commit.author) + .help(commit.author.name) } else if phase.error != nil { defaultAvatar - .help(commit.author) + .help(commit.author.name) } else { defaultAvatar - .help(commit.author) + .help(commit.author.name) } } VStack(alignment: .leading) { - Text(commit.author) + Text(commit.author.name) .fontWeight(.bold) Text(commit.date.formatted(date: .abbreviated, time: .shortened)) .font(.subheadline) @@ -90,7 +91,7 @@ struct CommitDetailsHeaderView: View { Spacer() - Text(commit.hash) + Text(commit.id.abbreviated) .font(.subheadline) .fontDesign(.monospaced) .background( @@ -110,8 +111,8 @@ struct CommitDetailsHeaderView: View { .padding(.horizontal, 16) .frame(alignment: .leading) - if !commit.body.isEmpty { - Text(commit.body) + if let body = commit.body { + Text(body) .padding(.horizontal, 16) .frame(alignment: .leading) } diff --git a/CodeEdit/Features/NavigatorArea/SourceControlNavigator/History/Views/CommitDetailsView.swift b/CodeEdit/Features/NavigatorArea/SourceControlNavigator/History/Views/CommitDetailsView.swift index ed63905c1..674af80a1 100644 --- a/CodeEdit/Features/NavigatorArea/SourceControlNavigator/History/Views/CommitDetailsView.swift +++ b/CodeEdit/Features/NavigatorArea/SourceControlNavigator/History/Views/CommitDetailsView.swift @@ -6,11 +6,12 @@ // import SwiftUI +import SwiftGitX struct CommitDetailsView: View { @EnvironmentObject var sourceControlManager: SourceControlManager - @Binding var commit: GitCommit? + @Binding var commit: Commit? @State var commitChanges: [GitChangedFile] = [] @@ -19,7 +20,7 @@ struct CommitDetailsView: View { func updateCommitChanges() async throws { if let commit = commit { let changes = await sourceControlManager - .getCommitChangedFiles(commitSHA: commit.commitHash) + .getCommitChangedFiles(commitSHA: commit.id.hex) commitChanges = changes } } diff --git a/CodeEdit/Features/NavigatorArea/SourceControlNavigator/History/Views/CommitListItemView.swift b/CodeEdit/Features/NavigatorArea/SourceControlNavigator/History/Views/CommitListItemView.swift index 5314cebbe..aa0e080ef 100644 --- a/CodeEdit/Features/NavigatorArea/SourceControlNavigator/History/Views/CommitListItemView.swift +++ b/CodeEdit/Features/NavigatorArea/SourceControlNavigator/History/Views/CommitListItemView.swift @@ -6,10 +6,11 @@ // import SwiftUI +import SwiftGitX struct CommitListItemView: View { - var commit: GitCommit + var commit: Commit var showRef: Bool var width: CGFloat @@ -22,7 +23,7 @@ struct CommitListItemView: View { } private func generateAvatarHash() -> String { - let hash = commit.authorEmail.md5(trim: true, caseSensitive: false) + let hash = commit.author.email.md5(trim: true, caseSensitive: false) return "\(hash)?d=404&s=64" // send 404 if no image available, image size 64x64 (32x32 @2x) } @@ -48,13 +49,13 @@ struct CommitListItemView: View { @Environment(\.openURL) private var openCommit - init(commit: GitCommit, showRef: Bool) { + init(commit: Commit, showRef: Bool) { self.commit = commit self.showRef = showRef self.width = 0 } - init(commit: GitCommit, showRef: Bool, width: CGFloat) { + init(commit: Commit, showRef: Bool, width: CGFloat) { self.commit = commit self.showRef = showRef self.width = width @@ -69,75 +70,76 @@ struct CommitListItemView: View { .resizable() .clipShape(Circle()) .frame(width: 32, height: 32) - .help(commit.author) + .help(commit.author.name) } else if phase.error != nil { defaultAvatar - .help(commit.author) + .help(commit.author.name) } else { defaultAvatar - .help(commit.author) + .help(commit.author.name) } } } VStack(alignment: .leading, spacing: 0) { HStack { - Text(commit.author) + Text(commit.author.name) .fontWeight(.bold) .font(.system(size: 11)) - if showRef { - if !commit.refs.isEmpty { - HStack { - ForEach(commit.refs, id: \.self) { ref in - HStack(spacing: 2.5) { - Image.branch - .imageScale(.small) - .foregroundColor(.secondary) - .help(ref) - Text(ref) - } - .font(.system(size: 10)) - .frame(height: 13) - .background( - RoundedRectangle(cornerRadius: 3) - .padding(.vertical, -1) - .padding(.leading, -2.5) - .padding(.trailing, -4) - .foregroundColor(Color(nsColor: .quaternaryLabelColor)) - ) - .padding(.trailing, 2.5) - } - } - } - - if !commit.tag.isEmpty { - HStack(spacing: 2.5) { - Image(systemName: "tag") - .imageScale(.small) - .foregroundColor(.primary) - .help(commit.tag) - Text(commit.tag) - } - .font(.system(size: 10)) - .frame(height: 13) - .background( - RoundedRectangle(cornerRadius: 3) - .padding(.vertical, -1) - .padding(.leading, -2.5) - .padding(.trailing, -4) - .foregroundColor(Color(nsColor: .purple).opacity(0.2)) - ) - .padding(.trailing, 2.5) - } - } + // TODO: Migrate to SwiftGitX +// if showRef { +// if !commit.branches.isEmpty { +// HStack { +// ForEach(commit.refs, id: \.self) { ref in +// HStack(spacing: 2.5) { +// Image.branch +// .imageScale(.small) +// .foregroundColor(.secondary) +// .help(ref) +// Text(ref) +// } +// .font(.system(size: 10)) +// .frame(height: 13) +// .background( +// RoundedRectangle(cornerRadius: 3) +// .padding(.vertical, -1) +// .padding(.leading, -2.5) +// .padding(.trailing, -4) +// .foregroundColor(Color(nsColor: .quaternaryLabelColor)) +// ) +// .padding(.trailing, 2.5) +// } +// } +// } +// +// if !commit.tag.isEmpty { +// HStack(spacing: 2.5) { +// Image(systemName: "tag") +// .imageScale(.small) +// .foregroundColor(.primary) +// .help(commit.tag) +// Text(commit.tag) +// } +// .font(.system(size: 10)) +// .frame(height: 13) +// .background( +// RoundedRectangle(cornerRadius: 3) +// .padding(.vertical, -1) +// .padding(.leading, -2.5) +// .padding(.trailing, -4) +// .foregroundColor(Color(nsColor: .purple).opacity(0.2)) +// ) +// .padding(.trailing, 2.5) +// } +// } } - Text("\(commit.message) \(commit.body)") + Text(commit.message) .font(.system(size: 11)) .lineLimit(2) } Spacer() VStack(alignment: .trailing, spacing: 5) { - Text(commit.hash) + Text(commit.id.abbreviated) .font(.system(size: 10, design: .monospaced)) .background( RoundedRectangle(cornerRadius: 3) @@ -164,38 +166,40 @@ struct CommitListItemView: View { Button("Copy Identifier") { let pasteboard = NSPasteboard.general pasteboard.clearContents() - pasteboard.setString(commit.commitHash, forType: .string) + pasteboard.setString(commit.id.hex, forType: .string) } - Button("Email \(commit.author)...") { + Button("Email \(commit.author.name)...") { let service = NSSharingService(named: NSSharingService.Name.composeEmail) - service?.recipients = [commit.authorEmail] + service?.recipients = [commit.author.email] service?.perform(withItems: []) } Divider() } Group { - Button("Tag \(commit.hash)...") {} + Button("Tag \(commit.id.hex)...") {} .disabled(true) // TODO: Implementation Needed - Button("New Branch from \(commit.hash)...") {} - .disabled(true) // TODO: Implementation Needed - Button("Cherry-Pick \(commit.hash)...") {} - .disabled(true) // TODO: Implementation Needed - } - Group { - Divider() - if let commitRemoteURL = commit.commitBaseURL?.absoluteString { - Button("View on \(commit.remoteString)...") { - let commitURL = "\(commitRemoteURL)/\(commit.commitHash)" - openCommit(URL(string: commitURL)!) - } - Divider() - } - Button("Check Out \(commit.hash)...") {} + Button("New Branch from \(commit.id.hex)...") {} .disabled(true) // TODO: Implementation Needed - Divider() - Button("History Editor Help") {} + Button("Cherry-Pick \(commit.id.hex)...") {} .disabled(true) // TODO: Implementation Needed } + + // TODO: Migrate to SwiftGitx +// Group { +// Divider() +// if let commitRemoteURL = commit.commitBaseURL?.absoluteString { +// Button("View on \(commit.remoteString)...") { +// let commitURL = "\(commitRemoteURL)/\(commit.id.hex)" +// openCommit(URL(string: commitURL)!) +// } +// Divider() +// } +// Button("Check Out \(commit.id.hex)...") {} +// .disabled(true) // TODO: Implementation Needed +// Divider() +// Button("History Editor Help") {} +// .disabled(true) // TODO: Implementation Needed +// } } } } diff --git a/CodeEdit/Features/NavigatorArea/SourceControlNavigator/History/Views/SourceControlNavigatorHistoryView.swift b/CodeEdit/Features/NavigatorArea/SourceControlNavigator/History/Views/SourceControlNavigatorHistoryView.swift index b615763aa..77de41615 100644 --- a/CodeEdit/Features/NavigatorArea/SourceControlNavigator/History/Views/SourceControlNavigatorHistoryView.swift +++ b/CodeEdit/Features/NavigatorArea/SourceControlNavigator/History/Views/SourceControlNavigatorHistoryView.swift @@ -7,6 +7,7 @@ import SwiftUI import CodeEditSymbols +import SwiftGitX struct SourceControlNavigatorHistoryView: View { enum Status { @@ -21,20 +22,17 @@ struct SourceControlNavigatorHistoryView: View { @EnvironmentObject var sourceControlManager: SourceControlManager @State var commitHistoryStatus: Status = .loading - @State var commitHistory: [GitCommit] = [] + @State var commitHistory: [Commit] = [] - @State var selection: GitCommit? + @State var selection: Commit? @State private var width: CGFloat = CGFloat.zero func updateCommitHistory() async { + guard let repository = sourceControlManager.repository else { return } + do { commitHistoryStatus = .loading - let commits = try await sourceControlManager - .gitClient - .getCommitHistory( - branchName: sourceControlManager.currentBranch?.name, - showMergeCommits: Settings.shared.preferences.sourceControl.git.showMergeCommitsPerFileLog - ) + let commits = try Array(repository.log(sorting: .time)) await MainActor.run { commitHistory = commits commitHistoryStatus = .ready diff --git a/CodeEdit/Features/NavigatorArea/SourceControlNavigator/Repository/Models/RepoOutlineGroupItem.swift b/CodeEdit/Features/NavigatorArea/SourceControlNavigator/Repository/Models/RepoOutlineGroupItem.swift index b78c0fea5..ceef36863 100644 --- a/CodeEdit/Features/NavigatorArea/SourceControlNavigator/Repository/Models/RepoOutlineGroupItem.swift +++ b/CodeEdit/Features/NavigatorArea/SourceControlNavigator/Repository/Models/RepoOutlineGroupItem.swift @@ -6,6 +6,7 @@ // import SwiftUI +import SwiftGitX struct RepoOutlineGroupItem: Hashable, Identifiable { var id: String @@ -16,6 +17,6 @@ struct RepoOutlineGroupItem: Hashable, Identifiable { var imageColor: Color? var children: [RepoOutlineGroupItem]? var branch: GitBranch? - var stashEntry: GitStashEntry? + var stashEntry: StashEntry? var remote: GitRemote? } diff --git a/CodeEdit/Features/NavigatorArea/SourceControlNavigator/Repository/Views/SourceControlNavigatorRepositoryView.swift b/CodeEdit/Features/NavigatorArea/SourceControlNavigator/Repository/Views/SourceControlNavigatorRepositoryView.swift index ecdbdea79..052bfb625 100644 --- a/CodeEdit/Features/NavigatorArea/SourceControlNavigator/Repository/Views/SourceControlNavigatorRepositoryView.swift +++ b/CodeEdit/Features/NavigatorArea/SourceControlNavigator/Repository/Views/SourceControlNavigatorRepositoryView.swift @@ -7,6 +7,7 @@ import SwiftUI import CodeEditSymbols +import SwiftGitX struct SourceControlNavigatorRepositoryView: View { @Environment(\.controlActiveState) @@ -23,8 +24,8 @@ struct SourceControlNavigatorRepositoryView: View { @State var isPresentingConfirmDeleteBranch: Bool = false @State var branchToDelete: GitBranch? @State var isPresentingConfirmDeleteStashEntry: Bool = false - @State var stashEntryToApply: GitStashEntry? - @State var stashEntryToDelete: GitStashEntry? + @State var stashEntryToApply: StashEntry? + @State var stashEntryToDelete: StashEntry? @State var isPresentingConfirmDeleteRemote: Bool = false @State var remoteToDelete: GitRemote? @State var keepStashAfterApplying: Bool = true diff --git a/CodeEdit/Features/SourceControl/Client/GitClient+Commit.swift b/CodeEdit/Features/SourceControl/Client/GitClient+Commit.swift index 9a8dc0c9a..b97ebd5f7 100644 --- a/CodeEdit/Features/SourceControl/Client/GitClient+Commit.swift +++ b/CodeEdit/Features/SourceControl/Client/GitClient+Commit.swift @@ -9,35 +9,6 @@ import Foundation import RegexBuilder extension GitClient { - /// Commit files - /// - Parameters: - /// - message: Commit message - func commit(message: String, details: String?) async throws { - let message = message.replacingOccurrences(of: #"""#, with: #"\""#) - let command: String - - if let msgDetails = details { - command = "commit --message=\"\(message + (msgDetails.isEmpty ? "" : ("\n\n" + msgDetails)))\"" - } else { - command = "commit --message=\"\(message)\"" - } - - _ = try await run(command) - } - - /// Add file to git - /// - Parameter file: File to add - func add(_ files: [URL]) async throws { - let output = try await run("add \(files.map { "'\($0.path(percentEncoded: false))'" }.joined(separator: " "))") - print(output) - } - - /// Add file to git - /// - Parameter file: File to add - func reset(_ files: [URL]) async throws { - _ = try await run("reset \(files.map { "'\($0.path(percentEncoded: false))'" }.joined(separator: " "))") - } - /// Returns tuple of unsynced commits both ahead and behind func numberOfUnsyncedCommits() async throws -> (ahead: Int, behind: Int) { let output = try await run("status -sb --porcelain=v2").trimmingCharacters(in: .whitespacesAndNewlines) diff --git a/CodeEdit/Features/SourceControl/Client/GitClient+CommitHistory.swift b/CodeEdit/Features/SourceControl/Client/GitClient+CommitHistory.swift deleted file mode 100644 index 6245fe773..000000000 --- a/CodeEdit/Features/SourceControl/Client/GitClient+CommitHistory.swift +++ /dev/null @@ -1,80 +0,0 @@ -// -// GitClient+CommitHistory.swift -// CodeEdit -// -// Created by Albert Vinizhanau on 10/20/23. -// - -import Foundation - -extension GitClient { - /// Gets the commit history log for the specified branch or file - /// - Parameters: - /// - branchName: Name of the branch - /// - maxCount: Maximum amount of entries to get - /// - fileLocalPath: Optional path of file to get history for - /// - Returns: Array of git commits - func getCommitHistory( - branchName: String? = nil, - maxCount: Int? = nil, - fileLocalPath: String? = nil, - showMergeCommits: Bool = false - ) async throws -> [GitCommit] { - let branchString = branchName != nil ? "\"\(branchName ?? "")\"" : "" - let fileString = fileLocalPath != nil ? "\"\(fileLocalPath ?? "")\"" : "" - let countString = maxCount != nil ? "-n \(maxCount ?? 0)" : "" - - let dateFormatter = DateFormatter() - - // Can't use `Locale.current`, since it'd give a nil date outside the US - dateFormatter.locale = Locale(identifier: Locale.current.identifier) - dateFormatter.dateFormat = "EEE, dd MMM yyyy HH:mm:ss Z" - - let output = try await run( - """ - log \(showMergeCommits ? "" : "--no-merges") -z \ - --pretty=%h¦%H¦%s¦%aN¦%ae¦%cn¦%ce¦%aD¦%b¦%D¦ \ - \(countString) \(branchString) -- \(fileString) - """.trimmingCharacters(in: .whitespacesAndNewlines) - ) - let remoteURL = try await getRemoteURL() - - return output - .split(separator: "\0") - .map { line -> GitCommit in - let parameters = String(line).components(separatedBy: "¦") - let infoRef = parameters[safe: 9] - var refs: [String] = [] - var tag = "" - if let infoRef = infoRef { - if infoRef.contains("tag:") { - tag = infoRef.components(separatedBy: "tag:")[1].trimmingCharacters(in: .whitespaces) - } else { - refs = infoRef.split(separator: ",").compactMap { - var element = String($0) - if element.contains("origin/HEAD") { return nil } - if element.contains("HEAD -> ") { - element = element.replacingOccurrences(of: "HEAD -> ", with: "") - } - return element.trimmingCharacters(in: .whitespaces) - } - } - } - - return GitCommit( - hash: parameters[safe: 0] ?? "", - commitHash: parameters[safe: 1] ?? "", - message: parameters[safe: 2] ?? "", - author: parameters[safe: 3] ?? "", - authorEmail: parameters[safe: 4] ?? "", - committer: parameters[safe: 5] ?? "", - committerEmail: parameters[safe: 6] ?? "", - body: parameters[safe: 8] ?? "", - refs: refs, - tag: tag, - remoteURL: remoteURL, - date: dateFormatter.date(from: parameters[safe: 7] ?? "") ?? Date() - ) - } - } -} diff --git a/CodeEdit/Features/SourceControl/Client/GitClient+Stash.swift b/CodeEdit/Features/SourceControl/Client/GitClient+Stash.swift deleted file mode 100644 index 69a0a8211..000000000 --- a/CodeEdit/Features/SourceControl/Client/GitClient+Stash.swift +++ /dev/null @@ -1,74 +0,0 @@ -// -// GitClient+Stash.swift -// CodeEdit -// -// Created by Austin Condiff on 11/20/23. -// - -import Foundation - -extension GitClient { - /// Add uncommited changes to stash - func stash(message: String?) async throws { - let command = message != nil ? "stash save --message=\"\(message ?? "")\"" : "stash" - - _ = try await self.run(command) - } - - /// Pops the latest entry from stash onto HEAD - func stashPop() async throws { - let command = "stash pop" - - _ = try await self.run(command) - } - - /// Lists all of the entries in stash - func stashList() async throws -> [GitStashEntry] { - let command = "stash list --date=local" - let output = try await run(command) - let stashEntries = parseGitStashEntries(output) - - return stashEntries - } - - /// Apply stash - func applyStashEntry(_ index: Int?) async throws { - if let idx = index { - _ = try await run("stash apply stash@{\(idx)}") - } else { - _ = try await run("stash apply") - } - } - - /// Delete stash - func deleteStashEntry(_ index: Int) async throws { - _ = try await run("stash drop stash@{\(index)}") - } -} - -func parseGitStashEntries(_ input: String) -> [GitStashEntry] { - var entries: [GitStashEntry] = [] - - let trimmedInput = input.trimmingCharacters(in: .whitespacesAndNewlines) - let lines = trimmedInput.split(separator: "\n", omittingEmptySubsequences: false) - - let dateFormatter = DateFormatter() - dateFormatter.locale = Locale.current - dateFormatter.dateFormat = "EEE MMM d HH:mm:ss yyyy" - - for (index, line) in lines.enumerated() { - let components = line.split(separator: ": ", maxSplits: 2, omittingEmptySubsequences: true) - guard components.count >= 3 else { continue } - - let dateString = String(components[0].replacingOccurrences(of: "stash@{", with: "").dropLast()) - guard let date = dateFormatter.date(from: dateString) else { continue } - - // Re-join the remaining parts of the message (if there are colons in the message) - let message = components[2...].joined(separator: ": ").trimmingCharacters(in: .whitespaces) - - let entry = GitStashEntry(index: index, message: message, date: date) - entries.append(entry) - } - - return entries -} diff --git a/CodeEdit/Features/SourceControl/Models/GitCommit.swift b/CodeEdit/Features/SourceControl/Models/GitCommit.swift deleted file mode 100644 index b0195b9f3..000000000 --- a/CodeEdit/Features/SourceControl/Models/GitCommit.swift +++ /dev/null @@ -1,68 +0,0 @@ -// -// GitCommit.swift -// CodeEditModules/Git -// -// Created by Marco Carnevali on 27/03/22. -// - -import Foundation.NSDate - -/// Model class to help map commit history log data -struct GitCommit: Equatable, Hashable, Identifiable { - var id = UUID() - let hash: String - let commitHash: String - let message: String - let author: String - let authorEmail: String - let committer: String - let committerEmail: String - let body: String - let refs: [String] - let tag: String - let remoteURL: URL? - let date: Date - - var commitBaseURL: URL? { - if let remoteURL { - if remoteURL.absoluteString.contains("github") { - return parsedRemoteUrl(domain: "https://github.com", remote: remoteURL) - } - if remoteURL.absoluteString.contains("bitbucket") { - return parsedRemoteUrl(domain: "https://bitbucket.org", remote: remoteURL) - } - if remoteURL.absoluteString.contains("gitlab") { - return parsedRemoteUrl(domain: "https://gitlab.com", remote: remoteURL) - } - // TODO: Implement other git clients other than github, bitbucket here - } - return nil - } - - private func parsedRemoteUrl(domain: String, remote: URL) -> URL { - // There are 2 types of remotes - https and ssh. While https has URL in its name, ssh doesn't. - // Following code takes remote name in format profileName/repoName and prepends according domain - var formattedRemote = remote - if formattedRemote.absoluteString.starts(with: "git@") { - let parts = formattedRemote.absoluteString.components(separatedBy: ":") - formattedRemote = URL.init(fileURLWithPath: "\(domain)/\(parts[parts.count - 1])") - } - - return formattedRemote.deletingPathExtension().appendingPathComponent("commit") - } - - var remoteString: String { - if let remoteURL { - if remoteURL.absoluteString.contains("github") { - return "GitHub" - } - if remoteURL.absoluteString.contains("bitbucket") { - return "BitBucket" - } - if remoteURL.absoluteString.contains("gitlab") { - return "GitLab" - } - } - return "Remote" - } -} diff --git a/CodeEdit/Features/SourceControl/Models/GitStashEntry.swift b/CodeEdit/Features/SourceControl/Models/GitStashEntry.swift deleted file mode 100644 index cbec68520..000000000 --- a/CodeEdit/Features/SourceControl/Models/GitStashEntry.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// GitStashEntry.swift -// CodeEdit -// -// Created by Austin Condiff on 11/20/23. -// - -import Foundation - -struct GitStashEntry: Hashable { - let index: Int - let message: String - let date: Date -} diff --git a/CodeEdit/Features/SourceControl/SourceControlManager+GitClient.swift b/CodeEdit/Features/SourceControl/SourceControlManager+GitClient.swift index fe9af2d7c..86ac8bb5c 100644 --- a/CodeEdit/Features/SourceControl/SourceControlManager+GitClient.swift +++ b/CodeEdit/Features/SourceControl/SourceControlManager+GitClient.swift @@ -6,6 +6,7 @@ // import Foundation +import SwiftGitX extension SourceControlManager { /// Validate repository @@ -69,21 +70,31 @@ extension SourceControlManager { } /// Delete stash entry - func deleteStashEntry(stashEntry: GitStashEntry) async throws { - try await gitClient.deleteStashEntry(stashEntry.index) + func deleteStashEntry(stashEntry: StashEntry) async throws { + guard let repository else { return } + + try repository.stash.drop(stashEntry) + try await refreshStashEntries() } /// Apply stash entry - func applyStashEntry(stashEntry: GitStashEntry) async throws { - try await gitClient.applyStashEntry(stashEntry.index) + func applyStashEntry(stashEntry: StashEntry) async throws { + guard let repository else { return } + + try repository.stash.apply(stashEntry) + try await refreshStashEntries() await refreshAllChangedFiles() } + // TODO: Add stash options /// Stash changes func stashChanges(message: String?) async throws { - try await gitClient.stash(message: message) + guard let repository else { return } + + try repository.stash.save(message: message) + try await refreshStashEntries() await refreshAllChangedFiles() } @@ -188,7 +199,10 @@ extension SourceControlManager { /// Commit files selected by user func commit(message: String, details: String? = nil) async throws { - try await gitClient.commit(message: message, details: details) + guard let repository else { return } + + let message = if let details { "\(message)\n\n\(details)" } else { message } + try repository.commit(message: message) await self.refreshAllChangedFiles() await self.refreshNumberOfUnsyncedCommits() @@ -197,13 +211,15 @@ extension SourceControlManager { /// Adds the given URLs to the staged changes. /// - Parameter files: The files to stage. func add(_ files: [URL]) async throws { - try await gitClient.add(files) + guard let repository else { return } + try repository.add(files: files) } /// Removes the given URLs from the staged changes. /// - Parameter files: The URLs to un-stage. func reset(_ files: [URL]) async throws { - try await gitClient.reset(files) + guard let repository else { return } + try repository.restore(.staged, files: files) } /// Refresh number of unsynced commits @@ -256,7 +272,9 @@ extension SourceControlManager { } func refreshStashEntries() async throws { - let stashEntries = (try? await gitClient.stashList()) ?? [] + guard let repository else { return } + + let stashEntries = try repository.stash.list() await MainActor.run { self.stashEntries = stashEntries } diff --git a/CodeEdit/Features/SourceControl/SourceControlManager.swift b/CodeEdit/Features/SourceControl/SourceControlManager.swift index 9a42cf7f1..090ab0897 100644 --- a/CodeEdit/Features/SourceControl/SourceControlManager.swift +++ b/CodeEdit/Features/SourceControl/SourceControlManager.swift @@ -8,6 +8,7 @@ import Foundation import AppKit import OSLog +import SwiftGitX /// This class is used to perform git functions such as fetch, pull, add/remove of changes, commit, push, etc. /// It also stores remotes, branches, current changes, stashes, and commits @@ -19,6 +20,9 @@ final class SourceControlManager: ObservableObject { /// The base URL of the workspace let workspaceURL: URL + /// The repository in the workspace + let repository: Repository? + let editorManager: EditorManager weak var fileManager: CEWorkspaceFileManager? @@ -35,7 +39,7 @@ final class SourceControlManager: ObservableObject { @Published var remotes: [GitRemote] = [] /// All stashed entries - @Published var stashEntries: [GitStashEntry] = [] + @Published var stashEntries: [StashEntry] = [] /// Number of unsynced commits with remote in current branch @Published var numberOfUnsyncedCommits: (ahead: Int, behind: Int) = (ahead: 0, behind: 0) @@ -119,7 +123,9 @@ final class SourceControlManager: ObservableObject { ) { self.workspaceURL = workspaceURL self.editorManager = editorManager + gitClient = GitClient(directoryURL: workspaceURL, shellClient: currentWorld.shellClient) + repository = try? Repository.open(at: workspaceURL) } /// Show alert for error