From eba79da0e606d99be4b077a626c5cd3e5a8ea695 Mon Sep 17 00:00:00 2001 From: Bob Sun Date: Sun, 30 Apr 2017 16:16:52 -0500 Subject: [PATCH] Polish and simplify PasswordStore model class --- pass.xcodeproj/project.pbxproj | 8 + pass/AppError.swift | 35 +++ .../CommitLogsTableViewController.swift | 15 +- pass/Controllers/GitCredential.swift | 107 ++++++++ pass/Models/PasswordStore.swift | 233 +++++------------- 5 files changed, 230 insertions(+), 168 deletions(-) create mode 100644 pass/AppError.swift create mode 100644 pass/Controllers/GitCredential.swift diff --git a/pass.xcodeproj/project.pbxproj b/pass.xcodeproj/project.pbxproj index 7652f77..4f384b7 100644 --- a/pass.xcodeproj/project.pbxproj +++ b/pass.xcodeproj/project.pbxproj @@ -59,6 +59,8 @@ DCC408C71E307DBB00F29B0E /* SVProgressHUD.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCC408C61E307DBB00F29B0E /* SVProgressHUD.framework */; }; DCC441521E8F6C06008A90C4 /* RawPasswordViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCC441511E8F6C06008A90C4 /* RawPasswordViewController.swift */; }; DCC441541E916382008A90C4 /* GitSSHKeyArmorSettingTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCC441531E916382008A90C4 /* GitSSHKeyArmorSettingTableViewController.swift */; }; + DCD9AD131EB678500093499A /* GitCredential.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCD9AD121EB678500093499A /* GitCredential.swift */; }; + DCD9AD151EB6829A0093499A /* AppError.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCD9AD141EB6829A0093499A /* AppError.swift */; }; DCDDEAB01E4639F300F68193 /* LabelTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = DCDDEAAF1E4639F300F68193 /* LabelTableViewCell.xib */; }; DCDDEAB31E4896BF00F68193 /* PasswordDetailTitleTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCDDEAB11E4896BF00F68193 /* PasswordDetailTitleTableViewCell.swift */; }; DCFB779A1E4F3BCF008DE471 /* TitleTextFieldTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCFB77981E4F3BCF008DE471 /* TitleTextFieldTableViewCell.swift */; }; @@ -142,6 +144,8 @@ DCC408C91E30BA1300F29B0E /* pass.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = pass.xcdatamodel; sourceTree = ""; }; DCC441511E8F6C06008A90C4 /* RawPasswordViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RawPasswordViewController.swift; sourceTree = ""; }; DCC441531E916382008A90C4 /* GitSSHKeyArmorSettingTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitSSHKeyArmorSettingTableViewController.swift; sourceTree = ""; }; + DCD9AD121EB678500093499A /* GitCredential.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GitCredential.swift; path = pass/Controllers/GitCredential.swift; sourceTree = SOURCE_ROOT; }; + DCD9AD141EB6829A0093499A /* AppError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppError.swift; sourceTree = ""; }; DCDDEAAF1E4639F300F68193 /* LabelTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LabelTableViewCell.xib; sourceTree = ""; }; DCDDEAB11E4896BF00F68193 /* PasswordDetailTitleTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasswordDetailTitleTableViewCell.swift; sourceTree = ""; }; DCFB77981E4F3BCF008DE471 /* TitleTextFieldTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TitleTextFieldTableViewCell.swift; sourceTree = ""; }; @@ -238,6 +242,7 @@ DCC408A31E2FCC9E00F29B0E /* PasswordStore.swift */, DC193FFF1E49E1A60077E0A3 /* PasscodeLockConfiguration.swift */, DC193FFD1E49E0760077E0A3 /* PasscodeLockRepository.swift */, + DCD9AD121EB678500093499A /* GitCredential.swift */, ); path = Models; sourceTree = ""; @@ -302,6 +307,7 @@ children = ( DC917BE21E2E8231000FDF54 /* Info.plist */, DC917BD61E2E8231000FDF54 /* AppDelegate.swift */, + DCD9AD141EB6829A0093499A /* AppError.swift */, DC19400D1E4B3A340077E0A3 /* Models */, DC19400C1E4B39400077E0A3 /* Controllers */, DC19400F1E4B3A9E0077E0A3 /* Views */, @@ -526,6 +532,7 @@ files = ( DCC408A41E2FCC9E00F29B0E /* PasswordStore.swift in Sources */, DC037CBF1E4ED4E100609409 /* TextViewTableViewCell.swift in Sources */, + DCD9AD151EB6829A0093499A /* AppError.swift in Sources */, DCC441541E916382008A90C4 /* GitSSHKeyArmorSettingTableViewController.swift in Sources */, DC8963C01E38EEB900828B09 /* SSHKeySettingTableViewController.swift in Sources */, DC193FFA1E49B4430077E0A3 /* AdvancedSettingsTableViewController.swift in Sources */, @@ -545,6 +552,7 @@ DC4914991E434600007FF592 /* PasswordDetailTableViewController.swift in Sources */, DC962CDF1E4B62C10033B5D8 /* AboutTableViewController.swift in Sources */, DC5734AE1E439AD400D09270 /* PasswordsViewController.swift in Sources */, + DCD9AD131EB678500093499A /* GitCredential.swift in Sources */, DC3E64E61E656F11009A83DE /* CommitLogsTableViewController.swift in Sources */, DC037CAA1E4B8EAE00609409 /* SpecialThanksTableViewController.swift in Sources */, DC037CA61E4B883900609409 /* OpenSourceComponentsTableViewController.swift in Sources */, diff --git a/pass/AppError.swift b/pass/AppError.swift new file mode 100644 index 0000000..34a7886 --- /dev/null +++ b/pass/AppError.swift @@ -0,0 +1,35 @@ +// +// AppError.swift +// pass +// +// Created by Mingshen Sun on 30/4/2017. +// Copyright © 2017 Bob Sun. All rights reserved. +// + +import Foundation + +enum AppError: Error { + case RepositoryNotSetError + case RepositoryRemoteMasterNotFoundError + case KeyImportError + case PasswordDuplicatedError + case GitResetError + case UnknownError + + var localizedDescription: String { + switch self { + case .RepositoryNotSetError: + return "Git repository is not set." + case .RepositoryRemoteMasterNotFoundError: + return "Cannot find remote branch origin/master." + case .KeyImportError: + return "Cannot import the key." + case .PasswordDuplicatedError: + return "Cannot add the password: password duplicated." + case .GitResetError: + return "Cannot decide how to reset." + case .UnknownError: + return "Unknown error." + } + } +} diff --git a/pass/Controllers/CommitLogsTableViewController.swift b/pass/Controllers/CommitLogsTableViewController.swift index 00f0b1d..dbe90ce 100644 --- a/pass/Controllers/CommitLogsTableViewController.swift +++ b/pass/Controllers/CommitLogsTableViewController.swift @@ -16,7 +16,7 @@ class CommitLogsTableViewController: UITableViewController { override func viewDidLoad() { super.viewDidLoad() NotificationCenter.default.addObserver(self, selector: #selector(updateCommitLogs), name: .passwordStoreUpdated, object: nil) - commits = passwordStore.getRecentCommits(count: 20) + commits = getCommitLogs() self.tableView.estimatedRowHeight = 50 self.tableView.rowHeight = UITableViewAutomaticDimension } @@ -41,8 +41,17 @@ class CommitLogsTableViewController: UITableViewController { return cell } - func updateCommitLogs () { - commits = passwordStore.getRecentCommits(count: 20) + func updateCommitLogs() { + commits = getCommitLogs() tableView.reloadData() } + + private func getCommitLogs() -> [GTCommit] { + do { + return try passwordStore.getRecentCommits(count: 20) + } catch { + Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil) + return [] + } + } } diff --git a/pass/Controllers/GitCredential.swift b/pass/Controllers/GitCredential.swift new file mode 100644 index 0000000..a114658 --- /dev/null +++ b/pass/Controllers/GitCredential.swift @@ -0,0 +1,107 @@ +// +// GitCredential.swift +// pass +// +// Created by Mingshen Sun on 30/4/2017. +// Copyright © 2017 Bob Sun. All rights reserved. +// + +import Foundation +import UIKit +import SwiftyUserDefaults +import ObjectiveGit +import SVProgressHUD + +struct GitCredential { + var credential: Credential + + enum Credential { + case http(userName: String, controller: UIViewController) + case ssh(userName: String, publicKeyFile: URL, privateKeyFile: URL, controller: UIViewController) + } + + init(credential: Credential) { + self.credential = credential + } + + func credentialProvider() throws -> GTCredentialProvider { + var attempts = 0 + var lastPassword: String? = nil + return GTCredentialProvider { (_, _, _) -> (GTCredential?) in + var credential: GTCredential? = nil + + switch self.credential { + case let .http(userName, controller): + var newPassword = Utils.getPasswordFromKeychain(name: "gitPassword") + if newPassword == nil || attempts != 0 { + if let requestedPassword = self.requestGitPassword(controller, lastPassword) { + newPassword = requestedPassword + Utils.addPasswordToKeychain(name: "gitPassword", password: newPassword) + } else { + return nil + } + } + attempts += 1 + lastPassword = newPassword + credential = try? GTCredential(userName: userName, password: newPassword!) + case let .ssh(userName, publicKeyFile, privateKeyFile, controller): + var newPassword = Utils.getPasswordFromKeychain(name: "gitSSHKeyPassphrase") + if newPassword == nil || attempts != 0 { + if let requestedPassword = self.requestGitPassword(controller, lastPassword) { + newPassword = requestedPassword + Utils.addPasswordToKeychain(name: "gitSSHKeyPassphrase", password: newPassword) + } else { + return nil + } + } + attempts += 1 + lastPassword = newPassword + credential = try? GTCredential(userName: userName, publicKeyURL: publicKeyFile, privateKeyURL: privateKeyFile, passphrase: newPassword!) + } + return credential + } + } + + func delete() { + switch credential { + case .http: + Utils.removeKeychain(name: "gitPassword") + case .ssh: + Utils.removeKeychain(name: "gitSSHKeyPassphrase") + } + } + + private func requestGitPassword(_ controller: UIViewController, _ lastPassword: String?) -> String? { + let sem = DispatchSemaphore(value: 0) + var password: String? + var message = "" + switch credential { + case .http: + message = "Please fill in the password of your Git account." + case .ssh: + message = "Please fill in the password of your SSH key." + } + + DispatchQueue.main.async { + SVProgressHUD.dismiss() + let alert = UIAlertController(title: "Password", message: message, preferredStyle: UIAlertControllerStyle.alert) + alert.addTextField(configurationHandler: {(textField: UITextField!) in + textField.text = lastPassword ?? "" + textField.isSecureTextEntry = true + }) + alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in + password = alert.textFields!.first!.text + sem.signal() + })) + alert.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in + password = nil + sem.signal() + }) + controller.present(alert, animated: true, completion: nil) + } + + let _ = sem.wait(timeout: .distantFuture) + return password + } +} + diff --git a/pass/Models/PasswordStore.swift b/pass/Models/PasswordStore.swift index a80ef27..a545963 100644 --- a/pass/Models/PasswordStore.swift +++ b/pass/Models/PasswordStore.swift @@ -13,99 +13,6 @@ import SwiftyUserDefaults import ObjectiveGit import SVProgressHUD -struct GitCredential { - var credential: Credential - - enum Credential { - case http(userName: String, controller: UIViewController) - case ssh(userName: String, publicKeyFile: URL, privateKeyFile: URL, controller: UIViewController) - } - - init(credential: Credential) { - self.credential = credential - } - - func credentialProvider() throws -> GTCredentialProvider { - var attempts = 0 - var lastPassword: String? = nil - return GTCredentialProvider { (_, _, _) -> (GTCredential?) in - var credential: GTCredential? = nil - - switch self.credential { - case let .http(userName, controller): - var newPassword = Utils.getPasswordFromKeychain(name: "gitPassword") - if newPassword == nil || attempts != 0 { - if let requestedPassword = self.requestGitPassword(controller, lastPassword) { - newPassword = requestedPassword - Utils.addPasswordToKeychain(name: "gitPassword", password: newPassword) - } else { - return nil - } - } - attempts += 1 - lastPassword = newPassword - credential = try? GTCredential(userName: userName, password: newPassword!) - case let .ssh(userName, publicKeyFile, privateKeyFile, controller): - var newPassword = Utils.getPasswordFromKeychain(name: "gitSSHKeyPassphrase") - if newPassword == nil || attempts != 0 { - if let requestedPassword = self.requestGitPassword(controller, lastPassword) { - newPassword = requestedPassword - Utils.addPasswordToKeychain(name: "gitSSHKeyPassphrase", password: newPassword) - } else { - return nil - } - } - attempts += 1 - lastPassword = newPassword - credential = try? GTCredential(userName: userName, publicKeyURL: publicKeyFile, privateKeyURL: privateKeyFile, passphrase: newPassword!) - } - return credential - } - } - - func delete() { - switch credential { - case .http: - Utils.removeKeychain(name: "gitPassword") - case .ssh: - Utils.removeKeychain(name: "gitSSHKeyPassphrase") - } - } - - private func requestGitPassword(_ controller: UIViewController, _ lastPassword: String?) -> String? { - let sem = DispatchSemaphore(value: 0) - var password: String? - var message = "" - switch credential { - case .http: - message = "Please fill in the password of your Git account." - case .ssh: - message = "Please fill in the password of your SSH key." - } - - DispatchQueue.main.async { - SVProgressHUD.dismiss() - let alert = UIAlertController(title: "Password", message: message, preferredStyle: UIAlertControllerStyle.alert) - alert.addTextField(configurationHandler: {(textField: UITextField!) in - textField.text = lastPassword ?? "" - textField.isSecureTextEntry = true - }) - alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in - password = alert.textFields!.first!.text - sem.signal() - })) - alert.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in - password = nil - sem.signal() - }) - controller.present(alert, animated: true, completion: nil) - } - - let _ = sem.wait(timeout: .distantFuture) - return password - } -} - class PasswordStore { static let shared = PasswordStore() let storeURL = URL(fileURLWithPath: "\(Globals.repositoryPath)") @@ -223,16 +130,16 @@ class PasswordStore { let keyPath = Globals.pgpPublicKeyPath self.publicKey = importKey(from: keyPath) if self.publicKey == nil { - throw NSError(domain: "me.mssun.pass.error", code: 2, userInfo: [NSLocalizedDescriptionKey: "Cannot import the public PGP key."]) + throw AppError.KeyImportError } case .secret: let keyPath = Globals.pgpPrivateKeyPath self.privateKey = importKey(from: keyPath) if self.privateKey == nil { - throw NSError(domain: "me.mssun.pass.error", code: 2, userInfo: [NSLocalizedDescriptionKey: "Cannot import the private PGP key."]) + throw AppError.KeyImportError } default: - throw NSError(domain: "me.mssun.pass.error", code: 2, userInfo: [NSLocalizedDescriptionKey: "Cannot import key: unknown PGP key type."]) + throw AppError.UnknownError } } @@ -329,9 +236,7 @@ class PasswordStore { Utils.removeFileIfExists(at: tempStoreURL) do { let credentialProvider = try credential.credentialProvider() - let options: [String: Any] = [ - GTRepositoryCloneOptionsCredentialProvider: credentialProvider, - ] + let options = [GTRepositoryCloneOptionsCredentialProvider: credentialProvider] storeRepository = try GTRepository.clone(from: remoteRepoURL, toWorkingDirectory: tempStoreURL, options: options, transferProgressBlock:transferProgressBlock) let fm = FileManager.default if fm.fileExists(atPath: storeURL.path) { @@ -352,16 +257,14 @@ class PasswordStore { } func pullRepository(credential: GitCredential, transferProgressBlock: @escaping (UnsafePointer, UnsafeMutablePointer) -> Void) throws { - if storeRepository == nil { - throw NSError(domain: "me.mssun.pass.error", code: 1, userInfo: [NSLocalizedDescriptionKey: "Git Repository is not set."]) + guard let repository = storeRepository else { + throw AppError.RepositoryNotSetError } do { let credentialProvider = try credential.credentialProvider() - let options: [String: Any] = [ - GTRepositoryRemoteOptionsCredentialProvider: credentialProvider - ] + let options = [GTRepositoryRemoteOptionsCredentialProvider: credentialProvider] let remote = try GTRemote(name: "origin", in: storeRepository!) - try storeRepository!.pull((storeRepository?.currentBranch())!, from: remote, withOptions: options, progress: transferProgressBlock) + try repository.pull(repository.currentBranch(), from: remote, withOptions: options, progress: transferProgressBlock) } catch { credential.delete() throw(error) @@ -429,21 +332,18 @@ class PasswordStore { } } - func getRecentCommits(count: Int) -> [GTCommit] { - guard storeRepository != nil else { + func getRecentCommits(count: Int) throws -> [GTCommit] { + guard let repository = storeRepository else { return [] } var commits = [GTCommit]() - do { - let enumerator = try GTEnumerator(repository: storeRepository!) - try enumerator.pushSHA(storeRepository!.headReference().targetOID.sha!) - for _ in 0 ..< count { - let commit = try enumerator.nextObject(withSuccess: nil) - commits.append(commit) - } - } catch { - print(error) - return commits + let enumerator = try GTEnumerator(repository: repository) + if let sha = try repository.headReference().targetOID.sha { + try enumerator.pushSHA(sha) + } + for _ in 0 ..< count { + let commit = try enumerator.nextObject(withSuccess: nil) + commits.append(commit) } return commits } @@ -464,7 +364,6 @@ class PasswordStore { do { if !withDir { passwordEntityFetch.predicate = NSPredicate(format: "isDir = false") - } let fetchedPasswordEntities = try context.fetch(passwordEntityFetch) as! [PasswordEntity] return fetchedPasswordEntities.sorted { $0.name!.caseInsensitiveCompare($1.name!) == .orderedAscending } @@ -511,11 +410,14 @@ class PasswordStore { func getLatestUpdateInfo(filename: String) -> String { - guard let blameHunks = try? storeRepository?.blame(withFile: filename, options: nil).hunks, - let latestCommitTime = blameHunks?.map({ + guard let repository = storeRepository else { + return "Unknown" + } + guard let blameHunks = try? repository.blame(withFile: filename, options: nil).hunks, + let latestCommitTime = blameHunks.map({ $0.finalSignature?.time?.timeIntervalSince1970 ?? 0 }).max() else { - return "unknown" + return "Unknown" } let lastCommitDate = Date(timeIntervalSince1970: latestCommitTime) let currentDate = Date() @@ -528,7 +430,7 @@ class PasswordStore { dateComponentsFormatter.unitsStyle = .full dateComponentsFormatter.maximumUnitCount = 2 dateComponentsFormatter.includesApproximationPhrase = true - autoFormattedDifference = (dateComponentsFormatter.string(from: diffDate)?.appending(" ago"))! + autoFormattedDifference = dateComponentsFormatter.string(from: diffDate)!.appending(" ago") } return autoFormattedDifference } @@ -537,21 +439,22 @@ class PasswordStore { } private func gitAdd(path: String) throws { - if let repo = storeRepository { - try repo.index().addFile(path) - try repo.index().write() + guard let repository = storeRepository else { + throw AppError.RepositoryNotSetError } + try repository.index().addFile(path) + try repository.index().write() } private func gitRm(path: String) throws { - if let repo = storeRepository { - if FileManager.default.fileExists(atPath: storeURL.appendingPathComponent(path).path) { - try FileManager.default.removeItem(at: storeURL.appendingPathComponent(path)) - } - try repo.index().removeFile(path) - try repo.index().write() + guard let repository = storeRepository else { + throw AppError.RepositoryNotSetError } - + if FileManager.default.fileExists(atPath: storeURL.appendingPathComponent(path).path) { + try FileManager.default.removeItem(at: storeURL.appendingPathComponent(path)) + } + try repository.index().removeFile(path) + try repository.index().write() } private func deleteDirectoryTree(at url: URL) throws { @@ -586,39 +489,39 @@ class PasswordStore { } private func gitCommit(message: String) throws -> GTCommit? { - if let repo = storeRepository { - let newTree = try repo.index().writeTree() - let headReference = try repo.headReference() - let commitEnum = try GTEnumerator(repository: repo) - try commitEnum.pushSHA(headReference.targetOID.sha!) - let parent = commitEnum.nextObject() as! GTCommit - let signature = gitSignatureForNow - let commit = try repo.createCommit(with: newTree, message: message, author: signature, committer: signature, parents: [parent], updatingReferenceNamed: headReference.name) - return commit + guard let repository = storeRepository else { + throw AppError.RepositoryNotSetError } - return nil + let newTree = try repository.index().writeTree() + let headReference = try repository.headReference() + let commitEnum = try GTEnumerator(repository: repository) + try commitEnum.pushSHA(headReference.targetOID.sha!) + let parent = commitEnum.nextObject() as! GTCommit + let signature = gitSignatureForNow + let commit = try repository.createCommit(with: newTree, message: message, author: signature, committer: signature, parents: [parent], updatingReferenceNamed: headReference.name) + return commit } - private func getLocalBranch(withName branchName: String) -> GTBranch? { - do { - let reference = GTBranch.localNamePrefix().appending(branchName) - let branches = try storeRepository!.branches(withPrefix: reference) - return branches[0] - } catch { - print(error) + private func getLocalBranch(withName branchName: String) throws -> GTBranch? { + guard let repository = storeRepository else { + throw AppError.RepositoryNotSetError } - return nil + let reference = GTBranch.localNamePrefix().appending(branchName) + let branches = try repository.branches(withPrefix: reference) + return branches.first } func pushRepository(credential: GitCredential, transferProgressBlock: @escaping (UInt32, UInt32, Int, UnsafeMutablePointer) -> Void) throws { + guard let repository = storeRepository else { + throw AppError.RepositoryNotSetError + } do { let credentialProvider = try credential.credentialProvider() - let options: [String: Any] = [ - GTRepositoryRemoteOptionsCredentialProvider: credentialProvider, - ] - let masterBranch = getLocalBranch(withName: "master")! - let remote = try GTRemote(name: "origin", in: storeRepository!) - try storeRepository?.push(masterBranch, to: remote, withOptions: options, progress: transferProgressBlock) + let options = [GTRepositoryRemoteOptionsCredentialProvider: credentialProvider] + if let masterBranch = try getLocalBranch(withName: "master") { + let remote = try GTRemote(name: "origin", in: repository) + try repository.push(masterBranch, to: remote, withOptions: options, progress: transferProgressBlock) + } } catch { credential.delete() throw(error) @@ -627,7 +530,7 @@ class PasswordStore { private func addPasswordEntities(password: Password) throws -> PasswordEntity? { guard !passwordExisted(password: password) else { - throw NSError(domain: "me.mssun.pass.error", code: 2, userInfo: [NSLocalizedDescriptionKey: "Cannot add password: password duplicated."]) + throw AppError.PasswordDuplicatedError } var passwordURL = password.url! @@ -815,7 +718,7 @@ class PasswordStore { guard let firstLocalCommit = localCommits.last, firstLocalCommit.parents.count == 1, let newHead = firstLocalCommit.parents.first else { - throw NSError(domain: "me.mssun.pass.error", code: 1, userInfo: [NSLocalizedDescriptionKey: "Cannot decide how to reset."]) + throw AppError.GitResetError } try self.storeRepository?.reset(to: newHead, resetType: GTRepositoryResetType.hard) self.setAllSynced() @@ -843,14 +746,14 @@ class PasswordStore { } private func getLocalCommits() throws -> [GTCommit]? { - // get the remote origin/master branch - guard let remoteBranches = try storeRepository?.remoteBranches(), - let index = remoteBranches.index(where: { $0.shortName == "master" }) - else { - throw NSError(domain: "me.mssun.pass.error", code: 1, userInfo: [NSLocalizedDescriptionKey: "Cannot find remote branch origin/master."]) + guard let repository = storeRepository else { + throw AppError.RepositoryNotSetError } - let remoteMasterBranch = remoteBranches[index] - //print("remoteMasterBranch \(remoteMasterBranch)") + // get the remote origin/master branch + guard let index = try repository.remoteBranches().index(where: { $0.shortName == "master" }) else { + throw AppError.RepositoryRemoteMasterNotFoundError + } + let remoteMasterBranch = try repository.remoteBranches()[index] // get a list of local commits return try storeRepository?.localCommitsRelative(toRemoteBranch: remoteMasterBranch)