# 工作在你的项目上
无论是建立一个全新的本地仓库(local repository)或是克隆一个远程仓库(remote repository)到本地计算机,这两者并没有多大的区别。现在在你的计算机上已经拥有了一个 Git 本地仓库了。这也就意味着你可以在这个项目上开始你的工作了,使用任何一个你常用的编辑器来完成对项目文件的修改、创建、删除、移动、拷贝或者是重命名。
##### 概念
#### 文件的状态
一般情况下在 Git 中文件有两种状态:
* **未被追踪的文件(untracked)**: 如果这个文件还未被纳入版本控制系统中,我们称之为“未被追踪的文件”。这就表示版本控制系统不能监视或者追踪它的改动。一般情况下未被追踪的文件会是那些新建的文件,或者是那些没有被纳入版本控制系统中的忽略文件。
* **已追踪的文件(tracked)**: 所有那些已经被纳入版本控制系统的项目文件我们称之为“已被追踪的文件”。 Git 会监视它的任何改动,并且你可以提交或放弃对它修改。
## 暂存区(Staging Area)
当你完成了对项目文件的改动后,你想要保存这个改动到你的项目中去。换句话说,你想要提交你在这些已追踪文件 (tracked files)上的改动。
##### 黄金法则
#### #1: 一次提交只对映一个相关的改动
当你想要进行提交时,非常重要的一点就是,它只能包含一个相关的改动。也就是说,不要在一次提交中包含一些和提交目的无关的改动,每一种不同的改动要打包在不同的提交中。比如,在一次提交中既添加了一个新的功能(例如:用户注册功能),又包括了对一些错误的修复(例如:修复了Bug#122):
* 理解这些复杂的提交对于开发团队里的其他成员来说是非常困难的。可能过了一段时间连你自己也糊涂了。你的队友不得不先要搞明白这个错误,弄清楚你为什么要同时修复它,之后才能试图了解这个新添加的用户注册功能。
* 不可能复原或者撤销其中一个改动。试想一下,那个新添加的用户注册功能可能存在很严重的错误,你想撤销它但是你又不想撤销那个修复错误的改动。你该怎么办呢?
也就是说,一次提交一定要包括一个且仅仅只能包括一个和提交目的相对映的改动:修复两个错误(最起码的)你要进行两次不同的提交。或者当开发一个新的非常庞大的功能时,你必须要把它分成几个小的并且在逻辑上有意义的提交,这样做可以有效地减少产生错误的可能性。
这种小的提交可以让开发团队的其他成员更好地理解这个改动。一旦发现这个改动有问题,就可以非常简单地撤销,而不会影响到其它的功能。
然而,当你专注于在你的项目开发中时,你不可能保证你的每次改动都只对应一个目的,因为你总是在多个方面同时工作的。
这就须要引入一个新的概念 “暂存区(Staging Area)”,这是一个 Git 中最强大的功能,而且也非常好用。它可以让你来确定哪些修改将被打包在下次提交中。因为在 Git 中并不是简单的把每一个改动后的文件都自动打包起来的。相反,每一次提交打包都要 “手动完成”,哪些改动需要被打包在下次提交中都需要你自己决定,是否需要 “添加到暂存区域” 或者简单的说 “被暂存”。
![](https://box.kancloud.cn/2016-05-04_572967d702200.png)
## 得到所有的改动的详情
让我们来看看到目前为止都已经完成了什么。为了得到你在上一次提交后的所有改动详情,你可以简单地使用这个命令 “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