Skip to content

Commit 2f4346b

Browse files
committed
made branchless-dependent version
1 parent 0000063 commit 2f4346b

File tree

1 file changed

+347
-0
lines changed

1 file changed

+347
-0
lines changed

git-branchless-linearize

Lines changed: 347 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,347 @@
1+
#!/bin/bash
2+
#
3+
# This script makes sure that all commit hashes have nice and incremental prefixes.
4+
#
5+
# Move this script to somewhere on your path, and run it as "git linearize".
6+
#
7+
# Built by Gustav Westling (@zegl) and friends.
8+
# https://github.com/zegl/extremely-linear
9+
#
10+
# Built with https://github.com/not-an-aardvark/lucky-commit
11+
#
12+
# Remember, this is a script that you've downloaded from the internet.
13+
# Don't run it on anything important unless you know what you're doing.
14+
# (Which I'm sure that you do, only epic rockstar ninja developers would use a program like this one).
15+
#
16+
#
17+
18+
set -euo pipefail
19+
20+
# Argument parsing :-)
21+
EL_FORMAT="%07d0"
22+
VERBOSE_LOG=0
23+
EL_CMD="linearize"
24+
EL_IF_BRANCH=""
25+
EL_REBASE_SPLITS=true
26+
27+
while test $# -gt 0; do
28+
case "$1" in
29+
-h | --help)
30+
LIGHT_GREEN='\033[1;32m'
31+
NC='\033[0m' # No Color
32+
echo -e "${LIGHT_GREEN}git linearize using git-branchless - Create an extremely linear git history - github.com/zegl/extremely-linear${NC}"
33+
echo ""
34+
echo "git linearize [command] [options]"
35+
echo ""
36+
echo "command: (default command is to run the linearization process)"
37+
echo " -h, --help show brief help"
38+
echo " --install-hook installs git-linearize as a post-commit hook (current repo only)"
39+
echo " --make-epoch makes the current commit the linearized epoch (00000000), use to adopt git-linearize in"
40+
echo " existing repositories without having to rewrite the full history"
41+
echo ""
42+
echo "general options (for all commands):"
43+
echo " -v, --verbose more verbose logging"
44+
echo " --short use shorter 6 digit prefix (quick mode)"
45+
echo " --format [format] specify your own prefix format (pritnf style)"
46+
echo " --full Rewrite entire history, including public commits on the main branch"
47+
echo " --no-rebase do not rebase branches that split off of this branch onto the new linearized branch"
48+
echo ""
49+
echo " All command generally support all general options. For example, specifying --format to --install-hook means"
50+
echo " that git-linearize will be called with the same format in the future when triggered by the hook."
51+
exit 0
52+
;;
53+
--short)
54+
EL_FORMAT="%06d"
55+
shift
56+
;;
57+
--format)
58+
shift
59+
if test $# -gt 0; then
60+
export EL_FORMAT=$1
61+
else
62+
echo "no format specified"
63+
exit 1
64+
fi
65+
shift
66+
;;
67+
--install-hook)
68+
shift
69+
EL_CMD="install_hook"
70+
;;
71+
--make-epoch)
72+
shift
73+
EL_CMD="make_epoch"
74+
;;
75+
--if-branch)
76+
shift
77+
EL_IF_BRANCH=$1
78+
shift
79+
;;
80+
-v | --verbose)
81+
shift
82+
VERBOSE_LOG=1
83+
;;
84+
--)
85+
shift
86+
EL_REBASE_SPLITS=false
87+
;;
88+
*)
89+
break
90+
;;
91+
esac
92+
done
93+
94+
########################################################################################################################
95+
# Helper functions #
96+
########################################################################################################################
97+
function echoinfo() {
98+
LIGHT_GREEN='\033[1;32m'
99+
NC='\033[0m' # No Color
100+
printf "${LIGHT_GREEN}%s${NC}\n" "$1"
101+
}
102+
103+
function echoerr() {
104+
RED='\033[0;31m'
105+
NC='\033[0m' # No Color
106+
printf "${RED}%s${NC}\n" "$1" >&2
107+
}
108+
109+
function echodebug() {
110+
if ((VERBOSE_LOG)); then
111+
echoinfo "$1"
112+
fi
113+
}
114+
115+
function debug() {
116+
if ((VERBOSE_LOG)); then
117+
cat >&2
118+
fi
119+
}
120+
121+
function git_root() {
122+
git rev-parse --show-toplevel
123+
}
124+
125+
function git_current_branch() {
126+
git rev-parse --abbrev-ref HEAD
127+
}
128+
129+
function linearize_root_commit() {
130+
# shellcheck disable=SC2059 # Disabled because EL_FORMAT contains the format
131+
printf "$EL_FORMAT" 0
132+
}
133+
134+
function find_format_base() {
135+
local pattern='%0[0-7]*([bxd])'
136+
137+
if [[ $EL_FORMAT =~ $pattern ]]; then
138+
local match="${BASH_REMATCH[1]}"
139+
# echo $match
140+
case "$match" in
141+
b) echo 2 ;; # Binary format specifier
142+
x) echo 16 ;; # Hexadecimal format specifier
143+
o) echo 8 ;; # Octal format specifier
144+
d) echo 10 ;; # Decimal format specifier
145+
*) echoerr "[x] Your format was kind of ok, but I still won't do anything with it >:)"
146+
exit 1 ;; # Unknown format specifier
147+
esac
148+
else
149+
echoerr "[x] Could not understand your format"
150+
exit 1 # No valid printf pattern found
151+
fi
152+
}
153+
154+
# function find_root_on_current_branch() {
155+
# # Find the most recent epoch commit (if any) on the current branch
156+
# # Returns the full commit hash of the root commit
157+
# git log --oneline --no-abbrev-commit |
158+
# grep "^$(linearize_root_commit)" |
159+
# head -n1 |
160+
# awk '{print $1}'
161+
# }
162+
163+
# function git_has_linearize_root() {
164+
# # Use custom root if there the repositoru has a 00000000-commit that has a parent (is not the repo root)
165+
# if git show "$(find_root_on_current_branch)^" >/dev/null 2>&1; then
166+
# return 0
167+
# else
168+
# return 1
169+
# fi
170+
# }
171+
172+
# function git_is_ready() {
173+
# # current directory has changes to tracked files
174+
# if git status --porcelain | grep -v -q "??"; then
175+
# return 1
176+
# fi
177+
178+
# # no changes, or only changes to untracked files
179+
# return 0
180+
# }
181+
182+
########################################################################################################################
183+
# Dependencies check #
184+
########################################################################################################################
185+
if ! command -v lucky_commit &>/dev/null; then
186+
echoerr "[!] Dependency lucky_commit was not found on your PATH"
187+
exit 1
188+
fi
189+
190+
if ! command -v git-branchless &>/dev/null; then
191+
echoerr "[!] Dependency git-branchless was not found on your PATH"
192+
exit 1
193+
fi
194+
195+
if ! git sl ; then
196+
echoerr "[!] Git branchless not initialized. Will do it for you :)"
197+
git branchless init
198+
fi
199+
200+
########################################################################################################################
201+
# cmd_install_hook installs git-linearize as a post-commit hook in the current repository #
202+
# #
203+
# Trigger with "--install-hook" #
204+
# #
205+
# If the arguments "--if-branch [name]" or "--format [format]" or "--short" are passed to "--install-hook" they will #
206+
# be forwarded to the execution of git-linearize when triggered by the hook. #
207+
########################################################################################################################
208+
function cmd_install_hook() {
209+
FILE="$(git_root)/.git/hooks/post-commit"
210+
if [ -f "$FILE" ]; then
211+
echoerr "post-commit hook already exists at $FILE. Aborting!"
212+
exit 1
213+
fi
214+
215+
FORWARD_FORMAT=""
216+
if [[ -n $EL_FORMAT ]]; then
217+
FORWARD_IF_BRANCH="--format ${EL_FORMAT}"
218+
fi
219+
220+
cat >"$FILE" <<-EOM
221+
#!/bin/bash
222+
git linearize ${FORWARD_FORMAT}
223+
EOM
224+
chmod +x "$FILE"
225+
226+
echoinfo "Installed hook to .git/hooks/post-commit!"
227+
}
228+
229+
########################################################################################################################
230+
# cmd_linearize is the default command of git-linearize, it rebases the current branch and gives all commits #
231+
# incremental commit sha1 prefixes. #
232+
# #
233+
# Use "--if-branch [name]" to only run if the currently checked out branch matches the specified name. #
234+
########################################################################################################################
235+
function cmd_linearize() {
236+
# Check branch
237+
# if [[ -n $EL_IF_BRANCH ]] && [[ ${EL_IF_BRANCH} != "$(git_current_branch)" ]]; then
238+
# echodebug "[x] Current branch is $(git_current_branch), expected ${EL_IF_BRANCH}. Skipping. :-)"
239+
# exit 0
240+
# fi
241+
242+
# if ! git_is_ready; then
243+
# echoerr "[x] The current git directory is not clean. Skipping. :-)"
244+
# echoerr " Don't worry, git linearize will clean up all commits all at once later."
245+
# exit 0
246+
# fi
247+
248+
# if git_has_linearize_root; then
249+
# found_root=$(find_root_on_current_branch)
250+
# echodebug "[x] Repository has a custom root commit (${found_root}), using it as the root"
251+
# commits=$(git log --format=format:%H --reverse "${found_root}^...HEAD")
252+
# else
253+
# echodebug "[x] Repository has a no custom root commit, using the repository root"
254+
# commits=$(git log --format=format:%H --reverse)
255+
# fi
256+
257+
# Remember which branch we are on <- this does not work with HEAD nicely
258+
pre_branch=$(git_current_branch)
259+
fbase=$(find_format_base)
260+
prefix=$(printf "$EL_FORMAT" 0)
261+
flength=${#prefix}
262+
echo $fbase
263+
# go back unto the first properly formatted commit
264+
while true; do
265+
# $get the sha
266+
# sha stands for "Short HAsh" to avoid confusion
267+
sha=$(git rev-parse --short HEAD)
268+
# first try printing the sha
269+
flsha="${sha:0:$flength}"
270+
if printf "$EL_FORMAT" $flsha >/dev/null 2>&1; then
271+
#this still fails if the commit format is shorter than the output of git rev-parse --short
272+
i=$(($fbase#$flsha))
273+
if [ $(printf "$EL_FORMAT" $i 2>/dev/null) == $flsha ]; then
274+
echoinfo "Found first well-formatted commit! $flsha"
275+
i=$(($i+1))
276+
git next > /dev/null 2>&1
277+
break
278+
else
279+
echodebug "Found commit $flsha that could format"
280+
echodebug "However, using $EL_FORMAT, base $fbase: $i does not give good results"
281+
fi
282+
fi
283+
# go to previous commit
284+
if ! git prev > /dev/null 2>&1; then
285+
echoinfo "Found no well-formatted commit until the root"
286+
i=0
287+
break
288+
fi
289+
done
290+
291+
echoinfo "Starting formatting"
292+
while true; do
293+
#lha stands for Long HAsh
294+
lha=$(git rev-parse HEAD)
295+
prefix=$(printf "$EL_FORMAT" $i)
296+
echoinfo "Looking for $lha #$i"
297+
lucky_commit $prefix
298+
#fha stands for Formatted HAsh
299+
fha=$(git rev-parse HEAD)
300+
echoinfo "Formatted $lha to $fha"
301+
git move -fs $lha > /dev/null 2>&1 # | debug
302+
git hide $lha > /dev/null 2>&1
303+
i=$(($i+1))
304+
if ! git next > /dev/null 2>&1; then
305+
if [ $pre_branch == "HEAD" ]; then
306+
echoinfo "encountered a branch and don't have a target: $pre_branch, aborting"
307+
exit 0
308+
fi
309+
logs=($(git log --reverse HEAD..$pre_branch --format=format:%H))
310+
if [ ${#logs[@]} -eq 0 ]; then
311+
echoinfo "All done, have a good day!"
312+
exit 0
313+
elif [ ${logs[0]} == $(git rev-parse $pre_branch) ]; then
314+
git checkout $pre_branch | debug
315+
else
316+
git checkout ${logs[0]} | debug
317+
fi
318+
fi
319+
done
320+
}
321+
322+
########################################################################################################################
323+
# cmd_make_epoch can be run with the --make-epoch flag #
324+
# #
325+
# Marks the current commit as the epoch (00000000), instead of using the repository root commit as the epoch. #
326+
# Respects the --format/--short flags #
327+
########################################################################################################################
328+
function cmd_make_epoch() {
329+
prefix=$(linearize_root_commit)
330+
echodebug "[x] Fixing $(git rev-parse HEAD) (looking for prefix=$prefix)"
331+
lucky_commit "$prefix"
332+
new_sha=$(git rev-parse HEAD)
333+
echoinfo "[x] All done, ${new_sha} is the new git-linearize epoch :-)"
334+
}
335+
336+
# Time to run something!
337+
case "$EL_CMD" in
338+
linearize)
339+
cmd_linearize
340+
;;
341+
install_hook)
342+
cmd_install_hook
343+
;;
344+
make_epoch)
345+
cmd_make_epoch
346+
;;
347+
esac

0 commit comments

Comments
 (0)