Linux伺服器 Linux伺服器

資工所專業課程上課教材

資工所專業課程 > 課程內容 > 專題 - 使用 git 進行版本控制

專題 - 使用 git 進行版本控制

上次更新日期 2020/09/23

開發任何軟體,都是需要版本控制的。以前都是直接使用一個目錄的名稱來進行備份而已,老實說,管理起來相當困難。 可以透過 git 這個軟體來控制你的版本,可以合成、可以查看、可以回朔到某個版本等等,會相當容易管理喔。 不過要注意的是,git 大部分就是用在『純文字檔案』方面的控制,如果是某些特定版本的輸出資料,要用 git 管理,就會比較麻煩。

使用 diff 搭配 patch 進行程式補丁的流程分析

  1. 取得上課實做環境:
    1. 寫下老師在白板上面的雲端教室系統 IP,請自行使用你的學號與真實姓名註冊,需要註冊至少兩個帳號,以利課程進行。 兩個帳號資料可以是: gxxxx_client, gxxxx_server 之類的樣式,比較好記憶。
    2. 開通帳號後,請先以 server 帳號登入系統,嘗試啟用任何一個硬碟,並開始使用 remote-viewer 登入, 開始操作 linux 系統。
    3. 修改你的網路參數,調整你的網路時間,設計你的主機名稱等任務
    4. 開始自動更新你的系統。
  2. 了解檔案比對工具: diff 軟體
    1. 先建立好兩個新舊檔案內容
      [dmtsai@study ~]$ mkdir -p /tmp/testpw <==先建立測試用的目錄
      [dmtsai@study ~]$ cd /tmp/testpw
      [dmtsai@study testpw]$ cp /etc/passwd passwd.old
      [dmtsai@study testpw]$ cat /etc/passwd | sed -e '4d' -e '6c no six line' > passwd.new
      # 注意一下, sed 後面如果要接超過兩個以上的動作時,每個動作前面得加 -e 才行!
      # 透過這個動作,在 /tmp/testpw 裡面便有新舊的 passwd 檔案存在了!
      
    2. 了解 diff 用法
      [dmtsai@study ~]$ diff [-bBi] from-file to-file
      選項與參數:
      from-file :一個檔名,作為原始比對檔案的檔名;
      to-file   :一個檔名,作為目的比對檔案的檔名;
      注意,from-file 或 to-file 可以 - 取代,那個 - 代表『Standard input』之意。
      
      -b  :忽略一行當中,僅有多個空白的差異(例如 "about me" 與 "about     me" 視為相同
      -B  :忽略空白行的差異。
      -i  :忽略大小寫的不同。
      
      範例一:比對 passwd.old 與 passwd.new 的差異:
      [dmtsai@study testpw]$ diff passwd.old passwd.new
      4d3    <==左邊第四行被刪除 (d) 掉了,基準是右邊的第三行
      < adm:x:3:4:adm:/var/adm:/sbin/nologin  <==這邊列出左邊(<)檔案被刪除的那一行內容
      6c5    <==左邊檔案的第六行被取代 (c) 成右邊檔案的第五行
      < sync:x:5:0:sync:/sbin:/bin/sync  <==左邊(<)檔案第六行內容
      ---
      > no six line                      <==右邊(>)檔案第五行內容
      # 很聰明吧!用 diff 就把我們剛剛的處理給比對完畢了!
      
    3. 使用 diff 比對不同目錄底下的檔案
      [dmtsai@study ~]$ diff /etc/rc0.d/ /etc/init.d/
      Only in /etc/init.d/: functions
      Only in /etc/init.d/: README
      
    4. 以新舊檔案的比對,製作出補釘的內容,通常取名為 xxx.patch
      範例一:以 /tmp/testpw 內的 passwd.old 與 passwd.new  製作補丁檔案
      [dmtsai@study testpw]$ diff -Naur passwd.old passwd.new > passwd.patch
      [dmtsai@study testpw]$ cat passwd.patch
      --- passwd.old  2015-07-14 22:37:43.322535054 +0800  <==新舊檔案的資訊
      +++ passwd.new  2015-07-14 22:38:03.010535054 +0800
      @@ -1,9 +1,8 @@  <==新舊檔案要修改資料的範圍,舊檔在第 1 行開始的 9 行,新檔在第 1 行開始的 8 行
       root:x:0:0:root:/root:/bin/bash
       bin:x:1:1:bin:/bin:/sbin/nologin
       daemon:x:2:2:daemon:/sbin:/sbin/nologin
      -adm:x:3:4:adm:/var/adm:/sbin/nologin     <==左側檔案刪除
       lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
      -sync:x:5:0:sync:/sbin:/bin/sync          <==左側檔案刪除
      +no six line                              <==右側新檔加入
       shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
       halt:x:7:0:halt:/sbin:/sbin/halt
       mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
      
    5. 了解 patch 指令的用法:
      [dmtsai@study ~]$ patch -pN < patch_file    <==更新
      [dmtsai@study ~]$ patch -R -pN < patch_file <==還原
      選項與參數:
      -p  :後面可以接『取消幾層目錄』的意思。
      -R  :代表還原,將新的檔案還原成原來舊的版本。
      
      範例二:將剛剛製作出來的 patch file 用來更新舊版資料
      [dmtsai@study testpw]$ patch -p0 < passwd.patch
      patching file passwd.old
      [dmtsai@study testpw]$ ll passwd*
      -rw-rw-r--. 1 dmtsai dmtsai 2035 Jul 14 22:38 passwd.new
      -rw-r--r--. 1 dmtsai dmtsai 2035 Jul 14 23:30 passwd.old  <==檔案一模一樣!
      
      範例三:恢復舊檔案的內容
      [dmtsai@study testpw]$ patch -R -p0 < passwd.patch
      [dmtsai@study testpw]$ ll passwd*
      -rw-rw-r--. 1 dmtsai dmtsai 2035 Jul 14 22:38 passwd.new
      -rw-r--r--. 1 dmtsai dmtsai 2092 Jul 14 23:31 passwd.old
      # 檔案就這樣恢復成為舊版本囉
      
  3. 小結:
    1. 基本上,不論是什麼文字檔案之類的,只要是純文字檔,都可以透過 diff 搭配 patch 進行軟體的升級! 這個補釘並不是升級你的二進位元程式,而是補釘你的程式碼,補釘完畢之後,你就可以自行 make 或 gcc 編譯你的軟體, 就可以達到升級的效果。當然,如果你是免編譯的 PHP 或 javascript 等,那直接升級,就立刻生效了。
    2. 從上面的範例也可以看出來,要使用簡單的版本控制,就需要 (1)指定要監測的檔名 (2)做出兩者的差異資料, 那接下來就可以透過類似 patch 的指令,來進行版本的更新或復原之意。
  4. 實做練習:
    1. 確定已經升級完畢,升級完畢後,重新開機,並且確認自己的核心已經與原本的核心不同,請處理:
      1. 如何確認目前的核心版本?
      2. 系統所有的核心版本驅動程式放置在哪裡?
      3. 要如何切換核心 (直接更換?重新開機?)
    2. 確定已經重新開機,並請處理:
      1. 網路參數是否正確設定且可以順利啟動?
      2. 主機名稱是否順利設定?並且也可以找到其他同學的內部網路主機名稱與 IP 對應?
      3. 能否連線到 Internet 呢?
    3. 開始進行簡單備份與分析
      1. 使用 cp -a 的機制,將 /etc 完整的複製到 /backups/etc 去
      2. man diff 找到 -r 與 -q 的功能意義
      3. 開始比對 /etc 以及 /backups/etc 是否有差異?
      4. 修改過 /etc/hosts 之後,再次比對一次兩個目錄,列出有更動過的檔案之檔名即可

建置一個簡易的網頁伺服器

  1. 在 CentOS 8 上面,最簡易的 http 伺服器,可以透過安裝 Apache 來處理。這個伺服器的安裝很簡單:
    # yum install httpd
    # systemctl start httpd
    # systemctl enable httpd
    # firewall-cmd --permanent --add-service=http
    # systemctl restart firewalld.service
    # firewall-cmd --list-all
    public (active)
      target: default
      icmp-block-inversion: no
      interfaces: ens3
      sources:
      services: cockpit dhcpv6-client http ssh
      ports:
      protocols:
      masquerade: no
      forward-ports:
      source-ports:
      icmp-blocks:
      rich rules:
    
  2. 建立伺服器的口訣:
    • 安裝
    • 啟動
    • 開機啟動
    • 防火牆
    • 測試
  3. 測試自己與測試他人,測試的方法,直接啟用瀏覽器,填上 http://localhost 或者是其他人的 IP 位址,就可以看到測試頁面。 如果顯示連覽不到,那就代表有問題了。另外,也可以使用 curl 或者是 links 等軟體作為文字界面瀏覽器之用。
  4. 建立顯示自己資料的首頁:
    # cd /var/www/html
    # echo "myname is XXX xxx" > index.html
    # curl http://localhost
    myname is XXX xxx
    
  5. 實做練習:
    1. 請讓你的 Apache 支援 http https PHP 等任務
    2. 請確認你的 https 是可以使用的
    3. 請建立 http://localhost/php.php 網頁,內容大致如下即可:
      <php 
      	phpinfo(); 
      ?>
      
      並且確認該網頁可以順利的啟用 PHP 的功能。
    4. 確認可以連線到隔壁同學的 https://your.classmate.ip/ 去查閱同學的網頁
    5. 使用 curl --help 查詢關鍵字 insecure,看看否使用 curl [option] https://your.classmate.ip/ 找到網頁資料
    6. 找出 http 與 https 啟用的 port number,並觀察你的系統是否有監聽這兩個埠口?

初次使用 git 控制你的網頁資料

  1. 安裝 git 軟體到你的系統上
    # yum install git
    # git --version
    git version 2.18.4
    
    # git --help
    usage: git [--version] [--help] [-C ] [-c =]
               [--exec-path[=]] [--html-path] [--man-path] [--info-path]
               [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--bare]
               [--git-dir=] [--work-tree=] [--namespace=]
                []
    
    These are common Git commands used in various situations:
    
    start a working area (see also: git help tutorial)
       clone      Clone a repository into a new directory
       init       Create an empty Git repository or reinitialize an existing one
    
    work on the current change (see also: git help everyday)
    ....
    
  2. 讓你的網頁空間開始支援 git 的管理。
    1. 如同 diff 所示,你需要有 (1)目前的檔案以及 (2)過去紀錄的檔案 兩個資料。在 git 的環境下, 其實 git 就是在『你要管理的目錄底下,建立一個名為 .git 的隱藏目錄,在該目錄底下建立新舊檔案的比對資料』的情況!
    2. 要建立 .git 目錄,應該要這樣處理:
      # cd /var/www/html
      # git init
      Initialized empty Git repository in /var/www/html/.git/
      
      # ll -a
      drwxr-xr-x. 3 root root  36 Sep  2 19:12 .
      drwxr-xr-x. 4 root root  33 Sep  2 18:28 ..
      drwxr-xr-x. 7 root root 119 Sep  2 19:12 .git
      -rw-r--r--. 1 root root  18 Sep  2 18:33 index.html
      
      # ll .git
      drwxr-xr-x. 2 root root    6 Sep  2 19:12 branches
      -rw-r--r--. 1 root root   92 Sep  2 19:12 config
      -rw-r--r--. 1 root root   73 Sep  2 19:12 description
      -rw-r--r--. 1 root root   23 Sep  2 19:12 HEAD
      drwxr-xr-x. 2 root root 4096 Sep  2 19:12 hooks
      drwxr-xr-x. 2 root root   21 Sep  2 19:12 info
      drwxr-xr-x. 4 root root   30 Sep  2 19:12 objects
      drwxr-xr-x. 4 root root   31 Sep  2 19:12 refs
      
      # git status
      On branch master
      
      No commits yet
      
      Untracked files:
        (use "git add <file>..." to include in what will be committed)
      
              index.html
      
      nothing added to commit but untracked files present (use "git add" to track)
      
      因為只是建立了儲存舊檔與相關資料的容器 (其實,就是那個 .git 目錄的意思),因此查閱狀態 (status) 時,只會顯示在 master 上頭, 且也還沒有任何檔案被找來『監測』,所以查看狀態時, git 就建議,此目錄下的 index.html 可以拿來監測這樣。
    3. 將 index.html 或相關的其他檔案加入監測檔名列表中:
      # git add index.html
      # git status
      On branch master
      
      No commits yet
      
      Changes to be committed:
        (use "git rm --cached ..." to unstage)
      
              new file:   index.html
      
      這樣就顯示了一個新的檔案,那就是 index.html 這個檔案已經是 new file 在列表當中了!但也只是檔名的列表而已, 我們尚未將這個檔案的內容送交。因此,接下來就得要將檔案的內容送交到容器資料中才行。
    4. 操作者資料的設定,因為 git 是可以多人共用的環境,因此,我們需要在本地容器當中,指定目前這個目錄的操作者相關資訊, 簡單的設定如下:
      # git config user.name "VBird Tsai"
      # git config user.email "dmtsai@mail.xxx.xxx"
      # cat .git/config
      [core]
              repositoryformatversion = 0
              filemode = true
              bare = false
              logallrefupdates = true
      [user]
              name = VBird Tsai
              email = dmtsai@mail.ksu.edu.tw
      
      再次說明, git 只會查閱操作時所在目錄是否擁有 .git 隱藏目錄來處理的!所以,你是可以建立不同的專案容器! 另外,目前的專案在 /var/www/html 底下,在該目錄底下的次目錄就無須重新 git init 了! git 會主動往子目錄去查詢相關內容的。
    5. 檔案內容的送交 (commit) 方式:
      # git commit -m "Initial contents of web server"
      [master (root-commit) adbb585] Initial contents of web server
       1 file changed, 1 insertion(+)
       create mode 100644 index.html
      
      # git status
      On branch master
      nothing to commit, working tree clean
      
      此目錄目前的檔案資料與本地端的 git 容器中同步了,因此會顯示主要分支 (branch master) 裡面的目錄與檔案是乾淨 (clean) 的! 這樣也多多少少能讓使用者知道,目前這個專案內,有沒有被新增、移除或修改某些檔案資料喔!至於 -m 則是加入適當的說明, 如果你沒有加上 -m 的話,那系統會主動呼叫預設編輯器 (CentOS 8 使用 vim) 來讓你填寫相關的說明資料!
    6. git 容器內資料的觀察:
      # git log
      commit adbb585ef24e3092c3c0a1119ef4fd528bce6066 (HEAD -> master)
      Author: VBird Tsai <dmtsai@mail.ksu.edu.tw>
      Date:   Wed Sep 2 19:59:02 2020 +0800
      
          Initial contents of web server
      
      # git show
      commit adbb585ef24e3092c3c0a1119ef4fd528bce6066 (HEAD -> master)
      Author: VBird Tsai <dmtsai@mail.ksu.edu.tw>
      Date:   Wed Sep 2 19:59:02 2020 +0800
      
          Initial contents of web server
      
      diff --git a/index.html b/index.html
      new file mode 100644
      index 0000000..2bf623b
      --- /dev/null
      +++ b/index.html
      @@ -0,0 +1 @@
      +myname is XXX xxx
      
      看到 git commit show 的顯示結果,有沒有覺得很熟悉?那就是 diff 的資訊囉!透過這個機制,我們可以知道:
      1. 每一個 commit 都有一個旗標,以上述案例,就是『 adbb585ef24e3092c3c0a1119ef4fd528bce6066 』那一段文字
      2. 每個 commit 都可以有說明 (log) ,說明內容是做了什麼事
      3. 最終 git 使用 diff 之類的方式來紀錄新舊檔案之間的變化,提供未來進行整合、更新與復原之用。
  3. 模擬更動檔案內容的送交狀態:
    1. 嘗試編輯 index.html 的內容,變成類似如下的模樣看看:
      # vim index.html
      <html>
      <head>
              <meta charset='utf-8' />
      </head>
      <body>
      myname is XXX xxx
      </body>
      </html>
      
      # curl http://localhost
      
    2. 假設這個項目修改好了,那將這次的變動送交到 git 容器上:
      # git status
      On branch master
      Changes not staged for commit:
        (use "git add <file>..." to update what will be committed)
        (use "git checkout -- <file>..." to discard changes in working directory)
      
              modified:   index.html
      
      no changes added to commit (use "git add" and/or "git commit -a")
      
      # git commit -a -m "modify to html format"
      [master a216cc4] modify to html format
       1 file changed, 7 insertions(+)
      
      # git status
      On branch master
      nothing to commit, working tree clean
      
      這樣就可以將容器內的資料更新了!
    3. 查閱 git 容器當中的變更資料:
      # git log
      commit a216cc45904633d84a0b692e38d5093febf258d5 (HEAD -> master)
      Author: VBird Tsai <dmtsai@mail.ksu.edu.tw>
      Date:   Wed Sep 2 21:32:35 2020 +0800
      
          modify to html format
      
      commit adbb585ef24e3092c3c0a1119ef4fd528bce6066
      Author: VBird Tsai <dmtsai@mail.ksu.edu.tw>
      Date:   Wed Sep 2 19:59:02 2020 +0800
      
          Initial contents of web server
      
      # git show a216cc45904633d84a0b692e38d5093febf258d5
      commit a216cc45904633d84a0b692e38d5093febf258d5 (HEAD -> master)
      Author: VBird Tsai <dmtsai@mail.ksu.edu.tw>
      Date:   Wed Sep 2 21:32:35 2020 +0800
      
          modify to html format
      
      diff --git a/index.html b/index.html
      index 2bf623b..0b51bc2 100644
      --- a/index.html
      +++ b/index.html
      @@ -1 +1,8 @@
      +<html>
      +<head>
      +       <meta charset='utf-8' />
      +</head>
      +<body>
       myname is XXX xxx
      +</body>
      +</html>
      
      這樣就知道這期間改了什麼,以及自己加上的註解資訊這樣。
    4. 將其他檔案加入 git 的管理中:
      # git status
      On branch master
      Untracked files:
        (use "git add <file>..." to include in what will be committed)
      
              php.php
      
      nothing added to commit but untracked files present (use "git add" to track)
      
      # git add php.php
      # git commit -a -m "add php.php file"
      [master 34bf8df] add php.php file
       1 file changed, 3 insertions(+)
       create mode 100644 php.php
      
      # git status
      
  4. 實做練習:
    1. 建立一個名為 myserver.php 的網頁檔,內容有點像這樣:
      <?php
              foreach ( $_SERVER as $myindex => $myvalue ) {
                      echo "index is " . $myindex . ", value is " . $myvalue . "\n";
              }
      ?>
      
      記得寫完腳本之後,使用類似 curl http://localhost/myserver.php 或 GUI 瀏覽器查閱結果,確認有資料輸出!
    2. 觀察網頁空間內的 git 狀態
    3. 使用 git add [tab][tab] 看看能不能自動的將新檔案加入 git 支援中
    4. 將變化結果送交,同時加上 "output my server array data" 的 log 訊息
  5. 小結語:
    1. 隨時可用 git status 查看專案中是否有變動的資料
    2. 加入新的檔案可以使用 git add file 或 git add -A 來加入新增的檔案到 git 列表中
    3. 使用 git commit -a -m 'log message' 將資料送交
    4. 使用 git show flag 來顯示某個 flag 的變動資訊

某些使用 git 的情境:檔名更改、複製專案、修改設定值

  1. 更動了檔名時,該如何處理才好呢?
    1. 上面的練習中,我們大多數時間都是在新增專案內的檔案,也就是說,這個專案是由無到有,所以,很容易就處理好所有的資料。 甚至於想要加入所有新的檔案,也能夠透過『 git add -A 』來加入列表。那麼當你需要修改檔名時,要如何處理這個被更新的檔名呢?
    2. 更改檔名的範例,例如,將 php.php 更名為 phpinfo.php ,這樣比較有意義時,應該要這樣處理:
      # ll
      -rw-r--r--. 1 root root  89  9月  2 21:26 index.html
      -rw-r--r--. 1 root root 126  9月  2 21:55 myserver.php
      -rw-r--r--. 1 root root  21  9月  2 21:46 php.php
      
      # git status
      On branch master
      nothing to commit, working tree clean
      
      # mv php.php phpinfo.php
      
      # git status
      On branch master
      Changes not staged for commit:
        (use "git add/rm <file>..." to update what will be committed)
        (use "git checkout -- <file>..." to discard changes in working directory)
      
              deleted:    php.php
      
      Untracked files:
        (use "git add <file>..." to include in what will be committed)
      
              phpinfo.php
      
      no changes added to commit (use "git add" and/or "git commit -a")
      
      # git rm php.php
      rm 'php.php'
      
      # git add phpinfo.php
      
      # git commit -m "rename php.php to phpinfo.php"
      [master 25e16ca] rename php.php to phpinfo.php
       1 file changed, 0 insertions(+), 0 deletions(-)
       rename php.php => phpinfo.php (100%)
      
      這樣就將檔名記載修改過來了!
    3. 上面的動作,主要是『手動修改了檔案之後,才來更改 git 的容器內容』方式,那如果一開始我們就知道要修改檔名, 能不能透過 git 直接改變檔名即可?可以的,使用 git mv 來處理。舉例來說,底下我們將 myserver.php 更名為 server.php 的樣式, 可以這樣處理:
      # git mv myserver.php server.php
      # git status
      On branch master
      Changes to be committed:
        (use "git reset HEAD <file>..." to unstage)
      
              renamed:    myserver.php -> server.php
      
      # git commit -m 'rename myserver.php to server.php'
      [master 4a1c7e5] rename myserver.php to server.php
       1 file changed, 0 insertions(+), 0 deletions(-)
       rename myserver.php => server.php (100%)
      
      # ll
      -rw-r--r--. 1 root root  89  9月  2 21:26 index.html
      -rw-r--r--. 1 root root  21  9月  2 21:46 phpinfo.php
      -rw-r--r--. 1 root root 126  9月  2 21:55 server.php
      
      無論是 (1)先改檔名再處理 git ,還是 (2)直接用 git mv 改檔名,最終都需要透過 commit 的舉動,才會讓目錄檔案資料與 git 容器內容同步! 相當重要喔!
  2. git 的設定資料與檔案
    1. git 是可以支援多人共用的環境,因此,每個用戶都可以有自己的名稱與 email 設定值,該設定會在 ~/.gitconfig 內,例如你用 student 或自己的帳號,指定了設定值,就會是這樣:
      # su - student
      $ git config --global user.name "student root"
      $ git config --global user.email "student@localhost"
      $ git config -l
      user.name=student root
      user.email=student@localhost
      
      $ ll .gitconfig
      -rw-rw-r--. 1 student student 55 Sep  3 22:35 .gitconfig
      
      $ cat .gitconfig
      [user]
              name = student root
              email = student@localhost
      
      $ exit
      
    2. git 是可以分個別專案進行獨立分支開發,因此每個專案也能夠有各自的設定值,資料放在 /some/dir/.git/config 內
    3. git 也有系統設定值,該設定值寫入 /etc/gitconfig 內
    4. 列出所有的 git 設定值,可以這樣用:
      # git config -l
      core.repositoryformatversion=0
      core.filemode=true
      core.bare=false
      core.logallrefupdates=true
      user.name=VBird Tsai
      user.email=dmtsai@mail.ksu.edu.tw
      
      事實上,上面的資料就是在 .git/config 裡面的資訊而已。
  3. 如果想要將整個專案複製出來,假設權限也是正確的,那又假設一般用戶的個人首頁是放置到 ~/www/ 時,那你可以這樣實做看看:
    # su - student
    $ git clone /var/www/html www
    Cloning into 'www'...
    done.
    # 因為是本地端的 git 容器,所以直接寫下相關的目錄名稱即可!
    
    $ ll
    drwxrwxr-x. 3 student student 73 Sep  3 22:58 www
    
    $ cd www
    $ git status
    On branch master
    Your branch is up to date with 'origin/master'.
    
    nothing to commit, working tree clean
    
    $ git show
    
    $ git log
    
    基本上,你雖然也能透過 cp -a /var/www/html ~/www 來複製,不過,如果使用 git 來複製的話,還是比較好! 因為,連你的複製也會被紀錄起來!
  4. 實做練習:請在 /var/www/html 底下實做
    1. 新增一個名為 project.html 的檔案,內容寫上你的專案名稱即可,並將該檔案加入到你的 git 支援,且送交資料, 最終 git status 時,該專案必須是 clean 的!
    2. 由於該檔案事實上是寫錯了!所以你要將他刪除掉。你怎麼動作?才能讓 git 也是不再針對該檔案進行維護呢?
    3. 變更到 student 身份去進行底下的任務:
      1. 前往 ~/www 目錄,並列出所有設定值,你會發現到你的設定值以及這個 git 的來源資訊位於何處等。
      2. 修改使用者姓名,變更成為你的名字
      3. 修改使用者 email,變更成為你的 email
      4. 測試,有沒有加上 --global 的差異是什麼呢?

檔案的復原與追蹤

上面的資料大多數都是在『新增、修改』檔案內容,然後給予紀錄備份的感覺,然後我們可以透過 git 去查看看, 這個專案進行期間做了哪些變動,方便未來的處理。那想幾個怪怪的例子,如果你不小心刪除了重要的檔案?怎麼救回來? 如果你後來的變更做的比較爛,之前的版本比較好,如何將之前抓回來?這就是需要注意的部份了!

  1. 誤刪檔案的救援方式:透過 git checkout 處理檔案的救援問題
    # ll
    -rw-r--r--. 1 root root  89  9月  2 21:26 index.html
    -rw-r--r--. 1 root root  21  9月  2 21:46 phpinfo.php
    -rw-r--r--. 1 root root 126  9月  2 21:55 server.php
    
    # rm phpinfo.php
    
    # git status
    On branch master
    Changes not staged for commit:
      (use "git add/rm >file>..." to update what will be committed)
      (use "git checkout -- <file>..." to discard changes in working directory)
    
            deleted:    phpinfo.php
    
    no changes added to commit (use "git add" and/or "git commit -a")
    
    # git checkout -- phpinfo.php
    
    # ll
    -rw-r--r--. 1 root root  89  9月  2 21:26 index.html
    -rw-r--r--. 1 root root  21  9月  4 00:03 phpinfo.php  # 檔案確實被重建,而且日期不一樣了!
    -rw-r--r--. 1 root root 126  9月  2 21:55 server.php
    
    很輕鬆的就將誤刪的檔案救了回來!
  2. 追蹤與回朔一個檔案的變化,你可以透過 git log 去追蹤某個檔案的變化情境:
    # git log
    
    # git log index.html
    commit a216cc45904633d84a0b692e38d5093febf258d5
    Author: VBird Tsai <dmtsai@mail.ksu.edu.tw>
    Date:   Wed Sep 2 21:32:35 2020 +0800
    
        modify to html format
    
    commit adbb585ef24e3092c3c0a1119ef4fd528bce6066
    Author: VBird Tsai <dmtsai@mail.ksu.edu.tw>
    Date:   Wed Sep 2 19:59:02 2020 +0800
    
        Initial contents of web server
    
    # git log -p a216cc4
    # 會顯示一大堆資料,就是 index.hml 的延續變化狀態!
    # 現在,假設我們要將檔案回朔到最原始的非 html 狀態,可以這樣做:
    
    # git checkout -p adbb58 index.html
    diff --git b/index.html a/index.html
    index 0b51bc2..2bf623b 100644
    --- b/index.html
    +++ a/index.html
    @@ -1,8 +1 @@
    -<html>
    -<head>
    -       <meta charset='utf-8' />
    -</head>
    -<body>
     myname is XXX xxx
    -</body>
    -</html>
    Apply this hunk to index and worktree [y,n,q,a,d,s,e,?]? y
    
    # cat index.html
    
    # git checkout -p a216 index.html
    
    透過追蹤與查詢,找到 commit 的旗標點,然後就可以透過 git checkout 去針對該旗標進行資料的回朔!你也可以只回朔部份檔案, 也可以將所有的檔案通通回朔!就看你有沒有寫特定的檔名囉!
  3. 單純加上檔名,沒有加上『 -p flag 』時,預設會將該檔案最後一次 commit 的狀態回朔回來!舉例來說, 如果你曾經修改過 server.php 這個檔案的內容,改完之後,覺得改錯了,而想要進行復原時,不需要額外指定 flag 名稱, 直接處理 checkout 即可。
    # vim server.php
    ...(隨便在最後一行打幾個程式碼即可)...
    
    # git status
    On branch master
    Changes not staged for commit:
      (use "git add ..." to update what will be committed)
      (use "git checkout -- ..." to discard changes in working directory)
    
            modified:   server.php
    
    no changes added to commit (use "git add" and/or "git commit -a")
    
    # git checkout server.php
    # 你會發現到,剛剛錯誤修改的程式碼就不見了,回到上一次 commit 的情境!
    
  4. 追蹤 .git 目錄內的相關資料:

    事實上, .git 內部的資料相當多,主要大致上有物件 (object)、樹狀資訊 (tree)、雜湊碼、blobs、上傳資訊 (commit) 等, 這些資料的內容除了使用 git log 或 git show 之外,如果想要看這些記載的資訊列表,還有許多方式可以應用。

    1. 使用 Linux 的功能,直接查看 .git 底下的檔名列表方式,可以這樣做:
      # find .git
      .git
      .git/branches
      .git/description
      .git/hooks
      .git/hooks/applypatch-msg.sample
      .....
      .git/objects
      .git/objects/pack
      .git/objects/info
      .git/objects/2b
      .git/objects/2b/f623b90905f980c503e1890d00c80859c6ffe4
      .git/objects/08
      .git/objects/08/58270c34ca83d72f462ba3a121ea31ab7b28f5
      .git/objects/ad
      .....
      
      上面的 .git/objects/* 就是 git 紀錄的物件了!要注意的是, git 主要不是紀錄檔案,而是紀錄檔案的內容差異, 這些差異資訊可以透過上述的雜湊碼來記載。
    2. 使用 git 的功能列出所有的資訊,主要的方法如下所示:
      # git cat-file --batch-check --batch-all-objects
      0858270c34ca83d72f462ba3a121ea31ab7b28f5 tree 38
      0b51bc257fba69b50bd869d0e2e313cd9573a283 blob 89
      1a8505abb6aa82606aeca20907f2ca40e8116405 tree 117
      1c4a5abf30b32a8780e097b50dc5e8e1142c5099 blob 21
      25e16cac1ce243eb2dd263ae6c045b39ff983149 commit 248
      2bf623b90905f980c503e1890d00c80859c6ffe4 blob 18
      34bf8dfdd53c967b6416a21b9116eb1cf98b65e4 commit 235
      4a1c7e5dd80f312f7e3b93c8873d9c5bbedd083a commit 252
      5b9cb4aa6e3e0711e64ad39c689f47859e0d9567 tree 38
      70440f870511efd009668902c7d21e2d8f1f61c2 tree 115
      733cacc399fdb1b7608c2d242c52d7019cc4f88c tree 113
      7df8e9aaf2a2d33507fceb0298376545a1d3ecbb commit 246
      a216cc45904633d84a0b692e38d5093febf258d5 commit 240
      adbb585ef24e3092c3c0a1119ef4fd528bce6066 commit 201
      b5e803760691bb66c5d11fa0710d2b0a08db83b9 tree 73
      e29921d7135ee6d147fcb2a0c064a0455397fabe blob 126
      
      看到上面的 commit 關鍵字了嘛?事實上,我們使用 git log 時,就是抓出後面身為 commit 的資訊來顯示而已。 至於 tree 一般是指內部記載的檔名資訊,而 blob 通常就是檔案內容了。例如想要查看 05b1bc 這個 blob 內容時,可以這樣做:
      # git cat-file -p 0b51bc
      <html>
      <head>
              <meta charset='utf-8' />
      </head>
      <body>
      myname is XXX xxx
      </body>
      </html>
      
      如果想要看到檔名資訊,那大概就得要查看 tree 的類別,如下所示:
      # git cat-file -p 70440
      100644 blob 0b51bc257fba69b50bd869d0e2e313cd9573a283    index.html
      100644 blob 1c4a5abf30b32a8780e097b50dc5e8e1142c5099    phpinfo.php
      100644 blob e29921d7135ee6d147fcb2a0c064a0455397fabe    server.php
      
    3. 查看目前 git 在此專案底下所追蹤的檔案,可以簡單的使用 ls-files 查看:
      # git ls-files
      index.html
      phpinfo.php
      server.php
      
      可以了解目前專案所紀錄的檔案!而如果想要了解每個檔案的內容,可以這樣追出其旗標:
    4. # git ls-files -s
      100644 0b51bc257fba69b50bd869d0e2e313cd9573a283 0       index.html
      100644 1c4a5abf30b32a8780e097b50dc5e8e1142c5099 0       phpinfo.php
      100644 e29921d7135ee6d147fcb2a0c064a0455397fabe 0       server.php
      
      如果想要知道每一個檔案的內容,就可以使用『 git cat-file -p flag 』去查看囉!
  5. 實做練習:請在 /var/www/html 底下使用 root 身分實做
    1. 你想要知道第一個 commit 的 index.html 長得怎樣,又不想要使用 git checkout 去抓出該檔案來看, 如何透過 git cat-file 的功能將該檔案內容顯示出來呢?

參考資料與修改歷史

  • O'REILLY、『版本控制使用 Git - 第二版』、碁峰出版社
  • 2020/09/03:初次完成,包括加入 git 控制、處理 git 復原等
  • 2020/09/23:加入 git cat-file -p flag 與 git ls-files -s 的功能
  • 2020/09/28:加入最後一個練習,找出舊版的檔案內容顯示