iT邦幫忙

2025 iThome 鐵人賽

DAY 21
0
Software Development

深入一點點認識 Git系列 第 21

Day 21-深入一點點認識 Git:用 git 指令把檔案刪掉

  • 分享至 

  • xImage
  •  

在 Day 20 文章中,我們提到要讓一個檔案退出預存區,除了 git reset 之外,還有一種指令是 git rm --cached,但當時只是快速帶過,在這篇文章中,我們將細究 git rm 指令,最後說明 git resetgit rm --cached 的差別。

從回顧 git add 與 git commit 開始

如果我們在工作目錄新增一個檔案,並要將其成為 commit 的一部分,要怎麼做呢?

我知道!就是 git addgit commit 嘛!前面文章一直有在操作,例如若想創建一個內文為 "Hello, world"、檔名為 hello_world.txt 的檔案,並把它加進 commit,指令分別為:

echo "Hello, world" > hello_world.txt
git add hello_world.txt
git commit -m "Initial commit"

再用 git log 檢視,就發現做好一個 commit 了:
https://ithelp.ithome.com.tw/upload/images/20250921/20178513LOmm38FKw6.png

用同樣的步驟,我們再建立第二個檔案,並形成第二個 commit:

echo "Bye" > goodbye.txt
git add goodbye.txt
git commit -m "Add goodbye.txt"

剛剛是用 git log 檢視 commit:
https://ithelp.ithome.com.tw/upload/images/20250921/20178513ApFeh8kpIe.png

前面的文章還有教我們透過觀察 .git/ 資料夾:

.git/
├── ...
│
├── objects/
│   ├── 09/
│   │   ├── 170083fcc42fe6f8a3335a9bfa1f11e614638c # goodbye.txt的blob物件
│   │   └── e9df849399a112e444eef8eb8de187737a294f # 指向0917008...與a5c1966...的tree物件
│   ├── a5/                                        # hello_world.txt的blob物件
│   │   └── c19667710254f835085b99726e523457150e03
│   ├── bf/                                        # 指向09e9df8...的commit物件
│   │   └── 7347ba1ea1356cd881fd142e6467a3048cc7f9
│   ├── cf/                                        # 指向e59896e...的commit物件
│   │   └── 771be40f6f2aaf1393647bee7b422589a474d0
│   ├── e5/                                        # 指向a5c1966...的tree物件
│   │   └── 9896eb1f48ef9c75e675692e23966e5ba7cdfb
│   ├── info/
│   └── pack/
│
├── refs/
│   ├── heads/
│   └── tags/
│
├── ...
└── index # 包含100644 09170083fcc42fe6f8a3335a9bfa1f11e614638c 0       goodbye.txt
          # 以及100644 a5c19667710254f835085b99726e523457150e03 0       hello_world.txt

對透過 .git/objects 裡的 git 物件做多次 git cat-file 還可得下列關係:
https://ithelp.ithome.com.tw/upload/images/20250921/20178513KPtF8gyFtP.png

那如果要刪除檔案呢?

以 git rm 刪除檔案

這流程非常有趣,跟上面幾乎是一樣的流程,差別在新增或更動檔案時,是使用 git add 把檔案放進預存區、讓 git 追蹤到變化;但在刪除檔案時,把檔案放進預存區的則是 git rm 指令。
https://ithelp.ithome.com.tw/upload/images/20250921/201785134sCzxJNHua.png

以既有的例子為例,假如我們要刪除 hello_world.txt,就可以輸入以下指令:

git rm hello_world.txt

根據 ls 指令檢視,此時 hello_world.txt 檔案已經從工作目錄消失,再根據 git status 確認,此變化有被 git 追蹤到:
https://ithelp.ithome.com.tw/upload/images/20250921/2017851332FIfhaHER.png

這時預存區長什麼樣子呢?
https://ithelp.ithome.com.tw/upload/images/20250921/20178513VwRDuJDxdY.png

100644 a5c19667710254f835085b99726e523457150e03 0 hello_world.txt 不見了!

一個 git rm 指令就同時改完工作目錄與預存區,比新增檔案少一個步驟,當然要刪除檔案也可以改用 rm,但這樣就只會改到工作目錄,還要另外 git add 才能讓此變化被 git 追蹤到。

https://ithelp.ithome.com.tw/upload/images/20250921/20178513bjRbWcR42S.png
使用 rm 刪除檔案要重新做一次 git add

再來看看 .git/ 資料夾:

.git/
├── ...
│
├── objects/   # 沒有變化
│   ├── 09/
│   │   ├── 170083fcc42fe6f8a3335a9bfa1f11e614638c
│   │   └── e9df849399a112e444eef8eb8de187737a294f
│   ├── a5/
│   │   └── c19667710254f835085b99726e523457150e03
│   ├── bf/
│   │   └── 7347ba1ea1356cd881fd142e6467a3048cc7f9
│   ├── cf/
│   │   └── 771be40f6f2aaf1393647bee7b422589a474d0
│   ├── e5/
│   │   └── 9896eb1f48ef9c75e675692e23966e5ba7cdfb
│   ├── info/
│   └── pack/
│
├── ...
└── index  # 只剩下100644 09170083fcc42fe6f8a3335a9bfa1f11e614638c 0       goodbye.txt

除了預存區之外,看起來還沒有變化。

我們再輸入以下指令建立新的 commit:

git commit -m "Delete hello_world.txt"

此時的 .git/ 資料夾如下:

.git/
├── ...
│
├── objects/
│   ├── 3b/                                        # 新增的commit物件
│   │   └── 0ff9b17ef7cff41cc5aa2807f2b082d07ee13e
│   ├── 09/
│   │   ├── 170083fcc42fe6f8a3335a9bfa1f11e614638c
│   │   └── e9df849399a112e444eef8eb8de187737a294f
│   ├── 33/                                  # 新增的tree物件,指向0917008...
│   │   └── 92d30461dc295ab8d321913b69489ac440085b
│   ├── a5/
│   │   └── c19667710254f835085b99726e523457150e03
│   ├── bf/
│   │   └── 7347ba1ea1356cd881fd142e6467a3048cc7f9
│   ├── cf/
│   │   └── 771be40f6f2aaf1393647bee7b422589a474d0
│   ├── e5/
│   │   └── 9896eb1f48ef9c75e675692e23966e5ba7cdfb
│   ├── info/
│   └── pack/
│
├── ...
└── index # 只剩下100644 09170083fcc42fe6f8a3335a9bfa1f11e614638c 0       goodbye.txt

這時 git 物件之間的關係如下:
https://ithelp.ithome.com.tw/upload/images/20250921/20178513P1saksp1m3.png

舊有的 blob 物件與 tree 物件都沒有消失,且依然被舊的 commit 物件指著,但在新的 commit 物件指向之新的 tree 物件,就不再指向被刪除之檔案對應之 blob 物件。

只想移出預存區,但想保留在工作目錄

剛剛的 git rm 會把檔案從工作目錄移除,但如果我希望移除預存區就好、工作目錄還是要有這份檔案,可以如何進行呢?

現在在一個新資料夾中輸入依序以下指令,回到有兩個 commit 的狀態:

git init

echo "Hello, world" > hello_world.txt
git add hello_world.txt
git commit -m "Initial commit"

echo "Bye" > goodbye.txt
git add goodbye.txt
git commit -m "Add goodbye.txt"

這時有兩個 commit,且工作目錄包含 hello_world.txtgoodbye.txt 兩個檔案、預存區包含兩檔案形成的 blob 物件:
https://ithelp.ithome.com.tw/upload/images/20250921/20178513tcBOc6Y1NU.png

.git/ 資料夾結構如下:

.git/
├── ...
│
├── objects/
│   ├── 09/
│   │   ├── 170083fcc42fe6f8a3335a9bfa1f11e614638c   # goodbye.txt的blob物件
│   │   └── e9df849399a112e444eef8eb8de187737a294f   # 指向0917008...與a5c1966...的tree物件
│   ├── 74/                                          # 第一個commit物件
│   │   └── 85b5bd411fe0849011205679b1d8732fac9ea3
│   ├── a5/                                          # hello_world.txt的blob物件
│   │   └── c19667710254f835085b99726e523457150e03
│   ├── c4/                                          # 第二個commit物件
│   │   └── 6981bd89ebf017dbc5ee0fbbb493ae7ab9fede
│   ├── e5/                                          # 指向a5c1966...的tree物件
│   │   └── 9896eb1f48ef9c75e675692e23966e5ba7cdfb
│   ├── info/
│   └── pack/
│
├── ...
└── index      # 目前仍有100644 09170083fcc42fe6f8a3335a9bfa1f11e614638c 0       goodbye.txt
               # 以及100644 a5c19667710254f835085b99726e523457150e03 0       hello_world.txt

圖示如下:
https://ithelp.ithome.com.tw/upload/images/20250921/20178513JTuuWX9Orz.png

這時如果下 git rm --cachedhello_world.txt 移出預存區:
https://ithelp.ithome.com.tw/upload/images/20250921/20178513WvV1Xev7rm.png

git status 還可以看出,git 已經偵測到 hello_world.txt 被移出預存區(綠色文字)、而現在 hello_world.txt 已不再被追蹤(紅色文字)。

如果這時候下 git commit 會發生什麼事呢?

git commit -m "Stop tracking hello_world.txt"

這時觀察終端機發現:
https://ithelp.ithome.com.tw/upload/images/20250921/20178513FiOmqQwmgX.png

新的 commit 被建立了,根據 git statushello_world.txt 依舊不會被 git 追蹤。

而此時 .git/ 資料夾長這樣:

.git/
├── ...
│
├── objects/
│   ├── 09/
│   │   ├── 170083fcc42fe6f8a3335a9bfa1f11e614638c
│   │   └── e9df849399a112e444eef8eb8de187737a294f
│   ├── 33/                                        # 新的tree物件,指向0917008...
│   │   └── 92d30461dc295ab8d321913b69489ac440085b
│   ├── 74/
│   │   └── 85b5bd411fe0849011205679b1d8732fac9ea3
│   ├── a5/
│   │   └── c19667710254f835085b99726e523457150e03
│   ├── c4/
│   │   └── 6981bd89ebf017dbc5ee0fbbb493ae7ab9fede
│   ├── ca/                                        # 新的commit物件  
│   │   └── df0e8f04fc35a1c3468f85bc59e77ff34906f3
│   ├── e5/
│   │   └── 9896eb1f48ef9c75e675692e23966e5ba7cdfb
│   ├── info/
│   └── pack/
│
├── ...
└── index # 剩下100644 09170083fcc42fe6f8a3335a9bfa1f11e614638c 0       goodbye.txt

圖示如下,其實跟剛才 git rm 一樣,舊的 commit 結構依然保持:
https://ithelp.ithome.com.tw/upload/images/20250921/20178513gl3Tp0UPrD.png

小結

  • git rm 會把檔案從「工作目錄」跟「預存區」都刪除,但只從下一次 commit 起有效。
  • git rm --cached <filename> 只會把檔案從「預存區」刪除,工作目錄中的檔案仍然保留,但一樣從下一次 commit 起有效。

在 Day 20 文章中,我們說 git resetgit rm --cached <filename> 都可以把檔案退出預存區,但後者還會進一步告訴 git 說:之後不要再追蹤指定檔案 <filename>

參考資料

  1. Day10|【Git】檔案管理 - 刪除檔案 git rm
  2. git-rm
  3. git rm –cached vs git reset HEAD? When should each be used?

上一篇
Day 20-深入一點點認識 Git:不是把檔案都塞進 .gitignore 就可確保其不被追蹤
下一篇
Day 22-深入一點點認識 Git:為什麼我不再使用 git checkout
系列文
深入一點點認識 Git22
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言