安裝git:
yum install zlib (系統默認已經裝上)
yum install zlib-devel
sudo yum install gcc-c++
yum install perl-ExtUtils-CBuilder perl-ExtUtils-MakeMaker
wget https://www.kernel.org/pub/software/scm/git/git-2.10.0.tar.gz
tar -zxvf git-2.10.0.tar.gz
cd git-2.10.0
./configure
make
make install
git config --global user.name "Your Name"
git config --global user.email "email@example.com"
創建版本庫:
mkdir learngit
git init //通過git init命令把這個目錄變成Git可以管理的倉庫,當前目錄下多了一個.git的目錄,用ls -ah命令就可以看見。
touch readme.txt //新建一個文件
git add readme.txt //把文件添加到倉庫
git commit -m "wrote a readme file" //把文件提交到倉庫
因為commit可以一次提交很多文件,所以你可以多次add不同的文件,比如:
git add file1.txt
git add file2.txt file3.txt
git commit -m "add 3 files."
git status命令可以讓我們時刻掌握倉庫當前的狀態。
git diff命令查看上次修改的內容。此命令比較的是工作目錄中當前文件和暫存區域快照之間的差異, 也就是修改之后還沒有暫存起來的變化內容。
git diff --staged命令查看已暫存的將要添加到下次提交里的內容。
git commit -a命令Git 就會自動把所有已經跟蹤過的文件暫存起來一并提交,從而可以跳過 git add 步驟。
git rm filename命令刪除暫存區中的文件。
git rm --cached filename命令刪除暫存和已經提交的文件。
git mv file_from file_to命令重命名文件,相當于$ mv README.md README,$ git rm README.md,$ git add README三個命令。
git log命令查看提交歷史記錄。回退后記錄被清除。也可加上--pretty=oneline參數:git log --pretty=oneline
git reflog命令查看命令歷史記錄。
版本回退:
Git必須知道當前版本是哪個版本,在Git中,用HEAD表示當前版本,也就是最新的提交(例如:3628164...882e1e0),上一個版本就是HEAD^, 上上一個版本就是HEAD^^, 往上100寫成HEAD~100。
git reset --hard HEAD^
git reset --hard 3628164 //版本號前幾位即可
- soft 參數:將本地版本庫的頭指針全部重置到指定版本,且將這次提交之后的所有變更都移動到暫存區。
- 默認的mixed參數:將本地版本庫的頭指針全部重置到指定版本,且會重置暫存區,即這次提交之后的所有變更都移動到未暫存階段(工作區)。
- hard參數:會將工作區代碼也回退到這個版本
工作區和暫存區:
比如learngit文件夾就是一個工作區,工作區有一個隱藏目錄.git,這個不算工作區,而是Git的版本庫。Git的版本庫里存了很多東西,其中最重要的就是稱為stage(或者叫index)的暫存區,還有Git為我們自動創建的第一個分支master,以及指向master的一個指針叫HEAD。
如上圖
第一步是用git add把文件添加進去,實際上就是把文件修改添加到暫存區;
第二步是用git commit提交更改,實際上就是把暫存區的所有內容提交到當前分支。
在創建Git版本庫時,Git自動為我們創建了唯一一個master分支,所以,現在,git commit就是往master分支上提交更改。
可以簡單理解為,需要提交的文件修改通通放到暫存區,然后,一次性提交暫存區的所有修改。
管理修改:
第一次修改 -> git add -> 第二次修改 -> git commit
Git管理的是修改,當你用git add命令后,在工作區的第一次修改被放入暫存區,準備提交。在工作區的第二次修改并沒有放入暫存區,所以git commit只負責把暫存區的修改提交了,也就是第一次的修改被提交了,第二次的修改不會被提交。
撤銷修改:
git checkout -- readme.txt
命令git checkout -- readme.txt意思就是,把readme.txt文件在工作區的修改全部撤銷,這里有兩種情況:
- readme.txt自修改后還沒有被放到暫存區,現在,撤銷修改就回到和版本庫一模一樣的狀態;
- readme.txt已經添加到暫存區后,又作了修改,現在,撤銷修改就回到添加到暫存區后的狀態。
總之,就是讓這個文件回到最近一次git commit或git add時的狀態。
git reset HEAD readme.txt
用命令git reset HEAD file可以把暫存區的修改撤銷掉(unstage)。
場景1:當修該了工作區某個文件的內容,想直接丟棄工作區的修改時,用命令git checkout -- file。
場景2:當修改了工作區某個文件的內容,還添加到了暫存區時,想丟棄修改,分兩步,第一步用命令git reset HEAD file,就回到了場景1,第二步按場景1操作。
場景3:已經提交了不合適的修改到版本庫時,想要撤銷本次提交,參考版本回退,不過前提是沒有推送到遠程庫。
刪除文件:
rm test.txt
git status //這個時候,Git知道你刪除了文件,因此,工作區和版本庫就不一致了,git status命令會立刻告訴你哪些文件被刪除了
git rm test.txt
git commit -m "remove test.txt"
rm test.txt
git status
git checkout -- test.txt //撤銷了刪除操作,其實是用版本庫里的版本替換工作區的版本,無論工作區是修改還是刪除,都可以“一鍵還原”。
GitHub:
在 https://github.com/settings/ssh 中添加公鑰。
git remote add origin git@github.com:zzhblh/HelloWorld.git //遠程庫的名字就是origin,這是Git默認的叫法
git push -u origin master
第一次推送master分支時,加上了-u參數,Git不但會把本地的master分支內容推送的遠程新的master分支,還會把本地的master分支和遠程的master分支關聯起來,在以后的推送或者拉取時就可以簡化命令。
更好的方式是先創建遠程庫,然后,從遠程庫克隆:
git clone git@github.com:zzhblh/HelloWorld.git
會在當前目錄創建一個叫做HelloWorld的git目錄。
分支管理:
一開始的時候,master分支是一條線,Git用master指向最新的提交,再用HEAD指向master,就能確定當前分支,以及當前分支的提交點:
當我們創建新的分支,例如dev時,Git新建了一個指針叫dev,指向master相同的提交,再把HEAD指向dev,就表示當前分支在dev上:
git checkout -b dev
git checkout命令加上-b參數表示創建并切換,相當于git branch dev
git checkout dev
。查看當前分支git branch
,當前分支前面會標一個*號。從現在開始,對工作區的修改和提交就是針對dev分支了,比如新提交一次后,dev指針往前移動一步,而master指針不變:
如果
git checkout master
切換回master,會發現master沒有改變,因為那個提交是在dev分支。假如我們在dev上的工作完成了,就可以把dev合并到master上。Git怎么合并呢?最簡單的方法,就是直接把master指向dev的當前提交,就完成了合并:
git checkout master
切換回master,git merge dev
命令用于合并指定分支到當前分支。直接把master指向dev的當前提交。合并完分支后,甚至可以刪除dev分支。刪除dev分支就是把dev指針給刪掉,刪掉后,我們就剩下了一條master分支:
git branch -d dev
刪除dev分支。解決沖突:
這種情況下,Git無法執行“快速合并”,只能試圖把各自的修改合并起來,但這種合并就可能會有沖突:
$ git merge feature1
Auto-merging readme.txt
CONFLICT (content): Merge conflict in readme.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.
#
# Unmerged paths:
# (use "git add/rm <file>..." as appropriate to mark resolution)
#
# both modified: readme.txt
#
no changes added to commit (use "git add" and/or "git commit -a")
我們可以直接查看readme.txt的內容:
Git is a distributed version control system.
Git is free software distributed under the GPL.
Git has a mutable index called stage.
Git tracks changes of files.
<<<<<<< HEAD
Creating a new branch is quick & simple.
=======
Creating a new branch is quick AND simple.
>>>>>>> feature1
Git用<<<<<<<,=======,>>>>>>>標記出不同分支的內容,我們修改如下后保存:
Creating a new branch is quick and simple.
再提交:
git add readme.txt
git commit -m "conflict fixed"
[master 59bc1cb] conflict fixed
現在,master分支和feature1分支變成了下圖所示:
用帶參數的git log也可以看到分支的合并情況:
git log --graph --pretty=oneline --abbrev-commit
* 59bc1cb conflict fixed
|\
| * 75a857c AND simple
* | 400b400 & simple
|/
* fec145a branch test
...
最后,刪除feature1分支:
git branch -d feature1
Deleted branch feature1 (was 75a857c)
分支策略:
在實際開發中,我們應該按照幾個基本原則進行分支管理:
首先,master分支應該是非常穩定的,也就是僅用來發布新版本,平時不能在上面干活。
干活都在dev分支上,也就是說,dev分支是不穩定的,到某個時候,比如1.0版本發布時,再把dev分支合并到master上,在master分支發布1.0版本。
每個人都在dev分支上干活,時不時地往dev分支上合并就可以了。
所以,團隊合作的分支看起來就像這樣:
Bug分支:
Git還提供了一個stash功能,需要把當前工作現場“儲藏”起來,等以后恢復現場后繼續工作。需要stash的原因是因為一個本地的git repo只有一個工作區和暫存區,而我們的checkout只是將HEAD指針從一個分支切換到另一個分支。所以工作區或暫存區不‘干凈’(有未commit或stash的內容)的話,會把當前的修改提交到切換之后的分支。
首先確定要在哪個分支上修復bug,假定需要在master分支上修復,就從master創建臨時分支:
$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 6 commits.
$ git checkout -b issue-101
Switched to a new branch 'issue-101'
現在修復bug,需要把“Git is free software ...”改為“Git is a free software ...”,然后提交:
$ git add readme.txt
$ git commit -m "fix bug 101"
[issue-101 cc17032] fix bug 101
1 file changed, 1 insertion(+), 1 deletion(-)
修復完成后,切換到master分支,并完成合并,最后刪除issue-101分支:
$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 2 commits.
$ git merge --no-ff -m "merged bug fix 101" issue-101
Merge made by the 'recursive' strategy.
readme.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
$ git branch -d issue-101
Deleted branch issue-101 (was cc17032).
現在,是時候接著回到dev分支干活了。為了保證dev上的相同bug被修復,需要將master合并至dev上:checkout dev
切換回dev分支,git merge master
合并master至dev上。
$ git checkout dev
Switched to branch 'dev'
$ git merge master
$ git status
# On branch dev
nothing to commit (working directory clean)
工作區是干凈的,剛才的工作現場存到哪去了?用git stash list命令看看:
$ git stash list
stash@{0}: WIP on dev: 6224937 add merge
工作區需要恢復,有兩個辦法:
一是用git stash apply恢復,但是恢復后,stash內容并不刪除,你需要用git stash drop來刪除;
另一種方式是用git stash pop,恢復的同時把stash內容也刪了。
你可以多次stash,恢復的時候,先用git stash list查看,然后恢復指定的stash,用命令:
$ git stash apply stash@{0}
推送/抓取分支:
多人協作時,大家都會往master和dev分支上推送各自的修改。當你從遠程倉庫克隆時,實際上Git自動把本地的master分支和遠程的master分支對應起來了,并且,遠程倉庫的默認名稱是origin。
要查看遠程庫的信息,用git remote:
$ git remote
origin
或者,用git remote -v顯示更詳細的信息:
$ git remote -v
origin git@github.com:zzhblh/HelloWorld.git (fetch)
origin git@github.com:zzhblh/HelloWorld.git (push)
上面顯示了可以抓取和推送的origin的地址。如果沒有推送權限,就看不到push的地址。
推送分支:
就是把該分支上的所有本地提交推送到遠程庫。推送時,要指定本地分支,這樣,Git就會把該分支推送到遠程庫對應的遠程分支上:
$ git push origin master
如果要推送本地dev分支到遠程dev分支,就改成:
$ git push origin dev:dev
抓取分支:
從遠程庫clone時,默認情況下,只能看到本地的master分支。可以用git branch命令查看:
$ git branch
* master
git fetch:相當于是從遠程獲取最新版本到本地,不會自動merge。
從遠程的origin的master分支到origin/master分支上:
Git fetch origin master
從遠程獲取origin的remotebranch分支到本地,本地會創建一個remotebranch的copy:
$git checkout localbranch
$git fetch origin remotebranch
$git branch
master
*localbranch
remotebranch
從遠程獲取master到本地的tmp分支上(不會自動切換到該分支),之后再進行比較合并:
git fetch origin master:tmp
git diff tmp
git merge tmp
git pull:相當于是從遠程獲取最新版本并merge到本地,其實相當于git fetch 和 git merge。
從遠程的origin的master主分支下載最新的版本,并與當前分支進行合并:
git pull origin master
在實際使用中,git fetch更安全一些。因為在merge前,我們可以查看更新情況,然后再決定是否合并。
跟蹤遠程分支:從遠程分支 checkout 出來的本地分支,稱為跟蹤分支 (tracking branch)。跟蹤分支是一種和某個遠程分支有直接聯系的本地分支。在跟蹤分支里輸入 git push/ git pull,Git 會自行推斷應該向哪個服務器的哪個分支推送/獲取數據。在克隆倉庫時,Git 通常會自動創建一個名為 master 的分支來跟蹤 origin/master。這正是 git push 和 git pull 一開始就能正常工作的原因。
$ git checkout -b dev origin/dev
或關聯本地已存在的dev分支:
$ git branch --set-upstream dev origin/dev
多人協作的工作模式通常是這樣:
首先,可以試圖用git push origin branch-name
推送自己的修改;
如果推送失敗,則因為遠程分支比你的本地更新,需要先用git pull試圖合并;
如果合并有沖突,則解決沖突,并在本地提交;
沒有沖突或者解決掉沖突后,再用git push origin branch-name
推送就能成功!
如果git pull
提示“no tracking information”,則說明本地分支和遠程分支的鏈接關系沒有創建,用命令git branch --set-upstream branch-name origin/branch-name
。
git倉庫:
安裝git后,創建一個git用戶組和用戶,用來運行git服務:
$ groupadd git
$ adduser git -g git
創建證書登錄
收集所有需要登錄的用戶的公鑰,客戶端的公鑰在客戶端的id_rsa.pub文件中,把我們的公鑰導入到剛創建的用戶git下的.ssh目錄(沒有.ssh目錄則創建)。/home/git/.ssh/authorized_keys文件里,一行一個。
如果沒有該文件創建它:
$ cd /home/git/
$ mkdir .ssh
$ chmod 700 .ssh
$ touch .ssh/authorized_keys
$ chmod 600 .ssh/authorized_keys
初始化Git倉庫
首先我們選定一個目錄作為Git倉庫,假定是/home/gitrepo/w3cschoolcc.git,在/home/gitrepo目錄下輸入命令:
$ cd /home
$ mkdir gitrepo
$ chown -R git:git gitrepo/
$ cd gitrepo
//創建一個裸倉庫,裸倉庫沒有工作區,因為服務器上的Git倉庫純粹是為了共享,所以不讓用戶直接登錄到服務器上去改工作區,并且服務器上的Git倉庫通常都以.git結尾
$ git init --bare w3cschoolcc.git
Initialized empty Git repository in /home/gitrepo/w3cschoolcc.git/
以上命令Git創建一個空倉庫,服務器上的Git倉庫通常都以.git結尾。然后,把倉庫所屬用戶改為git:
$ chown -R git:git w3cschoolcc.git
在其他機器上就可以克隆倉庫
$ git clone git@192.168.45.4:/home/gitrepo/w3cschoolcc.git
Cloning into 'w3cschoolcc'...
warning: You appear to have cloned an empty repository.
Checking connectivity... done.
在IntelliJ IDEA中使用倉庫
設置git.exe路徑:
重啟idea,在vcs下可以看到git菜單項。
把一個本地項目提交到剛剛創建的git倉庫:
- vcs->Import into Version Control->Create Git Repository選中目錄,把它變成git目錄。
- vcs->git->remotes關聯遠程庫,URL為倉庫地址git@192.168.45.4:/home/gitrepo/w3cschoolcc.git。
- vcs->git->push即可推送。
- vcs->git->branch -> +New Branch,輸入分支名稱,自動創建并切換到新的分支。
- vcs->git->branch->分支名->Checkout切換分支。
從倉庫獲取項目:
- vcs->Check Out from Version Control->Git。
-
填寫倉庫地址和本地目錄名字。
參考:
https://git-scm.com/book/zh/v1/
https://marklodato.github.io/visual-git-guide/index-zh-cn.html
https://segmentfault.com/q/1010000000156026