Add support for bare repos, ability to stash unsaved changes.

This commit is contained in:
Jonathan Bernard 2017-08-16 11:35:12 -05:00
parent fed862e9d5
commit 2538b357f9
2 changed files with 79 additions and 37 deletions

View File

@ -24,8 +24,12 @@ proc `$`(buf: OutputBuffer): string =
buf.outBuf.join("\n") & "\n" & buf.errBuf.join("\n") buf.outBuf.join("\n") & "\n" & buf.errBuf.join("\n")
proc git(repoDir: string, args: openArray[string]): bool = proc git(repoDir: string, args: openArray[string]): bool =
let res = exec("git", repoDir, args, env, {poUsePath}, outputHandler) if verbose:
return res.exitCode == 0 stdout.setForegroundColor(fgBlue, false)
stdout.writeLine("> git " & toSeq(args.items).mapIt("'" & it & "'").join(" "))
stdout.resetAttributes()
return exec("git", repoDir, args, env, {poUsePath}, outputHandler) == 0
proc isGitRepo(dir: string): bool = proc isGitRepo(dir: string): bool =
if not existsDir(dir): return false if not existsDir(dir): return false
@ -60,9 +64,11 @@ Options:
-V --version Print version -V --version Print version
-r --remote <remote> Pull from <remote> (defaults to "origin") -r --remote <remote> Pull from <remote> (defaults to "origin")
-b --branch <branch> Pull the <branch> branch (defaults to "master") -b --branch <branch> Pull the <branch> branch (defaults to "master")
-o --log-out <outlog> Log command output to <outfile>
-e --log-err <errlog> Log error output to <errfile>
""" """
let args = docopt(doc, version = "git_pull_all 0.1.0") let args = docopt(doc, version = "git_pull_all 0.2.0")
let rootDirs: seq[string] = let rootDirs: seq[string] =
if args["<root>"]: args["<root>"].mapIt(it) if args["<root>"]: args["<root>"].mapIt(it)
@ -88,9 +94,19 @@ Options:
outputHandler = combineProcMsgHandlers(outputHandler, outputHandler = combineProcMsgHandlers(outputHandler,
makeProcMsgHandler(stdout, stderr)) makeProcMsgHandler(stdout, stderr))
var outLog, errLog: File = nil
if args["--log-out"]: outLog = open($args["<outlog>"], fmRead)
if args["--log-err"]: errLog = open($args["<errlog>"], fmRead)
if outLog != nil or errLog != nil:
outputHandler = combineProcMsgHandlers(outputHandler,
makeProcMsgHandler(outLog, errLog))
# Foreach repo: # Foreach repo:
for repoDir in repos: for repoDir in repos:
let repoName = repoDir.extractFilename let repoName = repoDir.extractFilename
if verbose: stdout.writeLine("")
stdout.setForegroundColor(fgCyan, true) stdout.setForegroundColor(fgCyan, true)
stdout.write("Pulling ") stdout.write("Pulling ")
stdout.setForegroundColor(fgYellow, false) stdout.setForegroundColor(fgYellow, false)
@ -102,36 +118,69 @@ Options:
failRepos.add((repoName, reason)) failRepos.add((repoName, reason))
writeErrMsg(reason) writeErrMsg(reason)
# Is the repo clean? No -> fail to pull this repo var pullOutput: seq[string]
# Is the a bare repo clean?
cmdOutput.clear() cmdOutput.clear()
if not git(repoDir, ["status"]) or if not git(repoDir, ["status"]):
cmdOutput.outBuf.join("\n").find("working tree clean") < 0:
failRepo("working directory is not clean")
continue
# Are we on the correct branch? # pull --ff-only
cmdOutput.clear() if not git(repoDir, ["pull", "--ff-only", remote, branch]):
if not git(repoDir, ["branch"]): failRepo("unable to ffwd branch")
failRepo("could not get current branch")
continue
let branches = cmdOutput.outBuf.filterIt(it.find("* ") > 0)
let currentBranch =
if branches.len == 1: branches[0][7..^1]
else: nil
# not on correct branch, switch branch
if currentBranch != branch:
if not git(repoDir, ["checkout", branch]):
failRepo("could not check out " & branch & " branch")
continue continue
# pull --ff-only pullOutput = cmdOutput.outBuf
if not git(repoDir, ["pull", "--ff-only", remote, branch]):
failRepo("unable to ffwd branch")
continue
if cmdOutput.outBuf.anyIt(it.find("Already up-to-date") > 0): # Not bare
else:
# Not clean? Try to stash the changes.
var stashed = false
if cmdOutput.outBuf.join("\n").find("working tree clean") < 0:
cmdOutput.clear()
if not git(repoDir, ["stash", "save"]):
failRepo("error trying to stash uncommitted changes")
continue
else: stashed = true
# Are we on the correct branch?
cmdOutput.clear()
if not git(repoDir, ["branch"]):
failRepo("could not get current branch")
continue
let branches = cmdOutput.outBuf.filterIt(it.find("* ") > 0)
let currentBranch =
if branches.len == 1: branches[0][7..^1]
else: nil
# not on correct branch, switch branch
if currentBranch != branch:
if not git(repoDir, ["checkout", branch]):
failRepo("could not check out " & branch & " branch")
continue
# pull --ff-only
if not git(repoDir, ["pull", "--ff-only", remote, branch]):
failRepo("unable to ffwd branch")
continue
pullOutput = cmdOutput.outBuf
# restore original branch
if currentBranch != branch:
if not git(repoDir, ["checkout", currentBranch]):
stdout.setForegroundColor(fgWhite, true)
stdout.writeLine("WARNING: unable to checkout original branch (" & currentBranch & ")")
stdout.resetAttributes()
# restore stashed changes
if stashed:
if not git(repoDir, ["stash", "pop"]):
stdout.setForegroundColor(fgWhite, true)
stdout.writeLine("WARNING: unable to pop stashed changes")
stdout.resetAttributes()
if pullOutput.anyIt(it.find("Already up-to-date") > 0):
stdout.setForegroundColor(fgBlack, true) stdout.setForegroundColor(fgBlack, true)
stdout.writeLine("already up-to-date") stdout.writeLine("already up-to-date")
else: else:
@ -140,12 +189,5 @@ Options:
stdout.resetAttributes() stdout.resetAttributes()
successRepos.add(repoName) successRepos.add(repoName)
# restore original branch
if currentBranch != branch:
if not git(repoDir, ["checkout", currentBranch]):
stdout.setForegroundColor(fgWhite, true)
stdout.writeLine("WARNING: unable to checkout original branch (" & currentBranch & ")")
stdout.resetAttributes()
except: except:
stderr.writeLine "git_pull_all: " & getCurrentExceptionMsg() stderr.writeLine "git_pull_all: " & getCurrentExceptionMsg()

View File

@ -1,6 +1,6 @@
# Package # Package
version = "0.1.0" version = "0.2.0"
author = "Jonathan Bernard" author = "Jonathan Bernard"
description = "Small CLI utility to pull multiple git repos." description = "Small CLI utility to pull multiple git repos."
license = "MIT" license = "MIT"
@ -10,4 +10,4 @@ bin = @["git_pull_all"]
requires @["nim >= 0.16.1", "docopt >= 0.6.4"] requires @["nim >= 0.16.1", "docopt >= 0.6.4"]
requires "https://git.jdb-labs.com/jdb/nim-cli-utils.git" requires "https://git.jdb-labs.com/jdb/nim-cli-utils.git >= 0.3.0"