建立版本庫?
創建一個版本庫非常簡單,首先,選擇一個合適的地方,創建一個空目錄(repository):
$ mkdir learngit //創建learngit目錄
$ cd learngit //切換當前目錄為learngit目錄
$ pwd //用于顯示當前目錄
/Users/michael/learngit //最終路徑
第二步,通過git init
命令把這個目錄變成Git可以管理的倉庫:
$ git init
Initialized empty Git repository in /Users/michael/learngit/.git/
?
瞬間Git就把倉庫建好了。如果你沒有看到子目錄.git
,那是因為這個目錄默認是隱藏的,用ls -ah
命令就可以看見。
把文件添加到版本庫
先編寫一個readme.txt
文件,內容如下:
Git is a version control system.
Git is free software.
一定要放到learngit
目錄下(子目錄也行),因為這是一個Git倉庫,放到其他地方Git再厲害也找不到這個文件。
第一步,用命令git add
告訴Git,把文件添加到倉庫:
$ git add readme.txt
第二步,用命令git commit
告訴Git,把文件提交到倉庫:
$ git commit -m "wrote a readme file"
[master (root-commit) eaadf4e] wrote a readme file1 file changed, 2 insertions(+)create mode 100644 readme.txt
簡單解釋一下git commit
命令,-m
后面輸入的是本次提交的說明,可以輸入任意內容,當然最好是有意義的,這樣你就能從歷史
記錄里方便地找到改動記錄。
git commit
命令執行成功后會告訴你,1 file changed
:1個文件被改動(我們新添加的readme.txt文件);2 insertions
:插入
了兩行內容(readme.txt有兩行內容)。
為什么Git添加文件需要add
,commit
一共兩步呢?因為commit
可以一次提交很多文件,所以你可以多次add
不同的文件,比如:
$ git add file1.txt
$ git add file2.txt file3.txt
$ git commit -m "add 3 files."
小結
現在總結一下今天學的兩點內容:
初始化一個Git倉庫,使用git init
命令。
添加文件到Git倉庫,分兩步:
- 使用命令
git add <file>
,注意,可反復多次使用,添加多個文件; - 使用命令
git commit -m <message>
,完成。
?我們繼續修改readme.txt文件,改成如下內容:
Git is a distributed version control system.
Git is free software.
現在,運行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.txtno changes added to commit (use "git add" and/or "git commit -a")
git status
命令可以讓我們時刻掌握倉庫當前的狀態,上面的命令輸出告訴我們,readme.txt
被修改過了,但還沒有準備提交的
修改。
看看具體修改了什么內容,需要用git diff
這個命令看看:
$ git diff readme.txt
diff --git a/readme.txt b/readme.txt
index 46d49bf..9247db6 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1,2 +1,2 @@
-Git is a version control system.
+Git is a distributed version control system.Git is free software.
git diff
顧名思義就是查看difference,顯示的格式正是Unix通用的diff格式,可以從上面的命令輸出看到,我們在第一行添加了
一個distributed
單詞。
提交修改和提交新文件是一樣的兩步,第一步是git add
:
$ git add readme.txt
同樣沒有任何輸出。在執行第二步git commit
之前,我們再運行git status
看看當前倉庫的狀態:
$ git status
On branch master
Changes to be committed:(use "git reset HEAD <file>..." to unstage)modified: readme.txt
git status
告訴我們,將要被提交的修改包括readme.txt
,下一步,就可以放心地提交了:
$ git commit -m "add distributed"
[master e475afc] add distributed1 file changed, 1 insertion(+), 1 deletion(-)
提交后,我們再用git status
命令看看倉庫的當前狀態:
$ git status
On branch master
nothing to commit, working tree clean
Git告訴我們當前沒有需要提交的修改,而且,工作目錄是干凈(working tree clean)的。
小結
-
要隨時掌握工作區的狀態,使用
git status
命令。 -
如果
git status
告訴你有文件被修改過,用git diff
可以查看修改內容。
?在Git中,我們用git log
命令查看歷史記錄
$ git log
commit 1094adb7b9b3807259d8cb349e7df1d4d6477073 (HEAD -> master)
Author: Michael Liao <askxuefeng@gmail.com>
Date: Fri May 18 21:06:15 2018 +0800append GPLcommit e475afc93c209a690c39c13a46716e8fa000c366
Author: Michael Liao <askxuefeng@gmail.com>
Date: Fri May 18 21:03:36 2018 +0800add distributedcommit eaadf4e385e865d25c48e7ca9c8395c3f7dfaef0
Author: Michael Liao <askxuefeng@gmail.com>
Date: Fri May 18 20:59:18 2018 +0800wrote a readme file
git log
命令顯示從最近到最遠的提交日志,我們可以看到3次提交,最近的一次是append GPL
,上一次是add distributed
,最
早的一次是wrote a readme file
。
如果嫌輸出信息太多,看得眼花繚亂的,可以試試加上--pretty=oneline
參數:
$ git log --pretty=oneline
1094adb7b9b3807259d8cb349e7df1d4d6477073 (HEAD -> master) append GPL
e475afc93c209a690c39c13a46716e8fa000c366 add distributed
eaadf4e385e865d25c48e7ca9c8395c3f7dfaef0 wrote a readme file
需要友情提示的是,你看到的一大串類似1094adb...
的是commit id
(版本號)
把readme.txt
回退到上一個版本,也就是add distributed
的那個版本:
首先,Git必須知道當前版本是哪個版本,在Git中,用HEAD
表示當前版本,也就是最新的提交1094adb...
(注意我的提交ID和你
的肯定不一樣),上一個版本就是HEAD^
,上上一個版本就是HEAD^^
,當然往上100個版本寫100個^
比較容易數不過來,所以寫成
HEAD~100
。
現在,我們要把當前版本append GPL
回退到上一個版本add distributed
,就可以使用git reset
命令:
$ git reset --hard HEAD^
HEAD is now at e475afc add distributed
看看readme.txt
的內容是不是版本add distributed
:
$ cat readme.txt
Git is a distributed version control system.
Git is free software.
果然被還原了。
我們用git log
再看看現在版本庫的狀態:
$ git log
commit e475afc93c209a690c39c13a46716e8fa000c366 (HEAD -> master)
Author: Michael Liao <askxuefeng@gmail.com>
Date: Fri May 18 21:03:36 2018 +0800add distributedcommit eaadf4e385e865d25c48e7ca9c8395c3f7dfaef0
Author: Michael Liao <askxuefeng@gmail.com>
Date: Fri May 18 20:59:18 2018 +0800wrote a readme file
最新的那個版本append GPL
已經看不到了!好比你從21世紀坐時光穿梭機來到了19世紀,想再回去已經回不去了,腫么辦?
辦法其實還是有的,只要上面的命令行窗口還沒有被關掉,你就可以順著往上找啊找啊,找到那個append GPL
的commit id
是
1094adb...
,于是就可以指定回到未來的某個版本:
$ git reset --hard 1094a
HEAD is now at 83b0afe append GPL
版本號沒必要寫全,前幾位就可以了,Git會自動去找。當然也不能只寫前一兩位,因為Git可能會找到多個版本號,就無法確定
是哪一個了。
再小心翼翼地看看readme.txt
的內容:
$ cat readme.txt
Git is a distributed version control system.
Git is free software distributed under the GPL.
?可以!
現在,你回退到了某個版本,關掉了電腦,第二天早上就后悔了,想恢復到新版本怎么辦?找不到新版本的commit id
怎么辦?
在Git中,總是有后悔藥可以吃的。當你用$ git reset --hard HEAD^
回退到add distributed
版本時,再想恢復到append GPL
,
就必須找到append GPL
的commit id。Git提供了一個命令git reflog
用來記錄你的每一次命令:
$ 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
終于舒了口氣,從輸出可知,append GPL
的commit id是1094adb
,現在,你又可以乘坐時光機回到未來了。
?小結:
-
HEAD
指向的版本就是當前版本,因此,Git允許我們在版本的歷史之間穿梭,使用命令git reset --hard commit_id
。 -
穿梭前,用
git log
可以查看提交歷史,以便確定要回退到哪個版本。 -
要重返未來,用
git reflog
查看命令歷史,以便確定要回到未來的哪個版本。
工作區和暫存區:?
撤銷修改:
場景1:當你改亂了工作區某個文件的內容,想直接丟棄工作區的修改時,用命令git checkout -- file
。
場景2:當你不但改亂了工作區某個文件的內容,還添加到了暫存區時,想丟棄修改,分兩步,第一步用命令git reset HEAD
<file>
,就回到了場景1,第二步按場景1操作。
場景3:已經提交了不合適的修改到版本庫時,想要撤銷本次提交,參考版本回退一節,不過前提是沒有推送到遠程庫。
刪除文件:
一般情況下,你通常直接在文件管理器中把沒用的文件刪了,或者用rm
命令刪了:
$ rm test.txt
這個時候,Git知道你刪除了文件,因此,工作區和版本庫就不一致了,git status
命令會立刻告訴你哪些文件被刪除了:
$ git status
On branch master
Changes not staged for commit:(use "git add/rm <file>..." to update what will be committed)(use "git checkout -- <file>..." to discard changes in working directory)deleted: test.txtno changes added to commit (use "git add" and/or "git commit -a")
現在你有兩個選擇,一是確實要從版本庫中刪除該文件,那就用命令git rm
刪掉,并且git commit
:
$ git rm test.txt
rm 'test.txt'$ git commit -m "remove test.txt"
[master d46f35e] remove test.txt1 file changed, 1 deletion(-)delete mode 100644 test.txt
現在,文件就從版本庫中被刪除了。
?小提示:先手動刪除文件,然后使用git rm <file>和git add<file>效果是一樣的。
另一種情況是刪錯了,因為版本庫里還有呢,所以可以很輕松地把誤刪的文件恢復到最新版本:
$ git checkout -- test.txt
但是要小心,你只能恢復文件到最新版本,你會丟失最近一次提交后你修改的內容。?
git checkout
其實是用版本庫里的版本替換工作區的版本,無論工作區是修改還是刪除,都可以“一鍵還原”。
遠程倉庫(github)
在繼續閱讀后續內容前,請自行注冊GitHub賬號。由于你的本地Git倉庫和GitHub倉庫之間的傳輸是通過SSH加密的,所以,需
要一點設置:
第1步:創建SSH Key。在用戶主目錄下(我的是C:\Users\lw),看看有沒有.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,打開“settings”,“SSH and GPG Keys”頁面:
然后,點“New?SSH Key”,填上任意Title,在Key文本框里粘貼id_rsa.pub
文件的內容:
點“Add?Key”,你就應該看到已經添加的Key:
為什么GitHub需要SSH Key呢?因為GitHub需要識別出你推送的提交確實是你推送的,而不是別人冒充的,而Git支持SSH協
議,所以,GitHub只要知道了你的公鑰,就可以確認只有你自己才能推送。
當然,GitHub允許你添加多個Key。假定你有若干電腦,你一會兒在公司提交,一會兒在家里提交,只要把每臺電腦的Key都添
加到GitHub,就可以在每臺電腦上往GitHub推送了。
添加遠程庫:
現在github上創建一個New Repository,成功之后頁面會提示:可以從這個倉庫克隆出新的倉庫,也可以把一個已有的本地倉庫
與之關聯,然后,把本地倉庫的內容推送到GitHub倉庫。
現在,我們根據GitHub的提示,在本地的learngit
倉庫下運行命令:
$ git remote add origin git@github.com:michaelliao/learngit.git
每個新倉庫都會有https或者ssh地址,復制其中一個替代 git@github.com:michaelliao/learngit.git
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
分支關聯起來,在以后的推送或者拉取時就可以簡化命令。
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
推送最新修改;
?從遠程庫克隆:
獲取你要克隆的遠程庫地址,用命令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,
通過ssh
支持的原生git
協議速度最快。
分支管理:
分支在實際中有什么用呢?假設你準備開發一個新功能,但是需要兩周才能完成,第一周你寫了50%的代碼,如果立刻提交,由
于代碼還沒寫完,不完整的代碼庫會導致別人不能干活了。如果等代碼全部寫完再一次提交,又存在丟失每天進度的巨大風險。
現在有了分支,就不用怕了。你創建了一個屬于你自己的分支,別人看不到,還繼續在原來的分支上正常工作,而你在自己的分
支上干活,想提交就提交,直到開發完畢后,再一次性合并到原來的分支上,這樣,既安全,又不影響別人工作。
創建與合并分支:
原理詳細請戳這里
Git鼓勵大量使用分支:
查看分支:git branch
創建分支:git branch <name>
切換分支:git checkout <name>
創建+切換分支:git checkout -b <name>
合并某分支到當前分支:git merge <name>
刪除分支:git branch -d <name>
因為創建、合并和刪除分支非常快,所以Git鼓勵你使用分支完成某個任務,合并后再刪掉分支,這和直接在master
分支上工作效
果是一樣的,但過程更安全。
解決沖突:
這種情況下,Git無法執行“快速合并”,只能試圖把各自的修改合并起來,但這種合并就可能會有沖突,如上圖。
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: readme.txtno 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 cf810e4] conflict fixed
現在,master
分支和feature1
分支變成了下圖所示:
用帶參數的git log
也可以看到分支的合并情況:
$ git log --graph --pretty=oneline --abbrev-commit
* cf810e4 (HEAD -> master) conflict fixed
|\
| * 14096d0 (feature1) AND simple
* | 5dc6824 & simple
|/
* b17d20e branch test
* d46f35e (origin/master) remove test.txt
* b84166e add test.txt
* 519219b git tracks changes
* e43a48b understand how stage works
* 1094adb append GPL
* e475afc add distributed
* eaadf4e wrote a readme file
最后,刪除feature1
分支:
$ git branch -d feature1
Deleted branch feature1 (was 14096d0).
工作完成。
解決沖突
小結
當Git無法自動合并分支時,就必須首先解決沖突。解決沖突后,再提交,合并完成。
解決沖突就是把Git合并失敗的文件手動編輯為我們希望的內容,再提交。
用git log --graph
命令可以看到分支合并圖。
分支管理策略:
通常,合并分支時,如果可能,Git會用Fast forward
模式,但這種模式下,刪除分支后,會丟掉分支信息。
如果要強制禁用Fast forward
模式,即用--no-ff
方式的git merge
,Git就會在merge時生成一個新的commit,這樣,從分支歷史
上就可以看出分支信息。
如在master分支上合并dev分支,請注意--no-ff
參數,表示禁用Fast forward
:
$ git merge --no-ff -m "merge with no-ff" dev
Merge made by the 'recursive' strategy.readme.txt | 1 +1 file changed, 1 insertion(+)
因為本次合并要創建一個新的commit,所以加上-m
參數,把commit描述寫進去。
合并后,我們用git log
看看分支歷史:
$ git log --graph --pretty=oneline --abbrev-commit
* e1e9c68 (HEAD -> master) merge with no-ff
|\
| * f52c633 (dev) add merge
|/
* cf810e4 conflict fixed
...
可以看到,不使用Fast forward
模式,merge后就像這樣:
分支策略
在實際開發中,我們應該按照幾個基本原則進行分支管理:
首先,master
分支應該是非常穩定的,也就是僅用來發布新版本,平時不能在上面干活;
那在哪干活呢?干活都在dev
分支上,也就是說,dev
分支是不穩定的,到某個時候,比如1.0版本發布時,再把dev
分支合并到
master
上,在master
分支發布1.0版本;
你和你的小伙伴們每個人都在dev
分支上干活,每個人都有自己的分支,時不時地往dev
分支上合并就可以了。
所以,團隊合作的分支看起來就像這樣:
小結
Git分支十分強大,在團隊開發中應該充分應用。
合并分支時,加上--no-ff
參數就可以用普通模式合并,合并后的歷史有分支,能看出來曾經做過合并,而fast forward
合并就
看不出來曾經做過合并。
Bug分支 :
在Git中,由于分支是如此的強大,所以,每個bug都可以通過一個新的臨時分支來修復,修復后,合并分支,然后將臨時分支刪除。
當你接到一個修復一個代號101的bug的任務時,很自然地,你想創建一個分支issue-101
來修復它,但是,如果你當前正在dev
上
進行的工作還沒有提交,并不是你不想提交,而是工作只進行到一半,還沒法提交,預計完成還需1天時間。但是,必須在兩個
小時內修復該bug,怎么辦?
幸好,Git還提供了一個stash
功能,可以把當前工作現場“儲藏”起來,等以后恢復現場后繼續工作:
$ git stash
Saved working directory and index state WIP on dev: f52c633 add merge
現在,用git status
查看工作區,就是干凈的(除非有沒有被Git管理的文件),因此可以放心地創建分支來修復bug。
修復完后切換回工作分支,用git stash list
命令查看之前保存的工作現場
$ git stash list
stash@{0}: WIP on dev: f52c633 add merge
工作現場還在,Git把stash內容存在某個地方了,但是需要恢復一下,有兩個辦法:
一是用git stash apply
恢復,但是恢復后,stash內容并不刪除,你需要用git stash drop
來刪除;
另一種方式是用git stash pop
,恢復的同時把stash內容也刪了:
$ git stash pop
On branch dev
Changes to be committed:(use "git reset HEAD <file>..." to unstage)new file: hello.pyChanges 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.txtDropped refs/stash@{0} (5d677e2ee266f39ea296182fb2354265b91b3b2a)
再用git stash list
查看,就看不到任何stash內容了:
$ git stash list
你可以多次stash,恢復的時候,先用git stash list
查看,然后恢復指定的stash,用命令:
$ git stash apply stash@{0}
Feature(功能)分支以及強制刪除:
軟件開發中,總有無窮無盡的新的功能要不斷添加進來。
添加一個新功能時,你肯定不希望因為一些實驗性質的代碼,把主分支搞亂了,所以,每添加一個新功能,最好新建一個feature
分支,在上面開發,完成后,合并,最后,刪除該feature分支。
當然,如果開發完后突然接到上級命令需要取消該功能,雖然白干了,但是這個包含機密資料的分支還是必須就地銷毀:
$ git branch -d feature-vulcan
error: The branch 'feature-vulcan' is not fully merged.
If you are sure you want to delete it, run 'git branch -D feature-vulcan'.
銷毀失敗。Git友情提醒,feature-vulcan
分支還沒有被合并,如果刪除,將丟失掉修改,如果要強行刪除,需要使用大寫的-D
參數。。
現在我們強行刪除:
$ git branch -D feature-vulcan
Deleted branch feature-vulcan (was 287773e).
終于刪除成功!
多人協作(重要):
當你從遠程倉庫克隆時,實際上Git自動把本地的master
分支和遠程的master
分支對應起來了,并且,遠程倉庫的默認名稱是origin
。
要查看遠程庫的信息,用git remote
:
$ git remote
origin
或者,用git remote -v
顯示更詳細的信息:
$ git remote -v
origin git@github.com:michaelliao/learngit.git (fetch)
origin git@github.com:michaelliao/learngit.git (push)
上面顯示了可以抓取和推送的origin
的地址。如果沒有推送權限,就看不到push的地址。
推送分支
推送分支,就是把該分支上的所有本地提交推送到遠程庫。推送時,要指定本地分支,這樣,Git就會把該分支推送到遠程庫對應的遠程分支上:
$ git push origin master
如果要推送其他分支,比如dev
,就改成:
$ git push origin dev
但是,并不是一定要把本地分支往遠程推送,那么,哪些分支需要推送,哪些不需要呢?
-
master
分支是主分支,因此要時刻與遠程同步; -
dev
分支是開發分支,團隊所有成員都需要在上面工作,所以也需要與遠程同步; -
bug分支只用于在本地修復bug,就沒必要推到遠程了,除非老板要看看你每周到底修復了幾個bug;
-
feature分支是否推到遠程,取決于你是否和你的小伙伴合作在上面開發。
抓取分支
多人協作時,大家都會往master
和dev
分支上推送各自的修改。
現在,模擬一個你的小伙伴,可以在另一臺電腦(注意要把SSH Key添加到GitHub)或者同一臺電腦的另一個目錄下克隆:
$ git clone git@github.com:michaelliao/learngit.git
Cloning into 'learngit'...
remote: Counting objects: 40, done.
remote: Compressing objects: 100% (21/21), done.
remote: Total 40 (delta 14), reused 40 (delta 14), pack-reused 0
Receiving objects: 100% (40/40), done.
Resolving deltas: 100% (14/14), done.
當你的小伙伴從遠程庫clone時,默認情況下,你的小伙伴只能看到本地的master
分支。不信可以用git branch
命令看看:
$ git branch
* master
現在,你的小伙伴要在dev
分支上開發,就必須創建遠程origin
的dev
分支到本地,于是他用這個命令創建本地dev
分支:
$ git checkout -b dev origin/dev
現在,他就可以在dev
上繼續修改,然后,時不時地把dev
分支push
到遠程:
$ git add env.txt$ git commit -m "add env"
[dev 7a5e5dd] add env1 file changed, 1 insertion(+)create mode 100644 env.txt$ git push origin dev
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 308 bytes | 308.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To github.com:michaelliao/learngit.gitf52c633..7a5e5dd dev -> dev
你的小伙伴已經向origin/dev
分支推送了他的提交,而碰巧你也對同樣的文件作了修改,并試圖推送:
$ cat env.txt
env$ git add env.txt$ git commit -m "add new env"
[dev 7bd91f1] add new env1 file changed, 1 insertion(+)create mode 100644 env.txt$ git push origin dev
To github.com:michaelliao/learngit.git! [rejected] dev -> dev (non-fast-forward)
error: failed to push some refs to 'git@github.com:michaelliao/learngit.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
推送失敗,因為你的小伙伴的最新提交和你試圖推送的提交有沖突,解決辦法也很簡單,Git已經提示我們,先用git pull
把最新的提交從origin/dev
抓下來,然后,在本地合并,解決沖突,再推送:
$ git pull
There is no tracking information for the current branch.
Please specify which branch you want to merge with.
See git-pull(1) for details.git pull <remote> <branch>If you wish to set tracking information for this branch you can do so with:git branch --set-upstream-to=origin/<branch> dev
git pull
也失敗了,原因是沒有指定本地dev
分支與遠程origin/dev
分支的鏈接,根據提示,設置dev
和origin/dev
的鏈接:
$ git branch --set-upstream-to=origin/dev dev
Branch 'dev' set up to track remote branch 'dev' from 'origin'.
再pull:
$ git pull
Auto-merging env.txt
CONFLICT (add/add): Merge conflict in env.txt
Automatic merge failed; fix conflicts and then commit the result.
這回git pull
成功,但是合并有沖突,需要手動解決,解決的方法和分支管理中的解決沖突完全一樣。解決后,提交,再push:
$ git commit -m "fix env conflict"
[dev 57c53ab] fix env conflict$ git push origin dev
Counting objects: 6, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (6/6), 621 bytes | 621.00 KiB/s, done.
Total 6 (delta 0), reused 0 (delta 0)
To github.com:michaelliao/learngit.git7a5e5dd..57c53ab dev -> dev
因此,多人協作的工作模式通常是這樣:
-
首先,可以試圖用
git push origin <branch-name>
推送自己的修改; -
如果推送失敗,則因為遠程分支比你的本地更新,需要先用
git pull
試圖合并; -
如果合并有沖突,則解決沖突,并在本地提交;
-
沒有沖突或者解決掉沖突后,再用
git push origin <branch-name>
推送就能成功!
如果git pull
提示no tracking information
,則說明本地分支和遠程分支的鏈接關系沒有創建,用命令--set-upstream-to=origin/branch-name branch name
。
這就是多人協作的工作模式,一旦熟悉了,就非常簡單。
小結
-
查看遠程庫信息,使用
git remote -v
; -
本地新建的分支如果不推送到遠程,對其他人就是不可見的;
-
從本地推送分支,使用
git push origin branch-name
,如果推送失敗,先用git pull
抓取遠程的新提交; -
在本地創建和遠程分支對應的分支,使用
git checkout -b branch-name origin/branch-name
,本地和遠程分支的名稱最好一致; -
建立本地分支和遠程分支的關聯,使用
git branch --set-upstream-to=origin/branch-name branch name
; -
從遠程抓取分支,使用
git pull
,如果有沖突,要先處理沖突。
?創建標簽:
在Git中打標簽非常簡單,首先,切換到需要打標簽的分支上:
$ git branch
* devmaster
$ git checkout master
Switched to branch 'master'
然后,敲命令git tag <name>
就可以打一個新標簽:
$ git tag v1.0
可以用命令git tag
查看所有標簽:
$ git tag
v1.0
默認標簽是打在最新提交的commit上的。有時候,如果忘了打標簽,比如,現在已經是周五了,但應該在周一打的標簽沒有打,怎么辦?
方法是找到歷史提交的commit id,然后打上就可以了
比方說要對add merge
這次提交打標簽,它對應的commit id是f52c633
,敲入命令:
$ git tag v0.9 f52c633
再用命令git tag
查看標簽:
$ git tag
v0.9
v1.0
注意,標簽不是按時間順序列出,而是按字母排序的。可以用git show <tagname>
查看標簽信息:
$ git show v0.9
commit f52c63349bc3c1593499807e5c8e972b82c8f286 (tag: v0.9)
Author: Michael Liao <askxuefeng@gmail.com>
Date: Fri May 18 21:56:54 2018 +0800add mergediff --git a/readme.txt b/readme.txt
...
可以看到,v0.9
確實打在add merge
這次提交上。
還可以創建帶有說明的標簽,用-a
指定標簽名,-m
指定說明文字:
$ git tag -a v0.1 -m "version 0.1 released" 1094adb
用命令git show <tagname>
可以看到說明文字:
$ git show v0.1
tag v0.1
Tagger: Michael Liao <askxuefeng@gmail.com>
Date: Fri May 18 22:48:43 2018 +0800version 0.1 releasedcommit 1094adb7b9b3807259d8cb349e7df1d4d6477073 (tag: v0.1)
Author: Michael Liao <askxuefeng@gmail.com>
Date: Fri May 18 21:06:15 2018 +0800append GPLdiff --git a/readme.txt b/readme.txt
...
?注意:標簽總是和某個commit掛鉤。如果這個commit既出現在master分支,又出現在dev分支,那么在這兩個分支上都可以看到這個標簽。
操作標簽:
如果標簽打錯了,也可以刪除:
$ git tag -d v0.1
Deleted tag 'v0.1' (was f15b0dd)
因為創建的標簽都只存儲在本地,不會自動推送到遠程。所以,打錯的標簽可以在本地安全刪除。
如果要推送某個標簽到遠程,使用命令git push origin <tagname>
:
$ git push origin v1.0
Total 0 (delta 0), reused 0 (delta 0)
To github.com:michaelliao/learngit.git* [new tag] v1.0 -> v1.0
或者,一次性推送全部尚未推送到遠程的本地標簽:
$ git push origin --tags
Total 0 (delta 0), reused 0 (delta 0)
To github.com:michaelliao/learngit.git* [new tag] v0.9 -> v0.9
如果標簽已經推送到遠程,要刪除遠程標簽就麻煩一點,先從本地刪除:
$ git tag -d v0.9
Deleted tag 'v0.9' (was f52c633)
然后,從遠程刪除。刪除命令也是push,但是格式如下:
$ git push origin :refs/tags/v0.9
To github.com:michaelliao/learngit.git- [deleted] v0.9
要看看是否真的從遠程庫刪除了標簽,可以登陸GitHub查看。
小結
-
命令
git push origin <tagname>
可以推送一個本地標簽; -
命令
git push origin --tags
可以推送全部未推送過的本地標簽; -
命令
git tag -d <tagname>
可以刪除一個本地標簽; -
命令
git push origin :refs/tags/<tagname>
可以刪除一個遠程標簽。
忽略特殊文件 :
有些時候,你必須把某些文件放到Git工作目錄中,但又不能提交它們,比如保存了數據庫密碼的配置文件啦,等等
好在Git考慮到了大家的感受,這個問題解決起來也很簡單,在Git工作區的根目錄下創建一個特殊的.gitignore
文件,然后把要忽略的文件名填進去,Git就會自動忽略這些文件。
不需要從頭寫.gitignore
文件,GitHub已經為我們準備了各種配置文件,只需要組合一下就可以使用了。所有配置文件可以直接在線瀏覽:https://github.com/github/gitignore
忽略文件的原則是:
- 忽略操作系統自動生成的文件,比如縮略圖等;
- 忽略編譯生成的中間文件、可執行文件等,也就是如果一個文件是通過另一個文件自動生成的,那自動生成的文件就沒必要放進版本庫,比如Java編譯產生的
.class
文件;
忽略你自己的帶有敏感信息的配置文件,比如存放口令的配置文件。
舉個例子:
假設你在Windows下進行Python開發,Windows會自動在有圖片的目錄下生成隱藏的縮略圖文件,如果有自定義目錄,目錄下就會有Desktop.ini
文件,因此你需要忽略Windows自動生成的垃圾文件:
# Windows:
Thumbs.db
ehthumbs.db
Desktop.ini
然后,繼續忽略Python編譯產生的.pyc
、.pyo
、dist
等文件或目錄:
# Python:
*.py[cod]
*.so
*.egg
*.egg-info
dist
build
加上你自己定義的文件,最終得到一個完整的.gitignore
文件,內容如下:
# Windows:
Thumbs.db
ehthumbs.db
Desktop.ini# Python:
*.py[cod]
*.so
*.egg
*.egg-info
dist
build# My configurations:
db.ini
deploy_key_rsa
最后一步就是把.gitignore
也提交到Git,就完成了!當然檢驗.gitignore
的標準是git status
命令是不是說working directory clean
。
使用Windows的童鞋注意了,如果你在資源管理器里新建一個.gitignore
文件,它會非常弱智地提示你必須輸入文件名,但是在文本編輯器里“保存”或者“另存為”就可以把文件保存為.gitignore
了。
有些時候,你想添加一個文件到Git,但發現添加不了,原因是這個文件被.gitignore
忽略了:
$ git add App.class
The following paths are ignored by one of your .gitignore files:
App.class
Use -f if you really want to add them.
如果你確實想添加該文件,可以用-f
強制添加到Git:
$ git add -f App.class
或者你發現,可能是.gitignore
寫得有問題,需要找出來到底哪個規則寫錯了,可以用git check-ignore
命令檢查:
$ git check-ignore -v App.class
.gitignore:3:*.class App.class
Git會告訴我們,.gitignore
的第3行規則忽略了該文件,于是我們就可以知道應該修訂哪個規則。
小結
-
忽略某些文件時,需要編寫
.gitignore
; -
.gitignore
文件本身要放到版本庫里,并且可以對.gitignore
做版本管理!
?
?
?