︿
Top

2022年10月25日 星期二

行尾結束字元探討(End of Line) PART 1 : Git

前言

最近在 study vs code + dotne CLI 的開發方式, 當然也括 git 的部份. 當手工下達 git add . 指令時, 會出現 LF will be replaced by CRLF the next time Git touches it 的警告訊息.

PS D:\22-Projects.Git\52-ASP.NET Core\MvcFriends> git add .
warning: in the working copy of '.gitignore', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of '.vscode/launch.json', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css', LF will be replaced by CRLF the next time Git touches it

經查測, 發現背後有不少以前沒有注意到的事項, 記錄如下.

實際測試

先說結論:

  • 1.. 如 參考文件2 蔡煥麟老師 所述的:
  1. 如果開發成員在不同平台的話, 可以將 Windows 端的 core.autocrlf 設成 true, 在 Windows 端保留 CRLF; 在 Linux or Mac 端, 可以採用 LF.
  2. 如果開發成員在相同平台, 可以設成 false. 雖然會佔用一些 remote git repository 的空間 (多存放 CR 字元), 可以避免一些二進位檔不小心被納入版控, 而其內含有 CRLF 字元, 造成成誤轉.
  • 2.. 筆者以下的結論內容, 並不一定正確, 僅供參考.
    (1) 在相同開發平台下, 目前的版控應該都有 .gitignore 過濾掉一些不需包含的資料夾或檔案, 出狀況的機會, 應該低很多. 所以, 設成 true 應該也是 OK 的. (2) )如果是 vs code + remote container (一般是採用 Linux) 的狀況, 則因為程式是透過 vs code server 進行程式碼編修及版控的, 所以最好也是設成 false.
  • 3.. 下表為初步的結論, 建構在 Ubuntu 端沒有設定 core.autocrlf 的狀況, 亦即 Ubuntu 端的 git, 不作任何轉換.
  • 4.. 經實測, 前言裡的 "in the working copy of '...' LF will be replaced by CRLF the next time Git touches it" 警告訊息, 只是告知, 如果有人動到那個檔案, 並上傳至 GitHub 後, 別人再同步至本地端, 則會被改成 CRLF. 對於程式沒有影響.
Windows GitHub Ubuntu
A. Windows autocrlf=true (AutoCrLfTrueWeb) CRLF LF LF
B. Windows autocrlf=true (AutoCrLfTrueWeb) LF LF LF
C. Windows autocrlf=false (AutoCrLfFalseWeb) CRLF CRLF CRLF
D. Windows autocrlf=false (AutoCrLfFalseWeb) LF LF LF

註: 若在 Vs Code 採用 New C# → Class 產生的檔案, 都會是 LF. 故上述表格有 B. D. 這 2 個狀況.

背景說明:

  • 1.. 是否要作 CRLF 的轉換, 是由 Git 的 Client 端決定. 若需轉換, 也是由 Git 的 Client 端作處理.
  • 2.. autocrlf=true 是指 git 在 check-in 及 check-out 均會作轉換.
    上表中的 情境A. Windows 端的 autocrlf=true, Ubuntu 端的 autocrlf=false, 所以, 當由 Windows端 check-in (git add) 時, 會作轉換, 即 GitHub端 為 LF; 當 Ubuntu端 作 check-out (git clone 隱含 check-out) 時, 不作轉換, 故保留 LF.
  • 3.. autocrlf=false 是指 git 在 check-in 及 check-out 均不會作轉換.
    上表中的 情境C. Windows 端的 autocrlf=false, Ubuntu 端的 autocrlf=false, 所以, 當由 Windows 端 check-in (git add) 時, 不作轉換, 即 GitHub端 為 CRLF; 當 Ubuntu 作 check-out (git clone 隱含 check-out) 時, 不作轉換, 也是 CRLF.
  • 4.. Windows 端: 如 參考文件3 Will 保哥所述, 因安裝 Git for Windows, 安裝過程會有選項, 若採預設值時 (Checkout Windows-style, commit Unix-style line endings ), 會將 autocrlf 設為 true.
  • 5.. Linux 端: 因為 git 預設是用 LF, 而 Linux 預設也是 LF, 所以如果沒有特別設定, autocrlf 會是 false.

步驟概要:

  • 1.. 以 dotnet CLI 建立 AutoCrLfTrueWeb 及 AutoCrLfTrueWeb
  • 2.. 設定 repository level 的 core.autocrlf
  • 3.. 加入 .gitignore
  • 4.. 本地端 git add 及 git commit
  • 5.. 在 GitHub 建立空的 repository
  • 6.. 推送到 GitHub
  • 7.. 在 Ubuntu clone GitHub repository

步驟細節:

1.. 以 dotnet CLI 建立 AutoCrLfTrueWeb 及 AutoCrLfTrueWeb

PS D:\22-Projects.Git\52-ASP.NET Core\GitAutoCrLf> dotnet new mvc -o AutoCrLfTrueWeb
範本「ASP.NET Core Web App (Model-View-Controller)」已成功建立。

正在處理建立後的動作...
正在 D:\22-Projects.Git\52-ASP.NET Core\GitAutoCrLf\AutoCrLfTrueWeb\AutoCrLfTrueWeb.csproj 上執行 'dotnet restore'...
  正在判斷要還原的專案...
  已還原 D:\22-Projects.Git\52-ASP.NET Core\GitAutoCrLf\AutoCrLfTrueWeb\AutoCrLfTrueWeb.csproj (90 ms 內)。
還原成功。
PS D:\22-Projects.Git\52-ASP.NET Core\GitAutoCrLf> dotnet new mvc -o AutoCrLfFalseWeb
範本「ASP.NET Core Web App (Model-View-Controller)」已成功建立。

正在處理建立後的動作...
正在 D:\22-Projects.Git\52-ASP.NET Core\GitAutoCrLf\AutoCrLfFalseWeb\AutoCrLfFalseWeb.csproj 上執行 'dotnet restore'...
  正在判斷要還原的專案...
  已還原 D:\22-Projects.Git\52-ASP.NET Core\GitAutoCrLf\AutoCrLfFalseWeb\AutoCrLfFalseWeb.csproj (83 ms 內)。
還原成功。

2.. 設定 repository level 的 core.autocrlf

PS D:\22-Projects.Git\52-ASP.NET Core\GitAutoCrLf\AutoCrLfTrueWeb> git init
Initialized empty Git repository in D:/22-Projects.Git/52-ASP.NET Core/GitAutoCrLf/AutoCrLfTrueWeb/.git/
PS D:\22-Projects.Git\52-ASP.NET Core\GitAutoCrLf\AutoCrLfTrueWeb> git config core.autocrlf
true
PS D:\22-Projects.Git\52-ASP.NET Core\GitAutoCrLf\AutoCrLfFalseWeb> git init
Initialized empty Git repository in D:/22-Projects.Git/52-ASP.NET Core/GitAutoCrLf/AutoCrLfFalseWeb/.git/
PS D:\22-Projects.Git\52-ASP.NET Core\GitAutoCrLf\AutoCrLfFalseWeb> git config core.autocrlf false
PS D:\22-Projects.Git\52-ASP.NET Core\GitAutoCrLf\AutoCrLfFalseWeb> git config core.autocrlf      
false

3.. 加入 .gitignore

PS D:\22-Projects.Git\52-ASP.NET Core\AutoCrLfTrueWeb> dotnet new gitignore
範本「dotnet gitignore 檔案」已成功建立。
PS D:\22-Projects.Git\52-ASP.NET Core\AutoCrLfFalseWeb> dotnet new gitignore
範本「dotnet gitignore 檔案」已成功建立。

4.. 本地端 git add 及 git commit

PS D:\22-Projects.Git\52-ASP.NET Core\GitAutoCrLf\AutoCrLfTrueWeb> git add .
warning: in the working copy of 'wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css', LF will be replaced by CRLF the next time Git touches it
...

PS D:\22-Projects.Git\52-ASP.NET Core\GitAutoCrLf\AutoCrLfTrueWeb> git commit -m "Initial Commit"
[master (root-commit) 2e8df02] Initial Commit
 80 files changed, 74496 insertions(+)
 create mode 100644 .gitignore 
 create mode 100644 AutoCrLfTrueWeb.csproj
 create mode 100644 Controllers/HomeController.cs
 create mode 100644 Models/ErrorViewModel.cs
 create mode 100644 Program.cs
 ...
 
PS D:\22-Projects.Git\52-ASP.NET Core\GitAutoCrLf\AutoCrLfFalseWeb> git add .

PS D:\22-Projects.Git\52-ASP.NET Core\GitAutoCrLf\AutoCrLfFalseWeb> git commit -m "Initial Commit"
[master (root-commit) cb0f5a9] Initial Commit     
 92 files changed, 91015 insertions(+)
 create mode 100644 .gitignore 
 create mode 100644 .vscode/launch.json
 create mode 100644 .vscode/tasks.json
 create mode 100644 AutoCrLfFalseWeb.csproj       
 create mode 100644 Controllers/HomeController.cs 
 ...

5.. 在 GitHub 建立空的 repository
截圖 (略)

https://github.com/jasper-lai/AutoCrLfTrueWeb.git  
https://github.com/jasper-lai/AutoCrLfFalseWeb.git  

6.. 推送到 GitHub

PS D:\22-Projects.Git\52-ASP.NET Core\GitAutoCrLf\AutoCrLfTrueWeb> git remote add origin https://github.com/jasper-lai/AutoCrLfTrueWeb.git

PS D:\22-Projects.Git\52-ASP.NET Core\GitAutoCrLf\AutoCrLfTrueWeb> git push -u origin master
Enumerating objects: 100, done.
Counting objects: 100% (100/100), done.
Delta compression using up to 8 threads
Compressing objects: 100% (94/94), done.
Writing objects: 100% (100/100), 912.08 KiB | 2.67 MiB/s, done.
Total 100 (delta 31), reused 0 (delta 0), pack-reused 0        
remote: Resolving deltas: 100% (31/31), done.
To https://github.com/jasper-lai/AutoCrLfTrueWeb.git
 * [new branch]      master -> master
branch 'master' set up to track 'origin/master'.
PS D:\22-Projects.Git\52-ASP.NET Core\GitAutoCrLf\AutoCrLfFalseWeb> git remote add origin https://github.com/jasper-lai/AutoCrLfFalseWeb.git

PS D:\22-Projects.Git\52-ASP.NET Core\GitAutoCrLf\AutoCrLfFalseWeb> git push -u origin master
Enumerating objects: 100, done.
Counting objects: 100% (100/100), done.
Delta compression using up to 8 threads
Compressing objects: 100% (94/94), done.
Writing objects: 100% (100/100), 912.35 KiB | 2.82 MiB/s, done.
Total 100 (delta 31), reused 0 (delta 0), pack-reused 0        
remote: Resolving deltas: 100% (31/31), done.
To https://github.com/jasper-lai/AutoCrLfFalseWeb.git
 * [new branch]      master -> master
branch 'master' set up to track 'origin/master'.

目前已推送至 GitHub, 接下來由 Ubuntu 進行 repository clone.

7.. 在 Ubuntu clone GitHub repository

user01@ubuntu-db:~/GitAutoCrLf$ git clone https://github.com/jasper-lai/AutoCrLfTrueWeb.git
正複製到 'AutoCrLfTrueWeb'...
remote: Enumerating objects: 100, done.
remote: Counting objects: 100% (100/100), done.
remote: Compressing objects: 100% (63/63), done.
remote: Total 100 (delta 31), reused 100 (delta 31), pack-reused 0
接收物件中: 100% (100/100), 912.08 KiB | 1.27 MiB/s, 完成.
處理 delta 中: 100% (31/31), 完成.
user01@ubuntu-db:~/GitAutoCrLf$ git clone https://github.com/jasper-lai/AutoCrLfFalseWeb.git
正複製到 'AutoCrLfFalseWeb'...
remote: Enumerating objects: 100, done.
remote: Counting objects: 100% (100/100), done.
remote: Compressing objects: 100% (63/63), done.
remote: Total 100 (delta 31), reused 100 (delta 31), pack-reused 0
接收物件中: 100% (100/100), 912.35 KiB | 2.80 MiB/s, 完成.
處理 delta 中: 100% (31/31), 完成.

一開始的 core.autocrlf 是沒有設定的, 而且 GitHub repository 也沒有 .git 的資料夾; 但看來是不會作轉換的 (沒有設定, 應該是等於 false); 故可以用 vi 看一下檔案 Program.cs

(1) AutoCrLfTrueWeb:
Windows 上的 Program.cs 是 CRLF,
GitHub 上的 Program.cs 是 LF,
Ubuntu 上的 Program.cs 是 LF (因為不轉換, 所以等同 GitHub 上的內容)
After Git Clone, Ubuntu is LF

(2) AutoCrLfFalseWeb:
Windows 上的 Program.cs 是 CRLF,
GitHub 上的 Program.cs 是 CRLF,
Ubuntu 上的 Program.cs 是 CRLF (因為不轉換, 所以等同 GitHub 上的內容)
After Git Clone, Ubuntu is CRLF

參考文件

  1. 基本上, 如果你的專案會同時在不同的作業系統(如 Windows、Linux)上存取, 最好是設定為 true.
  2. 在 checkin 時, Git 會將純文字類型的檔案中的所有 CRLF 字元轉換為 LF, 也就是版本庫中的換行符號一律存成 LF;
  3. 在 checkout 時, 則會將 LF 轉換成目前作業系統的換行符號, 例如在 Windows 上面就是轉成 CRLF.

先說結論:我不讓 Git 自動轉換任何換行字元,無論是純文字還是二進位檔案. Git 的預設行為是自動幫我們處理換行字元的轉換. 為了避免每台機器設定不同而產生互相踩腳的情形, 最好是在建立 repo(檔案庫)時就針對那個 repo 編寫適當的組態檔(.gitAttributes).如此一來, 無論檔案庫被複製到哪裡, 設定都不會跑掉.

  1. 如果依預設值, autocrlf=true.
  2. 當執行 git add 命令時, 文字檔案中出現的 CRLF 斷行字元會自動被轉換成 LF 字元.
  3. 而利用 git checkout 取出檔案到工作目錄時, 則會自動將 LF 字元, 轉換成 CRLF 字元.

The attributes allow a fine-grained control, how the line endings are converted. Here is an example that will make Git normalize .txt, .vcproj and .sh files, ensure that .vcproj files have CRLF and .sh files have LF in the working directory, and prevent .jpg files from being normalized regardless of their content.

沒有留言:

張貼留言