I want to clean up the history of a repo with a year's worth of commits, about 4000 in all.
One contributor has consistently disagreed with formatting standards and has repeatedly changed the Prettier config file, or not used Prettier at all. As a result, the git history is a sort of tug-of-war of cosmetic changes with huge diffs, and the real changes are difficult to find.
The name of the front-end directory was renamed at some point. We load the project from within that directory, making it a nuisance to access earlier git history from VSCode.
A TypeScript transpiler was added at some point, generating a
file.js
andfile.js.map
for everyfile.ts
across the project. The files aren't generated consistently (sometimes they have a particular comment at the end, sometimes not), which adds to the noise in the git history.
My tentative plan is to rebase everything:
I'll keep a backup of the repo just in case, and then rebase, doing the following on every commit:
- Apply consistent Prettier settings;
- Rename the front-end directory if necessary;
- Delete all the unnecessary
file.js
andfile.js.map
files.
Our team would then move to the new repo.
Specifically:
GIT_SEQUENCE_EDIT=cat git rebase
--strategy recursive --strategy-option theirs --rebase-merges \
--exec '../cleanup.sh && git add . && git commit --amend --no-edit --no-verify --allow-empty' \
e709bcd1
where e709bcd1
is the SHA of a good place to start, using the script cleanup.sh
:
#! /usr/bin/env zsh
setopt nullglob
echo $(git rev-parse HEAD) > commit.log
# If both directories exist, assume old_front_end is the real one,
# so delete new_front_end to allow us to rename old_front_end.
# (Otherwise, `mv` will move the one directory into the other.)
if [[ -d "old_front_end" ]] && [[ -d "new_front_end" ]]; then
rm -rf new_front_end
fi
# Rename old_front_end if necessary
if [[ -d "old_front_end" ]] && [[ ! -d "new_front_end" ]]; then
mv old_front_end new_front_end
fi
if [[ -d "new_front_end" ]]; then
# Clean up JS files
for file in "new_front_end/src/**/*.ts"; do
[[ ! -e $file ]] && continue # skip following if no such file
rm "${file%.*}.js"
rm "${file%.*}.js.map"
done
# Apply consistent Prettier settings
prettier --config ~/external_source_of_truth/.prettierrc -w "new_front_end/src/**/*.{js,ts,svelte,gql,css,scss}" || true
fi
Questions:
- I don't have much experience rebasing or writing shell scripts. Is this a reasonable plan? Will it have unfortunate consequences?
- I've tried running the script, and it gets stuck fairly often with a merge conflict. It seems that I can always resolve the conflict simply by doing
git add . && git rebase --continue
, but I'd rather not have to do this hundreds of times. Can I automate this?
CodePudding user response:
Answers to the two questions:
Seems reasonable enough.
Solved using
git filter-repo
and the associated toollint-history
:
Rename front-end directory:
git filter-repo --path-rename old_front_end/:new_front_end/
Clean up JS files:
git filter-repo --force --filename-callback '
if filename.endswith(b".js"):
ts_file = filename[:-3] b".ts"
if os.path.isfile(ts_file.decode("utf-8")):
return None
if filename.endswith(b".js.map"):
ts_file = filename[:-7] b".ts"
if os.path.isfile(ts_file.decode("utf-8")):
return None
return filename
'
Prettier
lint-history --relevant '
return filename.endswith(b".ts") or filename.endswith(b".js")
or filename.endswith(b".svelte") or filename.endswith(b".css")
or filename.endswith(b".scss") or filename.endswith(b".gql")
' --filenames-important maybe-prettier
I needed to write a helper script called maybe-prettier
because some commits had bad files with unresolved merge symbols (<<<<<<<< HEAD
and so on). Prettier was unable to format those files and exited with an error, halting the progress of filter-repo
. To get around this, maybe-prettier
first checks to see if a file is able to be formatted; it always exits with success.
maybe-prettier
#! /usr/bin/env zsh
# Prettier gives three exit codes:
# 0 file is good
# 1 needs formatting
# 2 error
prettier --config ~/external_source_of_truth/.prettierrc -c $1
if [[ $? = 1 ]]; then
prettier --config ~/external_source_of_truth/.prettierrc -w $1
fi