Git 與 SVN 差別
Git是一個開源的分布式版本控制系統,用于靈活高效地處理任何或小或大的項目。Git 是 Linus Torvalds 為了幫助管理 Linux 核心開發而開發的一個開放源碼的版本控制軟體。Git 與常用的版本控制工具 CVS, Subversion 等不同,它采用了分布式版本庫的方式,不必伺服器端軟體支援。
GIT不僅僅是個版本控制系統,它也是個内容管理系統(CMS),工作管理系統等。如果你是一個具有使用SVN背景的人,你需要做一定的思想轉換,來适應GIT提供的一些概念和特征。Git 與 SVN 差別點:
1、GIT是分布式的,SVN不是:這是GIT和其它非分布式的版本控制系統,例如SVN,CVS等,最核心的差別。
2、GIT把内容按中繼資料方式存儲,而SVN是按檔案:所有的資源控制系統都是把檔案的元資訊隐藏在一個類似.svn,.cvs等的檔案夾裡。
3、GIT分支和SVN的分支不同:分支在SVN中一點不特别,就是版本庫中的另外的一個目錄。
4、GIT沒有一個全局的版本号,而SVN有:目前為止這是跟SVN相比GIT缺少的最大的一個特征。
5、GIT的内容完整性要優于SVN:GIT的内容存儲使用的是SHA-1雜湊演算法。這能確定代碼内容的完整性,確定在遇到磁盤故障和網絡問題時降低對版本庫的破壞。
Git工作原理
Git工作流程可以分一下步驟:
- 克隆 Git 資源作為工作目錄;
- 在克隆的資源上添加或修改檔案;
- 如果其他人修改了,你可以更新資源;
- 在送出前檢視修改。送出修改;
- 在修改完成後,如果發現錯誤,可以撤回送出并再次修改并送出;
Git 工作區、暫存區和版本庫:
- 工作區:就是你在電腦裡能看到的目錄。
- 暫存區:英文叫stage, 或index。一般存放在"git目錄"下的index檔案(.git/index)中,是以我們把暫存區有時也叫作索引(index)。
- 版本庫:工作區有一個隐藏目錄.git,這個不算工作區,而是Git的版本庫。
常見操作
全局配置使用者資訊
git config --global user.name "smyhvae"
git config --global user.email "[email protected]"
Git 建立倉庫
git init repository
如果目前目錄下有幾個檔案想要納入版本控制,需要先用 git add 指令告訴 Git 開始對這些檔案進行跟蹤,然後送出:
$ git add *.c
$ git add README
$ git commit -m 'initial project version'
從現有倉庫克隆
$ git clone git://github.com/test/test.git
Git基本操作指令
git init
建立項目指令,在目錄中建立新的 Git 倉庫。 你可以在任何時候、任何目錄中這麼做,完全是本地化的。在目錄中執行 git init,就可以建立一個 Git 倉庫。
$ mkdir test
$ cd test
$ git init
git clone
使用 git clone 拷貝一個 Git 倉庫到本地,讓自己能夠檢視該項目,或者進行修改。
$ git clone git://github.com/test/test.git
git push
其作用是将本地分支的更新推送到遠端主機
$ git push <遠端主機名> <本地分支名>:<遠端分支名>
git pull
其作用是将遠端主機更新到本地分支
$ git pull <遠端主機名> <本地分支名>:<遠端分支名>
git add
git add 指令可将該檔案添加到緩存,如我們添加以下兩個檔案:
$ touch README
$ touch hello.php
$ ls
README hello.php
$ git status -s
?? README
?? hello.php
$
git status
指令用于檢視項目的目前狀态
$ git add README hello.php
$ git status -s
A README
A hello.php
$
git diff
git diff 指令顯示已寫入緩存與已修改但尚未寫入緩存的改動的差別。git diff 有兩個主要的應用場景:
尚未緩存的改動:git diff
檢視已緩存的改動: git diff --cached
檢視已緩存的與未緩存的所有改動:git diff HEAD
顯示摘要而非整個 diff:git diff --stat
$ git status -s
A README
AM hello.php
$ git diff
diff --git a/hello.php b/hello.php
git commit
使用 git add 指令将想要快照的内容寫入了緩存, 而執行 git commit 記錄緩存區的快照。Git 為你的每一個送出都記錄你的名字與電子郵箱位址,是以第一步需要配置使用者名和郵箱位址。
$ git config --global user.name 'admin'
$ git config --global user.email [email protected]
$ git add hello.php
$ git status -s
A README
A hello.php
$ git commit -m 'test comment from test.cn'
[master (root-commit) 85fc7e7] test comment from test.cn
2 files changed, 4 insertions(+)
create mode 100644 README
create mode 100644 hello.php
現在我們已經記錄了快照。如果我們再執行 git status:
$ git status
# On branch master
nothing to commit (working directory clean)
git log
檢視曆史送出記錄
$ git log
commit 88afe0e02adcdfea6844bb627de97da21eb10af1
Merge: 14b4dca d7e7346
Author: admin
Date: Sun Mar 1 15:03:42 2020 +0800
Merge branch 'change_site'
Conflicts:
test.txt
commit 14b4dcadbdc847207651d5a9fae0d315057f346e
Author: admin
Date: Sun Mar 1 14:53:15 2015 +0800
git tag
可以使用 git tag 給打上标簽
git reset HEAD
指令用于取消緩存已緩存的内容。
$ git status -s
M README
M hello.php
$ git add .
$ git status -s
M README
M hello.pp
$ git reset HEAD -- hello.php
Unstaged changes after reset:
M hello.php
$ git status -s
M README
M hello.php
git rm
将檔案從緩存區中移除。
$ git rm hello.php
rm 'hello.php'
$ ls
README
git mv
指令做得所有事情就是 git rm --cached, 重命名磁盤上的檔案,然後再執行 git add 把新檔案添加到緩存區。是以,雖然有 git mv 指令,但它有點多餘 。
分支的合并
場景:基于master分支的代碼,開發一個新的特性
如果你直接在master分支上開發這個新特性,是不好的,萬一你在開發
特性1
的時候,上司突然又要叫你去開發
特性2
,就不好處理了。難道開發的兩個特性都送出到master?一會兒送出特性1的commit,一會兒送出特性2的commit?這會導緻commit記錄很混亂。
是以,我給你的建議做法是:給每個特性都單獨建一個的新的分支。
比如說,我專門給
特性1
建一個分支
feature_item_recommend
。具體做法如下:
(1)基于master分支,建立一個新的分支,起名為
feature_item_recommend
:
$ git checkout -b feature_item_recommend
Switched to a new branch 'feature_item_recommend'
上面這行指令,相當于:
$ git branch feature_item_recommend // 建立新的分支
$ git checkout feature_item_recommend //切換到新的分支
(2)在新的分支
feature_item_recommend
上,完成開發工作,并 commit 、push。
(3)将分支
feature_item_recommend
上的開發進度合并到master分支:
$ git checkout master //切換到master分支
$ git merge feature_item_recommend //将分支 feature_item_recommend 的開發進度合并到 master 分支
合并之後,
master
分支和
feature_item_recommend
分支會指向同一個位置。
(3)删除分支
feature_item_recommend
既然 特性1 開發完了,也放心地送出到master了,那我們就可以将這個分支删除了。
git branch -d feature_item_recommend
注意,我們目前是處于
master
分支的位置,來删除
feature_item_recommend
分支。如果目前是處于
feature_item_recommend
分支,是沒辦法删除它自己的。
同理,當我轉身去開發
特性2
的時候,也是采用同樣的步驟。
合并分支時,如果存在分叉
比如說上面這張圖中,最早的時候,master分支是位于
C2
節點。我基于
C2
節點,new出一個新的分支
iss53
,我在
iss53
上送出了好幾個commit。
現在,我準備把
iss53
上的幾個commit合并到master上,此時發現,master分支已經前進到C4了。那該怎麼合并呢?
合并的指令仍然是:
$ git checkout master
$ git merge iss53
解釋:
這次合并的實作,并不同于簡單的并入方式。這一次,我的開發曆史是從更早的地方開始分叉的。
由于目前 master 分支所指向的commit (C4)并非想要并入分支(iss53)的直接祖先,Git 不得不進行一些處理。就此例而言,Git 會用兩個分支的末端(C4 和C5)和它們的共同祖先(C2)進行一次簡單的三方合并計算。
Git 沒有簡單地把分支指針右移,而是對三方合并的結果作一新的快照,并自動建立一個指向它的commit(C6)(如下圖所示)。我們把這個特殊的commit 稱作合并送出(mergecommit),因為它的祖先不止一個。
值得一提的是Git 可以自己裁決哪個共同祖先才是最佳合并基礎;這和CVS 或Subversion(1.5 以後的版本)不同,它們需要開發者手工指定合并基礎。是以此特性讓Git 的合并操作比其他系統都要簡單不少。
解決合并時發生的沖突
如果 feature1和feature2修改的是同一個檔案中代碼的同一個位置,那麼,把feature1合并到feature2時,就會産生沖突。這個沖突需要人工解決。步驟如下:
(1)手動修改檔案:手動修改沖突的那個檔案,決定到底要用哪個分支的代碼。
(2)git add:解決好沖突後,輸入
git status
,會提示
Unmerged paths
。這個時候,輸入
git add
即可,表示:修改沖突成功,加入暫存區。
(3)git commit 送出。
然後,我們可以繼續把 feature1 分支合并到 master分支,最後删除feature1、feature2。
注意:兩個分支的同一個檔案的不同地方合并時,git會自動合并,不會産生沖突。
比如分支feture1對index.html原來的第二行之前加入了一段代碼。
分支feature2對index.html在原來的最後一行的後面加入了一段代碼。
這個時候在對兩個分支合并,git不會産生沖突,因為兩個分支是修改同一檔案的不同位置。
git自動合并成功。不管是git自動合并成功,還是在人工解決沖突下合并成功,送出之前,都要對代碼進行測試。
日常操作積累
修改密碼(曲線救國)
網上查了很久,沒找到答案。最終,在cld童鞋的提示下,采取如下方式進行曲線救國。
# 設定目前倉庫的使用者名為空
git config user.name ""
然後,當我們再輸入
git pull
等指令行時,就會被要求重新輸入新的賬号密碼。此時,密碼就可以修改成功了。最後,我們還要輸入如下指令,還原目前倉庫的使用者名:
git config user.name "smyhvae"
修改已經push的某次commit的作者資訊
已經push的記錄,如果要修改作者資訊的話,隻能 通過--force指令。
将 branch1
的某個 commit1
合并到 branch2
當中
branch1
commit1
branch2
切換到branch2中,然後執行如下指令:
git cherry-pick commit1
20200118-修改GitHub已送出的使用者名和郵箱
在執行
./email.sh
後,如果出現
permission denied
的錯誤,可以先執行
chmod 777 email.sh
,修改檔案的權限。
20200520-将Git 項目遷移到另一個倉庫
我們假設舊倉庫的項目名稱叫
old-repository
,新倉庫的項目名稱叫
new-repository
。操作如下:
(1)建立舊倉庫的裸克隆:
git clone --bare https://github.com/exampleuser/old-repository.git
執行上述指令後,會在本地生成一個名叫
old-repository.git
的檔案夾。
(2)遷移到新倉庫:
cd old-repository.git
git push --mirror https://github.com/exampleuser/new-repository.git
這樣的話,項目就已經遷移到新倉庫了。
注意,我們不需要手動建立一個空的新倉庫,當我們執行上述指令之後,新倉庫就已經自動建立好了。
參考連結:
- 複制倉庫
- Git 本地倉庫和裸倉庫
Git 伺服器搭建
我們将以 Centos 為例搭建 Git 伺服器。
1、安裝Git
$ yum install curl-devel expat-devel gettext-devel openssl-devel zlib-devel perl-devel
$ yum install git
建立一個git使用者組和使用者,用來運作git服務:
$ groupadd git
$ adduser git -g git
2、建立證書登入
收集所有需要登入的使用者的公鑰,公鑰位于id_rsa.pub檔案中,把我們的公鑰導入到/home/git/.ssh/authorized_keys檔案裡,一行一個。如果沒有該檔案建立它:
$ cd /home/git/
$ mkdir .ssh
$ chmod 700 .ssh
$ touch .ssh/authorized_keys
$ chmod 600 .ssh/authorized_keys
3、初始化Git倉庫
首先我們標明一個目錄作為Git倉庫,假定是/home/gitrepo/hello.git,在/home/gitrepo目錄下輸入指令:
$ cd /home
$ mkdir gitrepo
$ chown git:git gitrepo/
$ cd gitrepo
$ git init --bare hello.git
Initialized empty Git repository in /home/gitrepo/hello.git/
以上指令Git建立一個空倉庫,伺服器上的Git倉庫通常都以.git結尾。然後,把倉庫所屬使用者改為git:
$ chown -R git:git w3cschoolcn.git
4、克隆倉庫
$ git clone [email protected]:/home/gitrepo/w3cschoolcn.git
Cloning into 'w3cschoolcn'...
warning: You appear to have cloned an empty repository.
Checking connectivity... done.
192.168.45.4 為 Git 所在伺服器 ip ,你需要将其修改為你自己的 Git 服務 ip。這樣我們的 Git 伺服器安裝就完成了,接下來我們可以禁用 git 使用者通過shell登入,可以通過編輯/etc/passwd檔案完成。找到類似下面的一行:
git:x:503:503::/home/git:/bin/bash
改為:git:x:503:503::/home/git:/sbin/nologin
Git用戶端推薦
TortoiseGit
對這隻小烏龜估計沒有開發人員會不認識,SVN的超廣泛使用也使得這個超好用的Svn用戶端成了幾乎每個開發人員的桌面必備軟體。小烏龜隻提供Windows版本,提供中文版支援的,對于中國的開發者來說者絕對是福音。
小烏龜的檔案管理器右鍵菜單的操作方式對于新手來說非常的容易上手,而且容易了解。
TortoiseGit(git用戶端工具)
Sublime Merge for Mac(git用戶端工具)
擁有簡潔的界面,三向合并工具,強大搜尋功能,文法突出顯示等特點,Sublime Merge for Mac是一款Git用戶端工具,完成了Sublime。與Sublime Text的制造商會面。一個快速的使用者界面,三向合并工具,并排差異,文法高亮等等。Stage Files,iSwiftHunks和Lines沒有等待 – Sublime Merge真的非常非常快。
GitHub for Desktop
全球開發人員交友俱樂部提供的強大工具,功能完善,使用友善。對于使用GitHub的開發人員來說是非常便捷的工具。界面幹淨,用起來非常順手,上面的這條timeline非常漂亮,也可以直接送出PR。
唯一讓我失望的是GitHub for Desktop不帶三方合并工具,你必須自己手動解決沖突才可以。– 免費
– 同時支援 Windows 和 Mac:對于需要經常在不同的作業系統間切換的開發人員來說非常友善。
– 漂亮的界面:作為每天盯着看的工具,顔值是非常重要的
– 支援Pull Request:直接從用戶端送出PR,很友善
– Timeline 支援:直接在時間線上顯示每次送出的時間點和大小
– 支援git LFS:存儲大檔案更加節省空間和高效
– 不支援三方合并:需要借助第三方工具才行
Source Tree
SourceTree是老牌的Git GUI管理工具了,也号稱是最好用的Git GUI工具。我的體驗是确實強大,功能豐富,基本操作和進階操作都設計得非常流暢,适合初學者上手。
這個工具很有特色的一個功能就是支援Git Flow,你可以一鍵建立Git Flow的工作流。Git Flow是非常高效的團隊協作模型和流程,Git的一大特色就是靈活輕量的分支,但如何在自己的團隊中用好這個功能來比對自己的研發流程是個問題。内置Git Flow讓那些不太熟悉的開發人員也可以很快上手,并且将研發的業務流程固化在工具中,可以說是非常貼心的設計。
推薦書籍
- 《pro.git中文版》