Created
November 22, 2018 13:01
-
-
Save peterekepeter/05f891eda5670c80472a69a5e2ded64f to your computer and use it in GitHub Desktop.
Node.js script that cleans up old branches in Git!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const { exec } = require('child_process'); | |
const readline = require('readline').createInterface({ | |
input: process.stdin, | |
output: process.stdout | |
}) | |
const exe = (command, success, error, after) => exec(command, (err, stdout, stderr) => { | |
if (stderr || err) { | |
if (error != null){ | |
error(stderr || err); | |
} else { | |
console.error("FAILED EXECUTIN OF ", command, " BECAUSE ", stderr || err); | |
} | |
} else { | |
if (success != null){ | |
success(stdout); | |
} else { | |
console.log(stdout || command); | |
} | |
} | |
if (after != null) { | |
after(); | |
} | |
}); | |
function parseGitBranchV(str) { | |
const regex = /^\s*([^\s]+)\s+([a-f0-9]+)\s+([^\n]+)$/gm; | |
const results = []; | |
let m; | |
while ((m = regex.exec(str)) !== null) { | |
// This is necessary to avoid infinite loops with zero-width matches | |
if (m.index === regex.lastIndex) { | |
regex.lastIndex++; | |
} | |
const branch = m[1]; | |
const commit = m[2]; | |
const message = m[3]; | |
results.push({branch, commit, message}); | |
} | |
return results; | |
} | |
function getRemoteBranches(done){ | |
exe("git branch -rv", str => { | |
const data = parseGitBranchV(str); | |
done(data); | |
}); | |
} | |
function extractSingle(regex, str){ | |
let m; | |
if ((m = regex.exec(str)) !== null) { return m[1]; } | |
return null; | |
} | |
function getCommitDetails(commit, done){ | |
exe(`git show ${commit} -s`, str => { | |
var obj = { | |
fullHash : extractSingle(/commit ([0-9a-f]+)/, str), | |
author: extractSingle(/Author:\s*([^\n]*)/, str), | |
date: new Date(extractSingle(/Date:\s*([^\n]*)/, str)) | |
} | |
done(obj); | |
}); | |
} | |
function getRemoteBranchesWithDetails(done) | |
{ | |
const results = []; | |
let count = 0; | |
getRemoteBranches(data => { | |
data.forEach((value, index) => { | |
count++; | |
getCommitDetails(value.commit, details => { | |
data[index].details = details; | |
results.push({ | |
commit: details.fullHash || value.commit, | |
message: value.message, | |
branch: value.branch, | |
author: details.author, | |
date: details.date | |
}); | |
count--; | |
if (count===0) { done(results); } | |
}); | |
}); | |
}) | |
} | |
function cleanup(){ | |
getRemoteBranchesWithDetails(results => { | |
// 6 months ago | |
var someTimeAgo = new Date().getTime() - (6 * 30 * 24 * 60 * 60 * 1000); | |
var oldStuff = results.filter(item => item.date < someTimeAgo); | |
const remote = "origin"; | |
const toTrim = remote + "/"; | |
console.log("Branches mathcing your filters, sorted from oldest to newest!") | |
oldStuff.sort((a,b) => a.date - b.date) | |
const toDelete = []; | |
oldStuff.forEach((item, index) => { | |
let branch = item.branch; | |
if (branch.indexOf(toTrim) == 0) { branch = branch.substr(toTrim.length); } | |
let cmd = `git push --delete ${remote} ${branch}`; | |
toDelete.push({cmd, item}); | |
console.log(item.date, cmd, item.author, item.message); | |
}) | |
console.log(`Filter matched ${toDelete.length} branches!`); | |
readline.question("\nAre you sure you want to proceed and delete matched branches? (type 'yes')", (awnser) => { | |
if (({ "yes":true, "y":true })[awnser.toLocaleLowerCase()] !== true){ | |
console.log('Aborting: user changed their mind!'); | |
} else { | |
scheduleDeletion(toDelete); | |
} | |
readline.close(); | |
}); | |
}) | |
} | |
function scheduleDeletion(list){ | |
console.log('Deletion is scheduled with rate 0.5/sec to avoid throttling! Press CTRL+C to abort'); | |
let index = 0; | |
function actuallyDelete(){ | |
const item = list[index]; | |
console.log(`${(index+1)}/${list.length} EXE: `, item.cmd, ); | |
setTimeout(timeout => { | |
exec(item.cmd, (null, null, done => { | |
index++; | |
if (index < list.length){ | |
setTimeout(actuallyDelete, 1); | |
} | |
else { | |
console.log("finshed!") | |
} | |
})) | |
}, 2000); | |
} | |
if (index < list.length){ | |
setTimeout(actuallyDelete, 2000); | |
} | |
} | |
cleanup(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment