# 🌿 Tích hợp Git và terminal trong Neovim

By 3T1C

Sau module 2, bạn đã có LSP, Telescope và Treesitter chạy ổn, leader key đã thuộc. Bước tiếp theo để cấu hình "dùng được cho công việc thật" là rút ngắn vòng lặp làm việc: xem diff, stage hunk, chạy test và build ngay trong Neovim, không phải alt-tab sang cửa sổ khác. Bài này ghép hai mảnh đó lại và xử lý phần Windows 11 hay làm hỏng trải nghiệm.

## Mục tiêu bài này

- Thêm `gitsigns.nvim` và biết khi nào cần thêm `vim-fugitive` hoặc `neogit`.
- Khai báo shell tường minh trên Windows 11 để `:!`, `:make` và `:terminal` chạy đúng.
- Dựng terminal buffer bằng `toggleterm.nvim` với keybinding vào workflow đã có.
- Nhận diện và sửa bốn lỗi hiển thị, hiệu lực phổ biến nhất.

## Ví dụ mẫu: từ buffer trống đến vòng lặp Git và terminal

Mình vẫn giữ nguyên tắc tách theo domain, không nhồi vào `init.lua`.

```tree
%LOCALAPPDATA%\nvim\
  init.lua
  lua\
    config\
      options.lua
      keymaps.lua
    plugins\
      git.lua
      terminal.lua
```

Trên Windows 11, `~/.config/nvim` chính là `%LOCALAPPDATA%\nvim`. Nếu bạn dùng đường dẫn Unix trong tài liệu trên mạng, nhớ quy đổi.

1. Khai báo shell trong `lua/config/options.lua`, trước khi cài bất kỳ plugin nào.
2. Thêm `lua/plugins/git.lua` với `gitsigns.nvim` và keybinding hunk.
3. Thêm `vim-fugitive` hoặc `neogit` chỉ khi bạn thật sự cần.
4. Thêm `lua/plugins/terminal.lua` với `toggleterm.nvim`.
5. Chạy `:Lazy sync`, mở một file trong repo và kiểm tra vòng lặp làm việc.

**Bước 1. Shell trước, plugin sau.**

```lua
-- lua/config/options.lua
if vim.fn.has("win32") == 1 then
  local pwsh = vim.fn.exepath("pwsh.exe")
  vim.o.shell = pwsh ~= "" and pwsh or "powershell.exe"
  vim.o.shellcmdflag = "-NoLogo -NoProfile -ExecutionPolicy RemoteSigned -Command "
  vim.o.shellquote = ""
  vim.o.shellxquote = ""
  vim.o.termguicolors = true
end
```

Lý do từng dòng: `vim.fn.exepath` trả về chuỗi rỗng khi không tìm thấy, nên bạn ưu tiên PowerShell 7 rồi mới fallback về `powershell.exe` có sẵn. Dấu cách ở cuối `-Command ` là bắt buộc, thiếu nó Neovim nối liền lệnh vào flag và PowerShell hiểu sai tham số. `shellquote` và `shellxquote` để rỗng vì PowerShell tự xử lý quote theo cách riêng; giữ giá trị mặc định của Vim sẽ tạo ra lệnh lồng quote sai.

**Bước 2. Lớp Git trong buffer.**

```lua
-- lua/plugins/git.lua
return {
  {
    "lewis6991/gitsigns.nvim",
    event = { "BufReadPre", "BufNewFile" },
    opts = {
      signs = {
        add = { text = "+" },
        change = { text = "~" },
        delete = { text = "_" },
      },
      on_attach = function(bufnr)
        local gs = require("gitsigns")
        local map = function(lhs, rhs, desc)
          vim.keymap.set("n", lhs, rhs, { buffer = bufnr, desc = desc })
        end
        map("]h", function() gs.nav_hunk("next") end, "Hunk kế tiếp")
        map("[h", function() gs.nav_hunk("prev") end, "Hunk trước")
        map("<leader>gp", gs.preview_hunk, "Preview hunk")
        map("<leader>gs", gs.stage_hunk, "Stage hunk")
        map("<leader>gr", gs.reset_hunk, "Reset hunk")
        map("<leader>gb", function() gs.blame_line({ full = true }) end, "Blame dòng")
      end,
    },
  },
}
```

`event = { "BufReadPre", "BufNewFile" }` để sign có sẵn ngay khi mở file, không phải chờ gõ lệnh đầu tiên. Mapping đặt `buffer = bufnr` để chỉ ảnh hưởng buffer hiện tại, nhờ vậy `]h` và `[h` không đè lên bất cứ thứ gì global. Nếu bản gitsigns của bạn cũ hơn, thay `gs.nav_hunk("next")` bằng `gs.next_hunk` và `gs.prev_hunk`.

**Bước 3. Chỉ thêm Git UI khi cần.**

```lua
{ "tpope/vim-fugitive", cmd = { "Git", "G" } },
```

`cmd` khiến plugin chỉ nạp khi bạn gõ `:Git`, nên không tốn thời gian khởi động. Chọn theo nhu cầu: cần `:Git blame`, `:Git push` dạng command thì dùng fugitive; muốn giao diện đầy đủ kiểu Magit thì dùng neogit. Đừng cài cả ba nếu bạn chỉ cần xem và stage hunk, vì mỗi lớp thêm là một chỗ nữa để lỗi.

**Bước 4. Terminal tích hợp.**

```lua
-- lua/plugins/terminal.lua
return {
  {
    "akinsho/toggleterm.nvim",
    version = "*",
    opts = {
      size = 12,
      open_mapping = [[<C-\>]],
      direction = "horizontal",
      shade_terminals = false,
      start_in_insert = true,
    },
  },
}
```

`shade_terminals = false` là mẹo riêng cho Windows: lớp phủ nền tối của toggleterm dựa vào truecolor, và trên một số terminal ngoài nó tạo ra dải màu lạ. Tắt đi thì an toàn hơn, bạn vẫn phân biệt được cửa sổ terminal nhờ border và statusline.

> **Note:** 
> Nếu bạn quen Git Bash, có thể đặt `vim.o.shell = "C:\\Program Files\\Git\\bin\\bash.exe"` cùng `vim.o.shellcmdflag = "-c"`. Đổi lại, bạn phải tự lo phần quote và một số lệnh Windows sẽ không gọi được. Với công việc hằng ngày, PowerShell 7 là lựa chọn ít ma sát nhất.

**Bước 5. Kiểm tra.** Chạy `:Lazy sync`, mở một file trong repo, đi `]h` vài lần, bấm `<leader>gp`, rồi `<C-\>` và gõ `git status`. Muốn về normal mode từ terminal buffer, nhấn `<Esc>` hoặc `<C-\><C-n>`.

## Bài thực hành trên cấu hình capstone

**Bài 1.** Repo capstone của bạn có một file bị sửa 3 chỗ. Dùng gitsigns để stage riêng từng hunk mà không rời Neovim. Viết lại chuỗi thao tác.

**Gợi ý và đáp án**

Mở file, đi `]h` để tới hunk đầu, bấm `<leader>gp` xem trước, `<leader>gs` để stage, lặp lại cho hai hunk còn lại. Kiểm tra bằng `:Gitsigns diffthis` hoặc mở terminal và chạy `git diff --staged`. Điểm cần nhớ: `stage_hunk` thao tác trên hunk tại vị trí con trỏ, không phải toàn file.

**Bài 2.** Viết đoạn Lua đặt shell ưu tiên `pwsh.exe`, fallback `powershell.exe`, và in ra shell đang dùng khi bạn nghi ngờ cấu hình sai.

**Gợi ý và đáp án**

```lua
local pwsh = vim.fn.exepath("pwsh.exe")
vim.o.shell = pwsh ~= "" and pwsh or "powershell.exe"
vim.keymap.set("n", "<leader>ush", function()
  print(vim.o.shell .. " | " .. vim.o.shellcmdflag)
end, { desc = "Xem shell hiện tại" })
```

Mapping này cực hữu ích khi bạn sửa `options.lua` nhiều lần trong ngày: in ra cả `shellcmdflag` để phát hiện ngay lỗi thiếu dấu cách cuối.

**Bài 3.** Chuyển terminal sang dạng float cao 80% màn hình, và giải thích vì sao không nên đặt `direction = "vertical"` khi màn hình laptop 14 inch.

**Gợi ý và đáp án**

```lua
opts = { open_mapping = [[<C-\>]], direction = "float", size = 12 }
```

Với `direction = "float"`, `size` là tỉ lệ phần trăm chiều cao, nên chỉnh `size = 80`. Terminal dọc ăn mất chiều ngang của buffer, trong khi output test và log thường dài theo chiều ngang, nên hướng ngang hoặc float đọc dễ hơn.

## Bốn lỗi hay gặp khi ghép Git và terminal

| Triệu chứng | Nguyên nhân | Cách xử lý |
| --- | --- | --- |
| `:!` hoặc `:make` báo lỗi khó hiểu, lệnh không chạy | Thiếu dấu cách cuối `-Command` trong `shellcmdflag` | Thêm lại dấu cách, in `vim.o.shellcmdflag` để kiểm tra |
| Terminal hiện ký tự rác, màu lệch | Chưa bật `termguicolors`, hoặc terminal ngoài không hỗ trợ truecolor | Bật `termguicolors`, dùng Windows Terminal |
| `git diff` báo cả file thay đổi dù chỉ sửa một dòng | CRLF so với LF chưa chuẩn hóa | Thống nhất `.gitattributes`, kiểm tra `core.autocrlf` |
| `:Git` báo không phải repo | Đang mở file ngoài working tree, hoặc Git chưa có trong `PATH` | Kiểm tra `:pwd`, chạy `git --version` trong terminal tích hợp |

Một điểm nữa về `:terminal`: Windows 11 dùng ConPTY, có sẵn từ Windows 10 1809, nên bạn không cần cài `winpty` như các hướng dẫn cũ. Nếu thấy ai bảo cài `winpty`, đó là tài liệu lỗi thời.

## Ôn nhanh trước khi đóng gói cấu hình

Front: gitsigns.nvim hiển thị gì trong buffer?
Back: Dấu ở cột sign cho biết hunk thêm, sửa, xóa so với Git index.

Front: Ba lựa chọn công cụ Git trong Neovim và điểm khác biệt?
Back: gitsigns (hunk ngay trong buffer), vim-fugitive (:Git dạng command), neogit (UI đầy đủ kiểu Magit).

Front: Vì sao phải khai báo shell tường minh trên Windows 11?
Back: Để :!, :make và :terminal dùng đúng shell có pipe và UTF-8 thay vì cmd.exe mặc định.

Front: shellcmdflag đúng cho PowerShell là gì?
Back: -NoLogo -NoProfile -ExecutionPolicy RemoteSigned -Command , có dấu cách ở cuối.

Front: Thiếu dấu cách cuối trong shellcmdflag gây ra chuyện gì?
Back: Lệnh bị nối dính vào flag, PowerShell hiểu sai tham số, :! và :make hỏng.

Front: Trên Windows 11, :terminal chạy qua công nghệ nào?
Back: ConPTY, có sẵn từ Windows 10 1809, nên không cần winpty.

Front: Làm sao thoát terminal mode về normal mode?
Back: Nhấn <Esc> hoặc <C-\><C-n>.

Front: Vì sao gitsigns nên dùng buffer-local mapping?
Back: Để keybinding chỉ ảnh hưởng buffer hiện tại, không chiếm phím toàn cục.

Front: Lệnh mở terminal tích hợp ngay trong Neovim?
Back: :terminal (viết tắt :term), mở một buffer terminal chạy shell đã khai báo.

Front: Muốn terminal hiện dưới dạng cửa sổ nổi và bật tắt bằng một phím thì dùng gì?
Back: Plugin như toggleterm.nvim, hoặc tự viết hàm mở float window bọc :terminal.

Front: signcolumn phải ở trạng thái nào để thấy dấu của gitsigns?
Back: yes hoặc auto. Nếu là no thì buffer không có cột sign và không hiện dấu nào.

Front: gitsigns không hiện dấu, hai nguyên nhân phổ biến nhất?
Back: Buffer không nằm trong Git repo, hoặc Git chưa có trong PATH.

## Điền khuyết: kiểm tra lại các con số và tên gọi

Trên Windows 11, Neovim dùng _____ để chạy :terminal nên không cần cài winpty.
Giá trị shellcmdflag cho PowerShell phải kết thúc bằng một _____, thiếu nó thì lệnh bị nối dính vào flag.
Plugin _____ hiển thị thay đổi theo từng hunk ngay trong buffer.
Để thoát terminal mode về normal mode, nhấn _____ hoặc <C-\><C-n>.
Mapping của gitsigns nên khai báo _____ để không chiếm phím ở những buffer khác.

## Chốt lại: Git và terminal đã vào đúng chỗ

Bốn điểm cần mang theo khi đóng gói cấu hình:

- **Chọn một công cụ Git chính.** gitsigns cho feedback tức thì trong buffer, fugitive cho command quen tay, neogit cho UI đầy đủ. Cài cả ba cũng chạy được, nhưng chỉ nên map phím cho một cái để não không phải chuyển ngữ cảnh.
- **Shell phải tường minh.** Ba biến `shell`, `shellcmdflag` (và `shellquote` nếu dùng) là nền cho `:!`, `:make`, `:terminal`. Sai một dấu cách ở cuối `shellcmdflag` là hỏng cả chuỗi lệnh.
- **ConPTY lo phần terminal.** Không cài `winpty`, không thêm wrapper lạ. Nếu terminal hiển thị sai ký tự, nghĩ tới encoding trước, không nghĩ tới ConPTY.
- **Mapping theo ngữ cảnh.** Hành động chỉ có nghĩa trong buffer (stage hunk, reset hunk) thì khai báo buffer-local.

Cấu hình của bạn giờ đã có đường đi từ buffer tới Git và một terminal mở ngay trong editor. Bài tiếp theo đi vào phần đau nhất của cả khóa: **Khắc phục lỗi đặc thù trên Windows 11**, nơi chúng ta xử lý path, encoding, shell quoting và những plugin chậm bất thường, để cấu hình này không chỉ chạy được mà còn chạy ổn định mỗi ngày.

---

## Contents

Part of [🛠️ Config neovim trên win 11](https://everwise.study/courses/config-neovim-tren-win-11.md)

- **🧱 Nền tảng cấu hình bằng Lua và plugin manager**
  - [📁 Bộ khung init.lua và cách tổ chức file cấu hình trên Windows 11](https://everwise.study/courses/config-neovim-tren-win-11/bo-khung-init-lua-tren-windows-11.md)
  - [📦 Quản lý plugin với lazy.nvim và hướng mở rộng với vim.pack](https://everwise.study/courses/config-neovim-tren-win-11/quan-ly-plugin-lazy-nvim-va-vim-pack.md) (sign-in required)
  - [⌨️ Keybinding, leader key và workflow làm việc hiệu quả](https://everwise.study/courses/config-neovim-tren-win-11/keybinding-leader-key-va-workflow.md)
  - [✅ Checkpoint: Nền tảng cấu hình Lua và plugin manager](https://everwise.study/courses/config-neovim-tren-win-11/checkpoint-nen-tang-cau-hinh-lua.md)
- **🧠 Đưa Neovim lên mức IDE: LSP, Telescope, Treesitter**
  - [🔍 LSP, autocomplete và diagnostics](https://everwise.study/courses/config-neovim-tren-win-11/lsp-autocomplete-va-diagnostics.md)
  - [🔭 Telescope: tìm file và grep trong dự án](https://everwise.study/courses/config-neovim-tren-win-11/telescope-tim-file-va-grep.md)
  - [🌳 Treesitter: cú pháp, highlight và folding](https://everwise.study/courses/config-neovim-tren-win-11/treesitter-cu-phap-va-folding.md)
  - [✅ Checkpoint: LSP, Telescope và Treesitter](https://everwise.study/courses/config-neovim-tren-win-11/checkpoint-lsp-telescope-treesitter.md)
- **🚀 Capstone: cấu hình hoàn chỉnh dùng cho công việc thật**
  - [🌿 Tích hợp Git và terminal trong Neovim](https://everwise.study/courses/config-neovim-tren-win-11/tich-hop-git-va-terminal.md) — this lesson
  - [🩺 Khắc phục lỗi đặc thù trên Windows 11](https://everwise.study/courses/config-neovim-tren-win-11/khac-phuc-loi-dac-thu-tren-windows-11.md)
  - [🏗️ Capstone: đóng gói cấu hình Neovim cho công việc thật](https://everwise.study/courses/config-neovim-tren-win-11/capstone-dong-goi-cau-hinh-neovim.md) (premium content)
  - [✅ Checkpoint: Capstone và xử lý sự cố trên Windows](https://everwise.study/courses/config-neovim-tren-win-11/checkpoint-capstone-windows.md) (premium content)
- [Course cheatsheet](https://everwise.study/courses/config-neovim-tren-win-11/cheatsheet.md) (premium content)
