前言
Git是一個開源的分布式版本控制系統。其核心就在於版本控制。
在實際編碼過程中,我們往往會忘記上次對文件的修改內容。若是剛剛修改的還好說,撤銷操作即可。但若這是你昨天做的修改並關閉了IDE呢?所以我們需要有一個可以回退版本的工具。
Git還有另一個作用便是多人協作開發——將代碼文件上傳至雲端,partner可以隨時下載至自己的電腦,又可以隨時做好修改再上傳至雲端。
我將學習Git分為三塊,大家跟着我的步驟做一遍也就會了。
- 配置Git及配置Github
- 本地Git操作
- 遠程推送操作
# 配置Git及配置Github
首先分清楚Git和Github的作用分別是什么。你們可以理解為——Git是運輸的小車,Github是倉庫,我們將東西裝上車,然后讓他送到倉庫去。
要想使用Git及Github服務,必須得創建Github賬號,Github官網:https://github.com/。
創建好賬號后,再new一個repository。然后,我們接着配置Git。
我們需要先下載Git工具,這是Git官網:https://git-scm.com,根據自己計算機的系統下載即可。
而后我們需要在本地創建ssh key:
$ ssh-keygen -t rsa -C "your_email@youremail.com"
后面的your_email@youremail.com改為你在github上注冊的郵箱,之后會要求確認路徑和輸入密碼,我們這使用默認的一路回車就行。成功的話會在~/下生成.ssh文件夾,進去,打開id_rsa.pub,復制里面的ssh key。
回到github上,點擊右上角頭像,選擇Settings(賬戶配置),選擇左邊欄的SSH and GPG keys,點擊new SSH key,title隨便填,粘貼上你電腦上生成的key。
配置完ssh key后,我們還需要配置username和email:
$ git config --global user.name "your name"
$ git config --global user.email "your_email@youremail.com"
至此,Git的配置和Github的配置已經結束了,你已經有了可用的Git和可用的Github。接下來我們來學習如何進行本地Git操作。
# 本地Git操作
首先要學習幾個概念——工作區,暫存區,版本庫。他們之間的關系如下圖
這三個區域其實沒有什么神秘的,他們都是個文件夾(為樹結構)。他們的作用分別為
- 工作區:實際持有的文件夾,即你可以在 我的電腦 里找到的文件夾。用於實際項目開發。
- 暫存區:他就如同一個緩存,可以頻繁地將工作區中的文件發送到其中,從而使發送過來的修改后的文件覆蓋暫存區中的原文件。
- 版本庫:暫存區將他的文件發送到版本庫中,用於發送到Github倉庫中,作為最新版本。
這三個區域之間的關系及功能搞清楚了,接下來我會先說最重要的Git操作,學會這些操作后就可以直接進行生產活動了。而后,我再陳列其他較重要的的Git操作及更深層的辨析。
首先,創建一個文件夾做為工作區,不過要想這個文件夾成為工作區,需要cd到該文件夾,輸入git init
哦!這樣其才是工作區,而不是普通的文件夾,切記。
然后,我們在文件夾中隨意進行修改,如創建txt文件等。我在文件夾中創建了一個experiment.txt文件,並寫入內容。根據上圖,我們需要將工作區的文件發送到暫存區,使用git add -A
,該命令的意義為將所有的改動發送到暫存區中。(實際上還有很多種git add方法,但一般來說git add -A已經足夠。)
再然后,將暫存區的文件發送到版本庫中,使用git commit -m 'comments'
,其中comments為你對這個版本的說明文字。
好了,本地Git操作已經結束了,是不是非常簡單?只要三步就好了,我們來復習一遍。
$ git init
$ git add -A
$ git commit -m 'comments'
基礎的講好了,我們接下來進入更深的層次,如果覺得已經夠用的,可以跳過這一階段。
為什么要有暫存區
為什么要有暫存區,我們每次將實際改動的文件直接發送到版本庫不就好了?這樣也沒有任何問題啊,還省了一步。
其實你想的沒有錯,這樣是沒有問題的。不過這與軟件開發的一大基礎原則背道而馳了——單一職責原則。若是沒有暫存區,那么版本庫就得既負責提交版本,又得負責保存修改,使得Git一點也不優雅。這是兩個完全不同的功能,應該由兩個模塊去分別完成。
分支
引用自https://guides.github.com/activities/hello-world/ 的一句話:
Have you ever saved different versions of a file?
Branches accomplish similar goals in GitHub repositories.
意思就是:你是否為一個文件備份了許多不同版本,在GitHub中分支完成同樣的目標。
我們之所以為一個文件備份好多份文件,就是擔心我們不備份的話,代碼打着打着,突然發現出現了bug,而我們卻實在改不過來了,最簡單的方法就是回到沒有修改前的狀態,但我們已經回退不回去了。這樣的話,真的就是心態爆炸。
所以我們備份,但是最終我們發現,桌面一團糟。
所以分支就是這樣的作用——絕緣特性開發,簡而言之就是,給你一個調試bug的分支,同時不影響其他分支。
默認情況下,都有一條主分支——master分支
以下是有關分支的操作:
$ git branch 分支名 //用於創建新的分支,該分支的內容就是當前主分支(master)的內容。
$ git checkout 分支名 //切換到某一分支上
$ git branch -d 分支名 //刪除某一分支
$ git merge 分支名 //將該分支合並到當前分支上
要注意一點的是,git checkout 分支名
切換分支后,工作區、暫存區、版本區也都對應的全都改變,即一個分支對應一套獨立的工作區、暫存區、版本區,其中我們可以直觀地看到的是,切換分支后,工作區(也就是你電腦里的那個文件夾)的文件內容發生了改變。
其中,我要講的是$ git merge 分支名
這一操作。
分支合並($ git merge 分支名)
master是我們的主分支,而new是我們新建的分支,用於調試代碼。而當我們在該分支上調試成功了,我們是不是應該將該分支的內容復制到主分支上?畢竟該分支new僅僅是我們為了調試bug而創建出來的,master才是真正的主分支。
所以分支合並其實就是將在B分支中修改的文件覆蓋掉A分支中對應的文件,未修改的文件不覆蓋。
那么,又有一個問題來了——如果A分支和B分支都修改了這個文件呢?那么應該聽誰的呢?
首先,這個情況我們稱為分支合並沖突,其次,當發生分支合並沖突時,他會將兩個分支的修改都保存下來,但是要求你得手動修改文件后才能commit提交。
其中,Git用<<<<<<<,=======,>>>>>>>標記出來自不同分支的內容。
回退版本
之前我們說過,Git的核心在於版本控制。那么就自然要求Git得有版本回退的功能。
何為版本?每次commit到版本庫后就是一個版本。
首先,第一步——git log
查看歷史版本:
第二步——截取commit字樣后的任意字數,用作版本標識。如10feb9882453e57。
第三步——git reset --hard 第二步截取的版本標識
回退版本。
就這樣,我們就成功地回退版本啦!終於不用擔驚受怕自己一不小心腦子犯渾commit了一個錯誤的版本卻無法恢復了。
但由此又出現了一個問題,當你回退到以前的版本,那么在以前的版本后的所有版本就都無法通過git log
查找出來了。
對此,我們通過git reflog
來解決。其意義為查看所有分支的提交操作,其結果也含有版本標識。
小技巧:如果你想要知道各個版本文件內容修改的話,使用git log -p
命令即可。
查看修改內容
有時,我們想要知道還有什么文件需要add/commit,或是自己究竟做了什么樣的修改內容,那么我們可以通過git status
和git diff
來查看。
git status
git status
的意義為檢索本地倉庫的文件更新狀態。
這說的太籠統了,執行該命令顯示的內容如下:
- 已修改的,未暫存的文件名稱
- 已暫存,為提交的文件名稱
- 未追蹤到的文件
其中,要解釋一下未追蹤文件的意義:
- 已追蹤文件:在倉庫之前的版本快照中包含文件的記錄,在用戶工作一段時間過后,這些文件同樣能被追蹤到(如文件的修改和刪除)。可以理解為,原來就有的文件,非新增文件。
- 未追蹤文件:一些文件的新增
git diff
git diff
的意義為查看所有文件進行的具體的修改,添加到暫存區則看不了。
與其對應的是git diff staged
,意義為查看添加至暫存區所有的內容修改
# 遠程推送操作
當你學到這里的時候,離成功就只剩一步之遙了,堅持下去,加油!
現在我們在本地倉庫中的版本庫中准備好要推送到遠程倉庫的版本文件了,可是我們現在連要發送到哪一個遠程倉庫的地址都不知道,所以第一步是——建立本地倉庫與遠程倉庫的連接。
建立連接
建立連接的方法有一下幾種:
git remote add 遠程主機名 git@github.com:GitHub名稱/遠程倉庫名.git
。這個命令用於將本地倉庫和遠程倉庫連接起來。git clone git@github.com:GitHub名稱/遠程倉庫名.git
。會直接連接遠程倉庫,並將遠程倉庫的文件夾克隆到本地目錄中。
連接成功了之后,便可以進行push和pull操作了。我們要清楚的是,push和pull也是一種分支合並操作,只不過不再是本地的分支之間進行合並,而是遠程分支和本地分支之間的合並。
git push
該命令的意義是將本地版本庫的分支推送到遠程版本庫的對應分支進行合並。
git push
的一般形式為git push <遠程主機名> <本地分支名>:<遠程分支名>
。如git push origin master:master。
其中,一旦使用上面兩種方法連接遠程倉庫成功,則遠程倉庫名稱為origin。
git pull
該命令的意義是將遠程倉庫的分支下載至本地,並將其合並到本地對應分支,其實這一個命令是git fetch
和git merge 分支名
的縮寫。
git pull
的一般形式為git pull <遠程主機名> <本地分支名>:<遠程分支名>
。如git pull origin master:master。
好了,這些就是遠程推送操作的基本操作。不過,在實際的操作過程中還是會遇到好多好多棘手的問題,大家可以先進行實驗,在實驗的過程中遇到問題了,再來對比接下來的內容。
接下來,我來為大家列舉一些遠程推送操作中比較經常出現的問題。注意,接下來我都是基於只遠程連接一個遠程倉庫的情況。
分支追蹤
大家可能看到網上的其他教程中,只使用git push
便將版本上傳,而我們這樣做卻往往會報錯(但我前面寫的第二種連接方式不會報錯),為什么呢——因為沒有使本地分支跟蹤到遠程分支。
大家可以先看下這個鏈接的內容:https://www.cnblogs.com/qianqiannian/p/6008140.html。
其中清楚地說明了,要想省略參數的話,就得使分支之間建立追蹤關系。
如何建立追蹤關系呢——git branch --set-upstream-to=遠程主機名/遠程分支名 本地分支名
。如此,便可將本地分支追蹤到遠程分支,也就可以省去部分參數。
如果想要知道本地倉庫和遠程倉庫之間所有的追蹤關系的話,使用git branch -vv
。如果你想要知道遠程都有什么分支,使用git branch -r
。
出現! [rejected] master -> master (non-fast-forward)
出現這種情況的原因:
- 如果是push時報的錯,則是遠程倉庫有本地倉庫沒有的文件或者遠程倉庫有本地倉庫沒有的修改內容,則需要先pull下來,再push。
- 如果是pull時報的錯,則是本地倉庫有遠程倉庫沒有的文件或者本地倉庫有遠程倉庫沒有的修改內容,則需要先push上去,再pull。
如果git pull無效的話,就使用git pull origin 遠程分支名 --allow-unrelated-histories
便可解決
當push的時候,還會有種報錯的情況——分支合並沖突。解決方案是先pull,在本地解決完分支沖突的問題后再push。
小知識點
1.當你在本地新建一個分支,然后直接上傳是可行的,會自動在遠程倉庫中新建一個同名分支,並將內容上傳至此。
2.你從遠程倉庫中pull下來的內容,其工作區,暫存區,版本庫都是“干凈“的,即不需要你將新增的內容再add到暫存區,再commit到版本庫
3.切記,分支合並(包括push和pull)不是一個分支覆蓋另一個分支,而是一個分支的修改內容增添到另一個分支。
全文結束。
參考資料
- git add documents:https://www.git-scm.com/docs/git-add
- 為什么要先 git add 才能 git commit ?https://www.zhihu.com/question/19946553
- GitHub簡明教程:http://www.runoob.com/w3cnote/git-guide.html
- git status和git diff:https://www.cnblogs.com/kkz-org/p/9313205.html
- git 遠程倉庫和本地倉庫建立連接:https://blog.csdn.net/wowoniuzailushang/article/details/78545971
- Git push:https://www.cnblogs.com/qianqiannian/p/6008140.html
- git中出現“non-fast-forward”errors時的終極解決方案:https://blog.csdn.net/kiddd_fu/article/details/78247290
- 使用Git的Push出現rejected - non-fast-forward錯誤:https://www.cnblogs.com/xiaoyunxia/p/5975169.html