-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathgit-edit-commit
executable file
·90 lines (83 loc) · 2.28 KB
/
git-edit-commit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#!/usr/bin/env bash
#
# Run a rebase to `edit` or `reword` a specific commit:
#
# ```bash
# # Open the 3th commit back from HEAD, in rebase "edit" mode.
# # A subsequent `rebase --continue` will rebase the remaining 3 commits on top of it.
# git edit-commit HEAD~3
#
# # "Dry run" of the above:
# git edit-commit -n HEAD~3
# # Would run git rebase -i d6aae97^:
# # edit d6aae97 rm orphaned(?) `commit-filename{,s}` aliases
# # pick ed9447b `cmba="commit-body -a"`, `cnb="commit-body --amend"`
# # pick df33af4 `git-diff-json.py` fixes
# # pick 3c166c3 `dtl`/`details`: commit details
#
# # Same as above, but preserve the "committer dates" of `HEAD^` and `HEAD`:
# git edit-commit -p -m "new message" HEAD^
#
# # As an example, this should be a no-op (same HEAD SHA before and after).
# # `gby = git body = git log -1 --format=%B`, so this rewrites the parent commit with its existing
# # message, and preserves committer dates of both HEAD^ and HEAD.
# git edit-commit -p -m "`gby HEAD^`" HEAD^
# ```
dry_run=()
execs=()
msg=
msg_file=
cmd=edit
while getopts "m:nprx:" opt; do
case "$opt" in
m) msg="$OPTARG"; msg_file="$(mktemp)"; cmd=pick ;;
n) dry_run=(-n) ;;
p) execs+=("git reset-committer-date-rebase-head") ;;
r) cmd=reword ;;
x) execs+=("$OPTARG") ;;
*) exit 1 ;;
esac
done
shift $((OPTIND-1))
err() {
echo "$*" >&2
}
if [ $# -ne 1 ]; then
err "Usage: $0 [-m <message>] [-n] [-p] [-x <cmd>] <commit>"
err " -m: update commit message (non-interactively)"
err " -n: dry run"
err " -p: preserve committer dates"
err " -r: reword commit (interactively)"
err " -x: execute command after each commit"
exit 1
fi
ref="$1"; shift
ref="$(git log -1 --format=%h "$ref")"
sbj="$(git log -1 --format=%s "$ref")"
exec_lines() {
for exec in "${execs[@]}"; do
echo "exec $exec"
done
}
plan_file="$(mktemp)"
(
echo "$cmd $ref $sbj"
if [ -n "$msg" ]; then
echo "$msg" > "$msg_file"
echo "exec git commit --amend -F$msg_file"
fi
exec_lines
git log --reverse "--format=%h %s" "$ref..HEAD" | \
while IFS= read -r line; do
echo "pick $line"
exec_lines
done
) > "$plan_file"
git rebase-noninteractive "${dry_run[@]}" -f "$plan_file" "$ref^"
cleanup() {
rm -f "$plan_file"
if [ -n "$msg_file" ]; then
rm -f "$msg_file"
fi
}
trap cleanup EXIT