FTFY: support wordwrapping commit messages
It's common for commit messages to be wrapped at odd places. git-gui is often to blame. Adds support for automatically fixing up these messages if running ftfy --amend, and adds a new option --msg-only for fixing only the commit message. Change-Id: Ia7ea529f8cb7395d34d9b39f1192598e9a1e315b
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
self="$0"
|
self="$0"
|
||||||
|
dirname_self=$(dirname "$self")
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
cat <<EOF >&2
|
cat <<EOF >&2
|
||||||
@@ -9,9 +10,13 @@ This script applies a whitespace transformation to the commit at HEAD. If no
|
|||||||
options are given, then the modified files are left in the working tree.
|
options are given, then the modified files are left in the working tree.
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
|
-h, --help Shows this message
|
||||||
-n, --dry-run Shows a diff of the changes to be made.
|
-n, --dry-run Shows a diff of the changes to be made.
|
||||||
--amend Squashes the changes into the commit at HEAD
|
--amend Squashes the changes into the commit at HEAD
|
||||||
|
This option will also reformat the commit message.
|
||||||
--commit Creates a new commit containing only the whitespace changes
|
--commit Creates a new commit containing only the whitespace changes
|
||||||
|
--msg-only Reformat the commit message only, ignore the patch itself.
|
||||||
|
|
||||||
EOF
|
EOF
|
||||||
rm -f ${CLEAN_FILES}
|
rm -f ${CLEAN_FILES}
|
||||||
exit 1
|
exit 1
|
||||||
@@ -34,7 +39,7 @@ vpx_style() {
|
|||||||
|
|
||||||
|
|
||||||
apply() {
|
apply() {
|
||||||
patch -p1 < "$1"
|
[ $INTERSECT_RESULT -ne 0 ] && patch -p1 < "$1"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -59,8 +64,28 @@ EOF
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
show_commit_msg_diff() {
|
||||||
|
if [ $DIFF_MSG_RESULT -ne 0 ]; then
|
||||||
|
log "Modified commit message:"
|
||||||
|
diff -u "$ORIG_COMMIT_MSG" "$NEW_COMMIT_MSG" | tail -n +3
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
amend() {
|
amend() {
|
||||||
git commit -a --amend -C HEAD
|
show_commit_msg_diff
|
||||||
|
if [ $DIFF_MSG_RESULT -ne 0 ] || [ $INTERSECT_RESULT -ne 0 ]; then
|
||||||
|
git commit -a --amend -F "$NEW_COMMIT_MSG"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
diff_msg() {
|
||||||
|
git log -1 --format=%B > "$ORIG_COMMIT_MSG"
|
||||||
|
"${dirname_self}"/wrap-commit-msg.py \
|
||||||
|
< "$ORIG_COMMIT_MSG" > "$NEW_COMMIT_MSG"
|
||||||
|
cmp -s "$ORIG_COMMIT_MSG" "$NEW_COMMIT_MSG"
|
||||||
|
DIFF_MSG_RESULT=$?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -68,7 +93,10 @@ amend() {
|
|||||||
ORIG_DIFF=orig.diff.$$
|
ORIG_DIFF=orig.diff.$$
|
||||||
MODIFIED_DIFF=modified.diff.$$
|
MODIFIED_DIFF=modified.diff.$$
|
||||||
FINAL_DIFF=final.diff.$$
|
FINAL_DIFF=final.diff.$$
|
||||||
|
ORIG_COMMIT_MSG=orig.commit-msg.$$
|
||||||
|
NEW_COMMIT_MSG=new.commit-msg.$$
|
||||||
CLEAN_FILES="${ORIG_DIFF} ${MODIFIED_DIFF} ${FINAL_DIFF}"
|
CLEAN_FILES="${ORIG_DIFF} ${MODIFIED_DIFF} ${FINAL_DIFF}"
|
||||||
|
CLEAN_FILES="${CLEAN_FILES} ${ORIG_COMMIT_MSG} ${NEW_COMMIT_MSG}"
|
||||||
|
|
||||||
# Preconditions
|
# Preconditions
|
||||||
[ $# -lt 2 ] || usage
|
[ $# -lt 2 ] || usage
|
||||||
@@ -95,19 +123,22 @@ done
|
|||||||
git diff --no-color --no-ext-diff > "${MODIFIED_DIFF}"
|
git diff --no-color --no-ext-diff > "${MODIFIED_DIFF}"
|
||||||
|
|
||||||
# Intersect the two diffs
|
# Intersect the two diffs
|
||||||
$(dirname ${self})/intersect-diffs.py \
|
"${dirname_self}"/intersect-diffs.py \
|
||||||
"${ORIG_DIFF}" "${MODIFIED_DIFF}" > "${FINAL_DIFF}"
|
"${ORIG_DIFF}" "${MODIFIED_DIFF}" > "${FINAL_DIFF}"
|
||||||
INTERSECT_RESULT=$?
|
INTERSECT_RESULT=$?
|
||||||
git reset --hard >/dev/null
|
git reset --hard >/dev/null
|
||||||
|
|
||||||
if [ $INTERSECT_RESULT -eq 0 ]; then
|
# Fixup the commit message
|
||||||
|
diff_msg
|
||||||
|
|
||||||
# Handle options
|
# Handle options
|
||||||
if [ -n "$1" ]; then
|
if [ -n "$1" ]; then
|
||||||
case "$1" in
|
case "$1" in
|
||||||
-h|--help) usage;;
|
-h|--help) usage;;
|
||||||
-n|--dry-run) cat "${FINAL_DIFF}";;
|
-n|--dry-run) cat "${FINAL_DIFF}"; show_commit_msg_diff;;
|
||||||
--commit) apply "${FINAL_DIFF}"; commit;;
|
--commit) apply "${FINAL_DIFF}"; commit;;
|
||||||
--amend) apply "${FINAL_DIFF}"; amend;;
|
--amend) apply "${FINAL_DIFF}"; amend;;
|
||||||
|
--msg-only) amend;;
|
||||||
*) usage;;
|
*) usage;;
|
||||||
esac
|
esac
|
||||||
else
|
else
|
||||||
@@ -118,6 +149,5 @@ if [ $INTERSECT_RESULT -eq 0 ]; then
|
|||||||
git diff --stat
|
git diff --stat
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
|
||||||
|
|
||||||
rm -f ${CLEAN_FILES}
|
rm -f ${CLEAN_FILES}
|
||||||
|
@@ -182,7 +182,6 @@ def main():
|
|||||||
|
|
||||||
if out_hunks:
|
if out_hunks:
|
||||||
print FormatDiffHunks(out_hunks)
|
print FormatDiffHunks(out_hunks)
|
||||||
else:
|
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
69
tools/wrap-commit-msg.py
Executable file
69
tools/wrap-commit-msg.py
Executable file
@@ -0,0 +1,69 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
## Copyright (c) 2012 The WebM project authors. All Rights Reserved.
|
||||||
|
##
|
||||||
|
## Use of this source code is governed by a BSD-style license
|
||||||
|
## that can be found in the LICENSE file in the root of the source
|
||||||
|
## tree. An additional intellectual property rights grant can be found
|
||||||
|
## in the file PATENTS. All contributing project authors may
|
||||||
|
## be found in the AUTHORS file in the root of the source tree.
|
||||||
|
##
|
||||||
|
"""Wraps paragraphs of text, preserving manual formatting
|
||||||
|
|
||||||
|
This is like fold(1), but has the special convention of not modifying lines
|
||||||
|
that start with whitespace. This allows you to intersperse blocks with
|
||||||
|
special formatting, like code blocks, with written prose. The prose will
|
||||||
|
be wordwrapped, and the manual formatting will be preserved.
|
||||||
|
|
||||||
|
* This won't handle the case of a bulleted (or ordered) list specially, so
|
||||||
|
manual wrapping must be done.
|
||||||
|
|
||||||
|
Occasionally it's useful to put something with explicit formatting that
|
||||||
|
doesn't look at all like a block of text inline.
|
||||||
|
|
||||||
|
indicator = has_leading_whitespace(line);
|
||||||
|
if (indicator)
|
||||||
|
preserve_formatting(line);
|
||||||
|
|
||||||
|
The intent is that this docstring would make it through the transform
|
||||||
|
and still be legible and presented as it is in the source. If additional
|
||||||
|
cases are handled, update this doc to describe the effect.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__author__ = "jkoleszar@google.com"
|
||||||
|
import textwrap
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def wrap(text):
|
||||||
|
if text:
|
||||||
|
return textwrap.fill(text, break_long_words=False) + '\n'
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def main(fileobj):
|
||||||
|
text = ""
|
||||||
|
output = ""
|
||||||
|
while True:
|
||||||
|
line = fileobj.readline()
|
||||||
|
if not line:
|
||||||
|
break
|
||||||
|
|
||||||
|
if line.lstrip() == line:
|
||||||
|
text += line
|
||||||
|
else:
|
||||||
|
output += wrap(text)
|
||||||
|
text=""
|
||||||
|
output += line
|
||||||
|
output += wrap(text)
|
||||||
|
|
||||||
|
# Replace the file or write to stdout.
|
||||||
|
if fileobj == sys.stdin:
|
||||||
|
fileobj = sys.stdout
|
||||||
|
else:
|
||||||
|
fileobj.truncate(0)
|
||||||
|
fileobj.write(output)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
main(open(sys.argv[1], "r+"))
|
||||||
|
else:
|
||||||
|
main(sys.stdin)
|
Reference in New Issue
Block a user