1. 程式人生 > 實用技巧 >Git多人場景演練免去學習苦惱!

Git多人場景演練免去學習苦惱!

本文基於中國科學技術大學軟體學院孟寧老師部落格《五⼤場景玩轉 Git,只要這一篇就夠了!》

1.前言

早就聽聞孟寧老師的課風趣幽默,通俗易懂,入學後遂選擇了孟寧老師的高階軟體工程課。幾堂課下來,感覺確實名不虛傳。孟寧老師講的非常細緻,並且提供了很多有用的課外資料,整個課堂表現得也非常活躍,因此選擇孟寧老師無意是明智的。
那麼接下來進入正題,講講我對git的理解~~

2.git是什麼?

git 是一種分散式版本控制系統,能記錄程式碼版本改動,具備團隊協作功能,非常方便

3.幾個概念

3.1. 工作區(Working Directory): 在電腦裡能看到的目錄,你編輯程式碼的地方
3.2. 暫存區(Stage or Index):

當前哪些改動是被git所追蹤的(對的,不是所有修改都會被追蹤,所以你需要把修改新增到暫存區,否則可能就會丟失)
**3.3. 版本庫(Repository): ** 儲存程式碼版本的倉庫,在.git目錄中(跟暫存區不同的是,把修改提交到這裡會形成一個版本號,這樣就能在git的分支樹上體現出來了~~)

![](https://img2020.cnblogs.com/blog/984333/202010/984333-20201007213136316-1757617586.png)

整個流程如上圖

3.4.遠端庫(Remote Repository):在伺服器上儲存程式碼的倉庫。作為本地倉庫的備份,也可以通過遠端倉庫進行多人協作,如果你不想因為意外丟掉你的程式碼,請一定記得把本地倉庫的最新修改同步到遠端倉庫

4.多人場景演練

4.1 建立遠端庫

這個沒啥好說的,在自己的github域名下建一個倉庫就行

4.2 建立本地庫


由於遠端庫中沒有任何內容,需要新建本地庫然後與遠端庫關聯起來。如圖,先新建本地倉庫,再使用git init命令初始化本地倉庫,如此一來,就可以在這個倉庫裡放程式碼了
接下來來點java程式碼試試?

public class GitTest {
    public static void main(String[] args) {
        System.out.println("Hello World!");
    }
}

新增程式碼檔案後,使用git add fileName

命令,可以把程式碼檔案從工作區新增進暫存區,接下來繼續修改下程式碼檔案,使用git status可以檢視當前暫存區,快取區,和未追蹤檔案的狀態

接下來使用git commit -m "第一次提交"把暫存區的檔案提交到本地倉庫中,這樣就在本地形成一個版本號

4.3 關聯遠端庫

git remote add origin https://github.com/JohnsonGreen/GitTest.git
git push -u origin master

在本地倉庫增加遠端庫,並推送到遠端庫的master分支

4.4 建立個人開發分支

git checkout -b mybranch

不加-b引數則是直接切換到該分支。接下來我們新增一些程式碼,並推送到遠端

public class GitTest {
    public static void main(String[] args) {
        System.out.println("Hello World!");
        System.out.println("Hello ChenHeng");
    }
}

此時使用git log --oneline --graph顯示本地和遠端都已經有兩個節點

4.5 與同事協同開發

同事可以把遠端倉庫的內容完整的下載到本地,形成本地倉庫,並建立他的分支,並且同事下載的是隻有"Hello World"的程式碼,並在此基礎上建立了新的分支

git clone https://github.com/JohnsonGreen/GitTest.git
git branch -b hisbranch

改點程式碼試試

public class GitTest {
    public static void main(String[] args) {
        System.out.println("Hello World!");
        System.out.println("Hello ChenYuHong");
    }
}

然後推送到遠端建立遠端的hisbranch分支,並且此時想合併到遠端的master分支,那麼做法是,先在本地切換到master分支,然後使用git pull 將最新的內容更新到master分支,再使用 git merge --no-ff hisbranch (使用--no-ff引數可以在git分支樹上看到分叉 )合併到本地的master分支,但是,能合併成功嗎?

4.6 衝突解決

public class GitTest {
    public static void main(String[] args) {
        System.out.println("Hello World!");
<<<<<<< HEAD
        System.out.println("Hello ChenHeng");
=======
        System.out.println("Hello ChenYuHong");
>>>>>>> hisbranch
    }
}

我們看到,命令提示有衝突,並且程式碼中標註出了衝突位置,那這就需要選擇保留哪一項了。保留之後,就可以add,commit以及push了。注意:在改同事程式碼的時候記得一定聯絡下他,否則可能被打~~
合併之後的分支樹如圖所示

4.7 吃後悔藥

上次merge之後,有點後悔合併了,想還是繼續回到第一次提交時"hello world"的狀態,那麼在通過git log --graph查詢到該commit ID後,就可以使用下面這個命令完成回退啦!

git reset --hard 54a05c8

然後瞬間在master分支上只有一個結點了,驚不驚喜,意不意外?

4.8 吃後悔藥的後悔藥

經過上次回退之後,感覺又想把merge之後的程式碼找回來,但是此時只剩一個孤零零的"Hello World!",之前寫的程式碼全丟了,這可咋辦?
答案是依舊使用git reset --hard 命令,但是commit ID要怎麼找?
然後時光雞神器出現了

git reflog 

還好還好,有了git reflog這個能記錄你所有git操作的大神器,媽媽再也不用擔心我的程式碼丟失了

於是我們找到merge結點的commit ID,然後就可以乘坐時光雞回到未來了

4.8 終結雜亂無章的提交

現在,我的同事要向孟寧老師打招呼,但是同事是個結巴,只能一個字母一個字母的打,於是我們在同事的提交記錄上看到了一長串的commit,並且每個commit也就只打了一個字母

但是這一個一個字母形成的commit終究只是為了完成"Hello Meng Ning"這個打招呼的功能,真的有必要寫這麼多commit嗎?
接下來我們使用git rebase -i [startCommitID] [endCommitId]這個命令,來讓同事的招呼打得更順暢一些。

git rebase -i dc8e07b

在上圖中我們刪除除了最後一條的所有commit,只保留最後一條commit
然後出現了衝突,如下

public class GitTest {
    public static void main(String[] args) {
        System.out.println("Hello World!");
        System.out.println("Hello ChenYuHong");
<<<<<<< HEAD
=======
        System.out.println("Hello Meng Ning");
>>>>>>> 1704756... 新增g
    }
}

我們當然要保留向孟寧老師打招呼的內容,修改之後把內容add進暫存區,並使用git rebase --continue完成rebase操作,此時會跳出編輯框,讓我們把保留的這個commit的說明改一下,從"新增g"改為"向孟寧老師打招呼", 儲存並提交,然後合併到master,並推送到遠端庫

從而我們在繼續檢視遠端庫的master分支時,就不會繼續包含那些"結巴"的commit啦!

5. 一點小小的補充

5.1 為什麼需要暫存區?

做某件事情的時候可能會改好幾個檔案,例如a、b、c三個檔案,其中a和b檔案是程式碼邏輯的修改,c檔案是文案修改。這個時候為了讓提交日誌更可讀,會先提交a和b,再提交c,於是就有如下的git操作
>git add a b
git commit -m' change logic'
git add c
git commit -m 'change text'

如果沒有暫存區這個東西,那隻可能有一條提交,這個提交包含了邏輯調整和文案修改(可能是兩件毫不相干的事情)

5.2git push --force /-f 命令有什麼安全問題?

--force 會使用本地分支的提交覆蓋遠端推送分支的提交。也就是說,如果其他人在相同的分支推送了新的提交,你的這一舉動將“刪除”他的那些提交!就算在強制推送之前先 fetch 並且 merge 或 rebase 了也是不安全的,因為這些操作到推送之間依然存在時間差,別人的提交可能發生在這個時間差之內.
>2018年9月19日,一名程式設計師在美國某辦公樓向4名同事開槍,導致一人情況危機,兩人傷情嚴重,一人被子彈擦傷。目前,凶手已死,身份被警方查明。
目前,碼農持槍殺人的動機仍然是個謎。有人猜測道:“同事不寫註釋,不遵循駝峰命名,括號換行,最主要還天天 git push -f 等因素” 激怒了這名行凶者。

5.3 git reset 和 git revert 有什麼區別?

二者最大的區別是git revert是用新的一個commit來回滾之前的commit,HEAD是要繼續前進,但是git reset是直接刪除指定的HEAD,其是相當於在不斷後退

5.4 版本回退之後,想吃後悔藥怎麼辦?
  1. revert: 兩次revert ,負負得正
  2. 如果知道回退之前的HEAD所指向的結點的版本號,那麼依然可以使用 git reset --hard e475afc 來實現
5.5 正在dev分支進行開發,但是突然在master分支上有緊急bug要修復,怎麼辦?
1) git stash save "增加readme.md2"

會將工作區和暫存區的內容儲藏起來,前提是這些檔案已經被git追蹤
儲藏未跟蹤的檔案:git stash save -ugit stash save --include-untracked

2) git stash list

檢視儲藏列表
>stash@{0}: On dev: 修改為Yes
stash@{1}: On dev: 增加readme.md2

3)git stash apply1

應用最頂層的stash, 也可以指定哪個stash: git stash apply stash@{1}
git stash pop 與之類似,但會刪除stashs
stash 之後又對同一個位置做了修改,造成衝突?
先對修改進行commit或者git stash

4) git stash drop stash@{1}

刪除某個stash或者git stash clear 清除所有stash

5) git stash show -p stash@{1}

檢視當前分支與某個stash的對比

6) git stash branch newBranch stash@{1}

有的時候可以將儲藏起來的程式碼應用到新建的分支,而不是當前分支上

5.6 如果只是想丟棄掉工作區的內容,該怎麼做?
1) git reset --hard HEAD

直接重置版本號(丟棄掉所有檔案在工作區的修改 + 已經add到暫存區但沒有commit的修改,需要謹慎使用
變種: git reset HEAD~1 或 git reset HEAD^ (均是回退到HEAD之前的一個版本)

2)git checkout -- filename (針對選中的檔案,可以新增多個)

這個操作總是讓這個檔案回到最近一次 git commit 或 git add 時的狀態(commit之後修改沒有add或add之後又做了修改),都是丟棄工作區的修改(修改包含刪除操作)。
git checkout . 放棄當前目錄下的修改

3) 推薦使用git stash, 萬一後悔了呢?
5.7 merge錯了分支,咋辦?

git merge --abort
該命令僅僅在合併後導致衝突時才使用。git merge --abort將會拋棄合併過程並且嘗試重建合併前的狀態。但是,當合並開始時如果存在未commit的檔案,git merge --abort在某些情況下將無法重現合併前的狀態

5.8 在釋出版本號的時候,怎麼給結點打標籤?
1)git tag -a v1.4 -m "my version 1.4"

會在當前分支上的HEAD結點打上打標籤

2) git tag -a v1.1 -m "my tag" a0472e6

給某一個結點打標籤

3)推送標籤到遠端倉庫

git push並不會把tag標籤傳送到遠端伺服器上,只有通過顯式命令才能分享標籤到遠端倉庫
1. push單個tag,命令格式為git push origin [tagname]
2. push所有tag,命令格式為: git push --tagsgit push origin --tags
3. 標籤打錯了?
本地: git tag -d v0.1
遠端: git push origin :refs/tags/v0.9
4.展示標籤(該標籤對應的commit版本資訊): git show v1.2