(二)git常用基本概念
一、工作區和暫存區
- 工作區(Working Directory):就是你在電腦里能看到的目錄,比如我的learngit文件夾就是一個工作區:
- 版本庫(Repository):工作區有一個隱藏目錄.git,這個不算工作區,而是Git的版本庫。
- 暫存區(stage):Git的版本庫里存了很多東西,其中最重要的就是稱為stage(或者叫index)的暫存區
Git為我們自動創建的第一個分支master,以及指向master的一個指針叫HEAD。
前面講了我們把文件往Git版本庫里添加的時候,是分兩步執行的:
- 第一步:用git add把文件添加進去,實際上就是把文件修改添加到暫存區;
- 第二步:用git commit提交更改,實際上就是把暫存區的所有內容提交到當前分支。
因為我們創建Git版本庫時,Git自動為我們創建了唯一一個master分支,所以,現在,git commit就是往master分支上提交更改。
你可以簡單理解為,需要提交的文件修改通通放到暫存區,然后,一次性提交暫存區的所有修改。
俗話說,實踐出真知。現在,我們再練習一遍,先對readme.txt做個修改,比如加第三行的內容:
Git is a distributed version control system. Git is free software distributed under the GPL. Git has a mutable index called stage.然后,在工作區新增一個LICENSE文本文件(內容隨便寫)。
先用git status查看一下狀態:
$ 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: readme.txtUntracked files:(use "git add <file>..." to include in what will be committed)LICENSEno changes added to commit (use "git add" and/or "git commit -a")Git非常清楚地告訴我們,readme.txt被修改了,而LICENSE還從來沒有被添加過,所以它的狀態是Untracked。
現在,使用兩次命令git add,把readme.txt和LICENSE都添加后,用git status再查看一下:
$ git status On branch master Changes to be committed:(use "git reset HEAD <file>..." to unstage)new file: LICENSEmodified: readme.txt現在,暫存區的狀態就變成這樣了:
所以,git add命令實際上就是把要提交的所有修改放到暫存區(Stage),然后,執行git commit就可以一次性把暫存區的所有修改提交到分支。
$ git commit -m "understand how stage works" [master e43a48b] understand how stage works2 files changed, 2 insertions(+)create mode 100644 LICENSE一旦提交后,如果你又沒有對工作區做任何修改,那么工作區就是“干凈”的:
$ git status On branch master nothing to commit, working tree clean現在版本庫變成了這樣,暫存區就沒有任何內容了:
二、遠程倉庫
1、遠程倉庫服務器
如果要使用遠程倉庫,需要另一臺電腦充當服務器。不過我們不需要自己搭建git服務器,借用GitHub即可。在使用GitHub之前需要注冊一個GitHub賬號,就可以免費獲得Git遠程倉庫(在繼續閱讀后續內容前,請自行注冊GitHub賬號)。由于本地Git倉庫和GitHub倉庫之間的傳輸是通過SSH加密的,所以,需要一點設置:
第1步:創建SSH Key。在版本庫主目錄下,看看有沒有.ssh目錄,如果有,再看看這個目錄下有沒有id_rsa和id_rsa.pub這兩個文件,如果已經有了,可直接跳到下一步。如果沒有,打開Shell(Windows下打開Git Bash),創建SSH Key:
$ ssh-keygen -t rsa -C "youremail@example.com"你需要把郵件地址換成你自己的郵件地址,然后一路回車,使用默認值即可,由于這個Key也不是用于軍事目的,所以也無需設置密碼。如果一切順利的話,可以在用戶主目錄里找到.ssh目錄,里面有id_rsa和id_rsa.pub兩個文件,這兩個就是SSH Key的秘鑰對,id_rsa是私鑰,不能泄露出去,id_rsa.pub是公鑰,可以放心地告訴任何人。
第2步:登陸GitHub,打開“Account settings”,“SSH Keys”頁面:
然后,點“Add SSH Key”,填上任意Title,在Key文本框里粘貼id_rsa.pub文件的內容:
點“Add Key”,你就應該看到已經添加的Key:
2、創建遠程倉庫
現在的情景是,你已經在本地創建了一個Git倉庫后,又想在GitHub創建一個Git倉庫,并且讓這兩個倉庫進行遠程同步,這樣,GitHub上的倉庫既可以作為備份,又可以讓其他人通過該倉庫來協作,真是一舉多得。
首先,登陸GitHub,然后,在右上角找到“Create a new repo”按鈕,創建一個新的倉庫:
在Repository name填入learngit,其他保持默認設置,點擊“Create repository”按鈕,就成功地創建了一個新的Git倉庫:
目前,在GitHub上的這個learngit倉庫還是空的,GitHub告訴我們,可以從這個倉庫克隆出新的倉庫,也可以把一個已有的本地倉庫與之關聯,然后,把本地倉庫的內容推送到GitHub倉庫。
3、關聯遠程倉庫
現在,我們根據GitHub的提示,在本地的learngit倉庫下運行命令:
$ git remote add origin git@github.com:michaelliao/learngit.git請千萬注意,把上面的michaelliao替換成你自己的GitHub賬戶名,否則,你在本地關聯的就是我的遠程庫,關聯沒有問題,但是你以后推送是推不上去的,因為你的SSH Key公鑰不在我的賬戶列表中。
添加后,遠程庫的名字就是origin,這是Git默認的叫法,也可以改成別的,但是origin這個名字一看就知道是遠程庫。
下一步,就可以把本地庫的所有內容推送到遠程庫上:
$ git push -u origin master Counting objects: 20, done. Delta compression using up to 4 threads. Compressing objects: 100% (15/15), done. Writing objects: 100% (20/20), 1.64 KiB | 560.00 KiB/s, done. Total 20 (delta 5), reused 0 (delta 0) remote: Resolving deltas: 100% (5/5), done. To github.com:michaelliao/learngit.git* [new branch] master -> master Branch 'master' set up to track remote branch 'master' from 'origin'.把本地庫的內容推送到遠程,用git push命令,實際上是把當前分支master推送到遠程。
由于遠程庫是空的,我們第一次推送master分支時,加上了-u參數,Git不但會把本地的master分支內容推送的遠程新的master分支,還會把本地的master分支和遠程的master分支關聯起來,在以后的推送或者拉取時就可以簡化命令。
推送成功后,可以立刻在GitHub頁面中看到遠程庫的內容已經和本地一模一樣:
從現在起,只要本地作了提交,就可以通過命令:
$ git push origin master把本地master分支的最新修改推送至GitHub,現在,你就擁有了真正的分布式版本庫!
SSH警告
當你第一次使用Git的clone或者push命令連接GitHub時,會得到一個警告:
The authenticity of host 'github.com (xx.xx.xx.xx)' can't be established. RSA key fingerprint is xx.xx.xx.xx.xx. Are you sure you want to continue connecting (yes/no)?這是因為Git使用SSH連接,而SSH連接在第一次驗證GitHub服務器的Key時,需要你確認GitHub的Key的指紋信息是否真的來自GitHub的服務器,輸入yes回車即可。
Git會輸出一個警告,告訴你已經把GitHub的Key添加到本機的一個信任列表里了:
Warning: Permanently added 'github.com' (RSA) to the list of known hosts.這個警告只會出現一次,后面的操作就不會有任何警告了。
如果你實在擔心有人冒充GitHub服務器,輸入yes前可以對照GitHub的RSA Key的指紋信息是否與SSH連接給出的一致。
小結
要關聯一個遠程庫,使用命令git remote add origin git@server-name:path/repo-name.git;
關聯后,使用命令git push -u origin master第一次推送master分支的所有內容;
此后,每次本地提交后,只要有必要,就可以使用命令git push origin master推送最新修改;
分布式版本系統的最大好處之一是在本地工作完全不需要考慮遠程庫的存在,也就是有沒有聯網都可以正常工作,而SVN在沒有聯網的時候是拒絕干活的!當有網絡的時候,再把本地提交推送一下就完成了同步,真是太方便了!
4、克隆遠程倉庫
上次講了先有本地庫,后有遠程庫的時候,如何關聯遠程庫。
現在,假設從零開發,那么最好的方式是先創建遠程庫,后從遠程庫克隆。
首先,登陸GitHub,創建一個新的倉庫,名字叫gitskills:
我們勾選Initialize this repository with a README,這樣GitHub會自動為我們創建一個README.md文件。創建完畢后,可以看到README.md文件:
現在,遠程庫已經準備好了,下一步是用命令git clone克隆一個本地庫:
$ git clone git@github.com:michaelliao/gitskills.git Cloning into 'gitskills'... remote: Counting objects: 3, done. remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 3 Receiving objects: 100% (3/3), done.注意把Git庫的地址換成你自己的,然后進入gitskills目錄看看,已經有README.md文件了:
$ cd gitskills $ ls README.md如果有多個人協作開發,那么每個人各自從遠程克隆一份就可以了。
你也許還注意到,GitHub給出的地址不止一個,還可以用https://github.com/michaelliao/gitskills.git這樣的地址。實際上,Git支持多種協議,默認的git://使用ssh,但也可以使用https等其他協議。
使用https除了速度慢以外,還有個最大的麻煩是每次推送都必須輸入口令,但是在某些只開放http端口的公司內部就無法使用ssh協議而只能用https。
小結
要克隆一個倉庫,首先必須知道倉庫的地址,然后使用git clone命令克隆。
Git支持多種協議,包括https,但ssh協議速度最快。
三、分支管理
在版本回退里,你已經知道,每次提交,Git都把它們串成一條時間線,這條時間線就是一個分支。截止到目前,只有一條時間線,在Git里,這個分支叫主分支,即master分支。HEAD嚴格來說不是指向提交,而是指向master,master才是指向提交的,所以,HEAD指向的就是當前分支。
一開始的時候,master分支是一條線,Git用master指向最新的提交,再用HEAD指向master,就能確定當前分支,以及當前分支的提交點:
每次提交,master分支都會向前移動一步,這樣,隨著你不斷提交,master分支的線也越來越長。當我們創建新的分支,例如dev時,Git新建了一個指針叫dev,指向master相同的提交,再把HEAD指向dev,就表示當前分支在dev上:
你看,Git創建一個分支很快,因為除了增加一個dev指針,改改HEAD的指向,工作區的文件都沒有任何變化!不過,從現在開始,對工作區的修改和提交就是針對dev分支了,比如新提交一次后,dev指針往前移動一步,而master指針不變:
假如我們在dev上的工作完成了,就可以把dev合并到master上。Git怎么合并呢?最簡單的方法,就是直接把master指向dev的當前提交,就完成了合并:
所以Git合并分支也很快!就改改指針,工作區內容也不變!合并完分支后,甚至可以刪除dev分支。刪除dev分支就是把dev指針給刪掉,刪掉后,我們就剩下了一條master分支:
真是太神奇了,你看得出來有些提交是通過分支完成的嗎?下面開始實戰。
首先,我們創建dev分支,然后切換到dev分支:
$ git checkout -b dev Switched to a new branch 'dev'git checkout命令加上-b參數表示創建并切換,相當于以下兩條命令:
$ git branch dev $ git checkout dev Switched to branch 'dev'然后,用git branch命令查看當前分支:
$ git branch * devmastergit branch命令會列出所有分支,當前分支前面會標一個*號。
然后,我們就可以在dev分支上正常提交,比如對readme.txt做個修改,加上一行:
Creating a new branch is quick.然后提交:
$ git add readme.txt $ git commit -m "branch test" [dev b17d20e] branch test1 file changed, 1 insertion(+)現在,dev分支的工作完成,我們就可以切換回master分支:
$ git checkout master Switched to branch 'master'切換回master分支后,再查看一個readme.txt文件,剛才添加的內容不見了!因為那個提交是在dev分支上,而master分支此刻的提交點并沒有變:
現在,我們把dev分支的工作成果合并到master分支上:
$ git merge dev Updating d46f35e..b17d20e Fast-forwardreadme.txt | 1 +1 file changed, 1 insertion(+)git merge命令用于合并指定分支到當前分支。合并后,再查看readme.txt的內容,就可以看到,和dev分支的最新提交是完全一樣的。
注意到上面的Fast-forward信息,Git告訴我們,這次合并是“快進模式”,也就是直接把master指向dev的當前提交,所以合并速度非常快。
當然,也不是每次合并都能Fast-forward,我們后面會講其他方式的合并。
合并完成后,就可以放心地刪除dev分支了:
$ git branch -d dev Deleted branch dev (was b17d20e).刪除后,查看branch,就只剩下master分支了:
$ git branch * master因為創建、合并和刪除分支非常快,所以Git鼓勵你使用分支完成某個任務,合并后再刪掉分支,這和直接在master分支上工作效果是一樣的,但過程更安全。
switch
我們注意到切換分支使用git checkout <branch>,而前面講過的撤銷修改則是git checkout -- <file>,同一個命令,有兩種作用,確實有點令人迷惑。實際上,切換分支這個動作,用switch更科學。因此,最新版本的Git提供了新的git switch命令來切換分支:創建并切換到新的dev分支,可以使用:
$ git switch -c dev直接切換到已有的master分支,可以使用:
$ git switch master使用新的git switch命令,比git checkout要更容易理解。
小結
Git鼓勵大量使用分支:
查看分支:git branch
創建分支:git branch <name>
切換分支:git checkout <name>或者git switch <name>
創建+切換分支:git checkout -b <name>或者git switch -c <name>
合并某分支到當前分支:git merge <name>
刪除分支:git branch -d <name>
四、標簽管理
總結
以上是生活随笔為你收集整理的(二)git常用基本概念的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: (一)git简介及安装
- 下一篇: (三)git常用命令及方法大全