Giảm dung lượng repo (16.10.2026)
Ngày 16.10.2026 chủ repo quyết định đưa conanplatform về cỡ vài chục MB. Cây git theo dõi lúc đó nặng 561 MB, phần lớn là thứ không phải mã: ảnh raster, nội dung khoá học, migration chèn nội dung. Làm trong ba bước, mỗi bước một PR, theo thứ tự rủi ro tăng dần.
| Bước | Việc | Trạng thái |
|---|---|---|
| 1 | Ảnh raster sang R2, xoá ảnh chết | PR "Giảm dung lượng (1/3)" |
| 1b-1 | Bìa sách: cột cover_svg_hash, endpoint ảnh, mã đọc cả hai trạng thái | PR "Giảm dung lượng (1b-1/3)" |
| 1b-2 | Bìa sách: migration dữ liệu, xoá 216 SVG, luật 301 | PR "Giảm dung lượng (1b-2/3)", sau khi 1b-1 đã deploy |
| 2 | Nội dung + migration đã áp sang repo conan-content | PR "Giảm dung lượng (2/3)" (17.10.2026), cùng PR "Bộ biên dịch, CI, luật làm việc" ở conan-content |
| 3 | Viết lại lịch sử git (bỏ blob lớn khỏi mọi commit) | sắp làm, sau bước 2 |
Bước 1 và 2 chỉ làm nhẹ cây làm việc; .git vẫn giữ mọi blob cũ cho tới bước 3. Đừng đo kết quả bằng du -sh .git trước bước 3.
Bước 1: ảnh raster
Đã chuyển sang R2 (57 ảnh, 11,7 MB)
Ảnh được tải lên R2 bucket conan-cms (tên miền công khai https://image.conan.school) và so sha256 từng byte với bản trong git TRƯỚC khi xoá. Sổ ghi lại: scripts/media-r2-manifest.tsv, bốn cột cách nhau bằng tab:
| Cột | Nghĩa |
|---|---|
key | khoá R2, luôn site/<app>/<đường cũ dưới public/> |
sha256 | băm của file, 64 hex thường |
bytes | cỡ file |
source_path | đường cũ trong repo, <thư mục app>/public/... |
| Nhóm | Đường cũ | Khoá R2 | Số ảnh |
|---|---|---|---|
| tokyo, album | com.conan.school/public/albums/*.webp | site/tokyo/albums/ | 3 |
| tokyo, hero trang | com.conan.school/public/images/page-heroes/{calendar,deep-books,my-courses,paper}.jpg | site/tokyo/images/page-heroes/ | 4 |
| tokyo, ảnh khoá | com.conan.school/public/images/courses/* | site/tokyo/images/courses/ | 16 |
| video2, storyboard | video2.conan.school/public/images/storyboards/* | site/video2/images/storyboards/ | 34 |
Mã trỏ tới ảnh qua một hằng số mỗi app:
- tokyo:
MEDIA_BASE = 'https://image.conan.school/site/tokyo'trongcom.conan.school/app/env.public.ts, dùng ởapp/lib/course-images.ts, bốn route có hero (books,calendars,library,my-courses),app/components/events/EventsContent.tsxvàapp/routes/ccc.tsx. - video2: storyboard được đọc từ JSON tĩnh
public/api/videos/jobs-to-be-done.json, JSON không import được hằng số, nênimageUrlghi URL đủhttps://image.conan.school/site/video2/images/storyboards/....
Đã xoá vì không ai dùng (34 file, 75,9 MB)
Mỗi file được git grep theo tên trên toàn repo và tra D1 production (câu SELECT trên mọi cột TEXT có tên kiểu ảnh/URL của 122 cặp bảng-cột) trước khi xoá.
| File | Cỡ | Vì sao chết |
|---|---|---|
com.conan.school/public/albums/*.png (3) | 41,0 MB | bản gốc PNG của ba ảnh album; mã chỉ dùng bản .webp |
com.conan.school/public/mentors/*.png (13) | 16,6 MB | ảnh mentor trong D1 đã trỏ image.conan.school (hoặc dicebear); không mã nào đọc thư mục này |
com.conan.school/public/images/ccc/* (6) | 5,6 MB | trang /ccc dùng icon Lucide, không dùng ảnh này |
com.conan.school/public/images/page-heroes/{home,courses}.jpg | 0,6 MB | chỉ còn trong bảng của docs/ui-style-guide.md, không route nào dùng |
api.conan.school/assets/workshops/generated/* (10) | 12,1 MB | api không có binding [assets]; D1 (workshops.thumbnail_url, cover_url) trỏ https://image.conan.school/workshops/generated/*.webp, bản webp trên R2 trùng sha256 với bản trong git; năm file .png là bản gốc không ai đọc |
Tổng bước 1: 91 file, 87,6 MB, cây theo dõi từ 561 MB xuống 474 MB.
Lưới an toàn: _redirects
Tokyo và video2 là SPA trên Pages, public/_redirects kết thúc bằng /* /index.html 200. Một đường ảnh không có file sẽ rơi vào luật đó và trả index.html với 200: thẻ <img> vỡ, không 404, không lỗi console có nghĩa, không gì đỏ. Nên mỗi thư mục đã chuyển có một luật 301 sang R2, đứng trước luật SPA (Pages áp luật đầu tiên khớp):
/albums/* https://image.conan.school/site/tokyo/albums/:splat 301
/images/page-heroes/* https://image.conan.school/site/tokyo/images/page-heroes/:splat 301
/images/courses/* https://image.conan.school/site/tokyo/images/courses/:splat 301
/* /index.html 200Lưới chỉ đỡ cho bookmark, bộ đệm, nội dung ngoài repo còn trỏ đường cũ. Mã trong repo phải trỏ thẳng R2, và gác bắt điều đó.
Gác
scripts/check-media-r2.mjs (id media-r2 trong scripts/run-guards.mjs, npm run check:media-r2):
- Không
source_pathnào trong sổ có mặt lại trong cây (đã theo dõi, hoặc mới mà chưa bị ignore). - Không file nào trỏ vào đường cũ của ảnh đã chuyển, trừ khi đứng sau gốc R2 (
site/<app>), sauMEDIA_BASE}, hay sau/public(đường file khi tài liệu kể lại). Bắt cả dạng chuỗi mở đầu bằng thư mục cũ rồi nối biến (cáchcourse-images.tstừng ghép đường ảnh khoá). Miễn: chính sổ, chính gác, các file_redirects, và dòng mangmedia-r2-ok: <lý do>(smoke cố ý hỏi đường cũ để kiểm lưới 301). - Sổ đúng hình dạng: tiêu đề, bốn cột, sha256 64 hex, khoá =
site/<app>+ đường cũ, không khoá haysource_pathtrùng, app có trong bảngAPPScủa gác. public/_redirectscủa mỗi app có luật 301 cho mỗi thư mục đã chuyển, trước luật SPA.
--self-test chạy 26 mẫu (phải bắt / phải tha). CI không gọi mạng. Kiểm R2 thật bằng tay hay vòng đêm:
npm run check:media-r2-live # HEAD + tải từng URL trong sổ, so 200, content-type ảnh, cỡ, sha256Sau mỗi lượt deploy tokyo và video2, scripts/smoke-production.mjs HEAD vài URL R2 và hỏi đường ảnh cũ trên site có 301 tới đúng URL R2 không (200 là lưới hỏng). Xem smoke-production.
Thêm một ảnh raster mới (từ 16.10.2026)
Ảnh raster (jpg, png, webp, gif, avif) không vào git. SVG sinh bằng thuật toán (course-art, hình tài liệu lesson) vẫn ở trong git: chúng là mã, nhỏ, và có gác so bộ vẽ. Bìa sách là ngoại lệ, đã vào D1 ở bước 1b (dưới đây).
Nén ảnh (webp nếu được). Đặt khoá
site/<app>/<đường>,<app>làtokyo,video2... (tên miền con, không phải tên thư mục).Tải lên R2 bằng tài khoản có quyền R2 (người vận hành; agent không tự ghi R2 khi chưa được cho phép):
bashwrangler r2 object put conan-cms/site/tokyo/images/page-heroes/ten-anh.webp \ --file ten-anh.webp --content-type image/webp --remoteĐây là ghi dữ liệu vào bucket, không phải deploy, nên không trái luật "không deploy từ máy". Ảnh do người soạn tải qua admin (media library, mentor, album, learner stories) đi đường binding
IMAGES+ R2 của api như cũ, không qua bước này.Thêm một dòng vào
scripts/media-r2-manifest.tsv(shasum -a 256 ten-anh.webp,stat -f%z).Trong mã tokyo:
`${MEDIA_BASE}/images/page-heroes/ten-anh.webp`. App khác chưa cóMEDIA_BASEthì khai một hằng số ở module hằng số công khai của app đó, đừng rải URL.npm run check:media-r2rồinpm run check:media-r2-live.
Chuyển một thư mục ảnh mới sang R2 (không phải thêm từng ảnh) thì thêm luật 301 vào _redirects của app đó, trước luật SPA; gác đỏ cho tới khi có.
Bước 1b: SVG bìa sách vào D1 (16.10.2026)
Chủ repo quyết định: SVG bìa sinh bằng thuật toán sống trong cơ sở dữ liệu, không trong git. Trước đó 216 tệp com.conan.school/public/images/book-covers/*.svg (14,5 MB, trung vị 64 KB, lớn nhất 184 KB) do api.conan.school/scripts/book_art.py build ghi ra, và legacy_ebooks.cover_url giữ /images/book-covers/<slug>.svg?v=... (tokyo phục vụ tệp, api đổi thành URL tuyệt đối tới tokyo).
| Thứ | Trước | Sau |
|---|---|---|
| SVG | tệp trong git, tokyo Pages phục vụ | D1 legacy_ebooks.cover_svg |
| Băm | ?v= = sha1 8 ký tự của tệp (191/216 hàng còn ?v=2 cố định) | cover_svg_hash = 12 hex đầu sha256, cột riêng |
cover_url | /images/book-covers/<slug>.svg?v=... | NULL (ảnh tải lên, nếu có, luôn thắng và không bị đụng) |
| URL ra ngoài | https://tokyo.conan.school/images/book-covers/<slug>.svg?v=... | https://api.conan.school/api/public/ebooks/<id>/cover.svg?v=<băm> |
| Bộ đệm | tokyo _headers /* = no-store (mọi lượt mở kệ tải lại cả kệ) | immutable một năm khi ?v= khớp băm, Cache API của Worker, ETag |
| Nguồn để kiểm | tệp so với bản vẽ | stats.sha256 trong content/books/art/*.json so với bản vẽ; check --remote so D1 |
Migration. 20261115a_ebooks_cover_svg_hash.sql thêm cột. 18 tệp 20261115b_book_cover_svg_<cộng đồng>.sql (sinh bằng book_art.py sql) ghi SVG: tổng 15,9 MB, 0,74-1,06 MB mỗi tệp (tiền lệ lớn nhất trước đó 1,5 MB), 574 câu, câu lớn nhất 60.164 byte (D1 từ chối câu > ~100 KB). Mỗi quyển 1-4 câu một dòng: câu đầu ghi đoạn 1, các câu sau nối cover_svg || '...', câu cuối ghi băm + gỡ cover_url chỉ khi length(cover_svg) đã khớp. Mọi câu có điều kiện cover_url rỗng hoặc còn là đường tệp cũ. Chạy lại vô hại.
Con số thật của bước này, nói thẳng: cây làm việc bớt 14,5 MB SVG nhưng thêm 15,9 MB migration, tức không nhỏ đi ở bước 1b. Cái lợi là SVG giờ nằm cùng loại với mọi migration nội dung khác, nên bước 2 (dời migration nội dung sang conan-content) mang nó đi luôn; và tokyo Pages không còn phục vụ 216 tệp no-store.
Thứ tự triển khai: hai PR, mã đọc trước, dữ liệu sau. CI áp migration TRƯỚC bước deploy api trong cùng job (_deploy-cloudflare.yml). Nếu migration dữ liệu đi cùng mã mới thì có vài phút mã CŨ chạy trên dữ liệu MỚI: cover_url đã NULL, mã cũ lùi về svgDataUri(cover_svg), và /deepbooks, /content/ebooks phình thành JSON hàng chục MB (data URI cả kệ ~24 MB). Nên tách:
| PR | Gồm | Vì sao an toàn |
|---|---|---|
| 1b-1 | migration lược đồ 20261115a (thêm cột), endpoint ảnh, shared/bookCover.ts cho mọi đường đọc, miễn trừ middleware, book_art.py mới, sửa heartbeat + book_pack_to_sql, tài liệu | cột có trước mã (cùng job). Mã đọc đúng dữ liệu HÔM NAY: băm NULL và cover_url là đường tệp thì trả URL tuyệt đối tới tokyo như cũ; 216 tệp còn nguyên |
| 1b-2 | 18 migration 20261115b_*, xoá 216 tệp, luật 301 ở tokyo, lật cờ enforced của gác, phát lại migration trong test, smoke | mã đang chạy (1b-1) đã đọc băm, nên migration áp trước deploy không còn cửa sổ; tokyo xoá tệp sau khi không hàng D1 nào trỏ tới chúng |
1b-2 chỉ mở/merge SAU khi 1b-1 đã deploy (kiểm gh run list --workflow=deploy-api.yml theo SHA merge). Tokyo deploy ở workflow riêng: nếu ở 1b-2 nó lên trước api thì đường cũ 301 tới endpoint, mà endpoint (đã có từ 1b-1) trả bìa từ D1 sau khi migration đã áp, nên không có khoảng hở.
Đường cũ. Tên tệp cũ là <slug>.svg, nên tokyo _redirects chuyển /images/book-covers/* 301 tới https://api.conan.school/api/public/ebooks/covers/:splat, và route đó tra theo slug. Quét D1 chỉ đọc (16.10.2026, 535 cột văn bản có tên kiểu URL/ảnh/json/nội dung trong 319 bảng, bỏ bảng nhật ký): chỉ legacy_ebooks.cover_url (216, migration gỡ) và legacy_book_ops_issue_registry (216, bằng chứng của các issue COVER_BROKEN giả, xem known-risks) còn nhắc /images/book-covers/.
Gác. check-media-r2.mjs thêm MOVED_TO_D1 (cờ enforced, 1b-2 lật thành true): thư mục cũ không có tệp nào, luật 301 có mặt và đứng trước luật SPA. npm run test:book-covers (job api) kiểm endpoint và đường đọc ở cả hai trạng thái dữ liệu (1b-1), rồi phát lại migration lên SQLite và so sha256 đầy đủ của bốn quyển (kể cả quyển lớn nhất) với tệp cũ, cùng băm của cả 216 quyển (1b-2). Từ 1b-2, smoke-production.mjs hỏi endpoint ảnh bìa sau mỗi lượt deploy api, và hỏi đường cũ của tokyo có 301 đúng chỗ sau mỗi lượt deploy tokyo.
Hoàn tác. Theo thứ tự ngược: hoàn tác dữ liệu (1b-2) TRƯỚC, mã (1b-1) sau; mã 1b-1 đọc đúng cả hai trạng thái nên có thể ở lại. Dữ liệu: một migration mới đặt lại cover_url = '/images/book-covers/' || slug || '.svg' cho các hàng có cover_svg_hash, và khôi phục thư mục tệp từ lịch sử git (git checkout <sha trước PR> -- com.conan.school/public/images/book-covers). Không cần xoá cover_svg.
Bước 2: nội dung và migration đã áp sang conan-content (17.10.2026)
Repo mới dac2205/conan-content tách từ main 5917709d bằng git filter-repo (giữ lịch sử của các tệp), nằm cạnh repo này ở ~/ConanCode/conan-school/conan-content. Từ đây có ba repo: repo-split.
Cái gì đi, cái gì ở lại
| Thứ | Đi đâu | Ghi chú |
|---|---|---|
api.conan.school/content/{books,courses,jtbd,playbooks,playbook-plan,forum,library,brands,personas} | conan-content content/ | cây git giống hệt (so mã băm tree từng thư mục với main) |
api.conan.school/content/{bmc,whats-new,communities} | ở lại | Worker import content/bmc; gác whats-new và community_art_check.py đọc hai thư mục kia |
31 bộ biên dịch chỉ đọc nội dung và ghi migration (*_to_sql.py của sách, khoá, JTBD, playbook, thư viện nghiên cứu, jtbd_audit.py, book_blocks.py, balance_quiz_answers.py, coursepack_scaffold_l2.py, book_section_draft_save.py), scripts/forum-seed.mjs, scripts/audit-big-ideas.mjs | conan-content compilers/ | mỗi file ghi nguồn gốc ở đầu; mặc định ghi migration vào ../conanplatform/api.conan.school/migrations/ (CONAN_MIGRATIONS_DIR đè). Trừ playbook (17.10.2026): playbook_pack_to_sql.py từ chối đích conanplatform, chỉ còn --target cms ra migration nháp ở conan-web; playbook_meta_rewrite_to_sql.py đóng (editorial-cms-cutover) |
scripts/check-jtbd.mjs (gác jtbd), job CI coursepack | conan-content scripts/, .github/workflows/ci.yml | phép kiểm chéo hai repo bỏ qua thành tiếng ở CI bên đó |
Bộ vẽ: book_art.py, book_illustrations.ts, book_cover_svg.py, course_art_to_sql.py, course_covers_to_sql.py, audit_books.py, studio collections.ts --build | ở lại | cần mã vẽ của repo này (src/, cảnh bìa, SVG trong com.conan.school/public); đọc nội dung qua api.conan.school/scripts/content_repo.py (CONAN_CONTENT_DIR, CONAN_CONTENT_REPO, rồi ../conan-content, ../conan-school/conan-content, ../../conan-content); thiếu thì dừng rõ ràng |
whats_new_to_sql.py, text_rules.py, gen_library_shelf.py | ở lại (text_rules.py có thêm bản chép bên kia) | |
| 3.378 migration dữ liệu ĐÃ ÁP (305 MB) | conan-content migrations-archive/ | mỗi file một dòng api.conan.school/migrations/ARCHIVED.tsv |
Luật giữ migration ở lại (327 file, 29,3 MB)
Một file ở lại nếu khớp bất kỳ luật nào dưới đây; còn lại (dữ liệu, đã áp) thì lưu trữ:
- Tên
<= BASELINE(20260829o_backfill_queue.sql): 164 file. Bộ áp đếm chúng; giữ nguyên để phép đếm không phải đổi nghĩa. - Có DDL ở bất kỳ đâu trong file (
CREATE|ALTER|DROP ... TABLE|INDEX|TRIGGER|VIEW,ALTER TABLE,PRAGMA; khớp cả trong chuỗi, tức thà giữ thừa): 215 file. Lược đồ đang chạy phải mô tả được từ repo này (data-model.md, test dựng bảng). - Tên được nhắc trong mã còn ở lại (test,
select-jobs, danh mục bất biến D1, chú thíchsrc/): 39 file. - Gác đọc theo NỘI DUNG:
community_(library_docs|templates|collections)(gáclibrary-docs, 64 file),course_lesson_docs(lesson-docs, 6), URLtokyo.conan.school/lesson-docs/(figures, 3), tên chứawhats_new(whats-new, 3). - Tên
>= 20261106: 40 file, tức mọi migration thêm vào git trong bảy ngày 10-17.10.2026 (đo bằnggit log --diff-filter=A). Vì sao có luật này: đó là những PR vừa gộp mà phiên khác còn có thể đang làm tiếp (sửa nối, hoàn tác, so test); dời chúng ngay là đổi chỗ đứng của việc đang dở. Cái giá: ~8 MB migration studio và 15,9 MB SVG bìa sách (20261115b_*, cũng là file test đọc) ở lại. Đợt lưu trữ sau dời chúng theo quy trình ở skilldeploy-migrate.
Sổ lưu trữ và bộ áp
api.conan.school/migrations/ARCHIVED.tsv, năm cột tách bằng tab, xếp theo tên:
| Cột | Nghĩa |
|---|---|
filename | tên file đã lưu trữ (vẫn có trong sổ D1 schema_migrations) |
sha256 | băm của file, 64 hex thường |
bytes | cỡ file (có file rỗng, 0 byte) |
archive_repo | dac2205/conan-content |
archive_commit | SHA đủ của commit conan-content có migrations-archive/<filename> (lượt này: cafc7e66) |
Kiểm lúc soạn: 3.378/3.378 dòng có trong sổ D1 production (SELECT), sha256 + cỡ khớp đối tượng git ở archive_commit.
Bộ đọc dùng chung scripts/lib/archived-migrations.mjs. scripts/apply-d1-migrations.mjs:
- Mốc nền = file trên đĩa + dòng lưu trữ
<= BASELINE, vẫn phải bằng 164. - DỪNG nếu một tên trên đĩa có trong sổ (tên dùng lại, xem known-risks).
- DỪNG nếu một dòng lưu trữ không có trong sổ D1 (chỉ file đã áp mới được lưu trữ; đọc từ xa trong CI).
- Không bao giờ ghi sổ D1 cho dòng lưu trữ.
--dry-runchỉ đọc sổ và in những gì SẼ áp.
Dry-run lúc soạn (17.10.2026, production): 327 file trên đĩa, 3378 file đã lưu trữ (đều có trong sổ D1), không có migration mới. verify-d1-after-migrate.mjs và bất biến migrations-ghost-rows coi dòng ma = sổ D1 trừ (thư mục ∪ sổ lưu trữ): cả hai 0.
Gác archived-migrations (scripts/check-archived-migrations.mjs, 15 mẫu tự kiểm): hình dạng sổ, tên dùng lại, file .sql biến mất so với origin/main mà không ghi sổ, và sha256 từng dòng với conan-content khi thấy repo đó (CI không thấy: bỏ qua thành tiếng, chạy ở máy trước khi gộp PR chạm sổ).
Người đọc khác đã sửa
| Người đọc | Sửa |
|---|---|
check-library-docs, check-lesson-docs, build-lesson-doc-figures, check-whats-new, bảy test đọc migration | không đổi: mọi file chúng đọc nằm trong luật giữ |
api.conan.school/tests/d1-cleanup.test.mts | phép kiểm book_pack_to_sql.py chuyển sang conan-content/scripts/check-compilers.mjs |
scripts/ci/select-jobs.mjs | gỡ luật coursepack, mẫu tự kiểm 6 và 27 |
deploy-api.yml | bộ lọc đường dẫn chỉ còn hai thư mục nguồn ở lại |
audit-courses.yml | để trống slugs thì lấy khoá có course_seed.added_by LIKE 'coursepack%' từ D1 (206 khoá) thay vì file pack |
d1-invariants.yml | sparse-checkout lấy thêm ARCHIVED.tsv và bộ đọc |
Người soạn làm việc thế nào từ giờ
- Sửa JSON ở
conan-content/content/...,--checkbằng bộ biên dịch ởconan-content/compilers/. - Biên dịch ra một file migration MỚI (tên xếp sau file mới nhất, không trùng
ARCHIVED.tsv) trongconanplatform/api.conan.school/migrations/. - Hai PR: nguồn ở conan-content (gộp trước hoặc cùng lúc), migration ở conanplatform. CI của conanplatform áp như cũ.
npm run checkở conan-content với../conanplatformcó mặt, để phép kiểm chéo (Set JTBD đã có migration) không bị bỏ qua.
Luật làm việc của repo kia: conan-content/CLAUDE.md.
Con số
Trước (main 5917709d) | Sau | |
|---|---|---|
| File theo dõi | 8.817 | 3.198 |
Byte theo dõi (git ls-files -z | xargs -0 cat | wc -c) | 475,3 MB | 58,6 MB |
Đĩa (du -k cộng từng file) | 471 MB | 63 MB |
api.conan.school/migrations/ | 3.705 file, 334 MB | 327 file + sổ, 29,3 MB |
Như bước 1, .git vẫn giữ mọi blob cũ cho tới bước 3. Đo bằng du -k | awk '{s+=$1}' chứ không du -ck | tail -1: xargs chẻ danh sách thành nhiều lượt du, dòng total cuối chỉ là lượt cuối.
Hoàn tác
Hoàn tác PR này (revert) là đủ: mọi file nằm nguyên trong lịch sử git của repo này và trong conan-content. Bộ áp cũ không đọc ARCHIVED.tsv, và sổ D1 đã có mọi tên, nên file quay lại thư mục không bị áp lại. Đừng lưu trữ thêm file nào cho tới khi PR này đã deploy (kiểm gh run list --workflow=deploy-api.yml theo SHA merge).
Bước 3: viết lại lịch sử (xong 17.10.2026)
git filter-repo chạy trên một bản clone gương, giữ đúng các đường dẫn đang có ở đầu main cộng các file mà mỗi PR đang mở thực sự đổi (so với điểm rẽ nhánh), và gỡ khỏi TOÀN BỘ lịch sử mọi đường dẫn khác: thư mục đã xoá từ lâu, ảnh đã chuyển R2 hoặc đã chết, www.conan.school/ (sang conan-web), api.conan.school/content/* và 3.378 migration đã lưu trữ (sang conan-content).
| Trước | Sau | |
|---|---|---|
Gói .git của một bản clone | 398 MB | ~20 MB |
| Một bản clone (lịch sử + file) | ~760 MB | ~83 MB |
Commit trên main | 1.262 | 838 (commit chỉ chạm đường dẫn đã gỡ biến mất) |
Bất biến đã kiểm trước khi đẩy:
- Cây file của
mainsau khi viết lại có CÙNG mã băm với trước (ea56165792f9…): không một file đang dùng nào đổi. - Patch-id diff của 5/8 PR đang mở giữ nguyên; 3 PR còn lại chỉ mất phần thuộc www,
content/bookshoặc lệnh xoá file vốn đã chết (#331, #326, #256). - Đẩy 175 nhánh trong MỘT lượt
--atomic, mỗi nhánh kèm--force-with-leasetheo SHA cũ.
Sao lưu lịch sử cũ (đủ mọi nhánh): repo riêng tư dac2205/conanplatform-history-backup và bundle ở ~/ConanCode/conan-school/backups/conanplatform-full-2026-10-17-truoc-viet-lai.bundle.
Gốc lịch sử mới e23cf394…, gốc cũ 7ecd22aa…. Gác history-root (scripts/check-history-root.mjs) đỏ khi HEAD còn mang gốc cũ: một nhánh tạo từ bản clone cũ mà được merge là kéo nguyên 400 MB quay lại, mã vẫn đúng nên không gì khác đỏ.
Bản clone cũ trên máy: đừng git pull vào bản cũ (sẽ trộn hai lịch sử). Clone lại, rồi chép thay đổi chưa đẩy sang bằng git format-patch / git am hoặc chép file. Ở folder tổng, ~/ConanCode/conan-school/conanplatform là bản clone mới.
Liên quan
- Ba repo: conanplatform, conan-web, conan-content, lần tách đầu (www rời lõi) và bước 2 ở trên.
- known-risks: Tên migration dùng lại
- known-risks: SPA trả 200 cho ảnh không có