Git使用教程

本教程為小甲魚《極客Python之Git實(shí)用教程》課程筆記,大部分為原課程講義,其中穿插了自己實(shí)戰(zhàn)過(guò)程中遇到的一些問(wèn)題及解決辦法。


Git使用教程1---世界上最先進(jìn)的分布式版本控制系統(tǒng)簡(jiǎn)介
Git使用教程2---Git的安裝與理論基礎(chǔ)
Git使用教程3---實(shí)戰(zhàn)
Git使用教程4---狀態(tài)
Git使用教程5---回到過(guò)去
Git使用教程6---版本對(duì)比
Git使用教程7---修改最后一次提交、刪除文件和重命名文件
Git使用教程8---創(chuàng)建和切換分支
Git使用教程9---合并和刪除分支
Git使用教程10---匿名分支和checkout命令


Git使用教程2---Git的安裝與理論基礎(chǔ)

一、Git安裝

windows安裝:進(jìn)入網(wǎng)站https://git-scm.com/下載安裝,然后在cmd命令行配置

> git config --global user.name "FishC_Service"
> git config --global user.email "fishc_service@126.com"
#檢查信息是否寫入成功
git config --list 

ubuntu配置:apt-get install git

二、理論基礎(chǔ)

Git 記錄的是什么?

如上,如果每個(gè)版本中有文件發(fā)生變動(dòng),Git 會(huì)將整個(gè)文件復(fù)制并保存起來(lái)。這種設(shè)計(jì)看似會(huì)多消耗更多的空間,但在分支管理時(shí)卻是帶來(lái)了很多的益處和便利。

三棵樹

你的本地倉(cāng)庫(kù)有 Git 維護(hù)的三棵“樹”組成,這是 Git 的核心框架。這三棵樹分別是:工作區(qū)域、暫存區(qū)域和 Git 倉(cāng)庫(kù)

工作區(qū)域(Working Directory)就是你平時(shí)存放項(xiàng)目代碼的地方。

暫存區(qū)域(Stage)用于臨時(shí)存放你的改動(dòng),事實(shí)上它只是一個(gè)文件,保存即將提交的文件列表信息。

Git 倉(cāng)庫(kù)(Repository)就是安全存放數(shù)據(jù)的位置,這里邊有你提交的所有版本的數(shù)據(jù)。其中,HEAD 指向最新放入倉(cāng)庫(kù)的版本(這第三棵樹,確切的說(shuō),應(yīng)該是 Git 倉(cāng)庫(kù)中 HEAD 指向的版本)。

Git 的工作流程一般是:

  1. 在工作目錄中添加、修改文件;

  2. 將需要進(jìn)行版本管理的文件放入暫存區(qū)域;

  3. 將暫存區(qū)域的文件提交到 Git 倉(cāng)庫(kù)。

因此,Git 管理的文件有三種狀態(tài):已修改(modified)、已暫存(staged)和已提交(committed),依次對(duì)應(yīng)上邊的每一個(gè)流程。

Git使用教程3---實(shí)戰(zhàn)

一、初始化Git

在自己方便的盤中新建一個(gè)文件夾,這里以MyProject為例,注意路徑中不要含有中文字符。打開cmd命令窗口,操作如下:

#初始狀態(tài)在C盤中,更改路徑
C:\Users\lenovo>F:
F:\>
F:\>cd MyProject
F:\MyProject>
#初始化Git項(xiàng)目,成功后創(chuàng)建有一個(gè).git隱藏文件
F:\MyProject>git init
Initialized empty Git repository in F:/MyProject/.git/
#在文件夾MyProject中添加一個(gè)文本文件README,md格式指Markdown格式(建議使用Notepad++編輯)
#然后輸入以下命令將文件加入暫存區(qū)
F:\MyProject>git add README.md
#將文件提交到git倉(cāng)庫(kù)(-m表示添加本次提交的說(shuō)明,強(qiáng)制要求寫的)
F:\MyProject>git commit -m "add a readme file"
[master (root-commit) 9e08cf4] add a readme file
 1 file changed, 1 insertion(+)
 create mode 100644 README.md

如何將GItHub上別人的代碼“據(jù)為己有”?
F:\>Clone>git clone 目標(biāo)

Git使用教程4---狀態(tài)

我們?cè)趺粗滥男┪募切绿砑拥?,哪些文件已?jīng)加入了暫存區(qū)域呢?總不能讓我們自己拿個(gè)本本記下來(lái)吧?
當(dāng)然不,作為世界上最偉大的版本控制系統(tǒng),你能遇到的囧境,Git 早已有了相應(yīng)的解決方案。隨時(shí)隨地都可以使用git status查看當(dāng)前狀態(tài)

F:\MyProject>git status
On branch master
nothing to commit, working tree clean

On branch master: 我們位于一個(gè)叫做“master”的分支里,這是默認(rèn)的分支
nothing to commit, working directory clean : 說(shuō)明你的工作目錄目前是“干凈的”,沒(méi)有需要提交的文件(意思就是自上次提交后,工作目錄中的內(nèi)容壓根兒就沒(méi)改動(dòng)過(guò))。

在工作目錄中添加LICENSE文件(去掉后綴,用NotePad++打開):

Copyright (C) <year> <copyright holders>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

小甲魚友情提示:MIT 許可證幾乎是最寬松的版權(quán)約定,一旦你的程序采用,也就意味著別人只要在軟件包含上邊的版權(quán)聲明,就可以對(duì)你的程序?yàn)樗麨榱耍òā笆褂?、?fù)制、修改、合并、出版發(fā)行、散布、再授權(quán)和/或販?zhǔn)圮浖败浖母北尽保?/em>

輸入git status命令,提示如下:

F:\MyProject>git status
On branch master
Untracked files:
  (use "git add <file>..." to include in what will be committed)

        LICENSE(紅色)

nothing added to commit but untracked files present (use "git add" to track)

Untracked files 說(shuō)明存在未跟蹤的文件(下邊紅色的那個(gè))

所謂的“未跟蹤”文件,是指那些新添加的并且未被加入到暫存區(qū)域或提交的文件。它們處于一個(gè)逍遙法外的狀態(tài),但你一旦將它們加入暫存區(qū)域或提交到 Git 倉(cāng)庫(kù),它們就開始受到 Git 的“跟蹤”。

這里圓括號(hào)中的英文是 git 給我們的建議:使用 git add <file> 命令將待提交的文件添加到暫存區(qū)域。

F:\MyProject>git add LICENSE

F:\MyProject>git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        new file:   LICENSE(綠色)

use "git reset HEAD <file>..." to unstage 的意思是“如果你反悔了,你可以使用 git reset HEAD 命令恢復(fù)暫存區(qū)域”。如果后面接文件名,表示恢復(fù)該文件;如果不接文件名,則表示上一次添加的文件。

F:\MyProject>git reset HEAD

F:\MyProject>git status
On branch master
Untracked files:
  (use "git add <file>..." to include in what will be committed)

        LICENSE(紅色)

nothing added to commit but untracked files present (use "git add" to track)

再次添加到暫存區(qū)域,然后執(zhí)行 git commit -m "add a license file" 命令:

F:\MyProject>git add LICENSE

F:\MyProject>git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        new file:   LICENSE


F:\MyProject>git commit -m "add a license file"
[master 9fdf9f4] add a license file
 1 file changed, 7 insertions(+)
 create mode 100644 LICENSE

F:\MyProject>git status
On branch master
nothing to commit, working tree clean

關(guān)于修改

突然發(fā)現(xiàn)版權(quán)那塊忘了寫上自己的名字了……
打開 LICENSE 文件,將 Copyright (C) <year> <copyright holders> 改為 Copyright (C) 2016 FishC,保存……

執(zhí)行 git status 命令:

F:\MyProject>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:   LICENSE(紅色)

no changes added to commit (use "git add" and/or "git commit -a")

由于你對(duì)工作目錄的文件進(jìn)行了修改,導(dǎo)致這個(gè)文件和暫存區(qū)域的對(duì)應(yīng)文件不匹配了,所以 Git 又給你提出兩條建議:

  • 使用 git add 命令將工作目錄的新版本覆蓋暫存區(qū)域的舊版本,然后準(zhǔn)備提交
  • 使用 git checkout 命令將暫存區(qū)域的舊版本覆蓋工作目錄的新版本(危險(xiǎn)操作:相當(dāng)于丟棄工作目錄的修改)

還有一種情況我們沒(méi)分析,大家先把新版本的文件覆蓋掉暫存區(qū)域的舊版本:

F:\MyProject>git add LICENSE

F:\MyProject>git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   LICENSE(綠色)

然后我們打開 LICENSE 文件,將 FishC 改為 FishC.com,保存……

F:\MyProject>git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   LICENSE(綠色)

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:   LICENSE(紅色)

這次情況是:被綠的 LICENSE 說(shuō)明文件存放在暫存區(qū)域(待提交),同時(shí)紅色的 LICENSE 說(shuō)明文件還在工作目錄等待添加到暫存區(qū)域。

這種情況你應(yīng)該意識(shí)到這里存在兩個(gè)不同版本的 LICENSE 文件,這時(shí)如果你直接執(zhí)行 commit 命令,那么提交的是暫存區(qū)域的版本(FishC),如果你希望提交工作目錄的新版本(FishC.com),那么你需要先執(zhí)行 add 命令覆蓋暫存區(qū)域,然后再提交……

一步到位

從工作目錄一步添加到倉(cāng)庫(kù):git commit -am “說(shuō)明”

F:\MyProject>git commit -am "change the license file"

-a的意思是add。

git log 查看歷史操作記錄

Git使用教程5---回到過(guò)去

有關(guān)回退的命令有兩個(gè):reset 和 checkout

先執(zhí)行g(shù)it log命令,將此時(shí)的Git倉(cāng)庫(kù)可視化

三棵樹的情況:

回滾快照

注:快照即提交的版本,每個(gè)版本我們稱之為一個(gè)快照。

現(xiàn)在我們利用 reset 命令回滾快照,并看看 Git 倉(cāng)庫(kù)和三棵樹分別發(fā)生了什么。

執(zhí)行 git reset HEAD~ 命令:

注:HEAD 表示最新提交的快照,而 HEAD~ 表示 HEAD 的上一個(gè)快照,HEAD~~表示上上個(gè)快照,如果表示上10個(gè)快照,則可以用HEAD ~10

此時(shí)我們的快找回滾到了第二棵數(shù)(暫存區(qū)域)


第一次執(zhí)行reset后Git倉(cāng)庫(kù)
第一次執(zhí)行reset后三棵樹

git reset HEAD~ 命令其實(shí)是 git reset --mixed HEAD~ 的縮寫, --mixed 選項(xiàng)是默認(rèn)的。

默認(rèn)
git reset HEAD~ 命令其實(shí)影響了兩棵樹:首先是移動(dòng) HEAD 的指向,將其指向上一個(gè)快照(HEAD~);然后再將該位置的快照回滾到暫存區(qū)域。
--soft選項(xiàng)
git reset --soft HEAD~ 命令就相當(dāng)于只移動(dòng) HEAD 的指向,但并不會(huì)將快照回滾到暫存區(qū)域。相當(dāng)于撤消了上一次的提交(commit)。一不小心提交了,后悔了,那么你就執(zhí)行 git reset --soft HEAD~ 命令即可(此時(shí)執(zhí)行 git log 命令,也不會(huì)再看到已經(jīng)撤消了的那個(gè)提交)。
--hard選項(xiàng)
reset 不僅移動(dòng) HEAD 的指向,將快照回滾動(dòng)到暫存區(qū)域,它還將暫存區(qū)域的文件還原到工作目錄。

回滾指定快照
reset 不僅可以回滾指定快照,還可以回滾個(gè)別文件。

命令格式為: git reset 快照 文件名/路徑

這樣,它就會(huì)將忽略移動(dòng) HEAD 的指向這一步(因?yàn)槟阒皇腔貪L快照的部分內(nèi)容,并不是整個(gè)快照,所以 HEAD 的指向不應(yīng)該發(fā)生改變),直接將指定快照的指定文件回滾到暫存區(qū)域。

不僅可以往回滾,還可以往前滾!
這里需要強(qiáng)調(diào)的是:reset 不僅是一個(gè)“復(fù)古”的命令,它不僅可以回到過(guò)去,還可以去到“未來(lái)”。

唯一的一個(gè)前提條件是:你需要知道指定快照的 ID 號(hào)。

那如果不小心把命令窗口關(guān)了不記得ID號(hào)怎么辦?
命令:git reflog
Git記錄的每一次操作的版本ID號(hào)

Git使用教程6---版本對(duì)比

目的:對(duì)比版本之間有哪些不同

準(zhǔn)備工作

  1. 創(chuàng)建一個(gè)叫做 MyProject2 的新文件夾作為本次演示的項(xiàng)目,初始化 Git
  2. 創(chuàng)建一個(gè) game.py 的文本,將下邊代碼拷貝進(jìn)去:
import random

print('------------------我愛(ài)魚C工作室------------------')
secret = random.randint(1,10)
temp = input("不妨猜一下小甲魚現(xiàn)在心里想的是哪個(gè)數(shù)字:")
guess = int(temp)

while guess != secret:
    temp = input("哎呀,猜錯(cuò)了,請(qǐng)重新輸入吧:")
    guess = int(temp)
    if guess == secret:
        print("臥槽,你是小甲魚心里的蛔蟲嗎?!")
        print("哼,猜中了也沒(méi)有獎(jiǎng)勵(lì)!")
    else:
        if guess > secret:
            print("大了,大了~~~")
        else:
            print("小了,小了~~~")
            
print("游戲結(jié)束,不玩啦^_^")

再創(chuàng)建一個(gè) README.md 文件,寫清楚這是一個(gè)課后作業(yè)的項(xiàng)目:

課后作業(yè):文字游戲

  1. 執(zhí)行 git add README.md game.py 命令將文件添加到暫存區(qū)域,接著執(zhí)行 git commit -m "猜數(shù)字游戲" 提交項(xiàng)目的第一個(gè)快照
  2. 更改代碼如下:
import random

print('------------------我愛(ài)魚C工作室------------------')

times = 3
secret = random.randint(1,10)
guess = 0

print("不妨猜一下小甲魚現(xiàn)在心里想的是哪個(gè)數(shù)字:", end=" ")

while (guess != secret) and (times > 0):
    temp = input()
    guess = int(temp)
    times = times - 1 # 用戶每輸入一次,可用機(jī)會(huì)就-1
    if guess == secret:
        print("哇哦,你是小甲魚心里的蛔蟲嗎?!")
        print("哼,猜中了也沒(méi)有獎(jiǎng)勵(lì)!")
    else:
        if guess > secret:
            print("哥,大了大了~~~")
        else:
            print("嘿,小了,小了~~~")
        if times > 0:
            print("再試一次吧:", end=" ")
        else:
            print("機(jī)會(huì)用光咯T_T")
            
print("游戲結(jié)束,不玩啦^_^")

更改README文件

《零基礎(chǔ)入門學(xué)習(xí)Python》第004講
課后作業(yè):文字游戲

比較暫存區(qū)域與工作目錄

直接執(zhí)行 git diff 命令是比較暫存區(qū)域與工作目錄的文件內(nèi)容:

這里可能出現(xiàn)一個(gè)問(wèn)題,直接執(zhí)行的結(jié)果出現(xiàn)中文亂碼。在cmd界面的冒號(hào):后面輸入q退出,使用Notepad++打開README文件,點(diǎn)擊編碼→轉(zhuǎn)為UTF-8編碼。(舊版本中選擇UTF-8(無(wú)BOM),新版中的UTF-8默認(rèn)為無(wú)BOM)重新執(zhí)行g(shù)it diff發(fā)現(xiàn)還是不行!怎么肥四??在本菜查遍各網(wǎng)站論壇之后,終于找到了解決方法
執(zhí)行以下三條命令(別問(wèn)我明明是四條為啥說(shuō)三條...)

git config --global i18n.commitencoding utf-8
git config --global i18n.logoutputencoding utf-8

export LESSCHARSET=utf-8 ## linux bash配置環(huán)境變量
set LESSCHARSET=utf-8 #windows配置環(huán)境變量

哈哈哈,終于搞定了!


現(xiàn)在來(lái)解釋一下上面每一行的含義:
第一行:diff --git a/README.md b/README.md
表示對(duì)比的是存放在暫存區(qū)域的 README.md 和工作目錄的 README.md
第二行:index 7966837..472a180 100644
表示對(duì)應(yīng)文件的 ID 分別是 7966837 和 472a180,左邊暫存區(qū)域,后邊當(dāng)前目錄。最后的 100644 是指定文件的類型和權(quán)限
第三行:--- a/README.md
--- 表示該文件是舊文件(存放在暫存區(qū)域)
第四行:+++ b/README.md
+++ 表示該文件是新文件(存放在工作區(qū)域)
第五行:@@ -1 +1,2 @@
以 @@ 開頭和結(jié)束,中間的“-”表示舊文件,“+”表示新文件,后邊的數(shù)字表示“開始行號(hào),顯示行數(shù)”
第六、七行:

這是將兩個(gè)文件合并顯示的結(jié)果,前邊有個(gè) + 的綠色的那一行說(shuō)明是新文件獨(dú)有的,淺灰色的則是兩個(gè)文件所共有的內(nèi)容。所以,+1,2 表示新文件在合并顯示中從第 1 行開始,顯示 2 行。那為啥 -1 后邊沒(méi)有顯示的行數(shù)?因?yàn)樵诤喜@示的結(jié)果中,舊文件已經(jīng)完全包含在新文件中了(也就是舊文件沒(méi)有自己“獨(dú)有的”內(nèi)容)。
第八行:\ No newline at end of file
這是 Git 出于善意的提醒:文件不是以換行符結(jié)束。為什么會(huì)有這樣多此一舉的提醒呢?仔細(xì)推敲下你就會(huì)明白了:diff 將兩個(gè)文件合并打印,到達(dá)最后一個(gè)字符的時(shí)候,無(wú)論文件中是否存在換行符,diff 都需要打印一個(gè)換行符!為啥?為了好看唄??!所以如果你的文件最后一個(gè)字符不是以換行符結(jié)尾,但 diff 又自行添加了一個(gè)換行符,所以它覺(jué)得有義務(wù)提醒你它這么做了!
最后的(:)是什么?
意思是窗口太小,沒(méi)辦法顯示全部,正在等待命令(Vim編程知識(shí))

移動(dòng)命令
j、k:向下移動(dòng)一行/向上移動(dòng)一行
f、b:向下翻頁(yè)/向上翻頁(yè)
d、u:向下翻半頁(yè)/向上翻半頁(yè)
跳轉(zhuǎn)命令
g、G:跳轉(zhuǎn)到第一行/跳轉(zhuǎn)到最后一行
先輸入數(shù)字(如3),再輸入g,表示跳轉(zhuǎn)到該行(如第3行)
搜索命令
輸入斜杠(/)或問(wèn)號(hào)(?),后面接搜索關(guān)鍵字
區(qū)別:斜杠(/)表示從當(dāng)前位置向下搜索,問(wèn)號(hào)(?)表示從當(dāng)前位置向上搜索。
接著輸入 n 表示順著當(dāng)前的搜索方向快速跳轉(zhuǎn)到下個(gè)匹配的位置,大寫的 N 則是與當(dāng)前搜索方向相反。
退出和幫助
在點(diǎn)點(diǎn)(:)后邊輸入 q,表示退出 diff;輸入 h 表示進(jìn)入幫助界面,你會(huì)看到很多命令和功能,輸入 q 可以退出幫助界面。

比較兩個(gè)歷史快照

我們執(zhí)行 git commit -am "添加功能:玩家只有三次機(jī)會(huì)" 命令,添加并提交工作目錄中的所有文件。執(zhí)行 git log 命令,可以看到現(xiàn)在 Git 倉(cāng)庫(kù)中已經(jīng)有兩個(gè)快照了:

執(zhí)行 git diff 6e26975 ed3708c 命令,即可比較 Git 倉(cāng)庫(kù)中兩個(gè)快照的差異:

Git倉(cāng)庫(kù)中目前兩個(gè)快照的差異

比較當(dāng)前工作目錄和 Git 倉(cāng)庫(kù)中的快照

我們稍微改動(dòng)一下 README.md 文件的內(nèi)容:

目前我們的 Git 倉(cāng)庫(kù)應(yīng)該是:

三棵樹是這樣:

比較之前版本的快照與當(dāng)前工作目錄內(nèi)容
輸入 git diff ed3708c 命令即可

比較當(dāng)前版本快照與當(dāng)前工作目錄內(nèi)容
輸入 git diff HEAD 命令即可

比較Git倉(cāng)庫(kù)與暫存區(qū)域

執(zhí)行 git add README.md 命令,將第三版的 README.md 文件添加到暫存區(qū)域。
然后三棵樹就是這樣了:

如果希望比較最新提交的快照和暫存區(qū)域的文件,只需要執(zhí)行 git diff --cached 命令;當(dāng)然也可以指定其他快照,就是需要多寫上一個(gè) ID 值,即git diff --cached ID號(hào)。

哈哈哈,很亂有木有,其實(shí)就是這三棵樹之間的相互比較,最后奉上終極奧義圖:

Git使用教程7---修改最后一次提交、刪除文件和重命名文件

問(wèn)題出現(xiàn):
Situation One:版本剛一提交(commit)到倉(cāng)庫(kù),突然想起漏掉兩個(gè)文件還沒(méi)有添加(add)
Situation Two:版本剛一提交(commit)到倉(cāng)庫(kù),突然想起版本說(shuō)明寫得不夠全面,無(wú)法彰顯你本次修改的重大意義……

由于使用reset命令過(guò)于繁瑣,需要提交一個(gè)新的版本,這里可以使用帶 --amend 選項(xiàng)的 commit 命令,(即git commit --amend)Git 會(huì)“更正”最近的一次提交。由于這里沒(méi)有-m說(shuō)明,會(huì)進(jìn)入以下頁(yè)面:

這個(gè)界面其實(shí)只是讓你編輯提交說(shuō)明而已。如果不需要修改,可以連續(xù)按下兩個(gè)大寫Z來(lái)退出,或者按下(:)再輸入q!退出,Git會(huì)保留舊的提交說(shuō)明。如果需要提交說(shuō)明又不想用這么繁瑣的方式,輸入git commit --ammend -m “新的提交說(shuō)明” 即可。

刪除文件

問(wèn)題一:不小心刪除文件怎么辦?
現(xiàn)在從工作目錄中手動(dòng)刪除 README.md 文件,然后執(zhí)行 git status 命令:

提醒使用 checkout 命令可以將暫存區(qū)域的文件恢復(fù)到工作目錄:

文件就會(huì)重新返回。
問(wèn)題二:那么如何徹底刪除一個(gè)文件呢?
假如你不小心把小黃圖下載到了工作目錄,然后又不小心提交到了 Git 倉(cāng)庫(kù):

執(zhí)行 git rm yellow.jpg 命令:

此時(shí)工作目錄中的小黃圖(yellow.jpg)已經(jīng)被刪除……
但執(zhí)行 git status 命令,你仍然發(fā)現(xiàn) Git 還不肯松手:

意思是說(shuō)它在倉(cāng)庫(kù)的快照中發(fā)現(xiàn)有個(gè)叫 yellow 的東西,但似乎在暫存區(qū)域和當(dāng)前目錄不見(jiàn)了!

此時(shí)可以執(zhí)行 git reset --soft HEAD~ 命令將快照回滾到上一個(gè)位置,然后重新提交,Git 就不會(huì)再提小黃圖的事兒了:

注意:rm 命令刪除的只是工作目錄和暫存區(qū)域的文件(即取消跟蹤,在下次提交時(shí)不納入版本管理)

問(wèn)題三:我在工作目錄中增加一個(gè) test.py 文件,然后執(zhí)行 git add test.py 命令將其添加到暫存區(qū)域,此時(shí)我修改 test.py 文件的內(nèi)容,那么暫存區(qū)域和工作目錄就是兩個(gè)不同的 test.py 文件了,此時(shí)如果我執(zhí)行 git rm test.py 命令,Git 會(huì)下意識(shí)地阻止我,這是怎么辦?

因?yàn)閮蓚€(gè)不同內(nèi)容的同名文件,誰(shuí)知道你是不是搞清楚了都要?jiǎng)h掉?還是提醒一下好,別等一下出錯(cuò)了又要賴機(jī)器……
根據(jù)提示,執(zhí)行 git rm -f test.py 命令就可以把兩個(gè)都刪除。

問(wèn)題四:我只想刪除暫存區(qū)域的文件,保留工作目錄的,應(yīng)該怎么操作?
執(zhí)行 git rm --cached 文件名 命令。

重命名文件

直接在工作目錄重命名文件,執(zhí)行g(shù)it status出現(xiàn)錯(cuò)誤:

正確的姿勢(shì)應(yīng)該是:

git mv 舊文件名 新文件名

注:Windows 使用 ren 命令修改文件名,Linux 是使用 mv 命令

教程6彩蛋

如何讓Git 識(shí)別某些格式的文件,然后自主不跟蹤它們?
比如工作目錄中有三個(gè)文件1.temp、2.temp 和 3.temp,我們不希望后綴名為 temp 的文件被追蹤,可是每次執(zhí)行g(shù)it status都會(huì)出現(xiàn):

解決辦法:在工作目錄創(chuàng)建一個(gè)名為 .gitignore 的文件。

然后你發(fā)現(xiàn) Windows 壓根兒不允許你在文件管理器中創(chuàng)建以點(diǎn)(.)開頭的文件。windows需要在命令行窗口創(chuàng)建(.)開頭的文件。執(zhí)行 echo *.temp > .gitignore 命令,創(chuàng)建一個(gè) .gitignore 文件,并讓 Git 忽略所有 .temp 后綴的文件:

好了,Git 已經(jīng)忽略了所有的 *.temp 文件(你還可以把 .gitignore 文件也一并忽略)。

Git使用教程7---創(chuàng)建和切換分支

分支是什么?

假設(shè)你的大項(xiàng)目已經(jīng)上線了(有上百萬(wàn)人在使用),過(guò)了一段時(shí)間你突然覺(jué)得應(yīng)該添加一些新的功能,但是為了保險(xiǎn)起見(jiàn),你肯定不能在當(dāng)前項(xiàng)目上直接進(jìn)行開發(fā),這時(shí)候你就有創(chuàng)建分支的需要了。

對(duì)于其它版本控制系統(tǒng)而言,創(chuàng)建分支常常需要完全創(chuàng)建一個(gè)源代碼目錄的副本,項(xiàng)目越大,耗費(fèi)的時(shí)間就越多;而 Git 由于每一個(gè)結(jié)點(diǎn)都已經(jīng)是一個(gè)完整的項(xiàng)目,所以只需要?jiǎng)?chuàng)建多一個(gè)“指針”(像 master)指向分支開始的位置即可。

創(chuàng)建分支

來(lái)到之前創(chuàng)建的項(xiàng)目MyProject2,

目前倉(cāng)庫(kù)的狀態(tài)如下:

執(zhí)行g(shù)it status查看狀態(tài):

可以看到 README.md 文件被修改并添加到暫存區(qū)域(還沒(méi)有提交),所以當(dāng)前三棵樹應(yīng)該是這樣:

創(chuàng)建分支,使用 git branch 分支名 命令:

沒(méi)有任何提示說(shuō)明分支創(chuàng)建成功(一般也不會(huì)失敗啦,除非創(chuàng)建了同名的分支會(huì)提醒你一下),此時(shí)可以執(zhí)行 git log --decorate 命令查看:

如果希望以“精簡(jiǎn)版”的方式顯示,可以加上一個(gè) --oneline 選項(xiàng)(即 git log --decorate --oneline),這樣就只用一行來(lái)顯示一個(gè)快照記錄。

可以看到最新的快照后邊多了一個(gè) (HEAD -> master, feature)

它的意思是:目前有兩個(gè)分支,一個(gè)是主分支(master),一個(gè)是剛才我們創(chuàng)建的新分支(feature),然后 HEAD 指針仍然指向默認(rèn)的 master 分支。

所以目前倉(cāng)庫(kù)中的快照應(yīng)該是這樣:


切換分支

現(xiàn)在我們需要將工作環(huán)境切換到新創(chuàng)建的分支(feature)上,使用的就是之前我們欲言又止的 checkout 命令。執(zhí)行 git checkout feature 命令:

這樣 HEAD 指針就指向 feature 分支了:

現(xiàn)在我們進(jìn)行一次提交(暫存區(qū)域還有一個(gè)更改的文件沒(méi)有提交呢):

現(xiàn)在倉(cāng)庫(kù)中的快照應(yīng)該是醬紫(提交的快照由當(dāng)前HEAD指針指向的分支來(lái)管理):

然后我們將 HEAD 指針切回 master 分支:

細(xì)心的朋友會(huì)發(fā)現(xiàn)上一次對(duì) README.md 文件的修改已經(jīng)蕩然無(wú)存了,這是因?yàn)槲覀兊墓ぷ髂夸浺呀?jīng)回到 master 分支的狀態(tài)中:

現(xiàn)在對(duì) README.md 文件進(jìn)行修改(隨便改改),然后執(zhí)行 git commit -m "再次修改說(shuō)明文件":

目前倉(cāng)庫(kù)中的快照應(yīng)該變成了醬紫:

執(zhí)行 git log --oneline --decorate --graph --all 命令:

--graph 選項(xiàng)表示讓 Git 繪制分支圖,--all 表示顯示所有分支

Git使用教程9---合并和刪除分支

合并分支

當(dāng)一個(gè)子分支的使命完結(jié)之后,它就應(yīng)該回歸到主分支中去。

合并分支我們使用 merge 命令,執(zhí)行 git merge feature 命令,將 feature 分支合并到 HEAD 所在的分支(master)上:

從 Git 提示的內(nèi)容來(lái)看,我們知道這次的合并并沒(méi)有成功,Git 說(shuō):

合并 README.md 文件的時(shí)候出現(xiàn)沖突。

所以自動(dòng)合并失敗;請(qǐng)修改沖突的內(nèi)容并重新提交快照。

意思是說(shuō)現(xiàn)在你需要先解決沖突的問(wèn)題,Git 才能進(jìn)行合并操作。所謂沖突,無(wú)非就是像兩個(gè)分支中存在同名但內(nèi)容卻不同的文件,Git 不知道你要舍棄哪一個(gè)或保留哪一個(gè),所以需要你自己來(lái)決定。
此時(shí)執(zhí)行 git status 命令也會(huì)顯示需要你解決的沖突:

然后 Git 會(huì)在有沖突的文件中加入一些標(biāo)記,不信你打開 README.md 文件看看:

以“=======”為界,上到“<<<<<<< HEAD”的內(nèi)容表示當(dāng)前分支,下到“>>>>>>> feature”表示待合并的 feature 分支,之間的內(nèi)容就是沖突的地方。

現(xiàn)在我們將 README.md 統(tǒng)一修改(去掉 <<<<<<< HEAD 等內(nèi)容)
保存文件,然后提交快照:

執(zhí)行 git log --decorate --all --graph --oneline 命令,可以看到此時(shí)的分支已經(jīng)自動(dòng)合并了:

當(dāng)然,如果不存在沖突,就不用搞這么多了……舉個(gè)例子:
執(zhí)行 git checkout -b feature2 命令(相當(dāng)于 git branch feature2 和 git checkout feature2 兩個(gè)命令的合體):

在工作目錄隨便創(chuàng)建一個(gè)文本文件(feature2.txt)并提交快照:

執(zhí)行 git log --decorate --all --graph --oneline 命令:

可以看到,feature2 分支比 master 分支快了一步?,F(xiàn)在我們切換回 master 分支,并將 feature2 分支合并進(jìn)來(lái):

這次 Git 只顯示了 Fast-forward(快進(jìn))這詞兒,這是因?yàn)?feature2 這個(gè)分支的父結(jié)點(diǎn)是 master 分支,所以 Git 只需要簡(jiǎn)單移動(dòng) master 的指向即可。

執(zhí)行 git log --decorate --all --graph --oneline 命令:

刪除分支

刪除分支,使用 git branch -d 分支名 命令:

執(zhí)行 git log --decorate --all --graph --oneline 命令:

由于 Git 的分支原理實(shí)際上只是通過(guò)一個(gè)指針記載,所以創(chuàng)建和刪除分支都幾乎是瞬間完成。

注意:如果試圖刪除未合并的分支,Git 會(huì)提示你“該分支未完全合并,如果你確定要?jiǎng)h除,請(qǐng)使用 git branch -D 分支名 命令。

彩蛋:Git的兩種合并方式 --- Fast-forward 和 Three-way merge

Fast-forward

所謂的 Fast-forward 就是當(dāng)待合并的分支位于目標(biāo)分支的直接上游時(shí),Git 只需把目標(biāo)分支的指針直接移動(dòng)即可實(shí)現(xiàn)合并。

比如下面圖片中 master 分支是位于 feature2 分支的直接上游:

將 feature2 分支合并到 master 分支,只需要移動(dòng) master 的指針即可:

Three-way merge

如果待合并的兩個(gè)分支不在同一條線上,那么進(jìn)行合并就需要解決一個(gè)根本的問(wèn)題 —— 沖突!

為何兩個(gè)分支在同一條線上就不會(huì)沖突呢?

因?yàn)?Git 的快照是按時(shí)間順序提交的,所以在同一條線上的兩個(gè)快照,它們是有先后順序的,盡管兩者可能出現(xiàn)同名文件不同內(nèi)容,Git 會(huì)認(rèn)為這是“改變”而不是“沖突”。

舉個(gè)例子:

合并 C3 和 C4 得到 C5,但 C5 應(yīng)該如何處理“沖突”呢?

SVN 會(huì)把問(wèn)題拋給用戶,讓用戶自行解決;Git 則顯得更為高明,它會(huì)找到第三個(gè)快照,然后綜合三者特點(diǎn)自動(dòng)解決沖突。

那第三個(gè)快照應(yīng)該如何決定呢?

沒(méi)錯(cuò),應(yīng)該找兩者的共同“祖先”作為參照物,一比較就知道兩個(gè)分支都干了些什么。

圖片中 C3 和 C4 的共同祖先是 C1,可以看到 C3 和 C4 分別增加了 test2.py 和 test1.py 兩個(gè)文件。因?yàn)閷?duì)比之后發(fā)現(xiàn)三者并沒(méi)有沖突,所以 C5 應(yīng)該是三者的合體,即同時(shí)擁有 common.py、test1.py 和 test2.py 三個(gè)文件。

另外,值得一提的是,Git 這種合并方式也適用于同名文件的不同更改。

舉個(gè)例子:

這里 C3 和 C4 都只有一個(gè)文件(test.txt),但是內(nèi)容卻不一樣。如果這樣合并,你猜 Git 會(huì)不會(huì)報(bào)“沖突”?

答案是不會(huì)的!

因?yàn)?Git 找到它們的共同祖先 C1,可以看到 C3 和 C4 都是在 C1 的基礎(chǔ)上進(jìn)行添加(C4 在第 2 行添加了“I”,C3 在第 4 行增加了“FishC”,C1 第 3 行的“l(fā)ove”是它們共同擁有的),同時(shí)在每一行并沒(méi)有產(chǎn)生沖突的地方,所以自動(dòng)合并后的 C5 是這樣的:

# test.txt
I
love
FishC

注意:如果 Git 檢測(cè)到同一行有不同的內(nèi)容,還是會(huì)報(bào)沖突并讓你自行決定誰(shuí)去誰(shuí)留的!

Git使用教程10---匿名分支和checkout命令

匿名分支

創(chuàng)建一個(gè)新的文件夾(MyProject3),然后初始化 git。依次創(chuàng)建三個(gè)文件并提交(每創(chuàng)建一個(gè)文件提交一次):

上一節(jié)課我們講的是使用 branch 命令創(chuàng)建一個(gè)分支,然后使用 checkout 命令切換分支。

如果在沒(méi)創(chuàng)建分支的情況下執(zhí)行 checkout 命令,會(huì)有怎樣的事情發(fā)生呢?

我們不妨試試看,執(zhí)行 git checkout HEAD~ 命令:

當(dāng)前的 HEAD 指針處于分離狀態(tài),你可以環(huán)顧四周,做一些實(shí)驗(yàn)性修改并提交它們,并且你可以在這個(gè)狀態(tài)中丟棄任何你所做的提交,而不影響任何分支,做法是執(zhí)行 checkout 命令切換回別的分支。

如果你希望創(chuàng)建一個(gè)新分支,并保持你所做創(chuàng)建的提交,你可以(現(xiàn)在或稍后)通過(guò)使用帶有 -b 選項(xiàng)的 checkout 命令實(shí)現(xiàn)。例如:

git checkout -b <new-branch-name>

HEAD 指針當(dāng)前指向 52861cf... 2.txt

你使用了 checkout 命令但沒(méi)有指定分支名,所以 Git 幫你創(chuàng)建了一個(gè)匿名分支,OK,既然是匿名,那么當(dāng)你切換到別的分支時(shí),這個(gè)匿名分支中的所有提交都會(huì)被丟棄掉(因?yàn)槟愣紱](méi)給人家命名,反正以后都找不回了,不丟了干啥?)。因此,你可以利用匿名分支來(lái)做做實(shí)驗(yàn)什么的,反正不會(huì)有負(fù)面影響。

來(lái),我們?cè)囋?,在工作目錄中?chuàng)建一個(gè) 4.txt(你會(huì)發(fā)現(xiàn) 3.txt 不見(jiàn)了,這是因?yàn)?checkout 將環(huán)境切換到上一次提交了),然后提交

執(zhí)行 git log --decorate --all --graph --oneline 命令

可以看到有兩個(gè)分支,但 HEAD 所在的分支并沒(méi)有名字(匿名分支),那現(xiàn)在我們把 HEAD 切回 master 分支:

警告:你殘留一個(gè)沒(méi)有連接任何分支的提交:

178d909 4.txt

如果你想通過(guò)創(chuàng)建一個(gè)分支來(lái)連接它,這將是一個(gè)好時(shí)機(jī),請(qǐng)執(zhí)行:

git branch <new-branch-name> 178d909

已經(jīng)切換到 'master' 分支

現(xiàn)在再來(lái)查看提交日志,可以看到匿名分支已經(jīng)蕩然無(wú)存:

再論checkout

事實(shí)上呢,checkout 命令有兩種功能:

  1. 從歷史快照(或者暫存區(qū)域)中拷貝文件到工作目錄
  2. 切換分支

從歷史快照(或者暫存區(qū)域)中拷貝文件到工作目錄

當(dāng)給定某個(gè)文件名時(shí),Git 會(huì)從指定的提交中拷貝文件到暫存區(qū)域和工作目錄。比如執(zhí)行 git checkout HEAD~ README.md 命令會(huì)將上一個(gè)快照中的 README.md 文件復(fù)制到工作目錄和暫存區(qū)域中:

如果命令中沒(méi)有指定具體的快照 ID,則將從暫存區(qū)域恢復(fù)指定文件到工作目錄(git checkout README.md):

有些朋友可能會(huì)問(wèn):“上次看你在文件名的前邊有加兩個(gè)橫桿(--),這次怎么就沒(méi)有了呢?”

問(wèn)得好,Git 提醒你寫成 git checkout -- README.md 的形式,那是為了預(yù)防你恰好有一個(gè)分支叫做 README.md,那么它就搞不懂你要恢復(fù)文件還是切換分支了,所以約定兩個(gè)橫桿(--)后邊跟的是文件名。

反過(guò)來(lái)說(shuō),如果你確保你沒(méi)有一個(gè)叫做 README.md 的分支,你直接寫 git checkout README.md 也是妥妥的沒(méi)問(wèn)題啦

切換分支
首先我們知道 Git 的分支其實(shí)就是添加一個(gè)指向快照的指針,其次我們還知道切換分支除了修改 HEAD 指針的指向,還會(huì)改變暫存區(qū)域和工作目錄的內(nèi)容。

所以執(zhí)行 git checkout 373c0 命令,Git 主要就是做了下邊這兩件事(當(dāng)然事實(shí)上 Git 還做了更多):

那回過(guò)頭來(lái),如果我們只想恢復(fù)指定的文件/路徑,那么我們只需要指定具體的文件,Git 就會(huì)忽略第一步修改 HEAD 指向的操作,這不正跟之前講 reset 命令的時(shí)候一樣嗎?

checkout 命令和 reset 命令的區(qū)別

恢復(fù)文件

checkout 命令和 reset 命令都可以用于恢復(fù)指定快照的指定文件,并且它們都不會(huì)改變 HEAD 指針的指向。

下面開始劃重點(diǎn):

它們的區(qū)別是 reset 命令只將指定文件恢復(fù)到暫存區(qū)域(--mixed),而 checkout 命令是同時(shí)覆蓋暫存區(qū)域和工作目錄。

注意:也許你試圖使用 git reset --hard HEAD~ README.md 命令讓 reset 同時(shí)覆蓋工作目錄,但 Git 會(huì)告訴你這是徒勞(此時(shí) reset 不允許使用 --soft 或 --hard 選項(xiàng))。

這樣看來(lái),在恢復(fù)文件方面,reset 命令要比 checkout 命令更安全一些。

恢復(fù)快照

reset 命令是用來(lái)“回到過(guò)去”的,根據(jù)選項(xiàng)的不同,reset 命令將移動(dòng) HEAD 指針(--soft) -> 覆蓋暫存區(qū)域(--mixed,默認(rèn))-> 覆蓋工作目錄(--hard)。

checkout 命令雖說(shuō)是用于切換分支,但前面你也看到了,它事實(shí)上也是通過(guò)移動(dòng) HEAD 指針和覆蓋暫存區(qū)域、工作目錄來(lái)實(shí)現(xiàn)的。

那問(wèn)題來(lái)了:它們有什么區(qū)別呢?

下面開始劃重點(diǎn):

第一個(gè)區(qū)別是,對(duì)于 reset --hard 命令來(lái)說(shuō),checkout 命令更安全。因?yàn)?checkout 命令在切換分支前會(huì)先檢查一下當(dāng)前的工作狀態(tài),如果不是“clean”的話,Git 不會(huì)允許你這樣做;而 reset --hard 命令則是直接覆蓋所有數(shù)據(jù)。

另一個(gè)區(qū)別是如何更新 HEAD 指向,reset 命令會(huì)移動(dòng) HEAD 所在分支的指向,而 checkout 命令只會(huì)移動(dòng) HEAD 自身來(lái)指向另一個(gè)分支。

看文字你肯定懵,我們舉例說(shuō)明。

來(lái),大家先把剛才的例子改成下邊這樣:

執(zhí)行 git checkout feature 命令:

可以看到只是 HEAD 指針跑到 feature 分支那兒去了。

好,我們執(zhí)行 git checkout master 命令將其切回。

現(xiàn)在執(zhí)行 git reset --hard feature 命令:

彩蛋

通常情況下,Git 會(huì)盡可能地嘗試使用 Fast-forward 方式來(lái)合并分支,因?yàn)檫@樣效率非常高,只有在萬(wàn)不得已的時(shí)候才使用三方合并(Three-way merge)

但是,有時(shí)候 Fast-forward 的方式卻很容易讓我們忽略了分支的存在!

舉個(gè)例子,比如下面這樣:

如果我們把“feature”分支刪除,就會(huì)直接丟掉分支的信息:

根本就不知道有個(gè)分支存在過(guò)……

可有時(shí)候我們確實(shí)希望保留分支的信息,應(yīng)該怎么辦呢?

只需要在 merge 的時(shí)候使用 --no-ff 選項(xiàng)即可告訴 Git 不要使用 Fast-forward 方式合并分支。

這樣 Git 就會(huì)乖乖地使用三方合并(Three-way merge):

OK,這樣就算刪掉了“feature”分支,我們?nèi)匀豢梢院苋菀卓闯鲇袀€(gè)分支曾經(jīng)存在過(guò):


Git使用教程到這里就告一段落了,隨后會(huì)可能會(huì)更新小甲魚翻譯的Github官方教程奧~~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容