Git&GitHub-進階教程


1. 遠程倉庫-GitHub

Git是分布式版本控制系統,同一個Git倉庫,可以分布到不同的機器上。怎么分布呢?最早,肯定只有一台機器有一個原始版本庫,此后,別的機器可以“克隆”這個原始版本庫,而且每台機器的版本庫其實都是一樣的,並沒有主次之分。

實際情況往往是這樣,找一台電腦充當服務器的角色,每天24小時開機,其他每個人都從這個“服務器”倉庫克隆一份到自己的電腦上,並且各自把各自的提交推送到服務器倉庫里,也從服務器倉庫中拉取別人的提交。

1.1 本地電腦如何關聯GitHub?

由於你的本地Git倉庫和GitHub倉庫之間的傳輸是通過SSH加密的,所以,需要一點設置:

  • 創建SSH Key
$ ssh-keygen -t rsa -C "haochen273@gmail.com"

一路回車就可以了

如果一切順利的話,可以在用戶主目錄(C:\Users\haoch.ssh)里找到.ssh目錄,里面有id_rsaid_rsa.pub兩個文件,這兩個就是SSH Key的秘鑰對,id_rsa是私鑰,不能泄露出去,id_rsa.pub是公鑰,可以放心地告訴任何人。

  • 設置GitHub的SSH Key

在GitHub主頁找到Account Seetings, SSH Key頁面:將id_rsa.pub的內容粘貼到里面即可Add

GitHub為什么要用SSH Key呢?

因為GitHub需要識別出你推送的提交確實是你推送的,而不是別人冒充的,而Git支持SSH協議,所以,GitHub只要知道了你的公鑰,就可以確認只有你自己才能推送。

當然,GitHub允許你添加多個Key。假定你有若干電腦,你一會兒在公司提交,一會兒在家里提交,只要把每台電腦的Key都添加到GitHub,就可以在每台電腦上往GitHub推送了。

1.2. 創建並操控遠程庫GitHub

在GitHub中創建一個遠程庫名為learngit:

(1) 把一個已有的本地倉庫與之關聯,然后,把本地倉庫的內容推送到GitHub倉庫。:本地->遠程

  • 關聯
    在本地庫運行命令:
$ git remote add origin git@github.com:haochen95/learngit.git

添加后,遠程庫的名字就是origin,這是Git默認的叫法,也可以改成別的,但是origin這個名字一看就知道是遠程庫。

  • 推送
    使用命令git push -u origin master -f
$ git push -u origin master -f
Enumerating objects: 23, done.
Counting objects: 100% (23/23), done.
Delta compression using up to 8 threads
Compressing objects: 100% (17/17), done.
Writing objects: 100% (23/23), 1.82 KiB | 465.00 KiB/s, done.
Total 23 (delta 5), reused 0 (delta 0)
remote: Resolving deltas: 100% (5/5), done.
To github.com:haochen95/learngit.git
 + cc71eae...1f108b4 master -> master (forced update)
Branch 'master' set up to track remote branch 'master' from 'origin'.

把本地庫的內容推送到遠程,用git push命令,實際上是把當前分支master推送到遠程。

由於遠程庫是空的,我們第一次推送master分支時,加上了-u參數,Git不但會把本地的master分支內容推送的遠程新的master分支,還會把本地的master分支和遠程的master分支關聯起來,在以后的推送或者拉取時就可以簡化命令。

成功后的頁面

  • 以后的每次提交只寫一個命令
    $ git push origin master

把本地master分支的最新修改推送至GitHub,現在,你就擁有了真正的分布式版本庫!

(2) 從GitHub克隆庫到本地: 遠程->本地

現在我們另外創建一個空文件夾: D:\git_clone
克隆的代碼是: $ git clone git@github.com:haochen/learngit.git

$ git clone git@github.com:haochen95/learngit.git
Cloning into 'learngit'...
remote: Enumerating objects: 23, done.
remote: Counting objects: 100% (23/23), done.
remote: Compressing objects: 100% (12/12), done.
Receiving objects: 100% (23/23), done.
Resolving deltas: 100% (5/5), done.
remote: Total 23 (delta 5), reused 23 (delta 5), pack-reused 0

你看,本地的庫里面就有了跟GitHub一樣的內容

2. Git分支管理(重要)

創建了一個屬於你自己的分支,別人看不到,還繼續在原來的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到開發完畢后,再一次性合並到原來的分支上,這樣,既安全,又不影響別人工作。

2.1. 創建和合並分支

版本回退里,你已經知道,每次提交,Git都把它們串成一條時間線,這條時間線就是一個分支。截止到目前,只有一條時間線,在Git里,這個分支叫主分支,即master分支。HEAD嚴格來說不是指向提交,而是指向mastermaster才是指向提交的,所以,HEAD指向的就是當前分支

一開始的時候,master分支是一條線,Gitmaster指向最新的提交,再用HEAD指向master,就能確定當前分支,以及當前分支的提交點:

每次提交,master分支都會向前移動一步,這樣,隨着你不斷提交,master分支的線也越來越長:

1)增加分支的原理

我們創建新的分支,例如dev時,Git新建了一個指針叫dev,指向master相同的提交,再把HEAD指向dev,就表示當前分支在dev`上

增加分支的原理: 增加一個指針,更改HEAD的指向

2)增加分支后的提交變化

現在開始,對工作區的修改和提交就是針對dev分支了,比如新提交一次后,dev指針往前移動一步,而master指針不變:

3) 分支合並

就是直接把master指向dev的當前提交,就完成了合並

合並分支的原理: 改改指針(dev->master)

4)分支刪除

刪除dev分支就是把dev指針給刪掉,刪掉后,我們就剩下了一條master分支

代碼測試

1. 創建分支

$ git checkout -b dev
Switched to a new branch 'dev'

git checkout命令加上-b參數表示創建並切換

2. 查看分支

$ git branch
* dev
  master

3. 在分支上提交
增加一個文件命名為love.txt並且提交

$ git add love.txt

$ git commit -m "love"
[dev b3045f0] love
 1 file changed, 1 insertion(+)
 create mode 100644 love.txt

4. 從dev分支切換回master分支

$ git checkout master
Switched to branch 'master'
Your branch is up to date with 'origin/master'.

再次查看文件夾發現沒有love.txt文件,因為那個提交是在dev分支上,而master分支此刻的提交點並沒有變

5. master和dev分支合並

$ git merge dev
Updating 1f108b4..b3045f0
Fast-forward
 love.txt | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 love.txt

6. 刪除dev分支

$ git branch -d dev
Deleted branch dev (was b3045f0).

7. 再次查看branch信息

$ git branch
* master

2.2. 解決合並沖突

問題: 在分支上提交一個修改,轉到master后又提交一個修改,兩個修改都在同一個位置,請問如何合並?

現在,master分支和feature1分支各自都分別有新的提交,變成了這樣:

這種情況下,Git無法執行“快速合並”,只能試圖把各自的修改合並起來,但這種合並就可能會有沖突,我們試試看

$ git merge feature1
Auto-merging love.txt
CONFLICT (content): Merge conflict in love.txt
Automatic merge failed; fix conflicts and then commit the result.

需要手動解決沖突

通過git status查看狀態

$ git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.
  (use "git push" to publish your local commits)

You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>..." to mark resolution)

        both modified:   love.txt

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

我們可以直接查看love.txt的內容:

<<<<<<< HEAD
I love you
=======
I come from china
>>>>>>> feature1

我們重新修改為

I come from china

然后再提交

$ git commit -m "loving"
[master e6c7c5f] loving

現在,master分支和feature1分支變成了下圖所示:

我們可以通過命令$ git log --graph --pretty=oneline --abbrev-commit 查看分支情況

$ git log --graph --pretty=oneline --abbrev-commit
*   e6c7c5f (HEAD -> master) loving
|\
| * 445380a (feature1) from china
* | 62b6881 love you
|/
* b3045f0 love
* 1f108b4 (origin/master) remove text.txt
* f2863ca new text
* aa4f64e git tracks
* 45700b2 how it works
* 5103166 delete software
* 9fbb435 github infor
* 4fb84da add new line
* f7f8050 write to readme

2.3. 分支管理策略

  1. master分支應該是非常穩定的,也就是僅用來發布新版本,平時不能在上面干活;
  2. 干活都在dev分支上,也就是說,dev分支是不穩定的,到某個時候,比如1.0版本發布時,再把dev分支合並到master上,在master分支發布1.0版本
  3. 你和你的小伙伴們每個人都在dev分支上干活,每個人都有自己的分支,時不時地往dev分支上合並就可以了。
  4. 使用git merge --no-ff -m "merge with no-ff" dev合並會留下歷史信息,建議這樣用

2.4. Bug分支

假設,你正正在完成love.txt的編輯工作,但是這個文件需要2天才能完成,但是現在老板給你說readme.txt有一個錯誤(I love USA)你需要更改這個bug, 那怎么辦? 你想創建一個分支issue-101來修復它,但是,等等,當前正在feature1上進行的工作還沒有提交:

$ git status
On branch feature1
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   love.txt

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:   readme.txt

1) git stash

這個功能是把當前工作現場“儲藏”起來,等以后恢復現場后繼續工作:

$ git stash
Saved working directory and index state WIP on feature1: f1f394f modifyed

現在,用git status查看工作區,就是干凈的(除非有沒有被Git管理的文件),因此可以放心地創建分支來修復bug。

2) 創建issue分支解決bug

在master上創建分支

$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 4 commits.
  (use "git push" to publish your local commits)  

$ git checkout -b issue
Switched to a new branch 'issue'  

然后修改readme.txt的刪除I love China,在提交

$ git commit -m "fix issue101"
[issue bdc0b2e] fix issue101
 1 file changed, 1 deletion(-)

然后轉到master合並分支

$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 4 commits.
  (use "git push" to publish your local commits)


$ git merge --no-ff -m "merged bug fix 101" issue
Merge made by the 'recursive' strategy.
 readme.txt | 1 -
 1 file changed, 1 deletion(-)

哈哈,Bug解決啦,是時候回到feature1分支繼續工作啦

$ git checkout feature1
Switched to branch 'feature1'

$ git status
On branch feature1
nothing to commit, working tree clean

工作區是干凈的,剛才的工作現場存到哪去了?用git stash list命令看看

$ git stash list
stash@{0}: WIP on feature1: f1f394f modifyed

工作現場還在,Git把stash內容存在某個地方了,但是需要恢復一下,有兩個辦法

1)恢復辦法1:git stash apply

git stash apply恢復,但是恢復后,stash內容並不刪除,你需要用git stash drop來刪除;

2)恢復方法2: git stash pop

git stash pop,恢復的同時把stash內容也刪了

2.5. Feature分支

添加一個新功能時,你肯定不希望因為一些實驗性質的代碼,把主分支搞亂了,所以,每添加一個新功能,最好新建一個feature分支,在上面開發,完成后,合並,最后,刪除該feature分支。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM