git 使用

Undefined443發表於2024-06-06

git Reference

GitHub Training Kit

Git Cheat Sheet

Visual Git Cheat Sheet

GitHub Docs

Practice

# 要求 git 使用 vim 作為編輯器
git config --global core.editor "vim"

# 修改預設分支名為 main
git config --global init.defaultBranch main

如果你在 push 後,再使用 commit --amend 重新提交,然後再 push 就會產生提交衝突:

To github.com:Undefined443/Course.git
 ! [rejected]        main -> main (non-fast-forward)
error: failed to push some refs to 'github.com:Undefined443/Course.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

當然了不用擔心,只需要 git pull 後再 push 即可。

push 報錯

$ git push
kex_exchange_identification: Connection closed by remote host
Connection closed by 20.205.243.166 port 22
fatal: Could not read from remote repository.

原因:機場的代理服務節點禁止了 GitHub 的 SSH 連線。

解決辦法:使用 HTTPS 埠進行 SSH 連線

編輯 ~/.ssh/config:

Host github.com
  HostName ssh.github.com
  User git
  Port 443

kex_exchange_identification: Connection closed by remote host | 簡書

安裝與配置 Git

MacOS 自帶 Git 😃

Windows Git 詳細安裝教程 | CSDN

Git for Windows-Git BASH 命令提示符檔案位置:C:/Program Files/Git/etc/profile.d/git-prompt.sh

本地版本庫

自報家門

git config --global user.name "name"
git config --global user.email "email"

git config --get user.name  # 獲取 user.name

建立版本庫

  1. 建立版本庫
git init  # 將當前目錄設定為版本庫

# 可以更改預設主分支名:
git config --global init.defaultBranch <name>
# 或者更改當前分支名:
git branch -m <name>
  1. 把檔案新增到版本庫
# 提交到暫存區
git add <file>
git add .   # 提交當前目錄下所有檔案,包括 .gitignore
git add *   # 提交當前目錄下所有檔案,不包括 .gitignore,並且會提示 ignored 檔案
git add -A  # 提交版本庫所有檔案

# 暫存區提交到版本庫
git commit -m "message"
git commit -a                    # 自動將工作區所有修改的檔案新增到暫存區並提交
git commit --amend -m "message"  # 重新提交

我覺得 git add * 更好

不要使用 Windows 自帶的記事本編輯文字檔案

時光機穿梭

git status                # 檢視當前暫存區狀態

git diff <file>           # 檢視檔案的改動情況

git diff HEAD -- <file>   # 檢視工作區版本和版本庫最新版本的區別

git log                   # 顯示從最近到最久的提交日誌
git log --graph           # 顯示提交分支圖
git log --pretty=oneline  # 單行模式:只顯示 commit-id 和 commit-message
git log --abbrev-commit   # 只顯示前 7 位 commit-id
git log --graph --pretty=oneline --abbrev-commit  # 混合上面三種

git diff 後面兩個引數的順序對輸出是有影響的。習慣上應該這樣使用:git diff <old> <new>

運算子 ^ 與 ~

HEAD^:HEAD 的上一個結點

HEAD^^HEAD~2:HEAD 的上上個結點

當某結點有兩個父結點時,HEAD^ 指向第一個父結點,而 HEAD^2 指向第二個父結點。

這些運算子支援鏈式操作:HEAD~^2~2

reset

將當前分支指標置到指定結點,結點之後的日誌記錄被刪除(但是 commit-id 依然存在)。

git reset --hard HEAD^

image

reset 的三種模式

image

--hard:丟棄指定結點之後的修改。

--soft:修改保留到工作區且被 add 到暫存區,可以再次提交。(回到 commit 前的狀態)

--mixed(預設):修改保留到工作區,可以再次提交。(回到 add 前的狀態)

取消 reset

  1. 使用 git reflog 命令顯示參考日誌(命令日誌)
  2. 找到要恢復的版本的 commit-id,再次使用 git reset --hard <commit_id> 命令
$ git reflog
e849d38 (HEAD -> master) HEAD@{0}: reset: moving to HEAD^
188d795 HEAD@{1}: commit: appended a new line
e849d38 (HEAD -> master) HEAD@{2}: commit: modified self-introduction
1a0ec02 HEAD@{3}: commit (initial): added a self-introduction

$ git reset --hard 188d # commit-id 不用輸全,只要透過前幾位能確定版本就行
HEAD is now at 188d795 appended a new line

revert

可以理解為對某次提交進行反向修改,如果要回退已經上傳到版本庫的提交,則只能用這個命令。

git revert HEAD  # 撤銷 HEAD 提交的修改

工作區與暫存區

工作區就是我們在電腦裡能看到的目錄

工作區有一個隱藏目錄 .git,是 Git 的版本庫

image

git add 命令的作用就是將檔案修改新增到暫存區

git commit 命令的作用就是把暫存區的所有修改提交到當前分支

一旦提交後,並且對工作區沒有任何修改,那麼工作區就是 "clean" 的。

Git 管理的是修改,而不是檔案。

撤銷修改

git reset HEAD <file>        # 將 file 在暫存區的修改撤回到工作區
git restore --staged <file>  # 在 Git 2.23 版本引入了 git restore

git checkout -- <file>       # 將 file 恢復到暫存區或版本庫的最新版本,相當於丟棄工作區的修改

刪除檔案

git rm <file>  # 從工作區中刪除 file 並將修改提交到暫存區

等效於 rm <file> && git add <file>

刪除也是一種修改操作

分支管理

git branch                # 檢視分支

git branch <branch>       # 建立分支

git checkout <branch>     # 切換分支
git switch <branch>       # 新版本命令

git checkout -b <branch>  # 建立並切換分支
git switch -c <branch>    # 新版本命令

git merge <branch>        # 將 branch 合併到當前分支

git branch -d <branch>    # 刪除分支
git branch -D <branch>    # 刪除一個尚未被合併的分支

git branch -f <branch> <ref> # 將 branch 分支強制指向 ref

git log --graph --pretty=oneline --abbrev-commit  # 檢視分支的合併情況

使用 git merge 時,有時會遇到 "both added" 情況,即兩個分支都提交了一個同名的新檔案。這時需要我們選擇留下其中一個檔案:

git checkout --ours <file>  # 保留當前分支的檔案
git checkout --theirs <file>  # 保留合併分支的檔案

分支管理策略

合併分支時,Git 會優先採用 Fast forward 模式,即把當前分支直接指向合併的分支:

pic

在這種模式下,刪除分支後,在分支歷史上看不到分支資訊。

我們可以透過命令 git merge no-ff -m "message" <branch> 命令來禁用 Fast forward 模式進行合併:

image

此時 Git 會在 merge 時生成一個新的 commit。這樣,從分支歷史上就可以看出分支資訊。

因為禁用 Fast forward 模式合併時要生成一個新的 commit,因此在 git merge no-ff 命令中還要加上 -m 選項寫入描述資訊。

分支策略

在實際開發中,master 分支(主分支)用來發布新版本。dev 分支是所有人幹活的地方。每個人在 dev 分支上再建立自己的分支,完成自己的工作。

image

stash

git stash [push] ["message"]  # 將當前工作現場暫存到當前分支

git stash list  # 檢視暫存的工作現場

git stash pop [stash@{<n>}]   # 恢復工作現場並刪除 stash@{n}

# pop 等價於下面兩條命令
git stash apply [stash@{<n>}]  # 恢復 stash@{n}
git stash drop [stash@{<n>}]   # 刪除 stash@{n}

git stash clear  # 清除所有暫存的修改

當我們在一個分支上做了修改,改完之後發現自己用錯了分支,這時可以使用 stash 命令將改動暫存起來,等切換到正確的分支後再使用 stash pop 命令將改動應用到該分支上。

cherry-pick

git cherry-pick <ref>  # 摘取一個或幾個提交到 HEAD

push

git push <remote> <branch>        # 將 branch 的內容推送到遠端庫的 branch
git push <remote> <ref>:<branch>  # 將 ref 的內容推送到遠端庫的 branch。ref 可以是分支,也可以是結點。如果遠端庫的 branch 分支不存在,則會自動建立一個 branch 分支

遠端分支 remote/branch 也是儲存在本地的,它記錄了上次和遠端庫通訊時遠端庫的狀態。

如果不提供 remote 引數的話,Git 會將 branch 的內容推送到其 track 的分支。

如果使用 git push,若當前分支沒有 track 任何分支,或當前處於 Detached Head 狀態的話 push 會失敗。

如果不提供 ref 的話,Git 會刪除遠端庫的 branch。

fetch

fetch 可以獲取遠端庫上的內容而不對本地內容產生任何影響。透過 fetch 與 checkout 結合可以讓你檢視其他人在遠端庫上完成的工作而不影響你本地的工作。

git fetch  # 將遠端庫所有內容下載到本地的 remote/branch
git fetch <remote> [<branch>]  # 只下載 remote 庫的 branch 分支

pull

將遠端庫中指定分支的內容抓取下來,並與當前分支合併。

git pull <remote> <branch> 等價於:

git fetch <remote> <branch>
git merge <remote>/<branch>  # 注意是與當前分支合併,而不是 branch 關聯的分支
# 設定預設合併方式
git config pull.rebase false  # merge (the default strategy)
git config pull.rebase true   # rebase
git config pull.ff only       # fast-forward only

可以新增 --global 引數來設定全域性預設合併方式。

也可以新增 --rebase, --no-rebase, 或 --ff-only 引數在一次 pull 時指定合併方式。

Detached HEAD

一般 HEAD 都是指向某個分支,並隨分支一起移動。但當我們 checkout 到某個具體的 commit 時,這種繫結狀態被解除,成為分離頭指標狀態 (Detached HEAD)。在這種情況下,我們可以對檔案進行修改並提交,但在我們 checkout 到其他地方之前,必須建立一個新的分支來保留做的修改。

tag

git tag                  # 檢視所有標籤
git tag "1.0.0" [<ref>]  # 為 ref 建立一個 "1.0.0" 標籤
git tag -a "tag_name" -m "message" [<ref>] # 為 ref 建立帶說明的標籤
git show "1.0.0"         # 檢視標籤 "1.0.0" 的資訊
git tag -d <tag_name>                    # 刪除本地標籤

預設情況下,push 並不會把標籤推送到遠端庫,必須透過顯式命令才會推送標籤:

git push [<remote>] <tag_name>           # 推送一個標籤
git push [<remote>] --tags               # 推送所有標籤

git push <remote> :refs/tags/<tag_name>  # 刪除遠端標籤

標籤相當於一個錨點,可以用它來為一些里程碑式的修改進行標記。

標籤中不允許有空格。

describe 命令
$ git describe <ref>        # ref 可以是任何能被 Git 識別成提交記錄的引用,預設值為 HEAD
<tag>-<numCommits>-g<hash>  # tag 是 ref 之前最近的標籤,numCommits 是 tag 與 ref 相差的提交數,hash 是 ref 的雜湊值的前幾位

如果 ref 本身就有一個標籤,則只輸出該標籤名。

rebase

rebase 是從當前分支與目標分支分離的地方開始,把屬於當前分支的那一段挪到目標分支下面。

git rebase <branch>  # 把當前分支摘下來,放到指定分支上
git rebase -i <ref>  # 將當前分支以 ref 為根,重新整理中間的結點

pull request

當遠端分支被鎖定時,不允許你直接將本地分支內容 push 到遠端分支。你應該新建一個分支,push 這個分支並申請 pull request。

.gitignore

黑名單模式

file   # 忽略當前目錄下的 file 檔案
dir/   # 忽略當前目錄下的 dir 目錄
/file  # 忽略版本庫根目錄下的 file 檔案

* 指單個目錄或一個字串,** 指多個目錄:

*.c        # 忽略所有 .c 檔案
**/file    # 忽略 /file, a/file, a/b/file...
a/**/file  # 忽略 a/file, a/x/file, a/x/y/file..

白名單模式

*  # 忽略所有檔案
!.gitignore  # 跟蹤 .gitignore 檔案自身
!**/*.tex  # 跟蹤所有 .tex 檔案
!chapters/  # 白名單模式下預設所有目錄都被忽略,此時必須顯式指定要跟蹤的目錄
git check-ignore -v <file>  # 檢查 file 是否在 .gitignore 中

注意,.gitignore 只能忽略那些原來沒有被 track 的檔案。如果檔案已經被 track,則需要使用 git rm -r --cached . 命令清空本地快取,然後再提交。

貌似 .DS_Store 就能忽略所有的 .DS_Store 檔案(相當於 **/.DS_Store

gitignore 模板

gitignore reference

遠端版本庫

建立 SSH Key

ssh-keygen -t rsa -C "email"  # 接下來一路回車,以預設值建立 SSH Key
cat ~/.ssh/id_rsa.pub

公鑰相當於鎖,私鑰相當於鑰匙,如果版本庫上有你的鎖,那麼你就可以用鑰匙開啟版本庫。

登入 GitHub,點選頭像,開啟 Settings -> SSH and GPG keys,點選 New SSH key,填寫任意 Title,在 Key 中貼上 id_rsa.pub 檔案的內容,完成新增。

SSH 用於實現安全遠端登陸

Git 也支援 HTTPS 協議,但相比 SSH 速度慢,且每次推送都必須輸入口令。

新增遠端庫

在 GitHub 上新建一個庫。

複製 …or push an existing repository from the command line 下的命令:

git remote add origin <url>  # 關聯 url 指向的遠端庫並將其命名為 origin
git branch -M main           # 修改當前分支的名字為 main
git push -u origin main      # 將 main 分支推送到遠端庫 origin 並將 main 與 origin/main 關聯

新建立的分支並不會自動關聯到遠端庫,因此在 push 新分支時需要使用 git push <remote> [<branch>] 命令指定要將 branch 推送到哪個遠端庫。

-u | --set-upstream 選項可以在推送完成後設定 branch 與遠端庫的 remote/branch 相關聯。這樣以後在 push 和 pull 時可以省略 remote 引數。

關聯分支(track)

# 直接建立並進入一個跟蹤遠端分支的本地分支
git checkout -b <branch> <remote>/<branch>
git switch -c <branch> <remote>/<branch> # 新版本命令

git branch -u <remote>/<branch> [<branch>]  # 設定 branch 同步 remote/branch

💡 Tip: 只有在遠端分支 remote/branch 已經存在的情況下才能設定 branch 同步 remote/branch。

檢視所有遠端分支:git branch -r

關聯多個遠端庫

同步推送

git remote set-url --add <remote> <url>  # 為現有的遠端庫新增額外的 URL

或者分別推送

再新增一個遠端庫,以後在 push,pull 時都需指定 remote:

git remote add <remote> <url>

刪除遠端庫

可以先用 git remote -v 命令檢視遠端庫的詳細資訊。

詳細資訊中 fetch 表示可以抓取的地址,push 表示可以推送的地址。

然後執行 git remote rm <remote> 命令刪除遠端庫。

此處的刪除只是解除本地庫和遠端庫的繫結關係

克隆遠端庫

在 GitHub 庫的 <> Code 區的綠色按鈕 Code 中複製 SSH Key,然後執行命令:

git clone <url>

Git 將把遠端庫複製到當前目錄。注意,當前目錄下不能有 .git 檔案。

CNBlogs: gitignore

Advanced

比較兩個分支的差異:

git diff <branch1> <branch2> --stat

git 對比兩個分支差異 | 簡書

子模組 | git Docs

Git 教程 | 廖雪峰

Git 簡明指南

圖形化 Git 學習網站

上傳大檔案