summaryrefslogtreecommitdiff
path: root/.github
diff options
context:
space:
mode:
Diffstat (limited to '.github')
-rwxr-xr-x.github/workflows/linearize.sh102
-rw-r--r--.github/workflows/linearize.yml25
2 files changed, 127 insertions, 0 deletions
diff --git a/.github/workflows/linearize.sh b/.github/workflows/linearize.sh
new file mode 100755
index 00000000..ad6968ae
--- /dev/null
+++ b/.github/workflows/linearize.sh
@@ -0,0 +1,102 @@
+#!/usr/bin/env bash
+set -eEuo pipefail
+
+sort_authors() {
+ # Unify author aliases.
+ sed -e 's/^dibyendumajumdar <mobile@majumdar\.org\.uk>$/Dibyendu Majumdar <mobile@majumdar.org.uk>/g' \
+ -e 's/^Dibyendu Majumdar <dibyendumajumdar@users\.noreply\.github\.com>$/Dibyendu Majumdar <mobile@majumdar.org.uk>/g' |
+ # Rank by number of commits.
+ sort | uniq -c | sort -nr | sed -E 's/^ *[0-9]+ //'
+}
+
+IFS=$'\n'
+
+mkdir linear
+cd linear
+git init -q -b main
+
+repo=..
+
+last_committer_date='0 +0000'
+
+git -C "$repo" ls-tree HEAD --name-only | grep '^chapter' |
+while read -r chapter; do
+ echo "Processing $chapter"
+
+ chapter_number="$(sed 's/^chapter0*//' <<< "$chapter")"
+ chapter_title="$(head -n1 "$repo/$chapter/README.md" | sed 's/^# //')"
+
+ # Extract the authors for this chapter from commit authors and the
+ # Co-authored-by trailer.
+ authors="$(git -C "$repo" log --format='%an <%ae>' --no-merges "$chapter")"
+ co_authors="$(git -C "$repo" log --format=%B --no-merges "$chapter" |
+ tr -d $'\r' | ( grep '^ *Co-authored-by: ' || : ) | cut -d' ' -f2-)"
+ first_author="$(sort_authors <<< "$authors" | head -n1)"
+ all_authors="$(sort_authors <<< "$authors"$'\n'"$co_authors")"
+
+ message="$chapter_title"$'\n\n'
+ while read -r author; do
+ if [[ "$author" != "$first_author" ]]; then
+ message+="Co-authored-by: $author"$'\n'
+ fi
+ done <<< "$all_authors"
+
+ # Get the first and last dates this chapter was modified.
+ root_files=(README.md LICENSE pom.xml .gitignore .dir-locals.el transpile-tests)
+ author_root_files=()
+ if [[ $chapter = chapter01 ]]; then
+ # Only include the commit date of the root files for chapter01.
+ author_root_files+=("${root_files[@]}")
+ fi
+ author_date="$(git -C "$repo" log --format=%ad --date=raw "$chapter" "${author_root_files[@]}" | tail -n1)"
+ committer_date="$(git -C "$repo" log --format=%ad --date=raw -1 "$chapter" "${root_files[@]}")"
+ if [[ $committer_date < $last_committer_date ]]; then
+ committer_date="$last_committer_date"
+ else
+ last_committer_date="$committer_date"
+ fi
+
+ # Add the files for the chapter.
+ git rm -qr --ignore-unmatch .
+ cp -R "$repo/$chapter/." .
+ mv README.md docs/
+ mv docs chapter_docs
+ rm pom.xml
+ git add .
+
+ # Add the shared files in the root, except for README.md and pom.xml.
+ root_files=(LICENSE .gitignore .dir-locals.el transpile-tests)
+ cp -R "${root_files[@]/#/"$repo/"}" .
+ git add "${root_files[@]}"
+
+ # Fix README.md, docs/chapter??/README.md, and pom.xml.
+ if git rev-parse HEAD >/dev/null 2>/dev/null; then
+ # Restore the documentation and pom.xml from the previous chapter.
+ git diff --staged --diff-filter=D --name-only -- docs README.md pom.xml | xargs git checkout -q HEAD
+ else
+ # This is the first commit; setup shared files.
+ cp "$repo"/{README.md,pom.xml} .
+ mkdir docs
+ # Remove links to chapters.
+ sed -Ei 's,\[Chapter ([0-9]+)\]\(chapter0?\1/README\.md\),Chapter \1,' README.md
+ # Change to a JAR and delete the modules, for a single-project structure.
+ sed -i -e 's,<packaging>pom</packaging>,<packaging>jar</packaging>,' \
+ -e '/<modules>/,/^$/d' pom.xml
+ fi
+ git mv chapter_docs "docs/$chapter"
+ # Restore the link to this chapter.
+ sed -Ei "s,^\* Chapter $chapter_number: ,* [Chapter $chapter_number](docs/$chapter/README.md): ," README.md
+ # Repair links for this chapter.
+ sed -Ei 's,\bdocs/,,' "docs/$chapter/README.md"
+ git add README.md "docs/$chapter/README.md" pom.xml
+
+ if [[ ! "$first_author" =~ ^(.*)\ \<(.*)\>$ ]]; then
+ echo "First author does not match pattern" >&2
+ exit 1
+ fi
+ name="${BASH_REMATCH[1]}"
+ email="${BASH_REMATCH[2]}"
+ GIT_AUTHOR_NAME="$name" GIT_AUTHOR_EMAIL="$email" GIT_AUTHOR_DATE="$author_date" \
+ GIT_COMMITTER_NAME="$name" GIT_COMMITTER_EMAIL="$email" GIT_COMMITTER_DATE="$committer_date" \
+ git commit -q -m "$message"
+done
diff --git a/.github/workflows/linearize.yml b/.github/workflows/linearize.yml
new file mode 100644
index 00000000..4b1828d3
--- /dev/null
+++ b/.github/workflows/linearize.yml
@@ -0,0 +1,25 @@
+name: Linearize chapter history
+
+on:
+ push:
+ branches:
+ - main
+
+jobs:
+ linearize:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ ref: main
+ fetch-depth: 0 # Fetch the full history
+ - name: Construct linear history and push to linear branch
+ run: |
+ git config user.name 'GitHub Action'
+ git config user.email 'actions@github.com'
+ .github/workflows/linearize.sh
+ git remote add linear ./linear
+ git fetch linear
+ git checkout -b linear linear/main
+ git remote remove linear
+ git push -f origin linear