Skip to main content

Git Tips

Tutorials
全域設定檔
# Using git to edit the configuration
git config global --edit

# List the global configurations
git config --global --list

# Using vi/cat to edit the configuration
vi ~/.gitconfig

# Set the author's email address and name
git config --global user.email "alang.hsu@gmail.com"
git config --global user.name "Alang Hsu"

# Set default editor
git config --global core.editor "vi"

# Set default branch name
git config --global init.defaultBranch "main"

.gitconfig

[user]
	email = alang.hsu@gmail.com
	name = Alang Hsu
[core]
	editor = vi
[init]
	defaultBranch = main
建立新專案
mkdir test-git-push
cd test-git-push
git config --global user.name "<user-name>"
git config --global user.email "<your-email-addr>"
git init
echo "Test Git Push only" > README.md
git add .
git commit -m "Initial commit"
git remote add origin https://<user-name>@github.com/a-lang/test-git-push.git
git remote -v
git push -u origin master
Commit 訊息

簡單的 commit 訊息

git commit -m "Fixed a typo in somewhere"

免 add 快速 commit (-a)

  • 僅限 edited & deleted
  • 僅適用單一 commit 訊息套用一次性的所有異動
  • 通常用在簡短的異動時
git commit -a -m "<commit-message>"

比較複雜的訊息

# 設定 Vim 作為編輯器
git config --global core.editor "vim"

# 不要加 -m 參數
git commit 

Commit 訊息編寫原則

  1. 用一行空白行分隔標題與內容
  2. 標題:最多 50 字元,簡單描述更動的內容
  3. 標題開頭要大寫
  4. 標題不以句點結尾
  5. 以祈使句撰寫標題
  6. 內文:每行最多 72 字,可以多行,詳細描述更動的內容
  7. 用內文解釋 what 以及 why vs. how

檢視 Comments

git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative

--amend 修改(合併)最後一筆 Comment

--amend 會將目前的commit 與最近一次 commit 做合併。適用在小部分更動後卻不想產生單獨的 commit 紀錄;或者上次 commit 後發現遺漏某個檔案。

注意:已經 push 的 commit 不應該使用 --amend,因為這會造成其他協作者的混淆。

git commit --amend
什麼是 HEAD

HEAD 用來表示目前 checked-out 的專案快照,就像網頁的書籤用途。

Git diff
# 還沒執行 git add 前,比對目前檔案與最近一次 commit 版本的內容差異
git diff file

# 比對兩個檔案的內容差異
git diff --word-diff file1 file2

# 檢視兩個 Commit 版本的內容差
git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative
git diff <old-commit-id> <new-commit-id>
Undo in Git

不同階段的檔案回復的方式

git restore -p : 批次回復,當更動內容分布在不同行時,可選擇批次回復

Local unstaged changes (還沒有 add)

git status
git restore <filename>

或者,還沒 staging 之前,可以用 checkout 來回復

 checkout 會回復最近一次的 commited 或 staged 的內容

git checkout <file-name>

Local staged changes (已經 add 還沒有 commit)

git status
git restore --staged <filename>
git restore <filename>

Local committed changes (已經 commit 還沒有 push)

git status
git log
git reset --soft HEAD~
git log

NOTE: 上述指令是回復最新的 commit,如果是更早的 commit,可以執行 git reset <commit-id>,而 commit-id 可以從 git log --oneline 找到。

不過,如果 commit 已經 push 到遠端庫,必須改用 git revert

Public committed changes (已經 push) 

git log --oneline
git revert <last-commit-id> --no-edit
git push
git log

NOTE: 在執行 git revert後,log 會多一條 Revert "XXXX" 的 commit 紀錄,原先的 commit 紀錄也會保留。

Git Prompt with bash

.bashrc:

# Kali-like Prompt with Git status

git_stats() {
  local STATUS=$(git status -s 2> /dev/null)
  local UNTRACKED=$(echo "$STATUS" | grep '^??' | wc -l)
  local STAGED=$(($(echo "$STATUS" | grep '^M ' | wc -l) + $(echo "$STATUS" | grep '^D ' | wc -l) + $(echo "$STATUS" | grep '^R ' | wc -l) + $(echo "$STATUS" | grep '^C ' | wc -l)+$(echo "$STATUS" | grep '^A ' | wc -l)))
  local DRC=$(($(echo "$STATUS" | grep '^ D' | wc -l) + $(echo "$STATUS" | grep '^ R' | wc -l) + $(echo "$STATUS" | grep '^ C' | wc -l)))
  local MODIFIED=$(echo "$STATUS" | grep '^ M' | wc -l)
  local STATS=''
  if [ $UNTRACKED != 0 ]; then
    STATS="\e[43m untr: $UNTRACKED "
  fi
  if [ $MODIFIED != 0 ]; then
    STATS="$STATS\e[43m mod: $MODIFIED "
  fi
  if [ $DRC != 0 ]; then
    STATS="$STATS\e[43m drc: $DRC "
  fi
  if [ $STAGED != 0 ]; then
    STATS="$STATS \e[42m staged: $STAGED "
  fi
  if [ ! -z "$STATS" ]; then
  echo -e "\e[30m $STATS\e[0m"
  fi
}

function origin_dist {
 local STATUS="$(git status 2> /dev/null)"
 local DIST_STRING=""
 local IS_AHEAD=$(echo -n "$STATUS" | grep "ahead")
 local IS_BEHIND=$(echo -n "$STATUS" | grep "behind")
 if [ ! -z "$IS_AHEAD" ]; then
  local DIST_VAL=$(echo "$IS_AHEAD" | sed 's/[^0-9]*//g')
  DIST_STRING="$DIST_VAL AHEAD"
 elif [ ! -z "$IS_BEHIND" ]; then
  local DIST_VAL=$(echo "$IS_BEHIND" | sed 's/[^0-9]*//g')
  DIST_STRING="BEHIND $DIST_VAL"
 fi
 if [ ! -z "$DIST_STRING" ]; then
  echo -en "\e[97;45m $DIST_STRING"
 fi
}

__PS1_GIT_BRANCH='`__git_ps1` '
__PS1_GIT_DIST='`origin_dist`'
__PS1_GIT_STATS='`git_stats` '

if $(__git_ps1 2>/dev/null);then
    PS1="\[\033[38;5;209m\]┌──[\[\033[38;5;141m\]\u\[\033[38;5;209m\]@\[\033[38;5;105m\]\h\[\033[38;5;231m\]:\w\[\033[38;5;209m\]]\[\033[33m\]${__PS1_GIT_BRANCH}${__PS1_GIT_DIST}${__PS1_GIT_STATS}\[\033[00m\]\n\[\033[38;5;209m\]└─\\[\033[38;5;209m\]\\$\[\033[37m\] "
else
    source /usr/share/git-core/contrib/completion/git-prompt.sh
    PS1="\[\033[38;5;209m\]┌──[\[\033[38;5;141m\]\u\[\033[38;5;209m\]@\[\033[38;5;105m\]\h\[\033[38;5;231m\]:\w\[\033[38;5;209m\]]\[\033[33m\]${__PS1_GIT_BRANCH}${__PS1_GIT_DIST}${__PS1_GIT_STATS}\[\033[00m\]\n\[\033[38;5;209m\]└─\\[\033[38;5;209m\]\\$\[\033[37m\] "
fi
Rename & Delete files
# Deleting
git rm my.sh

# Renaming
git mv old.sh new.sh

Git Alias
git config --global alias.st status
git config --global alias.c commit
git config --global alias.br branch

~/.gitconfig

[alias]
    st = status
    c = commit
    loo = log --oneline
    # 重新修改最後一筆 commit 的 comment
    onemore = commit -a --amend --no-edit
    # 刪除最後一筆 commit, 保留文件的修改
    undo = reset --soft HEAD^
    # 刪除最後一筆 commit, 不保留文件
    cancel = reset --hard HEAD^
Gitignore

Git 對於專案庫目錄內的所有資料夾與檔案都會納入版本控制的範圍,然而,在大部分情況,專案目錄裡有些檔案或資料夾是不需要做版控的

不需要版控的檔案或資料夾:

  1. 編譯後的檔案,例如 .o .so

  2. 如果是 Node.js 專案,目錄 node_modules 內的檔案都是從外部的軟體庫下載,與自己的程式碼無關。

  3. 與 Log 有關的檔案或資料夾,例如 .log

  4. 系統環境變數檔,例如 .env

  5. 開發工具 (IDE) 自動產生的設定檔。

.gitignore

# ignore all directories with the name test
test/

.env
log/
node_modules/
make/*.o
make/*.so

# ignores all .md files
.md

# does not ignore the README.md file
!README.md
Git log
  • 輸入 commit-id 時只需要開頭的 4 - 8 碼字元就可以。 
# 基本檢視 commit 資訊
git log
git log --oneline

# -p: patch, 詳細檢視 commit 的異動內容
git log -p
git log -p -2     # 最近兩次的 commit 異動內容

# 另一種可檢視 commit 的詳細內容
git show <commit-id>

# 檢視 commit 的狀態, 包含有異動的檔名與行數
git --stat
Git revert (Rollback)

使用 git revert 可以將專案 rollback 到某個 commit 之前的時間點,並且完成後會新增一筆 Revert 的 commit 紀錄,原先的 commit 紀錄也都會保持不變 (這與 git reset 不同)

Rollback 最後的 commit 之前的版本

# Rollback the latest commit
git revert HEAD

Rollback 指定 commit 之前的版本

注意:當 revert 的 commit 遇到有異動衝突時,必須手動排除衝突的內容,方法同 git merge 發生的衝突。如果要放棄目前的 revert,可以執行 git revert --abort 
git revert <commit-id>