1. 程式人生 > 其它 >從不同使用場景來介紹git的基礎命令

從不同使用場景來介紹git的基礎命令

從不同使用場景來介紹git的基礎命令

自從上了高階軟體工程課,我對於Git在多人協作和版本控制方面的作用,產生了濃厚的興趣。之前本人也做過一些的小的專案,在開發者只有一人的情況下,自然不用考慮太多,尤其是涉及到多個分支,多人協同開發的情況。但現在的軟體行業,已經不是孤身一人就可以作戰,每個人或多或少的都會在某個專案中與他人互動。這就產生了以下幾個問題:如果幾個人同時對某個檔案進行了改動,且改動不一,該怎麼處理?如果當前的專案進展的不是很順利,想要使用之前寫過的版本,但過去的程式碼並未備份,這時候又該怎麼辦?

不用擔心,Git作為一個偉大的版本控制工具,為我們做了解答,下面就讓我們一起來看一下Git的基本使用方法。

鑑於Vscode支援Git的使用,接下來的演示,我會使用Windows上安裝的Vscode和Git工具。對於如何在Windows上安裝這兩款工具,請見連結:

https://blog.csdn.net/qq_32786873/article/details/80570783(Windows10下安裝git)

https://blog.csdn.net/x15011238662/article/details/85094006(Windows下安裝Vscode,並使用,以及中文配置)

以下我會分五個場景介紹Git的基礎使用,但在此之前,讓我們先來看一下,Git中涉及的一些概念。

以上就是Git的基本架構和操作邏輯。

Git的工作區域可以簡單分為兩類——本地(local)和遠端(remote)。

Git的本地工作區包括3個部分:

工作區(work directory):就是我們專案所在的根目錄,也是我們直接改動程式碼的地方;

暫存區(stage):與工作區的檔案進行直接互動,工作區檔案的提交或者回滾,首選都是通過暫存區

本地倉庫(repository):我們將專案設定為本地版本庫後,在專案的根目錄下,就會生成一個隱藏的“.git”資料夾,該目錄即為本地倉庫

看完了上述的幾個名詞,有些疑惑是嗎?沒關係,下面就讓我們來一個個解釋。

場景一.Git本地版本庫的基礎用法

我先在桌面新建一個名為”gitdisplay“的資料夾,右鍵點選,選擇”Git Bash Here“,便會開啟如下圖所示的視窗

當我們敲下如下命令:git init

我們會發現,在gitdisplay目錄下多了一個.git隱藏資料夾,也就是說我們通過命令初始化了一個本地倉庫。

此時如果我們通過Vscode開啟gitdisplay資料夾,並且新增一個"readme.md"檔案,Vscode的側邊欄,便會出現以下情況:

如果我們返回Git bash介面,輸入命令:git status

便可以檢視工作區的當前狀態

以上資訊說明,當前工作區的檔案“readme.md”未被跟蹤,因此我們需要把該檔案提交到暫存區

輸入命令 git add [FILES]

此時檢視工作區狀態,顯示檔案已被added,位於暫存區中,等待提交

命令git commit -m "added info" ,可以將暫存區中的檔案提交到本地倉庫

以上我們就完成了本地對原始碼的基本的版本控制,簡單來說,每當我們的工作區有了變動,我們就通過 git add 將改動新增到暫存區,然後通過 git commit 將其提交到本地倉庫。

如果此時我們試著向readme.md中寫入一些東西,然後重複上述操作,將其提交,通過 git log 指令,我們可以看到git bash介面顯示了兩次提交記錄。

由圖片可以看出,“HEAD”對準的是我們最新的提交。

當然,並不是我們的每一次提交都是有意義的提交,有的時候,我們需要的可能是之前的提交,而命令

git reset --hard HEAD^^/HEAD~100/commit-id/commit-id的頭幾個字元 可以讓我們回退到過去的某次提交,並且新的commit記錄也會消失。當我們輸入 git reset --hard 02bc9 ,然後用 git log 檢視提交記錄

我們驚訝地發現,只剩下了第一次的提交記錄,並且HEAD指標指向了該次提交記錄。同時,此時開啟readme.md,我們會發現,新增的語句已經沒有了。

然而,並不是所有回滾都是有意義的,可能某些同學回滾完了之後,又開始後悔了,可是通過git log 指令,我們已經無法檢視 第一次提交之後的記錄了。別擔心,指令 git reflog 可以檢視當前HEAD之後的提交記錄

看懂了嗎,HEAD指向我們工作區所依賴的版本,同時也是未來與過去的分界線。

當我們再次輸入 git reset --hard 7081a

恭喜,我們又回到了最新的提交版本。藉助HEAD,我們可以在過去和未來之間來回切換。

有關git reset 的其他模式,請參照 https://www.jianshu.com/p/c2ec5f06cf1a(git reset 三種模式)

場景二.git遠端版本庫的基本用法

還記得我們是怎麼來初始化一個本地版本庫的嗎?沒錯,我們使用的是git init。同樣的,我們也可以通過

git clone [連結地址] 的方式初始化。該命令的意思是從遠端倉庫克隆一個專案到本地倉庫。

那麼問題來了,這個所謂的遠端倉庫是在哪?一般來說,遠端倉庫位於github這個網站上。git與github的關係,經常有人混淆,其實git是個版本控制工具,而github就是個遠端倉庫。

首先,我們要做的就是新建一個遠端倉庫,關於此部分我就不再贅述,有興趣的同學可以參考一下連結:

https://www.jianshu.com/p/750527980651(git新增遠端倉庫)

當我們成功建立一個遠端倉庫後,就會獲得一個你的遠端倉庫的連結地址。接下我們要做的就是將遠端倉庫,和我們的本地倉庫聯絡起來。

需要輸入的命令為:

git remote add origin [your repository link]

git push -u origin master (僅第一次需要新增“-u”)

我們發現,遠端倉庫的內容,與本地倉庫已經一摸一樣了。

此後每當我們在本地工作區產生改動,當提交到本地倉庫後,再使用指令 git push origin master 使遠端倉庫與本地倉庫保持一致。

如果我們之後的開發只要按照這種流程,那麼也就不會有後續的煩惱了。但問題在於,我們不光可以在本地進行修改,也可以在遠端倉庫進行修改。當我們在遠端倉庫加入新的改動時,由於我們本地並未改動過,如果我們想以遠端倉庫的最新版本為基準,那我們需要使本地倉庫與遠端倉庫保持一致。這時,使用git fetchgit merge,便可以使本地倉庫與遠端倉庫保持一致。

當我們輸入git fetch

git bash中有如上顯示,但是本地檔案中,還是沒變。

同時我們檢視status時,

顯示我們的本地倉庫,已經落後了遠端倉庫1次提交。也就是說git fetch指令拉取了遠端倉庫的最新資訊,但並沒有立即合併到當前分支中。此時,如果我們輸入 git merge

現在我們可以看到本地倉庫與遠端倉庫,都保持一致了。

當然,如果我們總是以遠端倉庫的版本為基準,我們可以使用git pull指令,可以近似看做以上兩條指令的組合。

另外當git merge發生衝突時,我們需要處理衝突,之後再次提交。(假如本地和遠端修改了同一檔案的同一處的地方)

一個比較保險的做法,就是不管實在本地倉庫還是遠端倉庫,在對程式碼修改之前,都應該進行程式碼同步,以防止產生衝突和分叉。

場景三.團隊專案中的分叉與合併

之前介紹的情況,都是基於一點,那就是在master主分支上開發。master分支一般只用來發布重大版本,如果一個專案有多人協作,如果大家都在master分支上開發,那勢必會引起版本混亂。正確的做法應該是在其他分支上做開發,除錯好了再合併到主分支。

如果我們想建立一個分支並切換到該分支,可以使用命令 git checkout -b dev1 ,這樣我們就建立並切換到了分支dev1。


當我們用git branch命令檢視分支的情況時,發現共有兩個分支,且當前所在的分支為dev1。

如果我們在readme.md中添加了一行新的語句,並提交。然後再重複一遍該操作

我們發現在dev1分支下多了兩條提交記錄。

假如我們已經在dev1分支上完成了開發,希望將該分支上的變動合併到主分支master上,我們又該做什麼呢?

首先,我們先要用git checkout master 返回主分支。

然後,我們使用git pull命令使主分支與遠端倉庫同步。

之後我們使用命令git merge --no-ff dev1 將dev1分支上的變動,合併到master分支。

命令中的--no-ff,作用是禁止快進式合併,這樣可以使分支的提交歷史,變得更加清晰。

接下來我們要做的就是將本地的變動,push到遠端倉庫。好了,我們成功建立了一個分支,完成了一個功能修改,然後將主分支與其合併,然後使遠端倉庫與主分支保持一致。以上就是開發某個模組或者功能的基本流程了。

場景四.Git Rebase

有人可能對git rebase 命令的作用產生疑惑。一般來說,作用是精簡提交記錄。比如你自己在某個分支上 提交了多個記錄,但是並不是每一次提交都是有意義的提交。通過git rebase 可以使某些提交記錄合併為一個,從而變得簡潔明瞭。

假如我們在dev1分支上進行三次改動,每次都提交記錄,結果如下:

接著我們回到master主分支,並在該分支上進行三次操作,同樣每次都要提交記錄,結果如下:

圖中紅線圈起來的部分,就是在master和dev1分支上分別提交的3次記錄。如果我們嫌dev1分支上的3次提交過於繁瑣,想把它整合為一次,我們可以先切換為dev1分支,然後使用命令

git rebase -i [startpoint] [endpoint]

後續的endpoint如果不指定,則要合併的區間的終點,預設是當前分支的HEAD。

我們希望僅希望保留第三次提交記錄

儲存退出檔案,這時我們發現git bash顯示報錯,並開啟衝突檔案,因為前兩次提交相當於第三次提交的基礎,所以我們需要對衝突檔案進行修改。我們可以直接修改檔案,保留三次修改的內容,同時去掉提示符號,並儲存。

這時我們通過git status檢視

此時,我們需要通過git add 命令將修改好的檔案存入暫存區 ,然後輸入命令git rebase --continue

此時會出現以下畫面

提示我們需要重新修改記錄提交資訊。

這時我們發現,在dev1上的提交記錄,已經合併為一次了

接下來的操作就是,先切換為master分支,使用git pull指令使本地與遠端保持一致,然後合併dev1分支到master分支,並用git push origin master 向遠端倉庫推送。

最後,我們看到,雖然dev1有多次提交記錄,但最終只有一個提交記錄有顯示。

以上就是關於Git的一些基礎命令,感謝您的觀看。

參考文章:五⼤場景玩轉 Git,只要這一篇就夠了!

出處:https://www.cnblogs.com/chuanguo/p/13795951.html

=======================================================================================

五⼤場景玩轉 Git,只要這一篇就夠了!

推薦閱讀:

程式設計神器 VS Code,只要這一篇就夠了!

自由軟體江湖裡的碼頭和規矩

自己動手寫一個作業系統核心【內含視訊】

在瀏覽器中輸入網址按回車後發生了什麼?

智慧製造:從資訊化到智慧化

一文透析華為鴻蒙科技含量!華為的創新必將刺激國內外巨頭跟進

本文作者孟寧,未經許可禁止轉載!

Git 分散式版本控制系統

關於版本控制,其實我們應該都比較熟悉,甚至經常用到,比如我們寫一個文件的過程中,經常會另存為一個獨立檔案作為備份,來管理我們在寫文件的過程中產生的不同版本,這就是版本管理,只是這是用人工的方式來做版本控制。當不同版本的數量龐大的時候,人工進行版本管理就比較容易出差錯了,這時就出現了用軟體系統作為工具來進行版本控制,這就是版本控制系統。

版本控制的策略比較常見的有兩種:一種是獨立檔案或說整體備份的方式,比如使用另存為將整個專案或者整個文件整體備份一個版本;另一種就是補丁包的方式,比如使用 diff 命令將當前版本與上一個版本對比得出兩者之間的差異從而形成一個補丁包,這樣上一個版本加上這個補丁包就是當前版本。顯然前者會產生大量重複資料,消耗比較多的儲存資源,但是每一個版本都是獨立且完整的;後者幾乎沒有重複資料,儲存效率更高,但是每一個版本的補丁包無法獨立使用,因為它們都需要遞迴地依賴上一個版本才能合併出一個完整的版本。

版本控制系統大致分為兩大類,一類是中心版本控制系統,比如 Concurrent Versions System(簡稱 CVS)和 Subversion(簡稱 SVN);另一類就是分散式版本控制系統,比如我們即將重點介紹的 Git,是目前世界上最先進的分散式版本控制系統(沒有之一)。Git 的誕生的經過大致是這樣的。

Git誕生的歷史

在 2002 年以前,Linux 核心原始碼檔案是通過 diff 的方式生成補丁包發給 Linus,然後由 Linus 本人通過手工方式合併程式碼!Linus 堅定地反對 CVS 和 SVN,這些中心版本控制系統不但速度慢,而且必須聯網才能使用。有一些商用的版本控制系統,雖然比 CVS 和 SVN 好用,但那是要付費購買的,這和 Linux 的開源精神不符。

2002 年 Linus 選擇了一個商業的版本控制系統 BitKeeper,BitKeeper 的東家 BitMover 公司出於人道主義精神,授權 Linux 社群免費使用BitKeeper版本控制系統。其實應該是 Linus 與 BitMover 公司的創始人之間的私人關係促成了免費授權 Linux 社群使用 BitKeeper。

2005 年 BitMover 公司要收回 Linux 社群的免費使用權。為什麼 BitMover 公司要收回 Linux 社群的免費使用權?根據一些資料顯示,大概是因為 Linux 社群裡有人違反授權協議將 BitKeeper 用於其他專案從而侵害了 BitMover 公司的商業利益。

為了替代BitKeeper版本控制系統,結果Linus 花了兩週時間自己用 C 寫了一個分散式版本控制系統,這就是 Git!一個月之內,Linux 核心原始碼已經用 Git 管理了!大神 Linus 除了 Linux 核心之外又產出了一個神作——Git分散式版本控制系統。

大約經過了3 年時間的發展迅猛,到 2008 年,專門提供 Git 儲存的 GitHub 網站上線了,它為開源專案免費提供 Git 儲存,如今 GitHub 已經成為全球最大的程式設計師社交網站,在碼農中戲稱為全球最大的單身男性社交網站。

2016 年,也就是 Git 誕生 11 年之後,可能是由於 Git 太流行了,以致 BitKeeper 版本控制系統在商業上無法維繫運營,BitMover 公司只得將 BitKeeper 的原始碼貢獻給開源社群續命。

2018年,Github被微軟以75億美元收購。

Git 的基本操作

大概瞭解了Git的歷史,接下來我們看看Git 的基本操作大致如下圖。

對於本地 Repo如上圖中間儲存庫Repository,實際上本地 Repo都存在專案根目錄下.git資料夾中,內部可能有多個 branch,但至少有一個叫 master的branch。

本地Repo中的某個branch被checkout到當前workspace,就能在當前原始碼目錄中看到一份完整的原始碼,這份完整的原始碼就是workspace。

在workspace中新增檔案或修改檔案,只有完成add和commit兩步操作才能將新增或修改的檔案納入本地Repo的儲存庫中進行版本管理。add和commit兩步操作中間Index索引資料應該也是和本地 Repo一樣存在專案根目錄下.git目錄中。

到這裡Git與 CVS、SVN操作邏輯大致一致,只是實現了本地的中心化版本控制。接下來看Git是怎麼做到分散式的

本地 Repo 中的 branch 與一個或多個遠端 Repo(上圖中的Remote) 中的 branch 存在跟蹤關係,這樣就構成了Git分散式版本控制的網狀結構。

顯然Git的分散式網狀結構比中心化 CVS 和 SVN在理解和使用上更為複雜一些。比如增加了本地與遠端Repo之間資料同步操作clone、fetch、push、pull。

通過git --help命令可以檢視Git命令用法及常用的Git命令列表。

$ git --helpusage: git [--version] [--help] [-C <path>] [-c <name>=<value>]           [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--bare]           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]           <command> [<args>]

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) add Add file contents to the index mv Move or rename a file, a directory, or a symlink reset Reset current HEAD to the specified state rm Remove files from the working tree and from the index

examine the history and state (see also: git help revisions) bisect Use binary search to find the commit that introduced a bug grep Print lines matching a pattern log Show commit logs show Show various types of objects status Show the working tree status

grow, mark and tweak your common history branch List, create, or delete branches checkout Switch branches or restore working tree files commit Record changes to the repository diff Show changes between commits, commit and working tree, etc merge Join two or more development histories together rebase Reapply commits on top of another base tip tag Create, list, delete or verify a tag object signed with GPG

collaborate (see also: git help workflows) fetch Download objects and refs from another repository pull Fetch from and integrate with another repository or a local branch push Update remote refs along with associated objects

'git help -a' and 'git help -g' list available subcommands and someconcept guides. See 'git help <command>' or 'git help <concept>'to read about a specific subcommand or co

接下來我們用五大場景來介紹Git的基本用法,基本遵循著從簡單到複雜,最終再回歸到簡單的過程,同時這五大場景也基本能夠覆蓋大多數實際使用的應用場景。

場景一:Git 本地版本庫的基本用法

如果您使用了VS Code,那麼恭喜您,安裝VS Code時已經附帶安裝了Git,建議Windows使用者通過安裝VS Code完成Git軟體包的安裝。

如果您使用的Linux或者其他Unix類的作業系統,以Ubuntu為例大致可以通過類似如下命令安裝Git軟體包:

sudo apt install git

當然Linux和MacOS也可以通過安裝VS Code附帶安裝Git,但是在沒有圖形介面的伺服器端單獨安裝Git軟體包比較合適。

初始化一個本地版本庫

在VS Code中開啟資料夾( Ctrl/⌘+O),實際上就是開啟一個專案工作區(workspace),VS Code的工作區概念與Git的工作區概念基本一致,都是指當前專案資料夾裡的整套原始碼。

如果專案資料夾裡沒有Git儲存庫,這時開啟原始碼管理(Ctrl+Shift+G)大致如下圖,可以直接點選【初始化儲存庫】按鈕,初始化一個Git本地版本庫。

習慣於使用命令列的話,只需在專案根目錄下執行git init命令即可以完成初始化一個Git本地版本庫。

如果您已經在Gitee.com或者Github.com網站上建立了版本庫,可以通過git clone命令,將版本庫克隆到本地完成本地版本庫的初始化。git clone命令的用法如下:

git clone https://DOMAIN_NAME/YOUR_NAME/REPO_NAME.git

不管使用何種方式初始化一個Git本地版本庫,實際上都是在專案根目錄下建立了一個.git資料夾,感興趣的話可以進入.git資料夾進一步瞭解Git版本庫內部資料的儲存結構。

檢視當前 workspace 的狀態

在VS Code中開啟原始碼管理(Ctrl+Shift+G)可以看到與上一個版本比專案的所有更改,即當前 workspace 的狀態,比如如下圖中原始碼管理中以綠色U標記的檔案為沒有新增到版本庫進行跟蹤的檔案(Untracked files)、以橙色M標記的檔案為已修改(Modified)未提交的檔案(Changes not staged for commit)。

習慣於使用命令列的話,只需在專案目錄下執行git status命令即可檢視Git本地版本庫當前 workspace 的狀態。

$ git statusOn branch masterYour branch is up to date with 'origin/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: lab7/README.md

Untracked files: (use "git add <file>..." to include in what will be committed) "lab1/mybot/\357\202\267\343\200\212\345\267\245\347\250\213\345\214\226\347\274\226\347\250\213\345\256\236\346\210\230\343\200\213\350\242\253\346\225\231\350\202\262\351\203\250\350\256\244\345\256\232\344\270\272\345\233\275\345\256\266\347\262\276\345\223\201\345\234\250\347\272\277\345\274\200\346\224\276\350\257\276\347\250\213 (2018).txt"

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

暫存更改的檔案

在VS Code中開啟原始碼管理(Ctrl+Shift+G)可以看到與上一個版本比專案的所有更改,即當前 workspace 的狀態,如下圖所示只要在【更改】列表裡的檔案上點選加號“+”即可暫存更改(git add FILES),是將更改的檔案加入到【暫存的更改】列表裡,點選撤銷符號即可放棄更改(git checkout -- FILES / git checkout .),是將該檔案中的更改清除掉。

如下圖所示只要在【暫存的更改】列表裡的檔案上點選減號“-”即可取消暫存更改(git reset HEAD FILES),是將暫存更改的檔案從【暫存的更改】列表裡取消,重新回到【更改】列表裡。

習慣於使用命令列的話,需要清楚對應的幾個命令用法如下:

git add FILES # 指定檔案或檔案列表git add .     # 用“.”表示當前目錄

如上兩行命令是將特定檔案(FILES)或者當前目錄下所有檔案新增到暫存區(Index);

git reset HEAD FILES # 指定檔案或檔案列表git reset HEAD

如上兩行命令是取消將特定檔案(FILES)或者所有檔案新增到暫存區(Index),即從暫存區(Index)中刪除;只有在暫存區登記的檔案才會在提交程式碼時存入版本庫。

git checkout -- FILES # 不要忘記“--” ,不寫就把FILES當分支名了git checkout .

如上兩行命令的效果是放棄特定檔案(FILES)或者所有檔案的修改,實際上重新檢出特定檔案(FILES)或者所有檔案到工作區(workspace),注意會覆蓋掉已修改未暫存的內容,不希望被覆蓋的檔案可以先使用git add將其新增到暫存區。

把暫存區裡的檔案提交到倉庫

在VS Code中開啟原始碼管理(Ctrl+Shift+G),只要【暫存的更改】列表裡有檔案就可以直接點選對號“√”(Ctrl+Enter)將暫存的檔案提交到倉庫中,只是在提交之前會強制要求輸入提交日誌訊息。

注意一旦提交到倉庫,儘管也可以撤銷上次提交,但是依然會在倉庫裡留下記錄,可以通過git reflog檢視當前 HEAD 之後的提交記錄,稍後我們具體通過命令列方式詳細解釋。

習慣於使用命令列的話,把暫存區裡的檔案提交到倉庫主要使用git commit命令,但是還會涉及撤銷提交和檢視日誌的命令。

git commit -m "wrote a commit log infro"如上命令可以把暫存區裡的檔案提交到倉庫。git log如上命令可以檢視提交日誌,可以看到當前 HEAD 之前的所有提交記錄。git reset —hard HEAD^git reset —hard HEAD^^git reset —hard HEAD~100git reset —hard 128個字元的commit-idgit reset —hard 簡寫為commit-id的頭幾個字元

如上命令可以讓HEAD回退到任意一個版本,比如HEAD^表示HEAD的前一個版本、HEAD^^表示HEAD的前兩個版本、HEAD~100表示HEAD的前100個版本,也可以用版本號字串來指定任意一個版本。

值得注意的是HEAD只是一個指向特定版本的指標,通過git reset —hard 回退之後,HEAD指向的不是最新的版本,而git log只能檢視HEAD及其之前(時間更早)的提交記錄,這就會產生一個問題,我們可以通過git reset —hard回到過去,那怎麼回到未來?那就要有辦法查到HEAD指向的版本之後(時間更晚)的提交記錄。

git reflog

如上命令可以檢視當前 HEAD 之後(時間更晚)的 提交記錄,從而可以通過git reset —hard回到未來。

場景一:Git 本地版本庫用法參考

場景一主要是在本地對原始碼進行基本的版本控制,主要通過git add和git commit -m提交版本,有了提交記錄之後可以靈活地將當前工作區裡的原始碼回退到過去的某個版本,也就是回到過去。回到過去之後,也有可能發現之前撤銷的某個版本是有價值的,希望找回來,這就需要回到未來。過去和未來之間的分界點就是HEAD,即當前工作區所依賴的版本。

git init # 初始化一個本地版本庫git status # 檢視當前工作區(workspace)的狀態git add [FILES] # 把檔案新增到暫存區(Index)git commit -m "wrote a commit log infro” # 把暫存區裡的檔案提交到倉庫git log # 檢視當前HEAD之前的提交記錄,便於回到過去git reset —hard HEAD^^/HEAD~100/commit-id/commit-id的頭幾個字元 # 回退git reflog # 可以檢視當前HEAD之後的提交記錄,便於回到未來git reset —hard commit-id/commit-id的頭幾個字元 # 回退

場景二:Git 遠端版本庫的基本用法

如果您已經在Gitee.com或者Github.com等網站上建立了Git版本庫,可以通過git clone命令,將版本庫克隆到本地完成本地版本庫的初始化。git clone命令的用法如下:

git clone https://DOMAIN_NAME/YOUR_NAME/REPO_NAME.git

也可以VS Code中開啟原始碼管理(Ctrl+Shift+G),如果當前沒有開啟的專案資料夾,可以看到原始碼管理(Ctrl+Shift+G)介面上有【開啟資料夾】和【克隆儲存庫】兩個按鈕,這時點選【克隆儲存庫】如下介面,即可輸入類似https://DOMAIN_NAME/YOUR_NAME/REPO_NAME.git 的儲存庫URL,按Enter鍵選擇儲存的目錄位置,即可完成將遠端的版本庫克隆(git clone)到本地的任務。

這時通過 git clone 遠端版本庫從而在本地建立了一個版本庫,這時就可以參照上面場景一的本地版本庫基本用法,檢視工作區狀態(git status)、暫存更改的檔案(git add)、把暫存區提交到倉庫(git commit)、以及回到過去、回到未來(git reset —hard / git log / git reflog)等本地本地版本控制的基本操作。

遠端版本庫的基本命令簡介

場景二Git 遠端版本庫的基本用法我們假定遠端版本庫作為遠端備份或者公開原始碼的目的,還是像場景一一樣是單人的版本控制,為了循序漸進,暫時還是不涉及多人專案的協作。

這裡使用git clone之後預設的分支,即遠端為 origin/master 和本地 master,沒有建立其他分支。管理本地版本庫跟蹤的遠端儲存庫的命令為git remote。

$ git remoteorigin

直接輸入git remote可以看到git clone之後預設的遠端儲存庫名稱為orgin。

$ git remote -vorigin  https://github.com/mengning/menu.git (fetch)origin  https://github.com/mengning/menu.git (push)

使用git remote -v 可以檢視更詳細的遠端儲存庫資訊,包括fetch(抓取)的遠端儲存庫URL和push(推送)的遠端儲存庫URL。

git fetch、git push加上git clone是三個對遠端儲存庫的基本操作,而git pull(拉取)是實際上是 git fetch + git merge(合併)的組合。

  • git clone命令官方的解釋是“Clone a repository into a new directory”,即克隆一個儲存庫到一個新的目錄下。

  • git fetch命令官方的解釋是“Download objects and refs from another repository”,即下載一個遠端儲存庫資料物件等資訊到本地儲存庫。

  • git push命令官方的解釋是“Update remote refs along with associated objects”,即將本地儲存庫的相關資料物件更新到遠端儲存庫。

  • git merge命令官方的解釋是“Join two or more development histories together”,即合併兩個或多個開發歷史記錄。

  • git pull命令官方的解釋是“Fetch from and integrate with another repository or a local branch”,即從其他儲存庫或分支抓取併合併到當前儲存庫的當前分支。

如果不理解Git 背後的設計理念,或者說不理解其設計原理的話,記住這些命令及其實際作用還是有一些難度,而且在相對複雜的專案環境下也容易出錯。但是在深入到Git 背後的設計理念之前,還是可以有一些在預設條件下的基本用法,從而快速上手用起來。

場景二:Git 遠端版本庫用法參考

我們假定使用場景二所述的工作是序列的並且及時將本地與遠端同步,也就是對於一個單人專案,要麼在本地提交程式碼到倉庫,要麼通過Web頁面更新遠端倉庫,而且這兩種方式不會同時發生。不管是在本地倉庫還是遠端倉庫,對程式碼修改之前都首先進行程式碼同步操作,防止產生分叉和衝突。

在VS Code中版本庫同步操作已經簡化為了一個選單命令,如下圖:

在我們假定的使用場景中,此同步操作會將提交項(commits)推送(push)到遠端倉庫origin/master,並從遠端倉庫origin/master拉取(pull)提交項到本地master。

但是在我們假定的使用場景中,實際上只會有提交項被推送或拉取,不會同時有提交項被推送並被拉取,因此也不會產生衝突。

在我們假定的使用場景中,在命令環境下同步操作大致相當於如下兩條命令:

git pullgit push

同步完成後,不管是在本地倉庫還是在遠端倉庫提交程式碼,都能再次執行同步操作而不會產生分叉或衝突。

實際操作中難免會產生無法同步的情況,這時候需要在本地解決衝突,情形會稍微複雜一點。

首先我們通過git pull拉取遠端倉庫裡的提交項到本地倉庫併合併到當前分支,即將origin/master中的提交項fetch到本地倉庫並merge到本地master分支。在VS Code中版本庫有一個選單命令可以完成git pull的功能,如下圖:

拉取過程中有可能會有衝突的情況,無法完成merge合併動作,這時需要對衝突的程式碼進行修改並提交到本地倉庫,即利用git add、git commit -m等本地版本庫的操作。

這時本地倉庫的提交項是領先於遠端倉庫的,只需要通過git push將本地倉庫中的提交項推送到遠端倉庫,即可完成本地倉庫和遠端倉庫的同步。在VS Code中版本庫有一個選單命令可以完成git push的功能,如下圖:

注意推送過程一般需要使用者名稱和密碼驗證身份。

至此,在本地版本庫基本用法的基礎上,只需要用推送(git push)和拉取(git pull)就完成本地倉庫和遠端倉庫的同步。但是其中涉及了抓取(fetch)、合併(merge),以及分支(branch)、提交項(commits)的概念,要進一步深入學習Git、靈活使用Git,就需要理解Git 背後的設計理念,乃至Git倉庫的儲存方式作為基礎。

Git 背後的設計理念

使用場景二所述的單人專案工作時間線上是序列的,如下圖中分叉之前的部分,只要及時將本地與遠端同步就不會出現分叉的情況。

但在實際專案中往往是多人團隊開發專案,多人團隊的工作時間線常常是並行的,團隊中的每個人都向遠端版本庫 push,難免會發生如下圖所示的分叉現象,這種情況該如何處理呢?只有理解了 Git 背後的設計理念,才能比較準確地把握分叉合併的處理方法。

首先要清楚Git版本管理的基礎,是按行對比(line diff)將差異的部分作為一個增量補丁,通過git add新增到暫存區裡的每一個檔案都會由line diff得到該檔案的增量補丁,而git commit將暫存區裡的所有檔案的增量補丁合併起來存入倉庫就是一個commit。

通常在提交(commit)時,會生成一個SHA-1 Hash值作為commit ID。每個commit ID有40個十六進位制數字(如3d80c612175ce9126cd348446d20d96874e2eba6),就是對那次commit在Git倉庫中儲存的內容和頭資訊(Header)的一個校驗和(checksum)。Git使用了SHA-1並非是為了安全性,而是為了資料的完整性,即可以保證,在很多年後,重新checkout某個commit時,一定是它多年前的當時的狀態,完全一摸一樣,完全值得信任。

按時間線依次排列的一組提交記錄(commit)形成一個branch,比如預設master分支即為一個branch,也可以根據某種需要建立分支(branch)。

tag是某個關鍵commit的標籤,比如釋出1.0版本時的那次提交(commit)被專門打了個標籤v1.0,tag標籤就是一個別名,便於記憶和使用。

我們簡要總結一下以上幾個關鍵概念:

  • line diff是形成增量補丁的技術方法,即一個檔案按行對比(line diff)將差異的部分製作成一個增量補丁。

  • commit是儲存到倉庫裡的一個版本,是整個專案範圍內的一個或多個檔案的增量補丁合併起來,形成專案的增量補丁,是一次提交記錄。每個提交(commit)都生成一個唯一的commit ID。

  • branch是按時間線依次排列的一組提交記錄(commit),理論上可以通過當前branch上最初的提交(commit)依次打補丁直到HEAD得到當前工作區裡的原始碼。

  • tag標籤就是某次提交(commit)的commit ID的別名。

有了以上幾個概念再來理解合併(merge)的概念就有了一個基礎,合併操作比如遠端origin/master分支合併到本地master分支,或者某個分支合併到master分支。以下圖為例,專案在A版本處開始分叉,形成了兩個分支,分別提交了B、D、F和C、E、G幾個commit,這時希望將兩個分支合併,只要將F與A的差異部分,以及G和A的差異部分,都放入工作區,如果有衝突解決衝突後就可以提交一個版本H,即完成了兩個分支的合併。

簡要總結一下,合併操作可以用一個公式來表示:H = (F - A)+ (G - A),即F版本與A版本的差異,以及G版本與A版本的差異,合併起來形成一個新的版本H。

具體技術實現上,因為每一個版本都是上一個版本的增量補丁,只要將要合併的分支裡的B、D、F幾個增量補丁,合併到當前工作區G版本里,解決衝突後提交為H版本。上圖即為Github遠端origin/master分支合併到本地master分支的示意圖。

場景三:團隊專案中的分叉合併

團隊專案的一個參考工作流程

有了前面的基礎知識和技能之後,我們可以考慮更復雜一些的團隊專案合作的工作流程。

如果團隊專案像場景二的方法一樣多人同時向遠端origin/master分支頻繁提交程式碼,一來可能會有諸多衝突合併的情況發生;二來整個git log提交記錄中多個開發者或多個程式碼模組的commit是交錯排列在同一條時間線上,不利於回顧檢視和回退程式碼,讓跟蹤程式碼的成長軌跡變得異常困難。

我們需要考慮新的方式來能夠獨立維護不同的開發者或者不同的功能模組的程式碼,讓一段連續的工作在commit日誌的時間線上呈現為一段獨立的分支線段,只在關鍵節點處進行分支合併。

基於以上想法我們建議團隊專案的每一個開發者都採用的工作流程大致如下:

  1. 克隆或同步最新的程式碼到本地儲存庫;

  2. 為自己的工作建立一個分支,該分支應該只負責單一功能模組或程式碼模組的版本控制;

  3. 在該分支上完成某單一功能模組或程式碼模組的開發工作;

  4. 最後,將該分支合併到主分支。

特別注意的是預設的合併方式為"快進式合併"(fast-farward merge),會將分支裡commit合併到主分支裡,合併成一條時間線,與我們期望的呈現為一段獨立的分支線段不符,因此合併時需要使用--no-ff引數關閉"快進式合併"(fast-farward merge)。接下來我們會具體瞭解分支合併的具體方法。

分支的基本用法

在VS Code中建立分支方法如下圖所示。

如果使用Git命令的建立分支為如下命令:

git checkout -b mybranch

實際上不管是選單方式還是如上的命令方式建立分支,都是將當前分支分叉出一個分支(如上命令分支名稱為mybranch),並簽出(checkout)到工作區。這時使用git branch檢視分支列表,如下所示mybranch前面有一個*代表當前工作區處於mybranch分支。

$ git branch  master* mybranch$ git checkout masterSwitched to branch 'master'Your branch is up to date with 'origin/master'.$ git branch* master  mybranch

這時要將當前工作區切換到master分支只需要如上所示使用git checkout master命令即可,在VS Code中可以使用如下圖【簽出到...】選單,然後選擇要簽出的master分支即可完成分支切換。

假如要將mybranch合併到master,那麼首先確保當期工作區處於master分支,可以使用git checkout master命令切換到master分支,或者使用git branch檢視確認一下,或者在VS Code中原始碼管理(Ctrl+Shift+G)的訊息輸入框裡也能看到。然後可以使用如下圖【合併分支...】選單選擇mybranch分支。

這樣就可以將自己的工作合併到 master分支。如果建立mybranch分支之後master分支有更新過,合併過程可能會因為有衝突(都修改了同一位置)而失敗,這時mybranch分支的程式碼已經合併到了當前工作區,只要在當前工作區裡先解決衝突,然後提交到倉庫(git add和git commit -m)即可完成合並。

上述【合併分支...】選單合併mybranch分支到當前的master分支,可以也使用如下Git命令:

git merge mybranch

不管是如上命令還是【合併分支...】選單都是使用預設的合併方式,即"快進式合併"(fast-farward merge)。合併前後大致如下示意圖,也就是mybranch分支與master分支會合併到一條時間線中。

如果要保留mybranch分支為一段獨立的分支線段,則需要使用--no-ff引數關閉"快進式合併"(fast-farward merge),Git命令如下:

git merge --no-ff mybranch

使用--no-ff引數後,會執行正常合併,在Master分支上生成一個新節點。合併後大致如下示意圖。

為了保證版本演進路徑清晰,我們希望採用這種合併方法。不過這種方法在VS Code中好像沒有對應的選單選項,您可以在VS Code中可以通過 Ctrl+`(ESC 下面,1 左邊的鍵)組合鍵調出整合終端,使用Git命令來執行這種合併方法。

場景三:團隊專案工作流程參考

我們建議團隊專案的每一個開發者都採用的基本工作流程有如下四大步,以Git命令方式為例,其中每一步對應的VS Code選單操作一般都可以在本文前述內容中查詢到

1、克隆或同步最新的程式碼到本地儲存庫;

git clone https://DOMAIN_NAME/YOUR_NAME/REPO_NAME.gitgit pull

2、為自己的工作建立一個分支,該分支應該只負責單一功能模組或程式碼模組的版本控制;

git checkout -b mybranchgit branch

3、在該分支上完成某單一功能模組或程式碼模組的開發工作;多次進行如下操作:

git add FILESgit commit -m "commit log"

4、最後,先切換回master分支,將遠端origin/master同步最新到本地儲存庫,再合併mybranch到master分支,推送到遠端origin/master之後即完成了一項開發工作。

git checkout mastergit pullgit merge --no-ff mybranchgit push

這樣在Git分支網路圖中該工作有一段明確的分叉合併路徑,如果整個團隊每一項工作都參照這個工作流程,那麼最終Git分支網路圖中就會留下清晰的專案演進成長路徑,比如如下Github上的網路圖(Network graph),分支上有兩次合併到master。

場景四:Git Rebase

一般我們在軟體開發的流程中,有一個樸素的版本管理哲學:開發者的提交要儘量乾淨、簡單。開發者要把自己的程式碼修改按照功能拆分成一個個相對獨立的提交,一個提交對應一個功能點,而且要在對應的 commit log message 裡面描述清楚。因此在合併和 push 之前檢查修改一下 commit 記錄時常需要。

場景四實際就是在場景三團隊專案工作流程中增加一步Git Rebase,即在mybranch分支上完成自己的工作之後,為了讓 log 記錄將來更容易回顧參考,用 git rebase 重新整理一下提交記錄。注意不要通過rebase對任何已經提交到遠端倉庫中的commit進行修改。

git rebase命令格式大致如下:

git rebase -i  [startpoint]  [endpoint]

其中-i的意思是--interactive,即彈出互動式的介面讓使用者編輯完成合並操作,[startpoint] [endpoint]則指定了一個編輯區間,如果不指定[endpoint],則該區間的終點預設是當前分支的HEAD。

一般只指定[startpoint],即指定從某一個commit節點開始,可以使用HEAD^^、HEAD~100、commit ID或者commit ID的頭幾個字元來指定一個commit節點,比如下面的程式碼指定重新整理HEAD之前的三個commit節點。

$ git rebase -i HEAD^^^

這時會開啟命令列文字編輯器大致如下:

pick c5fe513 Apick ec777a8 Bpick 52c5ac5 C

# Rebase 902d019..52c5ac5 onto 902d019 (3 commands)## Commands:# p, pick <commit> = use commit# r, reword <commit> = use commit, but edit the commit message# e, edit <commit> = use commit, but stop for amending# s, squash <commit> = use commit, but meld into previous commit# f, fixup <commit> = like "squash", but discard this commit's log message# x, exec <command> = run command (the rest of the line) using shell# d, drop <commit> = remove commit# l, label <label> = label current HEAD with a name# t, reset <label> = reset HEAD to a label# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]# . create a merge commit using the original merge commit's# . message (or the oneline, if no original merge commit was# . specified). Use -c <commit> to reword the commit message.## These lines can be re-ordered; they are executed from top to bottom.## If you remove a line here THAT COMMIT WILL BE LOST.## However, if you remove everything, the rebase will be aborted.### Note that empty commits are commented out

該文字編輯器的用法與Vim大致相同(應該就是內建了一個vi文字編輯器),按i鍵進入插入編輯模式,可以刪除某個版本,也可以修改提交日誌訊息;按ESC鍵退出編輯模式回到一般命令模式(Normal Mode),這時按:鍵進入底線命令模式,輸入:wq儲存退出、輸入:q退出、輸入:q!強制退出。

不管做了怎樣的編輯操作,退出文字編輯器後,想撤銷git rebase操作的話,可以執行如下命令:

git rebase --abort

如果我們編輯刪除了B版本,即刪除“pick ec777a8 B”一行,然後:wq儲存退出,可以看到如下提示:

$ git rebase -i HEAD^^^Auto-merging git2.mdCONFLICT (content): Merge conflict in git2.mderror: could not apply 52c5ac5... C

Resolve all conflicts manually, mark them as resolved with"git add/rm <conflicted_files>", then run "git rebase --continue".You can instead skip this commit: run "git rebase --skip".To abort and get back to the state before "git rebase", run "git rebase --abort".

Could not apply 52c5ac5...

這時用VS Code開啟衝突檔案大致如下:

可以根據提示選擇保留哪個更改,也可以直接編輯檔案去掉提示資訊。

解決衝突後需要將修改後的檔案存入暫存區(git add),最後執行如下命令完成git rebase。

git rebase --continue

刪除的B版本的內容很可能會合併到C版本,這時往往需要重新修改C版本的提交日誌訊息,因此在完成之前會進入文字編輯器修改C版本的提交日誌,同樣按i鍵進入插入編輯模式,可以修改C版本的提交日誌訊息;按ESC鍵退出編輯模式回到一般命令模式(Normal Mode),這時按:鍵進入底線命令模式,輸入:wq儲存退出。儲存退出後即完成了git rebase操作。

$ git rebase --continue[detached HEAD adcb434] C 1 file changed, 6 insertions(+), 1 deletion(-)Successfully rebased and updated refs/heads/mybranch.

這時檢視提交日誌可以發現B版本已經不存在了。

$ git logcommit adcb434396ca664b11f19ed518f7901a27c81e1f (HEAD -> mybranch)Author: mengning <[email protected]>Date:   Sun Sep 20 11:19:57 2020 +0800

C

commit c5fe51360f8bc010ae8de3cabe0f550240ae78f8Author: mengning <[email protected]>Date: Sun Sep 20 11:19:07 2020 +0800

最後,和場景三的第4步一樣,先切換回master分支,將遠端origin/master同步最新到本地儲存庫,再合併mybranch到master分支,推送到遠端origin/master之後即完成了一項開發工作。

Git Rebase的操作較為複雜一些,這裡給出一道練習題如下。

在Github.com或Gitee.com上新建一個版本庫,並實現如下 commit 網路結點示意圖,要求 A 和 B 在本地存在過,但並不出現在遠端網路圖中。

場景五:Fork + Pull request

前面我們討論的場景三和場景四都是在緊密合作的開發團隊中使用的,這樣的開發團隊具有良好的信任關係,具有共同遵守的、規範的專案開發流程。但是開源社群開發活動往往是鬆散的團隊,團隊成員的技術水平或開發流程往往參差不齊、千差萬別。這時如果採用場景三和場景四中推薦的參考工作流程,專案倉庫的網路圖就會一團糟。

為了解決開源社群鬆散團隊的協作問題,Github提供了Fork+ Pull request的協作開發工作流程。

當你想更正別人倉庫裡的Bug或者向別人倉庫裡貢獻程式碼時,要走Fork+ Pull request的協作開發工作流程:

  1. 先 fork(分叉) 別人的倉庫,相當於拷貝一份;

  2. 做一些 bug fix或其他的程式碼貢獻;

  3. 發起 Pull request 給原倉庫;

  4. 原倉庫的所有者 review Pull request,如果沒有問題的話,就會 merge Pull request 到原倉庫中。

接下來按步驟簡要看一下整個 Pull request 的過程。

第一步,在某個專案頁面的右上角點選Fork,如下圖右上角的Fork按鈕。

系統會以該專案倉庫為藍本為你Fork一個版本庫,然後就直接進入新建的版本庫,如下圖。

注意上圖Fork的版本庫頁面中Pull request按鈕。

第二步,可以參考前面場景一、場景二、場景三和場景四的做法,在Fork的版本庫中獨立工作,最終將bug fix或其他的程式碼貢獻同步到遠端的Fork的版本庫中。

第三步,建立Pull request。即在上圖Fork的版本庫頁面中找到Pull request按鈕,點選Pull request按鈕,進入Comparing changes頁面,如下圖:

在Comparing changes頁面可以看到Fork的版本庫與原倉庫之間所有變更資訊,頁面上有綠色的Create pull request按鈕,點選Create pull request按鈕即跳轉到原倉庫,如下圖,即可稽核變更資訊提交Pull request。

第四步,處理Pull request。即原倉庫的所有者 review Pull request,大致如下圖可以看到所有的程式碼變更,如果沒有問題的話,就會 merge Pull request 到原倉庫中。

Fork + Pull request的具體操作,可以兩人一組互相Fork對方的倉庫,然後互相提交Pull request演練一下。

至此,我們由簡單到複雜、從實際操作到背後的基本原理,並通過VS Code和命令列兩種方式相互對照,在五大場景下給出了Git的參考用法。不管您是用到時快速參考,還是希望系統地掌握Git版本管理的技能,相信本文都能為您提供必要幫助。

本文作者孟寧,未經許可禁止轉載!

程式設計神器 VS Code,只要這一篇就夠了!

自由軟體江湖裡的碼頭和規矩

自己動手寫一個作業系統核心【內含視訊】

在瀏覽器中輸入網址按回車後發生了什麼?

智慧製造:從資訊化到智慧化

一文透析華為鴻蒙科技含量!華為的創新必將刺激國內外巨頭跟進

出處:https://mp.weixin.qq.com/s/Km5KuXPETvG0wCGHrvj9Vg

您的資助是我最大的動力!
金額隨意,歡迎來賞!
款後有任何問題請給我留言。

如果,您認為閱讀這篇部落格讓您有些收穫,不妨點選一下右下角的推薦按鈕。
如果,您希望更容易地發現我的新部落格,不妨點選一下綠色通道的關注我。(●'◡'●)

如果你覺得本篇文章對你有所幫助,請給予我更多的鼓勵,求打 付款後有任何問題請給我留言!!!

因為,我的寫作熱情也離不開您的肯定支援,感謝您的閱讀,我是【Jack_孟】!