Skip to content

Commit

Permalink
Group tmutil commands
Browse files Browse the repository at this point in the history
Pass multiple paths to `tmutil addexclusion` / `tmutil removeexclusion` commands
  • Loading branch information
samuelmeuli committed Oct 27, 2019
1 parent a5621e5 commit 5776a60
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 25 deletions.
62 changes: 50 additions & 12 deletions Sources/tmignore/TimeMachine.swift
Expand Up @@ -5,31 +5,69 @@ import Foundation
backups
*/
class TimeMachine {
/**
Takes the (potentially long) list of paths and splits it up into smaller chunks. Each chunk
is converted to a string of the form "'path1' 'path2' 'path3'" so these paths can be passed
as command arguments. The chunking is necessary because a size limit for shell commands
exists
*/
private static func buildArgStrs(paths: [String]) -> [String] {
let chunkSize = 200
var idx = 0
var argList = [String]()

while idx < paths.count {
let chunk = paths[idx..<min(idx + chunkSize, paths.count - 1)]
argList.append(chunk.map { "'\($0)'" }.joined(separator: " "))
idx += chunkSize
}

return argList
}

/**
Adds the provided path to the list of exclusions (it will not be included in future backups)
*/
static func addExclusion(path: String) {
let (status, _, stdErr) = runCommand("tmutil addexclusion '\(path)'")
if status == 0 {
logger.debug("Added Time Machine exclusion: \(path)")
} else {
logger.error("Failed to add Time Machine exclusion: \(stdErr ?? "")")
static func addExclusions(paths: [String]) {
if paths.isEmpty {
logger.info("No exclusions to add")
return
}

logger.info("Adding backup exclusions for \(paths.count) paths…")

for argStr in buildArgStrs(paths: paths) {
let (status, _, stdErr) = runCommand("tmutil addexclusion \(argStr)")
if status != 0 {
logger.error("Failed to add backup exclusions: \(stdErr ?? "")")
}
}

logger.info("Added backup exclusions for \(paths.count) paths")
}

/**
Removes the provided path from the list of exclusions (it will be included again in future
backups)
*/
static func removeExclusion(path: String) {
let (status, _, stdErr) = runCommand("tmutil removeexclusion '\(path)'")
if status == 0 || status == 213 {
static func removeExclusions(paths: [String]) {
if paths.isEmpty {
logger.info("No exclusions to remove")
return
}

logger.info("Removing backup exclusions for \(paths.count) paths…")

for argStr in buildArgStrs(paths: paths) {
let (status, _, stdErr) = runCommand("tmutil removeexclusion \(argStr)")
// 213: File path wasn't found and could therefore not be excluded from a Time Machine
// backup. This error occurs for cached exlusions which were deleted, therefore it is
// ignored
logger.debug("Removed Time Machine exclusion: \(path)")
} else {
logger.error("Failed to remove Time Machine exclusion: \(stdErr ?? "")")
if status != 0 && status != 213 {
logger.error("Failed to remove backup exclusions: \(stdErr ?? "")")
}
}

logger.info("Removed backup exclusions for \(paths.count) paths")
}
}
17 changes: 4 additions & 13 deletions Sources/tmignore/main.swift
Expand Up @@ -24,7 +24,7 @@ class RunCommand: Command {
let repoPaths = Git.findRepos(ignoredPaths: config.ignoredPaths)

// Build list of files/directories which should be excluded from Time Machine backups
logger.info("Applying whitelist")
logger.info("Building list of files to exclude from backups")
var exclusions = [String]()
for repoPath in repoPaths {
for path in Git.getIgnoredFiles(repoPath: repoPath) {
Expand All @@ -46,14 +46,8 @@ class RunCommand: Command {
) = getDiff(elementsV1: cachedExclusions, elementsV2: exclusions)

// Add/remove backup exclusions
logger.info("Excluding \(exclusionsToAdd.count) files/directories from future backups…")
for exclusionToAdd in exclusionsToAdd {
TimeMachine.addExclusion(path: exclusionToAdd)
}
logger.info("Removing \(exclusionsToRemove.count) backup exclusions…")
for exclusionToRemove in exclusionsToRemove {
TimeMachine.removeExclusion(path: exclusionToRemove)
}
TimeMachine.addExclusions(paths: exclusionsToAdd)
TimeMachine.removeExclusions(paths: exclusionsToRemove)

// Update cache file
cache.write(paths: exclusions)
Expand Down Expand Up @@ -89,10 +83,7 @@ class ResetCommand: Command {

// Parse all previously added exclusions from the cache file and undo those exclusions
let cachedExclusions = cache.read()
logger.info("Removing \(cachedExclusions.count) backup exclusions…")
for path in cachedExclusions {
TimeMachine.removeExclusion(path: path)
}
TimeMachine.removeExclusions(paths: cachedExclusions)

// Delete the cache directory
logger.info("Deleting the cache…")
Expand Down

0 comments on commit 5776a60

Please sign in to comment.