Skip to content

Instantly share code, notes, and snippets.

@unleashed
Created August 6, 2018 18:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save unleashed/fcaa8899a832b6ba6eef01c5af9e765c to your computer and use it in GitHub Desktop.
Save unleashed/fcaa8899a832b6ba6eef01c5af9e765c to your computer and use it in GitHub Desktop.
scripts that helped me rebase and opensource apisonator
#!/bin/bash
#
# This script receives a parameter, the revision from which to take
# the committer information, and applies that committer information
# to the current HEAD.
#
# N.B.: The machinery uses git commit --amend, so if you have staged
# changes you will add them to the commit! This is solvable, but not
# worth the effort.
value_from_key()
{
local str="${1}"
local key="${2}"
local sep="${3:-:}"
echo "${str}" | grep -m 1 "^${key}${sep}" | cut -d"${sep}" -s -f2-
}
git_show_cmd() {
git show --no-patch --pretty="CommitId:%H%nAuthor:%an%nAuthorEmail:%ae%nAuthorDate:%aI%nCommit:%cn%nCommitEmail:%ce%nCommitDate:%cI%nSubject:%s%nBody:%b%n" "${@}"
}
git_log_cmd() {
git log --pretty="CommitId:%H%nAuthor:%an%nAuthorEmail:%ae%nAuthorDate:%aI%nCommit:%cn%nCommitEmail:%ce%nCommitDate:%cI%nSubject:%s%nBody:%b%n" "${@}"
}
git_patch_id_from()
{
echo "${1}" | git patch-id | cut -d' ' -f 1
}
git_patch_id()
{
local data=$(git show --patch "${1}")
git_patch_id_from "${data}"
}
git_log_one()
{
git_log_cmd -n1 "${1}"
}
git_commit_info()
{
local rev="${1}"
local data=$(git_show_cmd --patch "${rev}")
echo "PatchId:$(git_patch_id_from "${data}")"
echo "${data}"
}
git_log_commit_ids()
{
git_log_cmd "${@}" | grep "^CommitId:" | cut -d':' -f2-
}
# can be used to search both by subject, body, or both at the same time
# just specify one or two parameters after the revision
git_find_by_commit_msg()
{
local rev="${1}"
local message="${2}"
local body="${3}"
local filters="'--grep=^${message}$'"
if test "x${body}" = "x"; then
git_log_commit_ids --grep="^${message}$" "${rev}"
else
git_log_commit_ids --grep="^${message}$" --grep="^${body}$" "${rev}"
fi
}
git_find_equivalent()
{
local me="${1}"
local other="${2}"
local data=$(git_commit_info "${me}")
local patchid=$(value_from_key "${data}" PatchId)
local subject=$(value_from_key "${data}" Subject)
local body=$(value_from_key "${data}" Body)
local candidates=$(git_find_by_commit_msg "${other}" "${subject}" "${body}")
if test "x${candidates}" = "x"; then
echo "No luck checking history for commit message, checking picked commit" >&2
# unfortunately git won't offer us a script friendly way to extract this
candidates=$(git status | grep -E -e "^N(ext|o) commands? " -B 3 | grep -E -e "^\s+(p|r|e)\w*\s+" | grep -v -E -e "^\s+exec\s+" | tail -n 1 | sed -E -e 's/\s+[a-z]+\s([a-z0-9]+).*/\1/')
if test "x${candidates}" = "x"; then
echo "No candidates found." >&2
return 1
fi
fi
echo "Found candidates ${candidates}" >&2
for sha in ${candidates}; do
echo "Dealing with '${sha}' candidate" >&2
local other_patchid=$(git_patch_id "${sha}")
if test "x${patchid}" = "x${other_patchid}"; then
echo Found commit ${sha} >&2
echo "${sha}"
return 0
fi
done
echo "No match. Candidates were: ${candidates}" >&2
return 1
}
recommit_as()
{
local rev="${1}"
echo "Recommitting with committer data from ${rev}" >&2
local data="$(git_log_one "${rev}")"
if test "x${data}" = "x"; then
return 1
fi
local committer="$(value_from_key "${data}" Commit)"
local commitemail="$(value_from_key "${data}" CommitEmail)"
local commitdate="$(value_from_key "${data}" CommitDate)"
GIT_COMMITTER_NAME=\""${committer}"\" \
GIT_COMMITTER_EMAIL=\""${commitemail}"\" \
GIT_COMMITTER_DATE=\""${commitdate}"\" \
git commit --amend --no-edit
}
usage()
{
local name="${1}"
echo -e "Usage: ${name} <branch | --from commit_id>\n" >&2
echo "* If branch is specified, we will try to match HEAD to a commit id" >&2
echo " in branch and apply its metadata to HEAD. This could fail." >&2
echo "* If commit_id is specified we will take the specified commit id" >&2
echo " metadata and apply it to HEAD" >&2
}
run()
{
local name=$(basename "${0}")
if test "$#" -eq 1; then
local commit_id=$(git_find_equivalent HEAD "${1}")
if test "x${commit_id}" = "x"; then
echo "Automatic commit matching failed. Find the original commit id" >&2
echo -e "and run this program with:\n" >&2
echo "${name} --from <commit_id>" >&2
return 1
fi
recommit_as ${commit_id}
elif test "$#" -eq 2; then
if test "x${1}" = "x--from"; then
echo "going with ${2} as commit id" >&2
recommit_as "${2}"
else
usage ${name}
return 1
fi
else
usage ${name}
return 1
fi
}
# Commands
#
pick() {
local CID="${1}"
echo -e "git cherry-pick ${CID}\ngit-recommit-as --from ${CID}"
git cherry-pick ${CID} && git-recommit-as --from ${CID} && echo pick Ok.
}
merge() {
local CID="${1}"
local branch="${2-base-oss}"
echo -e "git merge --no-edit --no-ff ${branch}\ngit commit --amend -e -C ${CID}\n"
echo -e "git-recommit-as --from ${CID}"
git merge --no-edit --no-ff ${branch} && git commit --amend -e -C ${CID} && \
git-recommit-as --from ${CID} && echo merge Ok.
}
rebase() {
local CID="${1}"
local from="${2-oss}"
local where="${3-rebased-oss}"
local branch="${4-base-oss}"
echo -e "git checkout -f ${branch}\ngit reset --hard ${CID}"
echo -e "git rebase -i -p --onto ${where} ${from} ${branch} --exec 'git-recommit-as ${CID}'"
git checkout -f ${branch} && git reset --hard ${CID} && \
git rebase -i -p --onto ${where} ${from} ${branch} \
--exec "git-recommit-as ${CID}" && \
echo rebase Ok.
}
rebase_no_ff() {
local merge_cid="${1}"
local merge_base=$(git merge-base ${merge_cid}^ ${merge_cid}^2)
local other_merge_base="${2-$(git_find_equivalent ${merge_base} rebased-oss)}"
local branch="${3-base-oss}"
rebase ${merge_cid}^2 ${merge_base} ${other_merge_base} ${branch}
}
[[ $0 != "$BASH_SOURCE" ]] || (set -euf -o pipefail; run "${@}")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment