Git-由淺入深-小白教程
Git簡介
Git是目前世界上最先進的分佈式版本控制系統(沒有之一)。
基本操作
- 創建空目錄
$ mkdir learngit //創建空目錄,儘量選擇無中文路徑創建
$ cd learngit //選擇目錄
$ pwd //顯示目錄路徑
/Users/michael/learngit
- 將目錄變爲Git倉庫
$ git init //初始化目錄爲Git管理倉庫
Initialized empty Git repository in /Users/michael/learngit/.git/
初始化後會多了一個.git的目錄,這個目錄是Git來跟蹤管理版本庫的,沒事千萬不要手動修改這個目錄裏面的文件,不然改亂了,就把Git倉庫給破壞了。
- 添加
$ git add readme.txt //把文件添加到倉庫,可添加多個文件,或同時添加多個文件,空格隔開
- 提交
$ git commit -m "wrote a readme file" //把之前add的文件都提交到倉庫,-m 後面是說明
[master (root-commit) eaadf4e] wrote a readme file
1 file changed, 2 insertions(+) //1 file changed:1個文件被改動;2 insertions:插入了兩行內容
create mode 100644 readme.txt
- 提交日誌
$ git log //添加 --pretty=oneline 參數查看簡潔版
commit 1094adb7b9b3807259d8cb349e7df1d4d6477073 (HEAD -> master) //commit id(版本號),HEAD :當前版本,
Author: Michael Liao <[email protected]>
Date: Fri May 18 21:06:15 2018 +0800
append GPL //說明
commit e475afc93c209a690c39c13a46716e8fa000c366
Author: Michael Liao <[email protected]>
Date: Fri May 18 21:03:36 2018 +0800
add distributed
commit eaadf4e385e865d25c48e7ca9c8395c3f7dfaef0
Author: Michael Liao <[email protected]>
Date: Fri May 18 20:59:18 2018 +0800
wrote a readme file
- 版本回退
$ git reset --hard HEAD^ //HEAD^:上一個版本,HEAD^^:上上一個版本,HEAD~100:往上一百個版本
HEAD is now at e475afc add distributed
- 版本還原回退
$ git reset --hard 1094a //還原版本版本號部分
HEAD is now at 83b0afe append GPL
- 操作日誌
$ git reflog //記錄所有操作及輸出
e475afc HEAD@{1}: reset: moving to HEAD^
1094adb (HEAD -> master) HEAD@{2}: commit: append GPL
e475afc HEAD@{3}: commit: add distributed
eaadf4e HEAD@{4}: commit (initial): wrote a readme file
- 查看當前狀態
$ 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.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
LICENSE
no changes added to commit (use "git add" and/or "git commit -a")
- 修改撤銷
1.
$ git checkout -- readme.txt //就是讓這個文件回到最近一次git commit或git add前的狀態。
2.
$ git reset HEAD readme.txt //git reset命令既可以回退版本,也可以把暫存區的修改回退到工作區。當我們用HEAD時,表示最新的版本。
Unstaged changes after reset:
M readme.txt
- 刪除和還原
$ rm test.txt //刪除文件
$ git rm test.txt //從版本庫中刪除該文件,那就用命令git rm刪掉,並且git commit
rm 'test.txt'
$ git commit -m "remove test.txt"
[master d46f35e] remove test.txt
1 file changed, 1 deletion(-)
delete mode 100644 test.txt
$ git checkout -- test.txt //git checkout其實是用版本庫裏的版本替換工作區的版本,無論工作區是修改還是刪除,都可以“一鍵還原”。
遠程倉庫
- 創建SSH Key
$ ssh-keygen -t rsa -C "[email protected]" //打開Shell(Windows下打開Git Bash),創建SSH Key,你需要把郵件地址換成你自己的郵件地址,然後一路回車
如果一切順利的話,可以在用戶主目錄裏找到.ssh目錄,裏面有id_rsa和id_rsa.pub兩個文件,這兩個就是SSH
Key的祕鑰對,id_rsa是私鑰,不能泄露出去,id_rsa.pub是公鑰,可以放心地告訴任何人。
- 在GitHub上添加自己電腦上的公鑰
登錄GitHub,打開設置,找到 SSH and GPG Keys,點擊 New SSH Key
Title隨意填寫,在key中粘貼公鑰,最後點擊Add Key,就ok了
- 遠程庫
- 關聯遠程庫
$ git remote add origin [email protected]:michaelliao/learngit.git //關聯遠程庫,遠程庫的名字就是origin,這是Git默認的叫法
把本地庫的內容推送到遠程,用git push命令,實際上是把當前分支master推送到遠程。
由於遠程庫是空的,我們第一次推送master分支時,加上了-u參數,Git不但會把本地的master分支內容推送的遠程新的master分支,還會把本地的master分支和遠程的master分支關聯起來,在以後的推送或者拉取時就可以簡化命令。
- 推送同步遠程庫
$ 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'.
由於遠程庫是空的,我們第一次推送master分支時,加上了-u參數,Git不但會把本地的master分支內容推送的遠程新的master分支,還會把本地的master分支和遠程的master分支關聯起來,在以後的推送或者拉取時就可以簡化命令。
- 提交遠程庫
$ git push origin master //把本地master分支的最新修改推送至GitHub
- 克隆遠程庫
$ git clone [email protected]: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.
創建遠程庫時勾選Initialize this repository with a README,這樣GitHub會自動爲我們創建一個README.md文件。創建完畢後,可以看到README.md文件
分支管理
分支相當於不同的時間線,互不影響
創建與合併分支
-
查看分支:
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 merge feature1 //合併產生衝突
Auto-merging readme.txt
CONFLICT (content): Merge conflict in readme.txt
Automatic merge failed; fix conflicts and then commit the result.
$ git cat //查看內容可看到衝突部分
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 log --graph --pretty=oneline --abbrev-commit
用git log --graph命令可以看到分支合併圖。
分支管理策略
- 合併分支(
Fast forward):$ git merge --no-ff -m "merge with no-ff" dev
強制禁用Fast forward模式,Git就會在merge時生成一個新的commit,保留原分支信息這樣,從分支歷史上就可以看出分支信息
- 分支策略
在實際開發中,我們應該按照幾個基本原則進行分支管理:
首先,master分支應該是非常穩定的,也就是僅用來發布新版本,平時不能在上面幹活;
那在哪幹活呢?幹活都在dev分支上,也就是說,dev分支是不穩定的,到某個時候,比如1.0版本發佈時,再把dev分支合併到master上,在master分支發佈1.0版本;
你和你的小夥伴們每個人都在dev分支上幹活,每個人都有自己的分支,時不時地往dev分支上合併就可以了。
所以,團隊合作的分支看起來就像這樣:
Bug分支
軟件開發中,bug就像家常便飯一樣。有了bug就需要修復,在Git中,由於分支是如此的強大,所以,每個bug都可以通過一個新的臨時分支來修復,修復後,合併分支,然後將臨時分支刪除。
當你接到一個修復一個代號101的bug的任務時,很自然地,你想創建一個分支issue-101來修復它,但是,等等,當前正在dev上進行的工作還沒有提交,如果此時切換分支,最後提交的時候再暫存區的修改將一併被提交。
- 保存分支狀態:
$ git stash
- 查看保存分支狀態列表:
$ git stash list
- 恢復分支狀態:
$ git stash apply
- 刪除分支狀態:
$ git stash drop
- 恢復並刪除分支狀態:
$ git stash pop
- 恢復指定狀態:
$ git stash apply stash@{0}
- 複製其他分支提交:
$ git cherry-pick 4c805e2
先複製提交 再 恢復
Feature分支
- 強制刪除未合併分支:
$ git branch -D feature-vulcan
多人協作
當你從遠程倉庫克隆時,實際上Git自動把本地的master分支和遠程的master分支對應起來了,並且,遠程倉庫的默認名稱是origin
- 查看遠程庫信息:
$ git remote
- 查看詳細遠程庫信息:
$ git remote -v
上面顯示了可以抓取和推送的origin的地址。如果沒有推送權限,就看不到push的地址。
- 推送對應分支到遠程庫:
$ git push origin master
但是,並不是一定要把本地分支往遠程推送,那麼,哪些分支需要推送,哪些不需要呢?
- master分支是主分支,因此要時刻與遠程同步;
- dev分支是開發分支,團隊所有成員都需要在上面工作,所以也需要與遠程同步;
- bug分支只用於在本地修復bug,就沒必要推到遠程了,除非老闆要看看你每週到底修復了幾個bug;
- feature分支是否推到遠程,取決於你是否和你的小夥伴合作在上面開發。
總之,就是在Git中,分支完全可以在本地自己藏着玩,是否推送,視你的心情而定!
- 創建遠程分支到本地:
$ git checkout -b dev origin/dev
當你的小夥伴從遠程庫clone時,默認情況下,你的小夥伴只能看到本地的master分支,需要別的分支還要遠程創建。
- 連接分支:
$ git branch --set-upstream-to=origin/dev dev
- 抓取分支:
$ git pull
相當於遠程分支與當前分支合併,抓取後能通過
$ git cat
查看修改修改內容
抓取分支前必須先連接遠程分支,才能識別要抓取的分支。
Rebase
分支太多容易出現混亂現象
$ git log --graph --pretty=oneline --abbrev-commit
* d1be385 (HEAD -> master, origin/master) init hello
* e5e69f1 Merge branch 'dev'
|\
| * 57c53ab (origin/dev, dev) fix env conflict
| |\
| | * 7a5e5dd add env
| * | 7bd91f1 add new env
| |/
* | 12a631b merged bug fix 101
|\ \
| * | 4c805e2 fix bug 101
|/ /
* | e1e9c68 merge with no-ff
|\ \
| |/
| * f52c633 add merge
|/
* cf810e4 conflict fixed
Git有一種稱爲rebase的操作,有人把它翻譯成“變基”。
看不懂不寫了!
標籤管理
在Git中打標籤非常簡單,標籤可以是版本例如 V1.0,類似每次提交的 -m"sdsfdsf" 提交說明,因爲標籤也是基於提交的。
創建標籤
如果是分支上打標籤,默認標籤是打在最新提交的commit上的,也可以單獨給某個版本打標籤,找到commit id 就行
- 分支上打標籤:
$ git tag v1.0
- 版本上打標籤:
$ git tag v0.9 f52c633
- 查看標籤:
$ git tag
- 查看標籤信息:
$ git show v0.9
- 創建標籤並附上說明:
$ git tag -a v0.1 -m "version 0.1 released" 1094adb
注意:標籤總是和某個commit掛鉤。如果這個commit既出現在master分支,又出現在dev分支,那麼在這兩個分支上都可以看到這個標籤。
操作標籤
- 刪除標籤:
$ git tag -d v0.1
因爲創建的標籤都只存儲在本地,不會自動推送到遠程。所以,打錯的標籤可以在本地安全刪除。
- 推送標籤到遠程:
$ git push origin v1.0
- 一次性推送全部:
$ git push origin --tags
- 遠程刪除標籤:
$ git push origin :refs/tags/v0.9
使用GitHub
我們一直用GitHub作爲免費的遠程倉庫,如果是個人的開源項目,放到GitHub上是完全沒有問題的。其實GitHub還是一個開源協作社區,通過GitHub,既可以讓別人參與你的開源項目,也可以參與別人的開源項目。
如何參與一個開源項目呢?比如人氣極高的bootstrap項目,這是一個非常強大的CSS框架,你可以訪問它的項目主頁https://github.com/twbs/bootstrap,點“Fork”就在自己的賬號下克隆了一個bootstrap倉庫,然後,從自己的賬號下clone:
git clone [email protected]:michaelliao/bootstrap.git
一定要從自己的賬號下clone倉庫,這樣你才能推送修改。如果從bootstrap的作者的倉庫地址[email protected]:twbs/bootstrap.git克隆,因爲沒有權限,你將不能推送修改。
Bootstrap的官方倉庫twbs/bootstrap、你在GitHub上克隆的倉庫my/bootstrap,以及你自己克隆到本地電腦的倉庫,他們的關係就像下圖顯示的那樣:
┌─ GitHub ────────────────────────────────────┐
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ twbs/bootstrap │────>│ my/bootstrap │ │
│ └─────────────────┘ └─────────────────┘ │
│ ▲ │
└──────────────────────────────────┼──────────┘
▼
┌─────────────────┐
│ local/bootstrap │
└─────────────────┘
如果你想修復bootstrap的一個bug,或者新增一個功能,立刻就可以開始幹活,幹完後,往自己的倉庫推送。
如果你希望bootstrap的官方庫能接受你的修改,你就可以在GitHub上發起一個pull request。當然,對方是否接受你的pull request就不一定了。
使用碼雲
- 碼雲
使用GitHub時,國內的用戶經常遇到的問題是訪問速度太慢,有時候還會出現無法連接的情況(原因你懂的)。
如果我們希望體驗Git飛一般的速度,可以使用國內的Git託管服務——碼雲(gitee.com)。
和GitHub相比,碼雲也提供免費的Git倉庫。此外,還集成了代碼質量檢測、項目演示等功能。對於團隊協作開發,碼雲還提供了項目管理、代碼託管、文檔管理的服務,5人以下小團隊免費。
- 使用碼雲
使用碼雲和使用GitHub類似,我們在碼雲上註冊賬號並登錄後,需要先上傳自己的SSH公鑰。選擇右上角用戶頭像 -> 菜單“修改資料”,然後選擇“SSH公鑰”,填寫一個便於識別的標題,然後把用戶主目錄下的.ssh/id_rsa.pub文件的內容粘貼進去
- 關聯碼雲遠程庫:
git remote add origin [email protected]:liaoxuefeng/learngit.git
如果在使用命令git remote add時報錯,這說明本地庫已經關聯了一個名叫origin的遠程庫,此時,可以先用git remote -v查看遠程庫信息
- 刪除已有關聯:
git remote rm origin
、
我們的本地庫就可以同時與多個遠程庫互相同步:
┌─────────┐ ┌─────────┐
│ GitHub │ │ Gitee │
└─────────┘ └─────────┘
▲ ▲
└─────┬─────┘
│
┌─────────────┐
│ Local Repo │
└─────────────┘
自定義Git
在安裝Git一節中,我們已經配置了user.name和user.email,實際上,Git還有很多可配置項。
比如,讓Git顯示顏色,會讓命令輸出看起來更醒目:$ git config --global color.ui true
忽略特殊文件
有些時候,你必須把某些文件放到Git工作目錄中,但又不能提交它們,比如保存了數據庫密碼的配置文件啦,等等,每次git status都會顯示Untracked files(未跟蹤文件:未add的文件) …,有強迫症的童鞋心裏肯定不爽
- 忽略特殊文件方法
在Git工作區的根目錄下創建一個特殊的.gitignore文件,然後把要忽略的文件名填進去,Git就會自動忽略這些文件。
不需要從頭寫.gitignore文件,GitHub已經爲我們準備了各種配置文件,只需要組合一下就可以使用了。所有配置文件可以直接在線瀏覽:配置文件大全
- 忽略文件的原則是
- 忽略操作系統自動生成的文件,比如縮略圖等;
- 忽略編譯生成的中間文件、可執行文件等,也就是如果一個文件是通過另一個文件自動生成的,那自動生成的文件就沒必要放進版本庫,比如Java編譯產生的.class文件;
- 忽略你自己的帶有敏感信息的配置文件,比如存放口令的配置文件。
- 配置格式
Thumbs.db //普通文件
*.py[cod] //*.pyc、*.pyo、*.pyd
*.so
*.egg
*.egg-info
dist //文件或目錄
build
- 提交配置文件
最後一步就是把.gitignore也提交到Git,就完成了!當然檢驗.gitignore的標準是git status命令是不是說working directory clean。
- 強制提交
有些時候,你想添加一個文件到Git,但發現添加不了,原因是這個文件被.gitignore忽略了,
如果你確實想添加該文件,可以用-f強制添加到Git:$ git add -f App.class
- 配置檢查
或者你發現,可能是.gitignore寫得有問題,需要找出來到底哪個規則寫錯了,可以用
$ git check-ignore
命令檢查:$ git check-ignore -v App.class
Git會告訴我們,.gitignore的第3行規則忽略了該文件,於是我們就可以知道應該修訂哪個規則。
配置別名
- 常用別名
//常用操作
$ git config --global alias.co checkout
$ git config --global alias.ci commit
$ git config --global alias.br branch
//神奇操作
//撤銷
$ git config --global alias.unstage 'reset HEAD'
$ git reset HEAD test.py //原版
$ git unstage test.py //簡化
//顯示最近一次提交
$ git config --global alias.last 'log -1'
$ git last //簡化
//喪心病狂操作
//顯示日誌(美化版)
$ git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
$ git lg //簡化
- 當前倉庫配置文件
配置Git的時候,加上–global是針對當前用戶起作用的,如果不加,那隻針對當前的倉庫起作用。
配置文件放哪了?每個倉庫的Git配置文件都放在.git/config文件中:
$ cat .git/config
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
[remote "origin"]
url = [email protected]:michaelliao/learngit.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master
[alias]
last = log -1
別名就在[alias]後面,要刪除別名,直接把對應的行刪掉即可。
- 當前用戶配置文件
而當前用戶的Git配置文件放在用戶主目錄下的一個隱藏文件.gitconfig中:
$ cat .gitconfig
[alias]
co = checkout
ci = commit
br = branch
st = status
[user]
name = Your Name
email = [email protected]
搭建Git服務器
遠程倉庫實際上和本地倉庫沒啥不同,純粹爲了7x24小時開機並交換大家的修改。
GitHub就是一個免費託管開源代碼的遠程倉庫
搭建Git服務器需要準備一臺運行Linux的機器,強烈推薦用Ubuntu或Debian,這樣,通過幾條簡單的apt命令就可以完成安裝。
- 安裝git:
$ sudo apt-get install git
- 創建git用戶,用來運行git服務:
$ sudo adduser git
- 創建證書登錄
收集所有需要登錄的用戶的公鑰,就是他們自己的id_rsa.pub文件,把所有公鑰導入到/home/git/.ssh/authorized_keys文件裏,一行一個。
- 初始化Git倉庫
先選定一個目錄作爲Git倉庫,假定是/srv/sample.git,在/srv目錄下輸入命令:
$ sudo git init --bare sample.git
Git就會創建一個裸倉庫,裸倉庫沒有工作區,因爲服務器上的Git倉庫純粹是爲了共享,所以不讓用戶直接登錄到服務器上去改工作區,並且服務器上的Git倉庫通常都以.git結尾。然後,把owner改爲git:
$ sudo chown -R git:git sample.git
- 禁用shell登錄
出於安全考慮,第二步創建的git用戶不允許登錄shell,這可以通過編輯/etc/passwd文件完成。找到類似下面的一行:
git:x:1001:1001:,,,:/home/git:/bin/bash
改爲:
git:x:1001:1001:,,,:/home/git:/usr/bin/git-shell
這樣,git用戶可以正常通過ssh使用git,但無法登錄shell,因爲我們爲git用戶指定的git-shell每次一登錄就自動退出。
- 克隆遠程倉庫
現在,可以通過git clone命令克隆遠程倉庫了,在各自的電腦上運行:
$ git clone git@server:/srv/sample.git
Cloning into 'sample'...
warning: You appear to have cloned an empty repository.
管理公鑰
如果團隊很小,把每個人的公鑰收集起來放到服務器的/home/git/.ssh/authorized_keys文件裏就是可行的。如果團隊有幾百號人,就沒法這麼玩了,這時,可以用Gitosis來管理公鑰。
這裏我們不介紹怎麼玩Gitosis了,幾百號人的團隊基本都在500強了,相信找個高水平的Linux管理員問題不大。
管理權限
有很多不但視源代碼如生命,而且視員工爲竊賊的公司,會在版本控制系統裏設置一套完善的權限控制,每個人是否有讀寫權限會精確到每個分支甚至每個目錄下。因爲Git是爲Linux源代碼託管而開發的,所以Git也繼承了開源社區的精神,不支持權限控制。不過,因爲Git支持鉤子(hook),所以,可以在服務器端編寫一系列腳本來控制提交等操作,達到權限控制的目的。Gitosis就是這個工具。
這裏我們也不介紹Gitosis了,不要把有限的生命浪費到權限鬥爭中。