前言
在前一篇 行尾結束字元探討 PART 1 : Git, 針對 行尾結束字元(End of Line) 在 Git 上的一些議題, 作了一些討論.
本篇再針對 End of Line 在 Vs Code 上的一些議題, 作一些討論.
經過實測發現, 雖然前一篇提到出現 "warning: in the working copy of '.gitignore', LF will be replaced by CRLF the next time Git touches it" 的訊息, 其實對整個版控並無影響, 只是一個提醒而已; 但這整個過程, 將以前沒有注意到的細節, 作了一次探討; 也第一次在 Ubuntu 上安裝 ASP.NET Core 6.0 SDK 及 Visual Studio Code, 並在 Ubuntu 上進行程式修及版控, 也算是有收穫了.
實際測試
Git 的 core.autocrlf 觀念釐清
- 1.. 所謂 Git 的 core.autocrlf 只要設定為 true, 不論在 Windows 端或 Ubuntu 端, 由 GitHub 同步回來時, 都會轉成 CRLF.
- 2.. 所謂 Git 的 core.autocrlf 只要設定為 false, 不論在 Windows 端或 Ubuntu 端, 由 GitHub 同步回來時, 都會是維持 GitHub 上的原始狀況.
步驟概要
- 1.. 由 dotnet new 產生的 *.proj, Program.cs, *.json 是 UTF8 + CRLF
- 2.. 由 dotnet new 產生的 HomeController.cs, Views\Home\Index.cshtml 都是 UTF8 with BOM + CRLF
- 3.. 由 dotnet aspnet-codegenerator controller 指令產生出來的 FriendsController.cs, Views\Friends\Index.cshtml 都是 UTF8 + CRLF
- 4.. 按 Ctrl + N 開新檔案, 都是 UTF8 + CRLF
- 5.. 在資料夾按滑鼠右鍵, New C# → Class (例如: City.cs) 會是 UTF8 + LF
結論
以下為筆者的觀察結論, 僅供參考. 或許有誤, 尚請指正.
Windows: CRLF
Windows | Ubuntu | Windows | GitHub | Ubuntu | |
A. autocrlf | true | false | CRLF | LF | LF |
B. autocrlf | true | true | CRLF | LF | CRLF |
C. autocrlf | false | true | CRLF | CRLF | CRLF |
D. autocrlf | false | false | CRLF | CRLF | CRLF |
Windows: LF
Windows | Ubuntu | Windows | GitHub | Ubuntu | |
W. autocrlf | true | false | LF | LF | LF |
X. autocrlf | true | true | LF | LF | CRLF |
Y. autocrlf | false | true | LF | LF | CRLF |
Z. autocrlf | false | false | LF | LF | LF |
(1) Git: core.autocrlf 可以採預設值, 也就是 Windows 為 true, Ubuntu 為 false. 主要是 Windows 端要檢查 git config --list 結果的 core.autocrlf 是否為 true; 並檢查一下 .gitattributes 是否有設定 * text=true
(2) Vs Code: EOL (End of Line) 的設定, 最好都是採用跟作業系統相同.
步驟細節:
-
0.. 檢查 Vs Code 預設的 End of Line 設定
-
1.. 由 dotnet new 產生的 *.proj, Program.cs, *.json 是 UTF8 + CRLF
-
2.. 由 dotnet new 產生的 HomeController.cs, Views\Home\Index.cshtml 都是 UTF8 with BOM + CRLF
-
3.. 由 dotnet aspnet-codegenerator controller 指令產生出來的 FriendsController.cs, Views\Friends\Index.cshtml 都是 UTF8 + CRLF
如下圖, 細節過程, 請參考 [[附錄一]] -
4.. 按 Ctrl + N 開新檔案, 都是 UTF8 + CRLF
-
5.. 在資料夾按滑鼠右鍵, New C# → Class (例如: City.cs) 會是 UTF8 + LF
重要: 經實測, 在 ubuntu 的 vs code 進行修改後, 傳至 GitHub, 再同步至 windows 10, 會變成 UTF8 + CRLF
附錄一: dotnet aspnet-codegenerator controller 指令細節過程
(1) 安裝必要的 nuget 套件
PS D:\22-Projects.Git\52-ASP.NET Core\GitAutoCrLf\AutoCrLfTrueWeb> dotnet add package Microsoft.EntityFrameworkCore.SqlServer PS D:\22-Projects.Git\52-ASP.NET Core\GitAutoCrLf\AutoCrLfTrueWeb> dotnet add package Microsoft.EntityFrameworkCore.Design PS D:\22-Projects.Git\52-ASP.NET Core\GitAutoCrLf\AutoCrLfTrueWeb> dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design
(2) 新增 Models\Friend.cs
namespace AutoCrLfTrueWeb.Models { public class Friend { public int Id { get; set; } public string Name { get; set; } = string.Empty; public string? Email { get; set; } = null; public string? Mobile { get; set; } = null; } }
(3) 新增 Data\DatabaseContext.cs
using Microsoft.EntityFrameworkCore; using AutoCrLfTrueWeb.Models; namespace AutoCrLfTrueWeb.Data { public class DatabaseContext : DbContext { public DatabaseContext(DbContextOptions<DatabaseContext> options) : base(options) { } // [jasper] 要加上 ? 以處理 CS8618 的警告訊息 public DbSet<Friend>? Friends { get; set; } // [jasper] 建立初始資料 protected override void OnModelCreating(ModelBuilder modelBuilder) { var obj1 = new Friend() { Id=1, Name="Mary", Email="mary@gmail.com", Mobile="123456789" }; var obj2 = new Friend() { Id=2, Name="John", Email="john@gmail.com", Mobile="987654321" }; var obj3 = new Friend() { Id=3, Name="Jasper", Email="jasper@gmail.com", Mobile="123456987" }; modelBuilder.Entity<Friend>().HasData( obj1, obj2, obj3 ); } } }
(4) 在 Program.cs 註冊 DatabaseContext 服務
using Microsoft.EntityFrameworkCore; using MvcFriends.Data; var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllersWithViews(); // ==(B)========== [jasper] 註冊 DbContext Service =============== // 參考 Visual Studio 2022 產出的範例程式 var connectionString = builder.Configuration.GetConnectionString("SampleContext"); builder.Services.AddDbContext<DatabaseContext>(options => options.UseSqlServer(connectionString)); // ==(E)========== [jasper] 註冊 DbContext Service =============== // var app = builder.Build();
(5) 在 appsettings.json 新增 SampleConnection 資料庫連線
"code class="language-json""{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*", "ConnectionStrings": { "SampleContext": "Data Source=(localdb)\\mssqllocaldb;Database=AutoCrLfTrueDB" } }
(6) 建立 Migration
PS D:\22-Projects.Git\52-ASP.NET Core\GitAutoCrLf\AutoCrLfTrueWeb> dotnet build MSBuild version 17.3.2+561848881 for .NET 正在判斷要還原的專案... 所有專案都在最新狀態,可進行還原。 AutoCrLfTrueWeb -> D:\22-Projects.Git\52-ASP.NET Core\GitAutoCrLf\AutoCrLfTrueWeb\bin\Debug\net6.0\AutoCrLfTrueWeb.dll 建置成功。 0 個警告 0 個錯誤 PS D:\22-Projects.Git\52-ASP.NET Core\GitAutoCrLf\AutoCrLfTrueWeb> dotnet ef migrations add InitialCreate Build started... Build succeeded. info: Microsoft.EntityFrameworkCore.Infrastructure[10403] Entity Framework Core 6.0.10 initialized 'DatabaseContext' using provider 'Microsoft.EntityFrameworkCore.SqlServer:6.0.10' with options: None Done. To undo this action, use 'ef migrations remove'
(7) 建立資料庫
PS D:\22-Projects.Git\52-ASP.NET Core\GitAutoCrLf\AutoCrLfTrueWeb> dotnet ef database update Build started... Build succeeded. info: Microsoft.EntityFrameworkCore.Infrastructure[10403] Entity Framework Core 6.0.10 initialized 'DatabaseContext' using provider 'Microsoft.EntityFrameworkCore.SqlServer:6.0.10' with options: None info: Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (900ms) [Parameters=[], CommandType='Text', CommandTimeout='60'] CREATE DATABASE [AutoCrLfTrueDB]; info: Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (531ms) [Parameters=[], CommandType='Text', CommandTimeout='60'] IF SERVERPROPERTY('EngineEdition') <> 5 BEGIN ALTER DATABASE [AutoCrLfTrueDB] SET READ_COMMITTED_SNAPSHOT ON; END; info: Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (6ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] SELECT 1 info: Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (259ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] CREATE TABLE [__EFMigrationsHistory] ( [MigrationId] nvarchar(150) NOT NULL, [ProductVersion] nvarchar(32) NOT NULL, CONSTRAINT [PK___EFMigrationsHistory] PRIMARY KEY ([MigrationId]) ); info: Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (42ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] SELECT 1 info: Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (33ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] SELECT OBJECT_ID(N'[__EFMigrationsHistory]'); info: Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (20ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] SELECT [MigrationId], [ProductVersion] FROM [__EFMigrationsHistory] ORDER BY [MigrationId]; info: Microsoft.EntityFrameworkCore.Migrations[20402] Applying migration '20221021070418_InitialCreate'. Applying migration '20221021070418_InitialCreate'. info: Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (35ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] CREATE TABLE [Friends] ( [Id] int NOT NULL IDENTITY, [Name] nvarchar(max) NOT NULL, [Email] nvarchar(max) NULL, [Mobile] nvarchar(max) NULL, CONSTRAINT [PK_Friends] PRIMARY KEY ([Id]) ); info: Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (402ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] IF EXISTS (SELECT * FROM [sys].[identity_columns] WHERE [name] IN (N'Id', N'Email', N'Mobile', N'Name') AND [object_id] = OBJECT_ID(N'[Friends]')) SET IDENTITY_INSERT [Friends] ON; INSERT INTO [Friends] ([Id], [Email], [Mobile], [Name]) VALUES (1, N'mary@gmail.com', N'123456789', N'Mary'); IF EXISTS (SELECT * FROM [sys].[identity_columns] WHERE [name] IN (N'Id', N'Email', N'Mobile', N'Name') AND [object_id] = OBJECT_ID(N'[Friends]')) SET IDENTITY_INSERT [Friends] OFF; info: Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (14ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] IF EXISTS (SELECT * FROM [sys].[identity_columns] WHERE [name] IN (N'Id', N'Email', N'Mobile', N'Name') AND [object_id] = OBJECT_ID(N'[Friends]')) SET IDENTITY_INSERT [Friends] ON; INSERT INTO [Friends] ([Id], [Email], [Mobile], [Name]) VALUES (2, N'john@gmail.com', N'987654321', N'John'); IF EXISTS (SELECT * FROM [sys].[identity_columns] WHERE [name] IN (N'Id', N'Email', N'Mobile', N'Name') AND [object_id] = OBJECT_ID(N'[Friends]')) SET IDENTITY_INSERT [Friends] OFF; info: Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (16ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] IF EXISTS (SELECT * FROM [sys].[identity_columns] WHERE [name] IN (N'Id', N'Email', N'Mobile', N'Name') AND [object_id] = OBJECT_ID(N'[Friends]')) SET IDENTITY_INSERT [Friends] ON; INSERT INTO [Friends] ([Id], [Email], [Mobile], [Name]) VALUES (3, N'jasper@gmail.com', N'123456987', N'Jasper'); IF EXISTS (SELECT * FROM [sys].[identity_columns] WHERE [name] IN (N'Id', N'Email', N'Mobile', N'Name') AND [object_id] = OBJECT_ID(N'[Friends]')) SET IDENTITY_INSERT [Friends] OFF; info: Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (26ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion]) VALUES (N'20221021070418_InitialCreate', N'6.0.10'); Done.
(8) 連接資料庫, 查看資料
(9) 建立 Controller 及 View
PS D:\22-Projects.Git\52-ASP.NET Core\GitAutoCrLf\AutoCrLfTrueWeb> dotnet aspnet-codegenerator controller --controllerName FriendsController -outDir Controllers -async -namespace AutoCrLfTrueWeb.Controllers -m Friend -dc DatabaseContext -udl Building project ... Finding the generator 'controller'... Running the generator 'controller'... Minimal hosting scenario! Attempting to compile the application in memory. Attempting to figure out the EntityFramework metadata for the model and DbContext: 'Friend' info: Microsoft.EntityFrameworkCore.Infrastructure[10403] Entity Framework Core 6.0.10 initialized 'DatabaseContext' using provider 'Microsoft.EntityFrameworkCore.SqlServer:6.0.10' with options: None Added Controller : '\Controllers\FriendsController.cs'. Added View : \Views\Friends\Create.cshtml Added View : \Views\Friends\Edit.cshtml Added View : \Views\Friends\Details.cshtml Added View : \Views\Friends\Delete.cshtml Added View : \Views\Friends\Index.cshtml RunTime 00:00:32.78 PS D:\22-Projects.Git\52-ASP.NET Core\GitAutoCrLf\AutoCrLfTrueWeb>
(10) 查看 Controllers\FriendsController.cs 的換行設定
參考文件
- 基本上, 如果你的專案會同時在不同的作業系統(如 Windows、Linux)上存取, 最好是設定為 true.
- 在 checkin 時, Git 會將純文字類型的檔案中的所有 CRLF 字元轉換為 LF, 也就是版本庫中的換行符號一律存成 LF;
- 在 checkout 時, 則會將 LF 轉換成目前作業系統的換行符號, 例如在 Windows 上面就是轉成 CRLF.
先說結論:我不讓 Git 自動轉換任何換行字元,無論是純文字還是二進位檔案.
Git 的預設行為是自動幫我們處理換行字元的轉換. 為了避免每台機器設定不同而產生互相踩腳的情形, 最好是在建立 repo(檔案庫)時就針對那個 repo 編寫適當的組態檔(.gitAttributes).如此一來, 無論檔案庫被複製到哪裡, 設定都不會跑掉.
- 如果依預設值, autocrlf=true.
- 當執行 git add 命令時, 文字檔案中出現的 CRLF 斷行字元會自動被轉換成 LF 字元.
- 而利用 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.
沒有留言:
張貼留言