-
Notifications
You must be signed in to change notification settings - Fork 3
/
git-flip-history
executable file
·107 lines (86 loc) · 2.46 KB
/
git-flip-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
#!/bin/bash
#
# This script flips the history of a branch that is structured in the following way.
#
# For example, let's have a history, where the first commit adds 4 features, and
# each of the 3 proceeding commits reverts 3 of the features one by one:
#
# Revert: A
# Revert: B
# Revert: C
# First: D
#
# The result branch after running this command will be the following:
#
# C
# B
# A
# D
#
set -euo pipefail
if [[ $(git diff --exit-code) ]] ; then
echo "Unclean state"
exit 1
fi
if [[ $(git diff --cached --exit-code) ]] ; then
echo "Unclean state"
exit 1
fi
# Gather versions with 'Revert: ', until bumping into a commit that does not have 'Revert: '.
versions=()
last_feature_commitmsg=$(mktemp /tmp/commitmsg.XXXXXX)
echo "# Commit message to describe the feature added by this history:" >> ${last_feature_commitmsg}
echo "#" >> ${last_feature_commitmsg}
commit=$(git rev-parse HEAD)
while [ 1 ] ; do
set +e
git show --pretty=%B --no-patch $commit | head -n 1 | grep ^Revert:
result=$?
set -e
versions+=($commit)
echo "# "$(git show --pretty=%B --no-patch $commit | head -n 1) >> ${last_feature_commitmsg}
if [[ "$result" != "0" ]] ; then
set +e
git show --pretty=%B --no-patch $commit | head -n 1 | grep ^First:
result=$?
set -e
if [[ "$result" != "0" ]] ; then
echo "Expected earliest commit to start with 'First: '"
exit -1
fi
last_commit=${commit}
base_commit=$(git rev-parse $commit~1)
break
fi
commit=$(git rev-parse $commit~1)
done
parent=${base_commit}
i=0
while [ "$i" != ${#versions[@]} ] ; do
commit=${versions[$i]}
if [[ $i == "0" ]] ; then
refcommit=${last_commit}
else
refcommit=${versions[$(( $i - 1 ))]}
fi
commitmsg=$(mktemp /tmp/commitmsg.XXXXXX)
tree=$(git show --pretty=%T --no-patch ${commit})
if [[ "${refcommit}" == ${last_commit} ]] ; then
git show --pretty=%B --no-patch ${refcommit} | sed -E '1s/^First: //g' > ${commitmsg}
else
git show --pretty=%B --no-patch ${refcommit} | sed -E '1s/^Revert: //g' > ${commitmsg}
fi
parent=$(git commit-tree ${tree} -p ${parent} -F ${commitmsg})
rm -f ${commitmsg}
i=$(($i + 1))
done
rm -f ${last_feature_commitmsg}
echo
echo "Changed to different history:"
echo
git --no-pager log --no-decorate --oneline --oneline ${base_commit}..
echo
echo "New history: ${parent}"
echo
git --no-pager log --no-decorate --oneline --oneline ${base_commit}..${parent}
git reset --hard ${parent}