Skip to content

Instantly share code, notes, and snippets.

@nmrugg
Created January 18, 2014 08:14
Show Gist options
  • Save nmrugg/8487693 to your computer and use it in GitHub Desktop.
Save nmrugg/8487693 to your computer and use it in GitHub Desktop.
Push all necessary commits one at a time. This is particularly useful when pushing a large number of commits on a flaky connection.
/// Push all necessary commits one at a time.
///
/// This is particularly useful when pushing a large number of commits on a flaky connection.
///
/// Usage: node git_push_slow.js [how_many_commits [branch_to_push]]
/// NOTE: By leaving off both params or setting the first to -1, it will try to determine how many commits behind the origin is.
/// TODO: Show upload progress.
///
/// License: MIT
///
/// nmrugg 2014
///
var exec = require("child_process").exec,
spawn = require("child_process").spawn;
function done()
{
/// Beep.
process.stdout.write("\u0007");
}
function retry(commits_behind, branch)
{
console.log("Something didn't work. Waiting...");
setTimeout(function wait()
{
console.log("Retrying.");
git_push_slow(commits_behind, branch)
}, 30000);
}
function git_branch(commits_behind, branch, cb)
{
exec("git branch -f slow-push-del-me " + branch + "~" + Number(commits_behind), cb);
}
function git_push(commits_behind, branch, cb)
{
console.log("Pushing " + commits_behind);
var push = spawn("git", ["push", "origin", "slow-push-del-me:" + branch]);
push.stdout.on("data", function (data)
{
if (data.toString().indexOf("To prevent you from losing history, non-fast-forward") > -1) {
console.log("Already pushed");
cb();
cb = function () {};
} else {
console.log(data.toString());
}
});
push.stderr.on("data", function (data)
{
console.log(data.toString());
});
push.on("close", function onclose(code)
{
/// Wait a moment to let the stdout be read in case the error is just non-fast-forward.
setTimeout(function wait()
{
cb(code);
}, 200);
});
}
function delete_branch(cb)
{
exec("git branch -d slow-push-del-me", cb);
}
function pushed(commits_behind, branch)
{
console.log("Pushed " + commits_behind);
commits_behind -= 1;
if (commits_behind < 0) {
return delete_branch(done);
}
git_push_slow(commits_behind, branch)
}
function git_push_slow(commits_behind, branch)
{
git_branch(commits_behind, branch, function onbranch(err)
{
if (err) {
console.log("Can't branch!");
console.log(err)
return;
}
git_push(commits_behind, branch, function onpush(err)
{
if (err) {
console.log(err)
return retry(commits_behind, branch);
}
pushed(commits_behind, branch);
});
});
}
function determine_how_far_behind(branch, cb)
{
exec("git log --abbrev-commit --all --graph --decorate --no-color", function ondone(err, stdout)
{
var matches,
ref_match,
i,
len,
branch_regex = new RegExp("origin/" + branch);
if (err || !stdout) {
console.log("Can't find commit count! Try this:");
console.log(" $", process.argv[0], process.argv[1], "NUMBER", branch);
throw err;
}
matches = stdout.match(/(?:^|\n)\*\scommit\s[0-9a-f]+[^\n]*/g);
if (!matches) {
console.log("Can't parse commit log! Try this:");
console.log(" $", process.argv[0], process.argv[1], "NUMBER", branch);
return;
}
len = matches.length;
for (i = 0; i < len; i += 1) {
ref_match = matches[i].match(/\*\scommit\s[0-9a-f]+([^\n]*)/)
if (ref_match && ref_match[1] && ref_match[1] && branch_regex.test(ref_match[1])) {
return cb(i - 1);
}
}
console.log("Can't find origin. Pushing all commits.")
cb(len - 1);
});
}
(function ()
{
var commits = Number(process.argv[2]),
branch = process.argv[3] || "master";
if (isNaN(commits) || commits < 0) {
determine_how_far_behind(branch, function onres(commits)
{
git_push_slow(commits, branch);
});
} else {
git_push_slow(commits, branch);
}
}());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment