GitHub:Start

Git原理

直接记录快照,而非差异比较

Git 和其它版本控制系统(包括 Subversion 和近似工具)的主要差别在于 Git 对待数据的方法。 概念上来区分,其它大部分系统以文件变更列表的方式存储信息。 这类系统(CVS、Subversion、Perforce、Bazaar 等等)将它们保存的信息看作是一组基本文件和每个文件随时间逐步累积的差异。存储每个文件与初始版本的差异,如下图所示 -

img

Git 不按照以上方式对待或保存数据。 反之,Git 更像是把数据看作是对小型文件系统的一组快照。 每次你提交更新,或在 Git 中保存项目状态时,它主要对当时的全部文件制作一个快照并保存这个快照的索引。 为了高效,如果文件没有修改,Git 不再重新存储该文件,而是只保留一个链接指向之前存储的文件。 Git 对待数据更像是一个 快照流。如下图所示 -

img

这是 Git 与几乎所有其它版本控制系统的重要区别。 因此 Git 重新考虑了以前每一代版本控制系统延续下来的诸多方面。 Git 更像是一个小型的文件系统,提供了许多以此为基础构建的超强工具,而不只是一个简单的 VCS。 稍后我们在 Git 分支讨论 Git 分支管理时,将探究这种方式对待数据所能获得的益处。

近乎所有操作都是本地执行

在 Git 中的绝大多数操作都只需要访问本地文件和资源,一般不需要来自网络上其它计算机的信息。 如果你习惯于所有操作都有网络延时开销的集中式版本控制系统,Git 在这方面会让你感到速度之神赐给了 Git 超凡的能量。 因为你在本地磁盘上就有项目的完整历史,所以大部分操作看起来瞬间完成。

举个例子,要浏览项目的历史,Git 不需外连到服务器去获取历史,然后再显示出来——它只需直接从本地数据库中读取。 你能立即看到项目历史。 如果想查看当前版本与一个月前的版本之间引入的修改,Git 会查找到一个月前的文件做一次本地的差异计算,而不是由远程服务器处理或从远程服务器拉回旧版本文件再来本地处理。

这也意味着你离线或者没有 VPN 时,几乎可以进行任何操作。 如你在飞机或火车上想做些工作,你能愉快地提交,直到有网络连接时再上传。 如你回家后 VPN 客户端不正常,你仍能工作。 使用其它系统,做到如此是不可能或很费力的。 比如,用 Perforce,你没有连接服务器时几乎不能做什么事;用 Subversion 和 CVS,你能修改文件,但不能向数据库提交修改(因为你的本地数据库离线了)。 这看起来不是大问题,但是你可能会惊喜地发现它带来的巨大的不同。

Git 保证完整性

Git 中所有数据在存储前都计算校验和,然后以校验和来引用。 这意味着不可能在 Git 不知情时更改任何文件内容或目录内容。 这个功能建构在 Git 底层,是构成 Git 哲学不可或缺的部分。 若你在传送过程中丢失信息或损坏文件,Git 就能发现。

Git 用以计算校验和的机制叫做 SHA-1 散列(hash,哈希)。 这是一个由 40 个十六进制字符(0-9a-f)组成字符串,基于 Git 中文件的内容或目录结构计算出来。 SHA-1 哈希看起来是这样:

1
24b9da6552252987aa493b52f8696cd6d3b0037

Git 中使用这种哈希值的情况很多,你将经常看到这种哈希值。 实际上,Git 数据库中保存的信息都是以文件内容的哈希值来索引,而不是文件名。

Git三种状态

请注意!如果你希望后面的学习更顺利,记住下面这些关于 Git 的概念。 Git 有三种状态,你的文件可能处于其中之一:已提交(committed)、已修改(modified)和已暂存(staged)。

  • 已提交表示数据已经安全的保存在本地数据库中。
  • 已修改表示修改了文件,但还没保存到数据库中。
  • 已暂存表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中。

由此引入 Git 项目的三个工作区域的概念:Git 仓库、工作目录以及暂存区域。工作目录、暂存区域以及 Git 仓库如下图所示 -

img

Git 仓库目录是 Git 用来保存项目的元数据和对象数据库的地方。 这是 Git 中最重要的部分,从其它计算机克隆仓库时,拷贝的就是这里的数据。

工作目录是对项目的某个版本独立提取出来的内容。 这些从 Git 仓库的压缩数据库中提取出来的文件,放在磁盘上供你使用或修改。

暂存区域是一个文件,保存了下次将提交的文件列表信息,一般在 Git 仓库目录中。 有时候也被称作‘索引’,不过一般说法还是叫暂存区域。

基本的 Git 工作流程如下:

  1. 在工作目录中修改文件。
  2. 暂存文件,将文件的快照放入暂存区域。
  3. 提交更新,找到暂存区域的文件,将快照永久性存储到 Git 仓库目录。

如果 Git 目录中保存着的特定版本文件,就属于已提交状态。 如果作了修改并已放入暂存区域,就属于已暂存状态。 如果自上次取出后,作了修改但还没有放到暂存区域,就是已修改状态。

术语

img

  • workspace:工作区
    • 通过git init创建的代码库的所有文件但是不包括.git文件(版本库)
  • index/stage:暂存区,也叫索引
    • 通过git add ./*/*Xxx/Xxxx* 添加的修改,都是进入到暂存区了,肉眼不可见 通过 git status 可以看到修改的状态。
  • repository:仓库区(本地仓库),也叫存储库
  • remote:远程仓库

code review

对代码进行评阅

bugfix

修复bug

pull request

提交合并请求

merge

合并其他人提出的pull请求,对pull进行审核,若满足则进行冲突解决并合并到项目当中

Git快速入门

能够配置并初始化一个仓库(repository)、开始或停止跟踪(track)文件、暂存(stage)或提交(commit)更改。 本章也将演示如何配置 Git 来忽略指定的文件和文件模式、如何迅速而简单地撤销错误操作、如何浏览项目的历史版本以及不同提交(commits)间的差异、如何向远程仓库推送(push)以及如何从远程仓库拉取(pull)文件。

远程仓库是什么?

Repository(仓库)包含的内容 - Git的目标是管理一个工程,或者说是一些文件的集合,以跟踪它们的变化。Git使用Repository来存储这些信息。一个仓库主要包含以下内容(也包括其他内容):

  • 许多commit objects
  • 到commit objects的指针,叫做heads
  • Git的仓库和工程存储在同一个目录下,在一个叫做.git的子目录中。

创建Repository(略过)

获取Git仓库

有两种取得 Git 项目仓库的方法。第一种是从一个服务器克隆一个现有的 Git 仓库。第二种是在现有项目或目录下导入所有文件到 Git 中;

更新提交到仓库中

记录每次更新到仓库

在一个真实的Git仓库当中,对一些文件作出修改,完成一个阶段性目标后,提交本次更新到仓库中。

工作目录下的每一个文件都不外乎这两种状态:已跟踪或未跟踪。 已跟踪的文件是指那些被纳入了版本控制的文件,在上一次快照中有它们的记录,在工作一段时间后,它们的状态可能处于未修改,已修改或已放入暂存区。 工作目录中除已跟踪文件以外的所有其它文件都属于未跟踪文件,它们既不存在于上次快照的记录中,也没有放入暂存区。 初次克隆某个仓库的时候,工作目录中的所有文件都属于已跟踪文件,并处于未修改状态。

编辑过某些文件之后,由于自上次提交后你对它们做了修改,Git 将它们标记为已修改文件。 我们逐步将这些修改过的文件放入暂存区,然后提交所有暂存了的修改,如此反复。所以使用 Git 时文件的生命周期如下:

img

检查当前文件状态 Git status

要查看哪些文件处于什么状态,可以用 git status 命令。 如果在克隆仓库后立即使用此命令,会看到类似这样的输出:

1
$ git status

跟踪新文件 Git add (file)

使用命令 git add 开始跟踪一个文件。 所以,要跟踪 mytext.txt 文件,运行:

1
$ git add mytext.txt

此时再运行 git status 命令,会看到 mytext.txt 文件已被跟踪,并处于暂存状态:

1
2
3
4
5
6
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

new file: mytext.txt

只要在 Changes to be committed 这行下面的,就说明是已暂存状态。 如果此时提交,那么该文件此时此刻的版本将被留存在历史记录中。git add 命令使用文件或目录的路径作为参数;如果参数是目录的路径,该命令将递归地跟踪该目录下的所有文件。

暂存已修改文件 git add

现在我们来修改一个已被跟踪的文件。 如果修改了一个名为 README.md 的已被跟踪的文件,打开文件 README.md并编辑其中的内容,在文件的未尾加入一行内容:”这是暂存已修改文件示例”,然后运行 git status 命令,会看到下面内容:

1
2
3
4
5
6
7
8
9
10
11
12
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

new file: mytext.txt

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.md

文件 README.md 出现在 Changes not staged for commit 这行下面,说明已跟踪文件的内容发生了变化,但还没有放到暂存区。要暂存这次更新,需要运行 git add 命令。 这是个多功能命令:可以用它开始跟踪新文件,或者把已跟踪的文件放到暂存区,还能用于合并时把有冲突的文件标记为已解决状态等。 将这个命令理解为“添加内容到下一次提交中”而不是“将一个文件添加到项目中”要更加合适。 现在让我们运行 git add 将”README.md“放到暂存区,然后再看看 git status 的输出:

1
2
3
4
5
6
7
8
9
10
11
12
$ git add README.md

Administrator@MY-PC /F/worksp/git-start (master)
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.

Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

modified: README.md
new file: mytext.txt

现在两个文件都已暂存,下次提交时就会一并记录到仓库。 假设此时,想要在 README.md 里再加条注释, 重新编辑存盘后,准备好提交。不过且慢,先向 “README.md” 文件加入一点内容,再运行 git status ,如下所示 -

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ echo "Add new Line content 1002 " >> README.md
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

new file: README
modified: mytext.txt

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.md

在上面可以看到,README.md出现在了两个地方

实际上 Git 只不过暂存了运行 git add 命令时的版本, 如果现在提交,README.md 的版本是最后一次运行 git add 命令时的那个版本,而不是运行 git commit 时,在工作目录中的当前版本。 所以,运行了 git add 之后又作了修订的文件,需要重新运行 git add 把最新版本重新暂存起来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ git add README.md

Administrator@MY-PC /F/worksp/git-start (master)
$ git status
warning: LF will be replaced by CRLF in README.md.
The file will have its original line endings in your working directory.
On branch master
Your branch is up-to-date with 'origin/master'.

Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

modified: README.md
new file: mytext.txt

状态简览 git status -s

git status 命令的输出十分详细,但其用语有些繁琐。 如果你使用 git status -s 命令或 git status --short 命令,将得到一种更为紧凑的格式输出。 运行 git status -s,状态报告输出如下:

1
2
3
4
5
6
$ git status -s
M README.md
MM Rakefile
A lib/git.rb
M lib/simplegit.rb
?? LICENSE.txt
  • 新添加的未跟踪文件前面有 ?? 标记
  • 新添加到暂存区中的文件前面有 A 标记
  • 修改过的文件前面有 M 标记。 你可能注意到了 M 有两个可以出现的位置
  • 出现在右边的 M 表示该文件被修改了但是还没放入暂存区
  • 出现在靠左边的 M 表示该文件被修改了并放入了暂存区。

例如,上面的状态报告显示: README 文件在工作区被修改了但是还没有将修改后的文件放入暂存区,lib/simplegit.rb 文件被修改了并将修改后的文件放入了暂存区。 而 Rakefile 在工作区被修改并提交到暂存区后又在工作区中被修改了,所以在暂存区和工作区都有该文件被修改了的记录。

忽略文件

一般我们总会有些文件无需纳入 Git 的管理,也不希望它们总出现在未跟踪文件列表。 通常都是些自动生成的文件,比如日志文件,或者编译过程中创建的临时文件等。 在这种情况下,我们可以创建一个名为 .gitignore 的文件,列出要忽略的文件模式。 来看一个实际的例子:

1
2
3
4
5
6
$ cat .gitignore
*.[oa]
*~


Shell

第一行告诉 Git 忽略所有以 .o.a 结尾的文件。一般这类对象文件和存档文件都是编译过程中出现的。 第二行告诉 Git 忽略所有以波浪符(~)结尾的文件,许多文本编辑软件(比如 Emacs)都用这样的文件名保存副本。 此外,你可能还需要忽略 logtmp 或者 pid 目录,以及自动生成的文档等等。 要养成一开始就设置好 .gitignore 文件的习惯,以免将来误提交这类无用的文件。

文件 .gitignore 的格式规范如下:

  • 所有空行或者以 开头的行都会被 Git 忽略。
  • 可以使用标准的 glob 模式匹配。
  • 匹配模式可以以(/)开头防止递归。
  • 匹配模式可以以(/)结尾指定目录。
  • 要忽略指定模式以外的文件或目录,可以在模式前加上惊叹号(!)取反。

所谓的 glob 模式是指 shell 所使用的简化了的正则表达式。 星号(*)匹配零个或多个任意字符;[abc]匹配任何一个列在方括号中的字符(这个例子要么匹配一个字符 a,要么匹配一个字符 b,要么匹配一个字符 c);问号(?)只匹配一个任意字符;如果在方括号中使用短划线分隔两个字符,表示所有在这两个字符范围内的都可以匹配(比如 [0-9] 表示匹配所有 09 的数字)。 使用两个星号(*) 表示匹配任意中间目录,比如a/**/z 可以匹配 a/z, a/b/za/b/c/z等。

下面再看一个 .gitignore 文件的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# no .a files
*.a

# but do track lib.a, even though you're ignoring .a files above
!lib.a

# only ignore the TODO file in the current directory, not subdir/TODO
/TODO

# ignore all files in the build/ directory
build/

# ignore doc/notes.txt, but not doc/server/arch.txt
doc/*.txt

# ignore all .pdf files in the doc/ directory
doc/**/*.pdf

提示:GitHub 有一个十分详细的针对数十种项目及编程语言的 .gitignore 文件列表,你可以在 http://github.com/github/gitignore 找到它。

查看已暂存和未暂存的修改

如果 git status 命令的输出对于你来说过于模糊,你想知道具体修改了什么地方,可以用 git diff 命令。 稍后我们会详细介绍 git diff,可能通常会用它来回答这两个问题:

  • 当前做的哪些更新还没有暂存?
  • 有哪些更新已经暂存起来准备好了下次提交?

尽管 git status 已经通过在相应栏下列出文件名的方式回答了这个问题,git diff 将通过文件补丁的格式显示具体哪些行发生了改变。

假如再次修改 README.md 文件后暂存,然后编辑 READ.md 文件并在文件的最后追加一行内容:”this is another line 1003“ 之后先不暂存, 运行 git status 命令将会看到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ echo "this is another line 1003 " >> README.md
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.

Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

modified: README.md
new file: mytext.txt

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.md

要查看尚未暂存的文件更新了哪些部分,不加参数直接输入 git diff

1
2
3
4
5
6
7
8
9
10
11
$ git diff
diff --git a/README.md b/README.md
index ea161e2..6679481 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,3 @@
Add new Line content 1001
Add new Line content 1002
+this is another line 1003
warning: LF will be replaced by CRLF in README.md.
The file will have its original line endings in your working directo(END)

上面输出显示有加一行“+this is another line 1003”,前面带有一个加号:“+”。

请注意,git diff 本身只显示尚未暂存的改动,而不是自上次提交以来所做的所有改动。 所以有时候你一下子暂存了所有更新过的文件后,运行 git diff 后却什么也没有,就是这个原因。

然后用 git diff --cached 查看已经暂存起来的变化:(--staged--cached 是同义词)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ git diff --cached
diff --git a/README.md b/README.md
index 2f88ca7..ea161e2 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,2 @@
-#git-start
-这是一个 Git 学习使用的Git仓库。
\ No newline at end of file
+Add new Line content 1001
+Add new Line content 1002
diff --git a/mytext.txt b/mytext.txt
new file mode 100644
index 0000000..1820ae1
--- /dev/null
+++ b/mytext.txt
@@ -0,0 +1 @@
+This is my first Git control file
warning: LF will be replaced by CRLF in mytext.txt.
The file will have its original line endings in your working directory.

如上图中所示,分别对比了两个文件:README.mdmytext.txt,其中绿色的内容表示添加,红色的内容表示删除。

注意:git diff 的插件版本,在本教程中,我们使用 git diff 来分析文件差异。 但是,如果你喜欢通过图形化的方式或其它格式输出方式的话,可以使用 git difftool 命令来用 Araxisemergevimdiff 等软件输出 diff 分析结果。 使用 git difftool --tool-help 命令来看你的系统支持哪些 Git Diff 插件。

提交更新 git commit

现在的暂存区域已经准备妥当可以提交了。 在此之前,请一定要确认还有什么修改过的或新建的文件还没有 git add 过,否则提交的时候不会记录这些还没暂存起来的变化。 这些修改过的文件只保留在本地磁盘。 所以,每次准备提交前,先用 git status 看下,是不是都已暂存起来了,如果没有暂存起来则要先使用命令:git add .将所有文件暂存起来, 然后再运行提交命令 git commit

1
2
3
$ git status
$ git add .
$ git commit

这种方式会启动文本编辑器以便输入本次提交的说明。 (默认会启用 shell 的环境变量 $EDITOR 所指定的软件,一般都是 vimemacs。使用 git config --global core.editor 命令设定你喜欢的编辑软件。)

编辑器会显示类似下面的文本信息(本例选用 Vim 的屏显方式展示):

1
2
3
4
5
6
7
8
9
10
11
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# Changes to be committed:
# new file: README
# modified: CONTRIBUTING.md
#
this is my commit info note.
~
~
".git/COMMIT_EDITMSG" 9L, 283C

可以看到,默认的提交消息包含最后一次运行 git status 的输出,放在注释行里,另外开头还有一空行,供你输入提交说明。完全可以去掉这些注释行,不过留着也没关系,多少能帮你回想起这次更新的内容有哪些。 (如果想要更详细的对修改了哪些内容的提示,可以用 -v 选项,这会将你所做的改变的 diff 输出放到编辑器中从而使你知道本次提交具体做了哪些修改。) 退出编辑器时,Git 会丢掉注释行,用输入提交附带信息生成一次提交。如上面示例中,提交的备注信息是:“this is my commit info note.”。

另外,也可以在 commit 命令后添加 -m 选项,将提交信息与命令放在同一行,如下所示:

1
2
3
4
$ git commit -m "this is my commit info note."
[master 463dc4f] Story 182: Fix benchmarks for speed
2 files changed, 2 insertions(+)
create mode 100644 README.md

现在已经创建了第一个提交! 可以看到,提交后它会告诉你,当前是在哪个分支(master)提交的,本次提交的完整 SHA-1 校验和是什么(463dc4f),以及在本次提交中,有多少文件修订过,多少行添加和删改过。

请记住,提交时记录的是放在暂存区域的快照。任何还未暂存的仍然保持已修改状态,可以在下次提交时纳入版本管理。 每一次运行提交操作,都是对你项目作一次快照,以后可以回到这个状态,或者进行比较。

跳过使用暂存区域

尽管使用暂存区域的方式可以精心准备要提交的细节,但有时候这么做略显繁琐。 Git 提供了一个跳过使用暂存区域的方式, 只要在提交的时候,给 git commit 加上 -a 选项,Git 就会自动把所有已经跟踪过的文件暂存起来一并提交,从而跳过 git add 步骤:

1
2
3
4
5
6
7
8
9
10
11
12
$ 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.md

no changes added to commit (use "git add" and/or "git commit -a")
$ git commit -a -m 'added new benchmarks'
[master 83e38c7] added new benchmarks
1 file changed, 5 insertions(+), 0 deletions(-)

看到了吗?提交之前不再需要 git add 文件“README.md”了。

移除文件(取消跟踪并删除文件) git rm (file)

要从 Git 中移除某个文件,就必须要从已跟踪文件清单中移除(确切地说,是从暂存区域移除),然后提交。 可以用 git rm 命令完成此项工作,并连带从工作目录中删除指定的文件,这样以后就不会出现在未跟踪文件清单中了。

如果只是简单地从工作目录中手工删除文件,运行 git status 时就会在 “Changes not staged for commit” 部分(也就是 未暂存清单)看到:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ rm mytext.txt
$ git status
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
(use "git push" to publish your local commits)

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: mytext.txt

no changes added to commit (use "git add" and/or "git commit -a")

下一次提交时,该文件就不再纳入版本管理了。 如果删除之前修改过并且已经放到暂存区域的话,则必须要用强制删除选项 -f(注:即 force 的首字母)。 这是一种安全特性,用于防止误删还没有添加到快照的数据,这样的数据不能被 Git 恢复。

另外一种情况是,我们想把文件从 Git 仓库中删除(亦即从暂存区域移除),但仍然希望保留在当前工作目录中。 换句话说,你想让文件保留在磁盘,但是并不想让 Git 继续跟踪。 当你忘记添加 .gitignore 文件,不小心把一个很大的日志文件或一堆 .a 这样的编译生成文件添加到暂存区时,这一做法尤其有用。 为达到这一目的,使用 --cached 选项:

1
2
3
4
5
6
7
8
9
10
$ git rm --cached mytext.txt
$ git status
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
(use "git push" to publish your local commits)

Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

deleted: mytext.txt

git rm 命令后面可以列出文件或者目录的名字,也可以使用 glob 模式。 比方说:

1
$ git rm log/\*.log

注意到星号 * 之前的反斜杠 \, 因为 Git 有它自己的文件模式扩展匹配方式,所以我们不用 shell 来帮忙展开。 此命令删除 log/ 目录下扩展名为 .log 的所有文件。 类似的比如:

1
$ git rm \*~

该命令为删除以 ~ 结尾的所有文件。

移动文件

不像其它的 VCS 系统,Git 并不 显式跟踪文件移动操作。 如果在 Git 中重命名了某个文件,仓库中存储的元数据并不会体现出这是一次改名操作。 不过 Git 非常聪明,它会推断出究竟发生了什么,至于具体是如何做到的,我们稍后再谈。

既然如此,当你看到 Git 的 mv 命令时一定会困惑不已。 要在 Git 中对文件改名,可以这么做:

1
$ git mv file_from file_to

它会恰如预期般正常工作。 实际上,即便此时查看状态信息,也会明白无误地看到关于重命名操作的说明:

1
2
3
4
5
6
7
$ git mv README.md README
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

renamed: README.md -> README

其实,运行 git mv 就相当于运行了下面三条命令:

1
2
3
$ mv README.md README
$ git rm README.md
$ git add README

如此分开操作,Git 也会意识到这是一次改名,所以不管何种方式结果都一样。 两者唯一的区别是,mv 是一条命令而另一种方式需要三条命令,直接用 git mv 轻便得多。 不过有时候用其他工具批处理改名的话,要记得在提交前删除老的文件名,再添加新的文件名。

查看提交历史

git log

撤销操作

重新提交 Git commit –amend

有时候我们提交完了才发现漏掉了几个文件没有添加,或者提交信息写错了。 此时,可以运行带有 --amend 选项的提交命令尝试重新提交:

1
$ git commit --amend

这个命令会将暂存区中的文件提交。 如果自上次提交以来你还未做任何修改(例如,在上次提交后马上执行了此命令),那么快照会保持不变,而你所修改的只是提交信息。

文本编辑器启动后,可以看到之前的提交信息。 编辑后保存会覆盖原来的提交信息。

例如,提交后发现忘记了暂存某些需要的修改,可以像下面这样操作:

1
2
3
$ git commit -m 'initial commit'
$ git add forgotten_file
$ git commit --amend

最终你只会有一个提交 - 第二次提交将代替第一次提交的结果。

取消暂存的文件 git reset head (file)

接下来的两个小节演示如何操作暂存区域与工作目录中已修改的文件。 这些命令在修改文件状态的同时,也会提示如何撤消操作。 例如,你已经修改了两个文件并且想要将它们作为两次独立的修改提交,但是却意外地输入了 git add * 暂存了它们两个。 如何只取消暂存两个中的一个呢? git status 命令提示:

1
2
3
4
5
6
7
8
9
10
11
$ git add *
$ git status
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
(use "git push" to publish your local commits)

Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

renamed: README.md -> README
deleted: mytext.txt

在 “Changes to be committed” 文字正下方,提示使用 git reset HEAD <file>... 来取消暂存。 所以,我们可以这样来取消暂存 mytext.txt 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ git reset HEAD mytext.txt
$ git status
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
(use "git push" to publish your local commits)

Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

renamed: README.md -> README

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: mytext.txt

撤消对文件的修改 git checkout – (file)

如果并不想保留对 mytext.txt 文件的修改怎么办? 该如何方便地撤消修改 - 将它还原成上次提交时的样子(或者刚克隆完的样子,或者刚把它放入工作目录时的样子)? 幸运的是,git status 也告诉了你应该如何做。 在最后一个例子中,未暂存区域是这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ git status
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
(use "git push" to publish your local commits)

Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

renamed: README.md -> README

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: mytext.txt

它非常清楚地告诉了如何撤消之前所做的修改。让我们来按照提示执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ git checkout -- mytext.txt

Administrator@MY-PC /F/worksp/git-start (master)
$ git status
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
(use "git push" to publish your local commits)

Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

renamed: README.md -> README


Administrator@MY-PC /F/worksp/git-start (master)
$ ls
README mytext.txt

可以看到,mytext.txt文件又回来了。

如果仍然想保留对那个文件做出的修改,但是现在仍然需要撤消,我们将会在 Git 分支介绍保存进度与分支;这些通常是更好的做法。

记住,在 Git 中任何已提交的东西几乎总是可以恢复的。甚至那些被删除的分支中的提交或使用 --amend 选项覆盖的提交也可以恢复。然而,任何你未提交的东西丢失后很可能再也找不到了。

远程仓库的使用

查看远程仓库 git remote

如果想查看你已经配置的远程仓库服务器,可以运行 git remote 命令。 它会列出你指定的每一个远程服务器的简写。 如果已经克隆了自己的仓库,那么至少应该能看到 origin - 这是 Git 给你克隆的仓库服务器的默认名字:

1
2
3
4
5
6
7
8
9
10
11
12
$ git clone http://git.oschina.net/yiibai/git-start.git
Cloning into 'ticgit'...
remote: Reusing existing pack: 1857, done.
remote: Total 157 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (1857/1857), 74.35 KiB | 168.00 KiB/s, done.
Resolving deltas: 100% (772/772), done.
Checking connectivity... done.


$ cd git-start
$ git remote
origin

也可以指定选项 -v,会显示需要读写远程仓库使用的 Git 保存的简写与其对应的 URL。

1
2
3
$ git remote -v
origin http://git.oschina.net/yiibai/git-start.git (fetch)
origin http://git.oschina.net/yiibai/git-start.git (push)

如果远程仓库不止一个,该命令会将它们全部列出。 例如,与几个协作者合作的,拥有多个远程仓库的仓库看起来像下面这样:

1
2
3
4
5
6
7
8
9
10
$ cd git-start
$ git remote -v
mydoor http://git.oschina.net/yiibai/git-start.git (fetch)
mydoor http://git.oschina.net/yiibai/git-start.git (push)
curry http://git.oschina.net/yiibai/git-start.git (fetch)
curry http://git.oschina.net/yiibai/git-start.git (push)
deepfun http://git.oschina.net/yiibai/git-start.git (fetch)
deepfun http://git.oschina.net/yiibai/git-start.git (push)
koke http://git.oschina.net/yiibai/git-start.git (fetch)
koke http://git.oschina.net/yiibai/git-start.git (push)

这样可以轻松拉取其中任何一个用户的贡献。 此外,大概还会有某些远程仓库的推送权限,虽然目前还不会在此介绍。

从远程仓库中抓取与拉取 git fetch(pull)

就如刚才所见,从远程仓库中获得数据,可以执行:

1
$ git fetch [remote-name]

这个命令会访问远程仓库,从中拉取所有还没有的数据。执行完成后,将会拥有那个远程仓库中所有分支的引用,可以随时合并或查看。

如果使用 clone 命令克隆了一个仓库,命令会自动将其添加为远程仓库并默认以 “origin” 为简写。 所以,git fetch origin 会抓取克隆(或上一次抓取)后新推送的所有工作。 必须注意 git fetch 命令会将数据拉取到本地仓库 - 它并不会自动合并或修改当前的工作。 当准备好时必须手动将其合并入你的工作区。

如果你有一个分支设置为跟踪一个远程分支,可以使用 git pull 命令来自动的抓取然后合并远程分支到当前分支。 这对你来说可能是一个更简单或更舒服的工作流程;默认情况下,git clone 命令会自动设置本地 master 分支跟踪克隆的远程仓库的 master 分支(或不管是什么名字的默认分支)。 运行 git pull 通常会从最初克隆的服务器上抓取数据并自动尝试合并到当前所在的分支。

推送到远程仓库

当想分享你的项目时,必须将其推送到上游。 这个命令很简单:git push [remote-name] [branch-name]。 当你想要将 master 分支推送到 origin 服务器时(再次说明,克隆时通常会自动帮你设置好那两个名字),那么运行这个命令就可以将所做的备份到服务器:

1
$ git push origin master

只有当你有所克隆服务器的写入权限,并且之前没有人推送过时,这条命令才能生效。 当你和其他人在同一时间克隆,他们先推送到上游然后你再推送到上游,你的推送就会毫无疑问地被拒绝。 你必须先将他们的工作拉取下来并将其合并进你的工作后才能推送。

GitHub的使用

  • 命令行的难度蛮大的,初入门经常出现各种的问题
  • 推荐使用工具source tree轻松解决问题
  • source tree需要翻墙注册,自带VPN哦

命令

  • git pull取回远程主机某个分支的更新,再与本地的指定分支合并
  • git fetch命令用于从另一个存储库下载对象和引用。
  • git checkout在取回远程主机的更新以后,可以在它的基础上,使用git checkout命令创建一个新的分支。

git add命令将文件内容添加到索引(将修改添加到暂存区)。也就是将要提交的文件的信息添加到索引库中

git push命令用于将本地分支的更新,推送到远程主机。

git merge命令用于将两个或两个以上的开发历史加入(合并)一起。

私人仓库

本来最近挺想弄个私人仓库的,苦于贫穷,一直没有好的解决办法,终于,被我找到了这个学生优惠,造作呀.

今天在 HN 上到一则消息,“Free private Github repos for students and edu people | 学生和教育人士可免费申请 Github 私有仓库”。

  • 如果你是学生,并且已有 Github 账号,那都可以去申请微型方案(Micro Plan)。私有仓库可用来托管你个人的课程项目、论文或和学位相关的研究。当然了,如果学生需要用于团队的私有仓库,同样可以申请。
  • 如果你是教师,或者是学生组织(诸如校园报社和机器人俱乐部)的赞助商,可以申请组织账号(Organization account )。

参考

  1. 申请GitHub学生免费私有仓库
  2. 武汉理工大学申请校园邮箱的网址
  3. 加快git clone 几十倍速度的小方法 (30KB vs 2M)
  4. 易百教程
  5. 设置多个SSH KEY