# 工作在你的項目上
無論是建立一個全新的本地倉庫(local repository)或是克隆一個遠程倉庫(remote repository)到本地計算機,這兩者并沒有多大的區別。現在在你的計算機上已經擁有了一個 Git 本地倉庫了。這也就意味著你可以在這個項目上開始你的工作了,使用任何一個你常用的編輯器來完成對項目文件的修改、創建、刪除、移動、拷貝或者是重命名。
##### 概念
#### 文件的狀態
一般情況下在 Git 中文件有兩種狀態:
* **未被追蹤的文件(untracked)**: 如果這個文件還未被納入版本控制系統中,我們稱之為“未被追蹤的文件”。這就表示版本控制系統不能監視或者追蹤它的改動。一般情況下未被追蹤的文件會是那些新建的文件,或者是那些沒有被納入版本控制系統中的忽略文件。
* **已追蹤的文件(tracked)**: 所有那些已經被納入版本控制系統的項目文件我們稱之為“已被追蹤的文件”。 Git 會監視它的任何改動,并且你可以提交或放棄對它修改。
## 暫存區(Staging Area)
當你完成了對項目文件的改動后,你想要保存這個改動到你的項目中去。換句話說,你想要提交你在這些已追蹤文件 (tracked files)上的改動。
##### 黃金法則
#### #1: 一次提交只對映一個相關的改動
當你想要進行提交時,非常重要的一點就是,它只能包含一個相關的改動。也就是說,不要在一次提交中包含一些和提交目的無關的改動,每一種不同的改動要打包在不同的提交中。比如,在一次提交中既添加了一個新的功能(例如:用戶注冊功能),又包括了對一些錯誤的修復(例如:修復了Bug#122):
* 理解這些復雜的提交對于開發團隊里的其他成員來說是非常困難的。可能過了一段時間連你自己也糊涂了。你的隊友不得不先要搞明白這個錯誤,弄清楚你為什么要同時修復它,之后才能試圖了解這個新添加的用戶注冊功能。
* 不可能復原或者撤銷其中一個改動。試想一下,那個新添加的用戶注冊功能可能存在很嚴重的錯誤,你想撤銷它但是你又不想撤銷那個修復錯誤的改動。你該怎么辦呢?
也就是說,一次提交一定要包括一個且僅僅只能包括一個和提交目的相對映的改動:修復兩個錯誤(最起碼的)你要進行兩次不同的提交。或者當開發一個新的非常龐大的功能時,你必須要把它分成幾個小的并且在邏輯上有意義的提交,這樣做可以有效地減少產生錯誤的可能性。
這種小的提交可以讓開發團隊的其他成員更好地理解這個改動。一旦發現這個改動有問題,就可以非常簡單地撤銷,而不會影響到其它的功能。
然而,當你專注于在你的項目開發中時,你不可能保證你的每次改動都只對應一個目的,因為你總是在多個方面同時工作的。
這就須要引入一個新的概念 “暫存區(Staging Area)”,這是一個 Git 中最強大的功能,而且也非常好用。它可以讓你來確定哪些修改將被打包在下次提交中。因為在 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)
#
# modified: css/about.css
# modified: css/general.css
# deleted: error.html
# modified: imprint.html
# modified: index.html
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
# new-page.html
no changes added to commit (use "git add" and/or "git commit -a")
```
值得慶幸的是,Git 給我們提供了一個相當詳細的總結,其中包括了三個主類別:
* “Changes not staged for commit” 改動的文件,但沒有打包在下次提交中。
* “Changes to be committed” 改動的文件,并且已打包在下次提交中。
* “Untracked files” 未被追蹤的文件。
## 準備開始提交
現在是時候來提交那些改動到暫存區(Staging Area)里去了,使用 “git add” 命令:
```
$ git add new-page.html index.html css/*
```
以上命名表示,我們添加一個新的文件 “new-page.html”,并且提交對文件 “index.html” 和在目錄 “css” 下所有的改動到暫存區。如果我們想把文件 “error.html” 從版本控制中移除掉,我們必須使用 “git rm” 來完成它:
```
$ git rm error.html
```
讓我們再次使用 “git status” 命令來查看一下詳情列表:
```
$ git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: css/about.css
# modified: css/general.css
# deleted: error.html
# modified: index.html
# new file: new-page.html
#
# 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: imprint.html
#
```
假設對文件 “imprint.html” 的改動和其他的改動沒有任何聯系,我們有意地不把這個文件添加到暫存區域中,那就表示,它不會被打包在下次提交中。對它來說現在僅僅是一個本地修改。我們可以繼續對它進行工作,可能在以后的某個時間再去提交它。
## 提交你的工作
有了精心準備好的暫存區后,在提交之前還剩下一件事,那就是注釋這次提交。
##### 黃金法則
#### #2: 高質量的提交注釋
花一點時間寫一個好的提交注釋是非常值得的,這樣可以讓開發團隊的其他成員非常容易地明白你做這次提交的目的和你的改動(過了一段時間對你自己也有幫助)。
針對你的改動寫一個簡短的注釋標題(原則上不要超過50個字符),然后使用一個空行來分隔注釋的內容。注釋的內容要盡可能的詳細并且要能回答以下幾個問題:為什么要做這次修改?與上一個版本相比你到底改動了什么?
“git commit” 命令將提交你的修改:
```
$ git commit -m "Implement the new login box"
```
如果你有一個很長的提交注釋,并且注釋中包含很多段落,那你就不需要使用 “-m” 參數了,Git 會為你打開一個文本編輯器(具體打開哪個文本編輯器,你可以在 “core.editor” 設置它)。
##### 概念
#### 什么才是一個好的提交
一個高質量的手動提交對你的項目和你自己是非常有意義的。什么才是一個好的提交呢?在這里有一些基本的原則:
1. 提交要僅僅對應一個相關的改動
首先,一次提交應該僅僅只對應一個相關的改動。不要把那些互相毫無關聯的改動打包在同一次提交中。如果這次提交出現了什么問題,解決和撤銷它將是非常困難的。
2. 完整的提交
千萬不要提交那些沒有完全完成的改動。如果你想要臨時保存一下你當前的工作,例如一個類似于剪貼板 (clipboard) 的功能,你可以使用 Git 提供的 “Stash” 功能(這個功能我們將在本書之后的章節為大家介紹)。但是一定不要直接提交它。
3. 提交前測試
當你提交你的改動時,不要理所當然地認為你的改動永遠正確。在你提交你的改動到你的倉庫前,進行有效的測試是非常重要的。
4. 高質量的提交注釋
一次高質量的提交需要一個好注釋。請參考本章之前的章節 “高質量的提交注釋”。
最后,你須要養成一個**頻繁地**進行提交的習慣。這樣做將自然而然的讓你避免一個很龐大的提交,并且使這些提交可以更好只對映一個相關的改動。
## 檢查的提交歷史
Git 會記錄對項目改動的所有提交。特別是當與其他團隊開發人員協同開發時。及時地查看那些最新的提交并理解它是非常重要的。
##### 注釋
在本書之后的章節 “[通過遠程倉庫共享工作](https://www.git-tower.com/learn/git/ebook/cn/command-line/remote-repositories)”,我們將向你詳細介紹如何同團隊的其他開發人員進行數據交換。
顯示在項目中所有的提交歷史記錄可以使用 “git log” 命令:
```
$ git log
```
所有在這個項目中的提交將按時間順序顯示出來,最新的提交會出現在最上面。如果說所有的提交歷史記錄不能完全顯示在一個屏幕中,在命名行界面的最下方會顯示一個冒號 (“:”)。這時你可以使用空格鍵來跳轉到下一頁,或是使用 “q” 鍵退出這個界面。
```
commit 2dfe283e6c81ca48d6edc1574b1f2d4d84ae7fa1
Author: Tobias Günther <support@learn-git.com>
Date: Fri Jul 26 10:52:04 2013 +0200
Implement the new login box
commit 2b504bee4083a20e0ef1e037eea0bd913a4d56b6
Author: Tobias Günther <support@learn-git.com>
Date: Fri Jul 26 10:05:48 2013 +0200
Change headlines for about and imprint
commit 0023cdddf42d916bd7e3d0a279c1f36bfc8a051b
Author: Tobias Günther <support@learn-git.com>
Date: Fri Jul 26 10:04:16 2013 +0200
Add simple robots.txt
```
每個提交記錄都包括(除其他事項外)如下的元數據(metadata):
* Commit Hash
* Author Name & Email (提交人的姓名和電子郵箱)
* Date (日期)
* Commit Message (提交注釋)
##### 名詞解釋
#### The Commit Hash
每一個提交都擁有一個唯一的 ID :這個40位字符的校驗碼我們稱之為 “commit hash”。在那些集中式的版本控制系統中(例如: Subversion, CVS...)會使用一個依次遞加的版本號碼。這樣雖然簡單,但是它卻不能被使用在 Git 這樣的分布式版本控制系統中。原因就是在于,在 Git 中每個人的工作都是并行的,改動的 **提交** 都是 **離線的** ,也就是說提交時并不需要連接到遠程倉庫。在這種情況下,遠程倉庫就不能確定哪個是第5次提交, 而哪個是在它之后的第6次提交。
在大多數項目中,這個哈希編碼的前七位字符就已經能足夠代表一個唯一的提交ID了,一般我們都會用這個簡短的7位 ID 來代表一個提交。
除了這些元數據(metadata),Git 也可以顯示出提交的更詳細的內容。在 “git log” 命令后使用 “-p” 參數來顯示一些更多的提交記錄詳情:
```
$ git log -p
commit 2dfe283e6c81ca48d6edc1574b1f2d4d84ae7fa1
Author: Tobias Günther <support@learn-git.com>
Date: Fri Jul 26 10:52:04 2013 +0200
Implement the new login box
diff --git a/css/about.css b/css/about.css
index e69de29..4b5800f 100644
--- a/css/about.css
+++ b/css/about.css
@@ -0,0 +1,2 @@
+h1 {
+ line-height:30px; }
\ No newline at end of file
di.ff --git a/css/general.css b/css/general.css
index a3b8935..d472b7f 100644
--- a/css/general.css
+++ b/css/general.css
@@ -21,7 +21,8 @@ body {
h1, h2, h3, h4, h5 {
color:#ffd84c;
- font-family: "Trebuchet MS", "Trebuchet"; }
+ font-family: "Trebuchet MS", "Trebuchet";
+ margin-bottom:0px; }
p {
margin-bottom:6px;}
diff --git a/error.html b/error.html
deleted file mode 100644
index 78alc33..0000000
--- a/error.html
+++ /dev/null
@@ -1,43 +0,0 @@
- <html>
-
- <head>
- <title>Tower :: Imprint</title>
- <link rel="shortcut icon" href="img/favicon.ico" />
- <link type="text/css" href="css/general.css" />
- </head>
-
```
在本書之后的章節 "[使用diffs檢查改動的詳情](https://www.git-tower.com/learn/git/ebook/cn/command-line/advanced-topics/diffs)", 我們將會來一起學習這些輸出內容的具體含義。
## 值得慶祝的時刻
祝賀你!你剛剛掌握了 Git 版本控制系統中的最基本步驟!休息一下,在我們開始下一步的學習之前先來干杯啤酒。
- Learn Version Control with Git 中文版
- 前言
- Part 1 - 基礎知識
- 什么是版本控制?
- 為什么要使用版本控制系統?
- 準備工作
- 版本控制的基本工作流程
- 從一個未被納入版本控制的項目開始
- 從一個已被納入版本控制的項目開始
- 工作在你的項目上
- Part 2 - 分支與合并
- 分支可以改變你的生命
- 在分支上工作
- 暫時保存更改
- 切換一個本地分支
- 合并改動
- 分支的工作流程
- Part 3 - 遠程倉庫
- 關于遠程倉庫
- 連接一個遠程倉庫
- 查看遠程數據
- 整合遠程的改動
- 發布一個本地分支
- 刪除分支
- Part 4 - 高級應用
- 撤銷操作
- 用 diff 來檢查改動
- 處理合并沖突
- Rebase 代替合并
- 子模塊
- git-flow 的工作流程
- 使用 SSH 公鑰驗證
- Part 5 - 工具與服務
- 桌面應用程序
- 比較和整合工具
- 代碼托管服務
- 更多學習資源
- 附錄
- 版本控制的最佳實踐
- 命令 101
- 從 Subversion 過渡到 Git
- 為什么選擇 Git