- Đội ngũ Gradle đã muốn đưa Jujutsu (jj), một VCS tương thích với Git, vào quy trình phát triển hằng ngày, nhưng đã hoãn việc áp dụng vì cách
gradlew.bat xử lý xuống dòng trên Windows
- Vấn đề cốt lõi là
jj không đọc được quy tắc EOL theo từng tệp trong .gitattributes, nên không thể áp dụng *.bat text eol=crlf
- Git chuẩn hóa
gradlew.bat thành LF trong kho lưu trữ và checkout thành CRLF trong thư mục làm việc, nhưng jj lại coi đây là thay đổi và liên tục tạo ra phantom modification
- Có thể lách bằng cách commit
gradlew.bat với CRLF nguyên trạng và đổi sang *.bat -text, nhưng nếu trình soạn thảo đổi sang LF thì Git sẽ không tự khôi phục, khiến độ ổn định khi chạy trên Windows có thể bị ảnh hưởng
- Trong thời gian tới, đội Gradle sẽ tiếp tục dùng
git worktree để vận hành nhiều thư mục làm việc, và sẽ thử lại khi jj có hỗ trợ eol
Nguyên nhân thực sự cản trở việc áp dụng: gradlew.bat và .gitattributes
- Jujutsu (
jj) là một hệ thống quản lý phiên bản tương thích Git; khi dùng backend Git, nó lưu commit và tệp vào Git và hoạt động cùng các kho Git hiện có
jj loại bỏ staging area và biểu diễn working copy bằng các commit thực được cập nhật liên tục
- Sau khi chỉnh sửa hoặc sắp xếp lại commit, nó tự động rebase các commit phía sau; xung đột có thể được ghi lại thay vì buộc dừng công việc, và cũng có thể hoàn tác qua operation log
- Việc kỹ sư Gradle cố thay
git bằng jj không bị chặn bởi mô hình của jj, mà bởi một chi tiết nhỏ trong cách dự án Gradle được đặt trên đĩa
Trên Windows, gradlew.bat cần CRLF
- Một kho Gradle thông thường có
gradlew.bat đi kèm Gradle Wrapper
gradlew.bat dùng label và goto, còn cmd.exe có thể xử lý sai các batch file chỉ có xuống dòng LF
- Để chạy ổn định trên Windows,
gradlew.bat phải được checkout với CRLF
- Dự án Gradle cưỡng chế yêu cầu này bằng
.gitattributes
*.bat text eol=crlf
- Trong Git, đây là luồng xử lý tiêu chuẩn
- Tệp được lưu vào index ở dạng đã chuẩn hóa
- Thuộc tính
eol=crlf sẽ hiện thực hóa CRLF vào working copy khi checkout
- Nội dung chỉnh sửa sẽ lại được chuẩn hóa khi lưu trở lại
Việc jj không hỗ trợ .gitattributes tạo ra phantom diff
jj không đọc .gitattributes, nên không thể áp dụng quy tắc xuống dòng theo từng tệp
- Tính năng liên quan đang được theo dõi trong issue cũ jj-vcs/jj#53, trong đó tối thiểu là hỗ trợ thuộc tính
eol đang được bàn tới
- Hiện tại, lựa chọn có thể dùng trong
jj chỉ là cấu hình toàn cục working-copy.eol-conversion
- Tương tự
core.autocrlf của Git, nó áp dụng đồng loạt cho mọi tệp
- Không thể chỉ định riêng cho tệp
.bat
- Với colocated repo, vấn đề lộ ra ngay lập tức
- Git lưu
gradlew.bat dưới dạng LF và checkout thành CRLF
- Vì
jj không tôn trọng thuộc tính đó, nó ghi nhận CRLF trên đĩa là thay đổi so với blob LF trong Git
- Trong các dự án Gradle bị ảnh hưởng,
gradlew.bat sẽ xuất hiện thay đổi giả liên tục
- Đây không chỉ là trường hợp riêng của đội Gradle
- Một người dùng cho biết phantom change đã xảy ra trên hàng trăm nghìn tệp, khiến họ từ bỏ
jj
Các cách lách được đề xuất và giới hạn của chúng
- Tác giả của
jj đã đề xuất trong issue dùng non-colocated workspace như một hướng đi
- Ví dụ: dùng
jj git init --git-repo=<path> từ bên ngoài working copy của Git
- Kỹ sư Gradle cho rằng cách này cũng không giải quyết được vấn đề
- Khi
jj hiện thực hóa gradlew.bat từ blob LF đã lưu, tệp trên đĩa vẫn sẽ là LF
gradlew.bat với LF sẽ không chạy được trên Windows
- Có thể ép CRLF bằng cấu hình toàn cục
working-copy.eol-conversion = input-output
- Nhưng điều này sẽ ghi lại mọi tệp văn bản thành CRLF khi checkout, tạo ra một vấn đề khác
Giải pháp hoạt động được nhưng mong manh
- Cách thực sự hoạt động là commit
gradlew.bat vào kho với CRLF nguyên trạng và dừng việc chuẩn hóa
- Quy tắc sẽ đổi như sau
- Trước đây:
*.bat text eol=crlf
- Đổi thành:
*.bat -text
- Khi đó, cả Git và
jj đều thống nhất dùng CRLF ở cả cây lưu trữ lẫn working copy
- Không còn chuyển đổi
- Phantom diff biến mất
- Khả năng chạy trên Windows vẫn được giữ nguyên
- Đổi lại,
*.bat -text sẽ từ bỏ cơ chế tự khôi phục mà text eol=crlf từng cung cấp
- Trước đây, nếu vô tình có LF lọt vào, nó sẽ âm thầm được chuẩn hóa lại khi commit
- Giờ đây LF sẽ còn nguyên, và
gradlew.bat có thể âm thầm hỏng trên Windows
- Việc chuyển đổi này chỉ cần làm một lần, và các chỉnh sửa sai thường có thể bị phát hiện trong quá trình review
- Dù vậy, đội Gradle cho rằng khó có thể đặt cược cả hệ sinh thái vào giả định rằng mọi dự án Gradle đều sẽ commit CRLF trực tiếp và không có trình soạn thảo nào lặng lẽ đổi chúng đi
Giải pháp thay thế tiếp tục dùng trong lúc này: git worktree
- Workspace của
jj cung cấp nhiều thư mục làm việc dùng chung một kho, giúp các lượt chạy CI dài hoặc thay đổi đang dang dở không chặn công việc tiếp theo
- Đội Gradle hiện đã xử lý tốt nhu cầu này bằng
git worktree
git worktree add ../gradle-feature-x feature-x
git worktree add ../gradle-hotfix hotfix-7.6
- Cách này dùng hai checkout nhưng chỉ một object store
- Có thể vừa làm việc song song vừa giữ một checkout
main tồn tại lâu dài mà không cần stash hay clone thứ hai
- Đội Gradle đã dùng mô hình này trong nhiều năm
Khả năng xem xét lại jj
- Trường hợp này không phải là phán quyết cuối cùng của dự án Gradle đối với
jj
- Chỉ riêng operation log của
jj cũng đã đủ đáng để xem xét sâu hơn
- Hỗ trợ
eol đang được xử lý như một vấn đề có thể giải quyết, và trong issue cũng có thảo luận về việc triển khai qua gix-filter
- Khi hỗ trợ đó xuất hiện, đội Gradle sẽ thử lại
- Còn trước mắt, họ vẫn giữ Git vì
gradlew.bat, còn git worktree sẽ đảm nhiệm vai trò cần thiết
1 bình luận
Các ý kiến trên Lobste.rs
Tôi cho rằng thiết lập
autocrlfcủa Git và thiết lập.gitattributessau đó ngay từ đầu đã là sai lầm, và về sau cũng sẽ vẫn như vậyHệ thống quản lý phiên bản không nên lưu trữ các tệp trong kho khác với các tệp trên đĩa
Nếu muốn áp đặt định dạng tệp, đó là việc của linter, commit hook, merge hook, chứ không phải vai trò của hệ thống quản lý phiên bản
core.{eol, safecrlf, autocrlf}vào diện dự kiến loại bỏChuyển đổi ký tự kết thúc dòng được thêm vào chỉ vì Windows; kiểu chỉ dùng ␍ thì đã chết từ trước Git, còn mọi nơi trừ Windows đều đã ổn định với kiểu chỉ dùng ␊
Giờ đây có lẽ đối tượng chính thực sự còn quan tâm đến ␍␊ chỉ là các tệp batch
Mọi ngôn ngữ đều cố gắng hỗ trợ ␍␊, nhưng bây giờ hầu như không còn gì yêu cầu hoặc tạo ra nó nữa
Nếu Notepad vẫn dùng ␍␊ làm mặc định hoặc chỉ hỗ trợ nó, thì người dùng Notepad phải tự chịu hậu quả
Một lĩnh vực thú vị hơn trong khả năng tương thích Windows là file mode
Git đã mang theo mode của Unix và dấu vết đó vẫn còn, nên khi làm việc trên Windows đôi khi có thể phát sinh lỗi mode ngoài ý muốn
Nếu công cụ đủ tốt để khi checkout có thể tự động dùng khoảng trắng, hoặc căn chỉnh theo một số lượng khoảng trắng cụ thể kỳ quặc mà ai đó cực kỳ cố chấp, thì ta có thể chấm dứt tranh cãi tab và space
Xa hơn nữa, thậm chí có thể cho rằng hệ thống quản lý phiên bản nên lưu cây cú pháp trừu tượng (AST) thay vì văn bản gốc
Hiện tại có thể khó thuyết phục, nhưng cũng có thể tưởng tượng ra một dòng thời gian khác hoặc một tương lai nơi cách làm này rất tự nhiên
Tôi không biết tệp
.batthì ngoài Windows ra còn chạy ở đâu nữaCũng thắc mắc là có trường hợp nào mà một hành vi khác dù chỉ một chút lại đáng mong muốn hay không
Tôi không bị thuyết phục bởi lập luận rằng không nên commit tệp đúng nguyên định dạng kết thúc dòng cần thiết
Điểm cốt lõi có vẻ là “ai đó có thể làm hỏng ký tự kết thúc dòng rồi commit, qua được review code và đưa vào một script bị hỏng”
Tất cả đều đúng, nhưng chẳng phải vì thế mới có kiểm tra lint trên CI sao?
Nếu quét các ký tự kết thúc dòng không phải CRLF trong tệp đó và khi phát hiện thì cho CI fail, thì thay đổi làm hỏng ký tự kết thúc dòng không thể vượt qua giai đoạn pull request
Câu trả lời “chỉ cần thêm CI là được” là chưa đủ
Dự án liên quan có thể là một mod Minecraft do người khá mới làm, và bất kỳ ai trong số đó cũng có thể làm hỏng
gradle.batmà không nhận raTôi cũng có trách nhiệm vì đã quảng bá jj như một lựa chọn thay thế tốt hơn nhiều so với CLI Git cho người mới bắt đầu
Liệu có khả thi không nếu chuyển từ
gradlew.batsanggradlew.ps1, tức là từcmd.exe/tệp batch sang PowerShell?Giờ thì MAYBE có thể giả định 90% nền tảng cài đặt Windows đã có PowerShell đủ mới
Nhưng vẫn có trường hợp phải nâng quyền thực thi mặc định, và trong môi trường doanh nghiệp thì có thể bị khóa
Không biết bạn đã dùng PowerShell nhiều đến đâu, nhưng nó không có cảm giác kiểu “có bash rồi thì chỉ cần chạy tệp
.sh”Tập tính năng khác nhau giữa các phiên bản Windows, cảm giác giống như so sánh
kshvớibash4vậy