# [X分鐘速成Y](http://learnxinyminutes.com/)
## 其中 Y=git
Git是一個分布式版本控制及源代碼管理工具
Git可以為你的項目保存若干快照,以此來對整個項目進行版本管理
## 版本
### 什么是版本控制
版本控制系統就是根據時間來記錄一個或多個文件的更改情況的系統。
### 集中式版本控制 VS 分布式版本控制
* 集中式版本控制的主要功能為同步,跟蹤以及備份文件
* 分布式版本控制則更注重共享更改。每一次更改都有唯一的標識
* 分布式系統沒有預定的結構。你也可以用git很輕松的實現SVN風格的集中式系統控制
[更多信息](http://git-scm.com/book/en/Getting-Started-About-Version-Control)
### 為什么要使用Git
* 可以離線工作
* 和他人協同工作變得簡單
* 分支很輕松
* 合并很容易
* Git系統速度快,也很靈活
## Git 架構
### 版本庫
一系列文件,目錄,歷史記錄,提交記錄和頭指針。 可以把它視作每個源代碼文件都帶有歷史記錄屬性數據結構
一個Git版本庫包括一個 .git 目錄和其工作目錄
### .git 目錄(版本庫的一部分)
.git 目錄包含所有的配置、日志、分支信息、頭指針等?[詳細列表](http://gitready.com/advanced/2009/03/23/whats-inside-your-git-directory.html)
### 工作目錄 (版本庫的一部分)
版本庫中的目錄和文件,可以看做就是你工作時的目錄
### 索引(.git 目錄)
索引就是git中的 staging 區. 可以算作是把你的工作目錄與Git版本庫分割開的一層 這使得開發者能夠更靈活的決定要將要在版本庫中添加什么內容
### 提交
一個 git 提交就是一組更改或者對工作目錄操作的快照 比如你添加了5個文件,刪除了2個文件,那么這些變化就會被寫入一個提交比如你添加了5個文件,刪除了2個文件,那么這些變化就會被寫入一個提交中 而這個提交之后也可以被決定是否推送到另一個版本庫中
### 分支
分支其實就是一個指向你最后一次的提交的指針 當你提交時,這個指針就會自動指向最新的提交
### 頭指針 與 頭(.git 文件夾的作用)
頭指針是一個指向當前分支的指針,一個版本庫只有一個當前活動的頭指針 而頭則可以指向版本庫中任意一個提交,每個版本庫也可以有多個頭
### 其他形象化解釋
* [給計算機科學家的解釋](http://eagain.net/articles/git-for-computer-scientists/)
* [給設計師的解釋](http://hoth.entp.com/output/git_for_designers.html)
## 命令
### 初始化
創建一個新的git版本庫。這個版本庫的配置、存儲等信息會被保存到.git文件夾中
~~~
$ git init
~~~
### 配置
更改設置。可以是版本庫的設置,也可以是系統的或全局的
~~~
# 輸出、設置基本的全局變量
$ git config --global user.email
$ git config --global user.name
$ git config --global user.email "MyEmail@Zoho.com"
$ git config --global user.name "My Name"
~~~
[關于git的更多設置](http://git-scm.com/docs/git-config)
### 幫助
git內置了對命令非常詳細的解釋,可以供我們快速查閱
~~~
# 查找可用命令
$ git help
# 查找所有可用命令
$ git help -a
# 在文檔當中查找特定的命令
# git help <命令>
$ git help add
$ git help commit
$ git help init
~~~
### 狀態
顯示索引文件(也就是當前工作空間)和當前的頭指針指向的提交的不同
~~~
# 顯示分支,為跟蹤文件,更改和其他不同
$ git status
# 查看其他的git status的用法
$ git help status
~~~
### 添加
添加文件到當前工作空間中。如果你不使用?`git add`?將文件添加進去, 那么這些文件也不會添加到之后的提交之中
~~~
# 添加一個文件
$ git add HelloWorld.java
# 添加一個子目錄中的文件
$ git add /path/to/file/HelloWorld.c
# 支持正則表達式
$ git add ./*.java
~~~
### 分支
管理分支,可以通過下列命令對分支進行增刪改查
~~~
# 查看所有的分支和遠程分支
$ git branch -a
# 創建一個新的分支
$ git branch myNewBranch
# 刪除一個分支
$ git branch -d myBranch
# 重命名分支
# git branch -m <舊名稱> <新名稱>
$ git branch -m myBranchName myNewBranchName
# 編輯分支的介紹
$ git branch myBranchName --edit-description
~~~
### 檢出
將當前工作空間更新到索引所標識的或者某一特定的工作空間
~~~
# 檢出一個版本庫,默認將更新到master分支
$ git checkout
# 檢出到一個特定的分支
$ git checkout branchName
# 新建一個分支,并且切換過去,相當于"git branch <名字>; git checkout <名字>"
$ git checkout -b newBranch
~~~
### clone
這個命令就是將一個版本庫拷貝到另一個目錄中,同時也將 分支都拷貝到新的版本庫中。這樣就可以在新的版本庫中提交到遠程分支
~~~
# clone learnxinyminutes-docs
$ git clone https://github.com/adambard/learnxinyminutes-docs.git
~~~
### commit
將當前索引的更改保存為一個新的提交,這個提交包括用戶做出的更改與信息
~~~
# 提交時附帶提交信息
$ git commit -m "Added multiplyNumbers() function to HelloWorld.c"
~~~
### diff
顯示當前工作空間和提交的不同
~~~
# 顯示工作目錄和索引的不同
$ git diff
# 顯示索引和最近一次提交的不同
$ git diff --cached
# 顯示工作目錄和最近一次提交的不同
$ git diff HEAD
~~~
### grep
可以在版本庫中快速查找
可選配置:
~~~
# 感謝Travis Jeffery提供的以下用法:
# 在搜索結果中顯示行號
$ git config --global grep.lineNumber true
# 是搜索結果可讀性更好
$ git config --global alias.g "grep --break --heading --line-number"
~~~
~~~
# 在所有的java中查找variableName
$ git grep 'variableName' -- '*.java'
# 搜索包含 "arrayListName" 和, "add" 或 "remove" 的所有行
$ git grep -e 'arrayListName' --and \( -e add -e remove \)
~~~
更多的例子可以查看:?[Git Grep Ninja](http://travisjeffery.com/b/2012/02/search-a-git-repo-like-a-ninja)
### log
顯示這個版本庫的所有提交
~~~
# 顯示所有提交
$ git log
# 顯示某幾條提交信息
$ git log -n 10
# 僅顯示合并提交
$ git log --merges
~~~
### merge
合并就是將外部的提交合并到自己的分支中
~~~
# 將其他分支合并到當前分支
$ git merge branchName
# 在合并時創建一個新的合并后的提交
$ git merge --no-ff branchName
~~~
### mv
重命名或移動一個文件
~~~
# 重命名
$ git mv HelloWorld.c HelloNewWorld.c
# 移動
$ git mv HelloWorld.c ./new/path/HelloWorld.c
# 強制重命名或移動
# 這個文件已經存在,將要覆蓋掉
$ git mv -f myFile existingFile
~~~
### pull
從遠端版本庫合并到當前分支
~~~
# 從遠端origin的master分支更新版本庫
# git pull <遠端> <分支>
$ git pull origin master
~~~
### push
把遠端的版本庫更新
~~~
# 把本地的分支更新到遠端origin的master分支上
# git push <遠端> <分支>
# git push 相當于 git push origin master
$ git push origin master
~~~
### rebase (謹慎使用)
將一個分支上所有的提交歷史都應用到另一個分支上?*不要在一個已經公開的遠端分支上使用rebase*.
~~~
# 將experimentBranch應用到master上面
# git rebase <basebranch> <topicbranch>
$ git rebase master experimentBranch
~~~
[更多閱讀](http://git-scm.com/book/en/Git-Branching-Rebasing)
### reset (謹慎使用)
將當前的頭指針復位到一個特定的狀態。這樣可以使你撤銷merge、pull、commits、add等 這是個很強大的命令,但是在使用時一定要清楚其所產生的后果
~~~
# 使 staging 區域恢復到上次提交時的狀態,不改變現在的工作目錄
$ git reset
# 使 staging 區域恢復到上次提交時的狀態,覆蓋現在的工作目錄
$ git reset --hard
# 將當前分支恢復到某次提交,不改變現在的工作目錄
# 在工作目錄中所有的改變仍然存在
$ git reset 31f2bb1
# 將當前分支恢復到某次提交,覆蓋現在的工作目錄
# 并且刪除所有未提交的改變和指定提交之后的所有提交
$ git reset --hard 31f2bb1
~~~
### rm
和add相反,從工作空間中去掉某個文件
~~~
# 移除 HelloWorld.c
$ git rm HelloWorld.c
# 移除子目錄中的文件
$ git rm /pather/to/the/file/HelloWorld.c
~~~
## 更多閱讀
* [tryGit - 學習Git的有趣方式](http://try.github.io/levels/1/challenges/1)
* [git-scm - 視頻教程](http://git-scm.com/videos)
* [git-scm - 文檔](http://git-scm.com/docs)
* [Atlassian Git - 教程與工作流程](https://www.atlassian.com/git/)
* [SalesForce Cheat Sheet](https://na1.salesforce.com/help/doc/en/salesforce_git_developer_cheatsheet.pdf)
* [GitGuys](http://www.gitguys.com/)