Git的理解和使用
Git介紹
Git(the stupid content tracker)是一個源自Linux內核項目的源碼管理工具。和傳統的CVS、SVN不同,git是一個分布式源碼管理工具。
| git init | 初始化一個本地的代碼倉庫。 |
| git clone | 從遠程復制一個代碼倉庫。 |
| git config | git選項設置。 |
| git add | 添加文件/目錄。 |
| git commit | 提交修改。 |
| git status | 顯示工作目錄的狀態以及緩沖區內的快照。 |
| git log | 已提交快照的日志。 |
| git branch | 創建分支。 |
| git checkout | 遷出/拉出/切換到一個分支。 |
| git merge | 合并分支。 |
| git revert | 撤銷commit快照。 |
| git reset | 撤銷本地工作目錄的修改。 |
| git clean | 刪除代碼倉庫以外的文件。 |
| git remote | 管理遠程git。 |
| git fetch | 從遠程獲取分支。 |
| git pull | 從遠程獲取分支。 |
| git push | 把代碼推到遠程分支。 |
這里列舉幾個常見的git命令,讓大家過過目?
基本操作?
git init //初始化版本庫?
git clone //克隆版本庫?
07 >git add //添加新文件?
git commit //提交到本地版本庫?
git checkout //檢出(分支、標簽)?
分支?
git branch //列出分支?
git branch -r //列出遠程分支?
git branch -a //列出所有分支?
git branch newBranch //基于當前分支創建新分支newBranch?
git branch -D branchName //刪除分支branchName?
git branch -d branchName //僅刪除已合并分支branchName?
git merge <--> //合并分支<br>>git tag ?
歷史?
git log //顯示全部歷史?
git log -p //顯示版本歷史,以及版本間的內容差異?
git log -5 //顯示最近的5個提交?
git log -5 -p //顯示最近的5個提交,以及版本間的內容差異?
...(很多很多參數...)?
git diff 112 115 //顯示112和115版本的差別?
基本概念
文件狀態
Git倉庫中的文件有幾種狀態:
- untracked?- 還沒添加到倉庫中。
- unmodified?- 自上次提交以來,文件未曾修改過。
- modified?- 文件修改了還沒提交。
- staged?- 文件提交到了暫存區中。一旦執行git commit就會轉換為unmodified狀態。
當我們往工作目錄添加一個文件的時候,這個文件默認是未跟蹤狀態的,我們肯定不希望編譯生成的一大堆臨時文件默認被跟蹤還要我們每次手動將這些文件清除出去。用以下命令可以跟蹤文件:
git add <file>
Git暫存區(Staged Area)的意思是:你把一個文件托付給Git跟蹤(git add),然后又修改了它,此時這個文件就位于暫存區了。暫存區內的文件幾乎只做一件事:等待你執行git commit,把它提交。
上圖中右邊3個狀態都是已跟蹤狀態,其中的灰色箭頭只表示untracked<-->tracked的轉換而不是untracked<-->unmodified的轉換,新添加的文件肯定算是被修改過的。那么,staged狀態又是什么呢?這就要搞清楚GIT的三個工作區域:本地數據(倉庫)目錄,工作目錄,暫存區,如下圖所示:
git directory就是我們的本地倉庫.git目錄,里面保存了所有的版本信息等內容。
working driectory,工作目錄,就是我們的工作目錄,其中包括未跟蹤文件及已跟蹤文件,而已跟蹤文件都是從git directory取出來的文件的某一個版本或新跟蹤的文件。
staging area,暫存區,不對應一個具體目錄,其時只是git directory中的一個特殊文件。
當我們修改了一些文件后,要將其放入暫存區然后才能提交,每次提交時其實都是提交暫存區的文件到git倉庫,然后清除暫存區。而checkout某一版本時,這一版本的文件就從git倉庫取出來放到了我們的工作目錄。
快照(snapshot)
Git與其他版本控制系統的區別在于:Git只關心文件是否變化,而不關心文件內容的變化。大多數版本控制系統都會忠實地記錄版本間的文件差異(diff),但Git不關心這些具體差異(哪一行有什么變動),Git只關心哪些文件修改了哪些沒有修改,修改了的文件直接復制形成新的blob(這就是所謂的快照snapshot)。當你需要切換到或拉出一個分支時,Git就直接加載當時的文件快照即可,這就是Git快的原因。說起來,這也是用空間換取時間的經典案例。
從這個角度看,Git更像是一個小型文件系統,并在這個系統上提供一系列的工具來輔助開發。
Git的地理觀
Git是一個分布式的版本控制系統,因此沒有所謂的中心。粗略來看Git可分為本地庫(local repository)和遠程庫(remote repository),細致地看可分為以下幾個部分:
- Working Directory?- 工作目錄。Git倉庫位于工作目錄之下,工作目錄下的文件有加入Git倉庫(tracked)和沒加入Git倉庫(untracked)的區別。
- Stage Area?- 暫存區。如上所述,已加入Git倉庫并被修改(尚未提交)的文件。
- Local Repository?- 本地倉庫。
- Remote Repository?- 遠程倉庫。
文件通常是:加入Git倉庫(git add)-> 修改后即位于暫存區 -> 提交到本地庫(git commit) -> 推送到遠程庫(git push)。
origin/master
這里主要筆記一些在Git上下文中經常遇見的術語。origin/master指遠程倉庫origin的master分支。
遠程倉庫/分支這樣的形式。雖然Git是分布式的系統,但通常把git clone的源頭叫做origin,origin也被視為中心倉庫(Central Repository)。
git入門
創建目錄,并用git init初始化:
$ mkdir learn-git && cd learn-git $ git init Initialized empty Git repository in /tmp/learn-git/.git/從git init輸出可知,git創建了一個名為.git的隱藏目錄。
創建一個文件,并用git add添加到倉庫,用git commit提交:
$ echo "hello git" > README.txt $ git add . $ git commit -m "readme file" [master (root-commit) cd27ac1] readme file1 file changed, 1 insertion(+)create mode 100644 README.txt接下來對已提交文件做一些修改,并新添加一個文件:
$ echo "learn files here" >> README.txt $ cp ~/.vimrc .用git diff查看文件差異(每次commit前應該先diff對比差異詳情):
$ git diff diff --git a/README.txt b/README.txt index 8d0e412..0219596 100644 --- a/README.txt +++ b/README.txt @@ -1 +1,2 @@hello git +learn files here差異對比可以用git diff --cached保存下來(如此差異則不輸出到屏幕)。
用git status查看git倉庫狀態:
$ 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) # # .vimrc no changes added to commit (use "git add" and/or "git commit -a")這里顯示README.txt被修改了,而.vimrc則等待添加。接下來,我們將.vimrc添加到git倉庫中,且將所有修改一并提交(git commit -a):
$ git commit -a -m "update readme && add vimrc" [master f6162f0] update readme && add vimrc2 files changed, 123 insertions(+)create mode 100755 .vimrcgit log輸出git日志,包括提交編號(如"f6162f04170e3665bc03744e43f764c903e4e38d"這樣的字串)、提交者、提交日期和提交日志。
git log的其他輸出:
$ git log -p # 詳細日志,并輸出到分頁程序 $ git log --stat --summary美化git log輸出
$ git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --修改全局配置:
$ 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 branch exp查看當前git倉庫的所有分支:
$ git branchexp * master切換到exp分支:
$ git checkout exp Switched to branch 'exp'修改文件,并提交:
$ echo "start branch: exp" >> README.txt $ git commit -a -m "modified readme in exp branch" [exp 2e825a4] modified readme in exp branch1 file changed, 1 insertion(+)切換回master分支:
$ git checkout master Switched to branch 'master'在master分支檢查文件,可見exp分支的修改并沒影響到master分支(注意,在exp分支的修改都已提交;如果沒有提交,則切換回master分支會看到文件已變)。接下來我們制造一個沖突:
$ echo "return branch: master" >> README.txt $ git commit -a -m "modified readme in master branch" [master 8dd9fb2] modified readme in master branch1 file changed, 1 insertion(+)用git merge exp合并分支:
$ git merge exp Auto-merging README.txt CONFLICT (content): Merge conflict in README.txt Automatic merge failed; fix conflicts and then commit the result.從git輸出可見,git嘗試自動合并但失敗了,因此提示需要解決沖突再提交
用git diff查看差異,且差異文件被修改:
$ git diff ... $ cat README.txt hello git learn files here <<<<<<< HEAD return branch: master ======= start branch: exp >>>>>>> exp手工解決沖突并再次提交:
(edit file) $ git commit -a -m "do merge"接下來,可以刪除exp分支:
$ git branch -d exp Deleted branch exp (was 2e825a4).git branch -d刪除分支時會檢查分支是否完全合并到主干,如果不是,則會刪除失敗,并提示需要合并:
$ git branch exp # 建立exp分支 $ git checkout exp # 切換到exp分支 $ echo "exp again" >> README.txt # 修改并提交 $ git commit -a -m "exp again" [exp 868e68c] exp again1 file changed, 1 insertion(+) $ git checkout master # 切換回master Switched to branch 'master' $ git branch -d exp # 刪除失敗 error: The branch 'exp' is not fully merged. If you are sure you want to delete it, run 'git branch -D exp'.可以用git branch -D exp忽略修改,完全刪除分支:
$ git branch -D exp Deleted branch exp (was 868e68c).查看遠端git
基礎命令是:git remote show, git remote show X。
$ git remote show origin web查看GitHub默認設置的origin
$ git remote show origin * remote originFetch URL: git@github.com:berlinix/blog.gitPush URL: git@github.com:berlinix/blog.gitHEAD branch: masterRemote branch:master trackedLocal branch configured for 'git pull':master merges with remote masterLocal ref configured for 'git push':master pushes to master (fast-forwardable)git命令快查
以下列出一些常用的git命令
| 基礎操作 | |
| git init | 初始化git倉庫 |
| git add X | 添加X文件/路徑到git倉庫 |
| git commit -m "COMMENTS" | 提交更新 |
| 分支管理 | |
| git branch X | 創建一個名為X的分支 |
| git checkout X | 切換到X分支 |
| git merge X | 自動合并X分支 |
| git branch -d X | 刪除X分支,需要先merge |
| git branch -D X | 強制刪除X分支,忽略其修改,無須先merge |
| 與遠程git交互 | |
| git remote show | 顯示遠程git倉庫 |
| git remote show X | 顯示遠程git一個名為X的倉庫 |
| git push origin master | 更新提交到GitHub |
Git日常問題
撤銷commit
剛與master合并并提交后就后悔了現在要做的是撤銷commit(revoke/undo merge/commit)。
查看當前所在分支:
$ git branchbs3 * coindevmaster查看日志:
$ git log --oneline 9b7ba39 merged with master 73a66e8 update FAQ用以下2個命令來撤銷提交(把COMMIT_SHA替換為實際的SHA值;把HEAD~N中的N替換為一個數字,表示回退幾步):
$ git reset --hard COMMIT_SHA $ git reset --hard HEAD~N例如回退到合并前:
$ git reset --hard 73a66e8 HEAD is now at 73a66e8 update FAQ回退后發現不對,因為現在這個commit還是在master中的(在merge之前master已經走的太遠),趕緊再次reset到merge時的狀態:
$ git reset --hard 9b7ba39 HEAD is now at 9b7ba39 merged with master如此一來就是就是merge后commit之前的狀態。接下來就是要完成undo merge(已經undo commit了):
$ git revert -m 1 9b7ba39這下就徹底回到merge前了,以防萬一再次檢查:
$ git diff --name-status master..coin看起來沒什么問題了,檢查下日志:
$ git log --oneline 2691516 Revert "merged with master" 9b7ba39 merged with master 73a66e8 update FAQ用git找回已刪除文件
首先找到與目標文件相關的最后一次commit。如果目標文件沒有出現在HEAD commit中,那么在這次commit時,文件就被刪除了:
$ git rev-list -n 1 HEAD -- htdocs/myfile.php 1e8182f58dc038c8e6bc2025e8430f463d372030接下來就是恢復工作了:
$ git checkout 1e8182f58dc038c8e6bc2025e8430f463d372030^ -- htdocs/myfile.php合并分支的部分文件
有時候只想合并分支里的部分文件,而不是整個分支,可以用這個命令:
git checkout BRANCH FILE ...例如,從test_branch分支中合并file_modified文件:
$ git checkout test_branch file_modified參考Git Tip: How to "Merge" Specific Files from Another Branch。
總結
- 上一篇: Android之万能适配器Adapter
- 下一篇: git之you can‘t overwr