-
Notifications
You must be signed in to change notification settings - Fork 2
/
git-dig-history
executable file
·131 lines (114 loc) · 5.65 KB
/
git-dig-history
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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#! /bin/sh
### Copyright (C) 2017 by Jim Klimov
usage() {
cat << EOF
You can call this script as
$0 find_commits_contains [PATTERN] commit context or content matches
$0 find_commits_show [PATTERN] colorful show of above commits body
$0 find_commits_intro [PATTERN] commits which add the pattern
$0 find_commits_drop [PATTERN] commits which remove the pattern
$0 fix_history [PATTERN] [REPLACEMENT]
Note that PATTERN and REPLACEMENT are fixed strings (not regexes) in this context
For the SED usage there is also a PATTERN_SED that you can optionally export
EOF
}
[ -n "${PATTERN-}" ] || PATTERN="needle"
[ -n "${REPLACEMENT-}" ] || REPLACEMENT="@CUSTOM_NEEDLE@"
### This script runs from the basedir of a git repo (or a few)
### Logs and other work files are commonly stored in the parent dir by default
[ -n "${LOGDIR-}" ] || LOGDIR="`pwd`/.."
[ -n "${LOGTAG-}" ] || LOGTAG="$(basename `pwd`)"
if [ -z "${GGREP-}" ] ; then
# TODO: check if GNU Grep extensions (-A<NUM>) are supported?
(command -v ggrep 2>/dev/null >/dev/null) && GGREP=ggrep || GGREP=grep
fi
find_commits_contains() {
### This lists which "$COMMITID:$PATHNAME:$LINETEXT" contain the PATTERN in the LINETEXT
### Note that this lists all commits whose checked-out workspace would have the pattern
[ -n "${WORKSPACE_MATCHES_FILE-}" ] || WORKSPACE_MATCHES_FILE="${LOGDIR}/gitdig_commits-workspace-contains__${LOGTAG}.txt"
git grep "${PATTERN}" $(git rev-list --all --remotes) \
| tee "${WORKSPACE_MATCHES_FILE}"
}
show_commits_color() {
### This finds which commits dealt with the pattern (whose diff
### adds or removes a line with it, or has it in context)
### Note that this starts with a colorful depiction of first-pass
### diffs, for esthetic viewing pleasure and is parsed including
### color markup by other tools below
git rev-list --all --remotes | \
while read CMT ; do ( \
git show --color --pretty='format:%b' "$CMT" | \
egrep "${PATTERN}" && echo "^^^ $CMT" \
) ; done
}
find_commits_show() {
# alias
show_commits_color "$@"
}
find_commits_intro() {
### This finds which commits INTRODUCE the pattern (whose diff adds a line with it)
### Note that this starts with a colorful depiction of first-pass diffs, for esthetic viewing pleasure
[ -n "${COMMIT_INTRODUCES_FILE-}" ] || COMMIT_INTRODUCES_FILE="${LOGDIR}/gitdig_commits-intro__${LOGTAG}.txt"
show_commits_color | tee "${COMMIT_INTRODUCES_FILE}".tmp
### ...and this picks out the lines for commits which actually add the PATTERN
### (because there are also not interesting context and removal lines as well,
### which should disappear after the rebase)
echo "LIST OF COMMITS THAT INTRODUCE PATTERN (cached in ${COMMIT_INTRODUCES_FILE}.tmp) :"
cat "${COMMIT_INTRODUCES_FILE}".tmp | \
egrep '^([\^]|.*32m\+)' | \
$GGREP -A1 '32m\+' | \
grep '^\^' \
| tee "${COMMIT_INTRODUCES_FILE}"
}
find_commits_drop() {
### This finds which commits REMOVE the pattern (whose diff drops a line with it)
### Note that this starts with a colorful depiction of first-pass diffs, for esthetic viewing pleasure
[ -n "${COMMIT_DROPS_FILE-}" ] || COMMIT_DROPS_FILE="${LOGDIR}/gitdig_commits-drop__${LOGTAG}.txt"
show_commits_color | tee "${COMMIT_DROPS_FILE}".tmp
### ...and this picks out the lines for commits which actually add the PATTERN
### (because there are also not interesting context and removal lines as well,
### which should disappear after the rebase)
echo "LIST OF COMMITS THAT DROP THE PATTERN (cached in ${COMMIT_DROPS_FILE}.tmp) :"
cat "${COMMIT_DROPS_FILE}".tmp | \
egrep '^([\^]|.*31m\-)' | \
$GGREP -A1 '31m\-' | \
grep '^\^' \
| tee "${COMMIT_DROPS_FILE}"
}
fix_history() {
### When the inspections are done with, we want to clean up the history
### Note that as part of this, we would drop "unreachable" commits that
### are not part of any branch's history (because these would still contain
### the original offending pattern.
###
### WARNING: This is the one destructive operation in this suite.
###
### You are advised to run it in a scratch full copy of your git repo.
###
### After it is successfully done, you are advised to destroy the published
### copy of the repo completely (e.g. on github) and force-push the cleaned
### one into a newly created instance of the repo, and have your team members
### destroy and re-fork their clones both on cloud platform and their local
### workspaces, so that the destroyed offending commits do not resurface.
### Note that you can stack more sed '-e ...' blocks below, e.g. to rewrite
### more patterns in one shot. Also note that the pattern and replacement
### representation for simple grep and regex in sed may vary... you may want
### to automate escaping of special chars.
[ -n "${PATTERN_SED-}" ] || PATTERN_SED="${PATTERN}"
git filter-branch --tree-filter "git grep '${PATTERN}' | sed 's,:.*\$,,' | sort | uniq | while read F ; do sed -e 's,${PATTERN_SED},${REPLACEMENT},g' -i '\$F'; done"
git reflog expire --expire-unreachable=now --all
git gc --prune=now
}
[ -n "$2" ] && PATTERN="$2"
[ -n "$3" ] && REPLACEMENT="$3"
ACTION=""
case "$1" in
-h|--help) usage; exit 0 ;;
find|grep) ACTION="find_commits_intro"; shift ;;
show|diff) ACTION="find_commits_show"; shift ;;
drop|remove|del|delete) ACTION="find_commits_drop"; shift ;;
fix_history|find_commits_contains|find_commits_intro|find_commits_drop|find_commits_show) ACTION="$1"; shift ;;
*) usage; exit 1 ;;
esac
echo "Running routine: '$ACTION'"
"$ACTION"