Git学习笔记

 本文为Git学习笔记。

 Git是一种先进的分布式版本控制系统。

 本文的主要参考资源如下:

Git主页

Git简介- 廖雪峰的官方网站

起步

关于版本控制

 版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统。

Git简史

 Git于2005年诞生于Linux开源社区。

Git基础

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

 Git近乎所有操作都是本地执行:提升速度。

 Git保证完整性:Git中所有数据在存储前都计算校验和,然后以校验和来引用。

 Git一般只添加数据:数据的可恢复性增强。

 Git有三种状态:已提交(committed)、已修改(modified)和已暂存(staged)。

 基本的Git工作流程如下:

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

命令行

 Git使用方式:命令行或者GUI模式。本文以命令行形式为例。

安装Git

 Windows上安装Git,Git下载地址:http://git-scm.com/download/win

初次运行Git前的配置

用户信息

 设置用户名与邮件地址,Git的每次提交都会使用这些信息。设置方法如下:

1
2
$ git config --global user.name "John Doe"
$ git config --global user.email johndoe@example.com

 如上,使用了–global选项,则该指令只需要运行一次,Git将在系统中默认使用上述信息。

文本编辑器

 设置默认文本编辑器,例如使用notepad++作为文本编辑器,则可以输入:

1
$ git config --global core.editor notepad++

获取帮助

 Git帮助信息查询方法如下:

1
2
3
$ git help <verb>
$ git <verb> --help
$ man git-<verb>

总结

Git基础

获取Git仓库

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

在现有目录中初始化仓库

1
2
3
4
5
$ git init

$ git add *.c
$ git add LICENSE
$ git commit -m 'initial project version'

克隆现有的仓库

 克隆仓库的命令格式是 git clone [url] 。 比如,要克隆 Git 的可链接库 libgit2,可以用下面的命令:

1
$ git clone https://github.com/libgit2/libgit2

记录每次更新到仓库

  • 工作目录下的文件只有2种状态:已跟踪未跟踪
  • 已跟踪的文件是指那些被纳入了版本控制的文件,在上一次快照中有它们的记录,在工作一段时间后,它们的状态可能处于未修改,已修改或已放入暂存区;
  • 作目录中除已跟踪文件以外的所有其它文件都属于未跟踪文件,它们既不存在于上次快照的记录中,也没有放入暂存区;
  • 初次克隆某个仓库的时候,工作目录中的所有文件都属于已跟踪文件,并处于未修改状态;
  • 编辑过某些文件之后,由于自上次提交后你对它们做了修改,Git 将它们标记为已修改文件。 我们逐步将这些修改过的文件放入暂存区,然后提交所有暂存了的修改,如此反复。

检查当前文件状态

从本节开始,操作将进行实际练习,在GitHub中新建名为git_train的仓库,同时添加README.md文件以及针对C语言的.gitignore

后续练习将基于改仓库,请使用git clone命令将仓库克隆到本地。

 要查看哪些文件处于什么状态,可以用 git status 命令。 在克隆仓库后立即使用改命令,输出如下:

1
2
3
4
5
$ git status
On branch master
Your branch is up to date with 'origin/master'.

nothing to commit, working tree clean

 在项目下新建一个新的README文件,如下:

1
echo 'My Project' > README

 可以看到工作目录下增加了README文件,此时使用git status命令,输出如下:

1
2
3
4
5
6
7
8
9
10
$ git status
On branch master
Your branch is up to date with 'origin/master'.

Untracked files:
(use "git add <file>..." to include in what will be committed)

README

nothing added to commit but untracked files present (use "git add" to track)

 可以看到README文件为未跟踪状态。

跟踪新文件

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

1
$ git add README

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

1
2
3
4
5
6
7
8
$ 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)

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

暂存已修改文件

 修改已经被暂存的文件README.md,命令如下:

1
echo 'new line' >> README.md

 再使用git status查看,会看到如下内容:

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

new file: README

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将README.md放到暂存区,然后再看git status的输出:

1
2
3
4
5
6
7
8
9
$ 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)

new file: README
modified: README.md

 现在2个文件都已暂存,下次提交时就会一并记录到仓库。此时,在README.md中再增加一条注释,再运行git status:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ echo 'another new line' >> 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)

new file: README
modified: README.md

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
warning: LF will be replaced by CRLF in README.md.
The file will have its original line endings in your working directory.

Di@DESKTOP-30MBFQB MINGW64 /e/git/git_train (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)

new file: README
modified: README.md

状态简览

 使用git status -s或者git status –short命令,可以得到更为紧凑的格式输出。

  • 新添加的未跟踪文件前面有 ?? 标记;
  • 新添加到暂存区中的文件前面有 A 标记;
  • 修改过的文件前面有 M 标记。 M 有两个可以出现的位置,出现在右边的 M 表示该文件被修改了但是还没放入暂存区,出现在靠左边的 M 表示该文件被修改了并放入了暂存区。

忽略文件

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

 下面是.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

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

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

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

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

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

git diff命令比较的是工作目录中当前文件暂存区域快照之间的差异, 也就是修改之后还没有暂存起来的变化内容。

 在README.md中新增一行,使用git diff查看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ echo 'new line 3' >> README.md

$ git diff
warning: LF will be replaced by CRLF in README.md.
The file will have its original line endings in your working directory.
diff --git a/README.md b/README.md
index 4fb6d92..6dd2996 100644
--- a/README.md
+++ b/README.md
@@ -2,3 +2,4 @@
git cmd train
new line
another new line
+new line 3

 请注意,git diff 本身只显示尚未暂存的改动,而不是自上次提交以来所做的所有改动。

 git diff的其它用法如下:

  • git diff View difference between Stage and Working Directory
  • git diff --staged View difference between HEAD and Stage
  • git diff HEAD View difference between HEAD and Working Directory

提交更新

 暂存区域可以使用git commit -m ”commit message“来提交,其中commit message为提交的注释信息。

1
2
3
4
5
6
7
8
$ git add .
warning: LF will be replaced by CRLF in README.md.
The file will have its original line endings in your working directory.

$ git commit -m "first commit"
[master 458c888] first commit
2 files changed, 4 insertions(+)
create mode 100644 README

 可以使用git commit –amend继续对提交信息进行补充修改,使用该指令将打开默认的编辑器进行编辑。

移除文件

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

 例如,删除README文件,命令如下:

1
2
3
4
5
6
7
8
9
10
11
12
$ git rm README
rm 'README'

$ 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: README

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

1
$ git rm --cached README

 之后需要进行的操作为:提交与推送,命令如下:

1
2
git commit -m "remove the now ignored file: README"
git push origin master

 为了避免后续继续跟踪不希望跟踪的文件,此时可以修改.gitignore并进行一次提交

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

1
$ git rm log/\*.log

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

1
$ git rm \*~

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

移动文件

 要在 Git 中对文件改名,可以这么做:

1
$ git mv file_from file_to

查看提交历史

查看提交历史

 使用git log命令可以查看提交历史:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ git log
commit 8fe15b61216fcbb2f2217ceca4b4287c7671dfdd (HEAD -> master, origin/master, origin/HEAD)
Author: jiaodi <jiaodi.sjtu@gmail.com>
Date: Sun Mar 3 13:12:42 2019 +0800

remove the now ignored file: README

commit 458c888023ffcf425ed7290a014f141c25e56736
Author: jiaodi <jiaodi.sjtu@gmail.com>
Date: Sun Mar 3 12:47:10 2019 +0800

first commit

commit 988f2df9742884c7db14d56203c0d0ab5e94d2ee
Author: jiaodi <jiaodi.sjtu@gmail.com>
Date: Sat Mar 2 18:40:36 2019 +0800

Initial commit

 默认不用任何参数的话,git log 会按提交时间列出所有的更新,最近的更新排在最上面。 正如你所看到的,这个命令会列出每个提交的 SHA-1 校验和、作者的名字和电子邮件地址、提交时间以及提交说明。

git log 的常用选项:

选项 说明
-p 按补丁格式显示每个更新之间的差异。
--stat 显示每次更新的文件修改统计信息。
--shortstat 只显示 –stat 中最后的行数修改添加移除统计。
--name-only 仅在提交信息后显示已修改的文件清单。
--name-status 显示新增、修改、删除的文件清单。
--abbrev-commit 仅显示 SHA-1 的前几个字符,而非所有的 40 个字符。
--relative-date 使用较短的相对时间显示(比如,“2 weeks ago”)。
--graph 显示 ASCII 图形表示的分支合并历史。
--pretty 使用其他格式显示历史提交信息。可用的选项包括 oneline,short,full,fuller 和 format(后跟指定格式)。

限制输出长度

选项 说明
-(n) 仅显示最近的 n 条提交
--since, --after 仅显示指定时间之后的提交。
--until, --before 仅显示指定时间之前的提交。
--author 仅显示指定作者相关的提交。
--committer 仅显示指定提交者相关的提交。
--grep 仅显示含指定关键字的提交
-S 仅显示添加或移除了某个关键字的提交

撤销操作

撤销操作

 如果在提交之后发现漏掉文件,或者提交信息写错等,此时,可以运行带有–amend选项的提交命令进行重新提交。

1
$ git commit --amend

 举例来说,修改README.md文件后暂存并且提交,命令如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$ echo 'new line test --amend' >> README.md

$ git status
On branch master
Your branch is up to date with 'origin/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 add .
warning: LF will be replaced by CRLF in README.md.
The file will have its original line endings in your working directory.

$ git commit -m "commit before --amend"
[master 70c7893] commit before --amend
1 file changed, 1 insertion(+)

$ git status
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
(use "git push" to publish your local commits)

nothing to commit, working tree clean

 此时,查看git log如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ git log
commit 70c7893046ad0cb38ac3337cb22aab0eb049d355 (HEAD -> master)
Author: jiaodi <jiaodi.sjtu@gmail.com>
Date: Sun Mar 3 16:55:40 2019 +0800

commit before --amend

commit 8fe15b61216fcbb2f2217ceca4b4287c7671dfdd (origin/master, origin/HEAD)
Author: jiaodi <jiaodi.sjtu@gmail.com>
Date: Sun Mar 3 13:12:42 2019 +0800

remove the now ignored file: README

commit 458c888023ffcf425ed7290a014f141c25e56736
Author: jiaodi <jiaodi.sjtu@gmail.com>
Date: Sun Mar 3 12:47:10 2019 +0800

first commit

commit 988f2df9742884c7db14d56203c0d0ab5e94d2ee
Author: jiaodi <jiaodi.sjtu@gmail.com>
Date: Sat Mar 2 18:40:36 2019 +0800

Initial commit

 此时,再在README.md中追加一行,然后暂存并且使用带有–amend选项的提交命令进行提交:

1
2
3
4
5
6
7
8
9
10
$ echo 'new line test --amend 2' >> README.md

$ git add .
warning: LF will be replaced by CRLF in README.md.
The file will have its original line endings in your working directory.

$ git commit --amend
[master 601c194] commit use --amend
Date: Sun Mar 3 16:55:40 2019 +0800
1 file changed, 2 insertions(+)

 注意,git commit –amend命令运行后,在文本编辑器中将提交信息修改为”commit use –amend“,运行git log输出如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ git log
commit 601c194e7ecf99d1466673d2925e0210055693e8 (HEAD -> master)
Author: jiaodi <jiaodi.sjtu@gmail.com>
Date: Sun Mar 3 16:55:40 2019 +0800

commit use --amend

commit 8fe15b61216fcbb2f2217ceca4b4287c7671dfdd (origin/master, origin/HEAD)
Author: jiaodi <jiaodi.sjtu@gmail.com>
Date: Sun Mar 3 13:12:42 2019 +0800

remove the now ignored file: README

commit 458c888023ffcf425ed7290a014f141c25e56736
Author: jiaodi <jiaodi.sjtu@gmail.com>
Date: Sun Mar 3 12:47:10 2019 +0800

first commit

commit 988f2df9742884c7db14d56203c0d0ab5e94d2ee
Author: jiaodi <jiaodi.sjtu@gmail.com>
Date: Sat Mar 2 18:40:36 2019 +0800

Initial commit

 可以看到,最近一次commit信息已经被修改。

取消暂存的文件

 使用 git reset HEAD <file>... 来取消暂存。

 例如再在README.md中追加一行,同时暂存,命令如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ echo 'new line test reset' >> README.md

$ git add .
warning: LF will be replaced by CRLF in README.md.
The file will have its original line endings in your working directory.

$ 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)

modified: README.md

 此时,README.md已经是暂存状态,使用 git reset HEAD <file>... 来取消暂存,命令如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ git reset HEAD README.md
Unstaged changes after reset:
M README.md

$ 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 <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")

 可以看到,README.md已经是未暂存状态了。

撤销对文件的修改

 接上一个过程,README.md文件现在是未暂存状态,使用git diff看一下修改,命令如下:

1
2
3
4
5
6
7
8
9
10
11
12
$ git diff
warning: LF will be replaced by CRLF in README.md.
The file will have its original line endings in your working directory.
diff --git a/README.md b/README.md
index cb33950..d262c46 100644
--- a/README.md
+++ b/README.md
@@ -5,3 +5,4 @@ another new line
new line 3
new line test --amend
new line test --amend 2
+new line test reset

 使用git checkout -- <file>可以撤销之前所作的修改,命令如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ git checkout -- README.md

Di@DESKTOP-30MBFQB MINGW64 /e/git/git_train (master)
$ git status
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
(use "git push" to publish your local commits)

nothing to commit, working tree clean

Di@DESKTOP-30MBFQB MINGW64 /e/git/git_train (master)
$ cat README.md
# git_train
git cmd train
new line
another new line
new line 3
new line test --amend
new line test --amend 2

 可以看到那些修改已经被撤销了。

 实际测试可知,git checkout -- <file>即使用暂存区的文件覆盖工作目录的文件,具体验证如下:

 首先修改README.md文件并暂存,命令如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$ echo 'new line test checkout --file' >> README.md

$ 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 <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 add .
warning: LF will be replaced by CRLF in README.md.
The file will have its original line endings in your working directory.

$ 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)

modified: README.md

 此时,暂存区中的README.md文件中最新一行为”new line test checkout –file“。注意此时并未进行提交。

 之后,再想README.md文件中追加一行”new line test checkout –file 2“,执行git checkout -- README.md,可以看到README.md文件中最后一行为”new line test checkout –file“,即使用暂存区的文件覆盖了工作目录的文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
$ echo 'new line test checkout --file 2' >> README.md

$ 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)

modified: README.md

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

Di@DESKTOP-30MBFQB MINGW64 /e/git/git_train (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)

modified: README.md


Di@DESKTOP-30MBFQB MINGW64 /e/git/git_train (master)

Di@DESKTOP-30MBFQB MINGW64 /e/git/git_train (master)
$ cat README.md
# git_train
git cmd train
new line
another new line
new line 3
new line test --amend
new line test --amend 2
new line test checkout --file

远程仓库的使用

查看远程仓库

 查看已经配置的远程仓库服务器,可以运行git remote命令。

1
2
$ git remote
origin

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

1
2
3
$ git remote -v
origin git@github.com:NemoJ/git_train.git (fetch)
origin git@github.com:NemoJ/git_train.git (push)

添加远程仓库

 运行 git remote add <shortname> <url> 添加一个新的远程 Git 仓库。

从远程仓库中抓取和拉取

 从远程仓库中获得数据,可以执行:

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

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

查看远程仓库

 如果想要查看某一个远程仓库的更多信息,可以使用 git remote show [remote-name] 命令。

1
2
3
4
5
6
7
8
9
10
11
$ git remote show origin
* remote origin
Fetch URL: git@github.com:NemoJ/git_train.git
Push URL: git@github.com:NemoJ/git_train.git
HEAD branch: master
Remote branch:
master tracked
Local branch configured for 'git pull':
master merges with remote master
Local ref configured for 'git push':
master pushes to master (fast-forwardable)

远程仓库的移除与重命名

打标签

Git别名

Git分支

 几乎所有的版本控制系统都以某种形式支持分支。 使用分支意味着你可以把你的工作从开发主线上分离开来,以免影响开发主线。

 Git 处理分支的方式可谓是难以置信的轻量,创建新分支这一操作几乎能在瞬间完成,并且在不同分支之间的切换操作也是一样便捷。 与许多其它版本控制系统不同,Git 鼓励在工作流程中频繁地使用分支与合并,哪怕一天之内进行许多次。 理解和精通这一特性,你便会意识到 Git 是如此的强大而又独特,并且从此真正改变你的开发方式。

分支简介

 在进行提交操作时,Git 会保存一个提交对象(commit object)。该提交对象会包含一个指向暂存内容快照的指针。 但不仅仅是这样,该提交对象还包含了作者的姓名和邮箱、提交时输入的信息以及指向它的父对象的指针。首次提交产生的提交对象没有父对象,普通提交操作产生的提交对象有一个父对象,而由多个分支合并产生的提交对象有多个父对象。

 举例来说,我们假设现在有一个工作目录里面包含了三个将要被暂存和提交的文件。 暂存操作会为每一个文件计算校验和,然后会把当前版本的文件快照保存到 Git 仓库中(Git 使用 blob 对象来保存它们),最终将校验和加入到暂存区域等待提交:

1
2
$ git add README test.rb LICENSE
$ git commit -m 'The initial commit of my project'

 当使用 git commit 进行提交操作时,Git 会先计算每一个子目录(本例中只有项目根目录)的校验和,然后在 Git 仓库中这些校验和保存为树对象。 随后,Git 便会创建一个提交对象,它除了包含上面提到的那些信息外,还包含指向这个树对象(项目根目录)的指针。如此一来,Git 就可以在需要的时候重现此次保存的快照。

 现在,Git 仓库中有五个对象:三个 blob 对象(保存着文件快照)、一个树对象(记录着目录结构和 blob 对象索引)以及一个提交对象(包含着指向前述树对象的指针和所有提交信息)。

 首次提交对象及其树结构如下图所示:

 做些修改后再次提交,那么这次产生的提交对象会包含一个指向上次提交对象(父对象)的指针,如下图所示:

 Git 的分支,其实本质上仅仅是指向提交对象的可变指针。 Git 的默认分支名字是 master。 在多次提交操作之后,你其实已经有一个指向最后那个提交对象的 master 分支。 它会在每次的提交操作中自动向前移动。

 下图展示了Git分支及其提交历史的示例。

创建分支

 Git中创建分支实际为创建一个可以移动的新的指针。使用git branch命令可以创建分支,如下:

1
$ git branch testing

 这会在当前所在的提交对象上创建一个指针,如下所示:

 Git如何确认当前在哪个分支呢?Git中有一个名叫HEAD的特殊指针,指向当前所在的本地分支(将HEAD想象为当前分支的别名)。使用git branch命令仅仅创建一个分支,并不会自动切换到新的分支中去,所以此时仍在master分支上。

 可以使用git log查看各个分支当前指向的对象,命令如下:

1
2
3
4
$ git log --oneline --decorate
f30ab (HEAD, master, testing) add feature #32 - ability to add new
34ac2 fixed bug #1328 - stack overflow under certain conditions
98ca9 initial commit of my project

 输出显示,master和testing均指向校验和以f30ab开头的提交对象。

切换分支

 切换到一个已存在的分支,可以使用git checkout命令,切换到testing分支的命令如下:

1
$ git checkout testing

此时HEAD将指向testing分支,如下所示:

 此时修改工作目录文件,再次进行提交,命令如下:

1
2
$ vim test.rb
$ git commit -a -m 'made a change'

 如上图所示,HEAD分支将随着提交自动前移。此时testing分支向前移动了,但是master分支却没有,现在切换回master分支,命令如下:

1
$ git checkout master

 可以看到,此时HEAD指向master分支。这条命令有2个作用:一是使HEAD指回master分支,二是将工作目录恢复成master分支所指向的快照内容。本质上,这就是忽略testing分支所作的修改,以便于向另外一个方向进行开发。

分支切换会改变你工作目录中的文件

在切换分支时,一定要注意你工作目录里的文件会被改变。 如果是切换到一个较旧的分支,你的工作目录会恢复到该分支最后一次提交时的样子。 如果 Git 不能干净利落地完成这个任务,它将禁止切换分支。

 再做一些修改然后提交,命令如下:

1
2
$ vim test.rb
$ git commit -a -m 'made other changes'

 项目分叉历史如下所示:

 可以使用git log命令查看分叉历史,命令如下:

1
2
3
4
5
6
7
$ git log --oneline --decorate --graph --all
* c2b9e (HEAD, master) made other changes
| * 87ab2 (testing) made a change
|/
* f30ab add feature #32 - ability to add new formats to the
* 34ac2 fixed bug #1328 - stack overflow under certain conditions
* 98ca9 initial commit of my project

 Git分支使得你可以在不同的分支来回切换和工作,并在实际成熟时将它们合并起来。

 由于 Git 的分支实质上仅是包含所指对象校验和(长度为 40 的 SHA-1 值字符串)的文件,所以它的创建和销毁都异常高效。

分支的新建与合并

 让我们来看一个简单的分支新建与分支合并的例子,实际工作中你可能会用到类似的工作流。 你将经历如下步骤:

  1. 开发某个网站。
  2. 为实现某个新的需求,创建一个分支。
  3. 在这个分支上开展工作。

 正在此时,你突然接到一个电话说有个很严重的问题需要紧急修补。 你将按照如下方式来处理:

  1. 切换到你的线上分支(production branch)。
  2. 为这个紧急任务新建一个分支,并在其中修复它。
  3. 在测试通过之后,切换回线上分支,然后合并这个修补分支,最后将改动推送到线上分支。
  4. 切换回你最初工作的分支上,继续工作。

新建分支

 首先,我们假设你正在你的项目上工作,并且已经有一些提交。一个简单的提交历史如下所示:

 现在,需要解决#53问题,因此需要新建一个分支并切换到该分支,此时可以运行命令如下:

1
2
$ git checkout -b iss53
Switched to a new branch "iss53"

 它是下面两条命令的缩写:

1
2
$ git branch iss53
$ git checkout iss53

 如下图所示,此时已经创建一个新分支指针。

 在#53上工作并进行提交。

1
2
$ vim index.html
$ git commit -a -m 'added a new footer [issue 53]'

 该过程中,iss53分支在不断地推进,如下图所示:

 现在,接到电话需要解决一个紧急问题。有了Git的帮助,此时无需将这个仅仅问题与iss53的修改混在一起,也无需花费力气还原关于iss53问题的修改,然后再添加关于这个紧急问题的修改,最后将这个修改提交到线上分支。这里需要做的仅仅是切换回master分支

 但是,在切换分支之前,需要留意你的工作目录与暂存区里那些还没被提交的修改,它们可能会和你即将检出的分支产生冲突而阻止Git切换到该分支。最好的办法是,在切换分支之前保持一个干净的状态。后续将介绍保存进度(stashing) 和 修补提交(commit amending)这两个命令来绕过这个问题。现在,假设所有修改已经提交,此时可以切回master分支:

1
2
$ git checkout master
Switched to branch 'master'

 此时,你的工作目录与你在开始#53问题之前一致,此时可以专心修复紧急问题。

请牢记:当你切换分支的时候,Git 会重置你的工作目录,使其看起来像回到了你在那个分支上最后一次提交的样子。 Git 会自动添加、删除、修改文件以确保此时你的工作目录和这个分支最后一次提交时的样子一模一样。

 接下来,修复这个紧急问题。建立一个针对该紧急问题的分支(hotfix branch),在该分支上工作直到问题解决:

1
2
3
4
5
6
$ git checkout -b hotfix
Switched to a new branch 'hotfix'
$ vim index.html
$ git commit -a -m 'fixed the broken email address'
[hotfix 1fb7853] fixed the broken email address
1 file changed, 2 insertions(+)

 测试后确保修改时正确的,则可以将其合并回你的master分支来部署到线上。可以使用git merge命令来达到上述目的:

1
2
3
4
5
6
$ git checkout master
$ git merge hotfix
Updating f42c576..3a0874c
Fast-forward
index.html | 2 ++
1 file changed, 2 insertions(+)

 在合并的时候,你应该注意到了”快进(fast-forward)”这个词。 由于当前 master 分支所指向的提交是你当前提交(有关 hotfix 的提交)的直接上游,所以 Git 只是简单的将指针向前移动。 换句话说,当你试图合并两个分支时,如果顺着一个分支走下去能够到达另一个分支,那么 Git 在合并两者的时候,只会简单的将指针向前推进(指针右移),因为这种情况下的合并操作没有需要解决的分歧——这就叫做 “快进(fast-forward)”。

 现在,最新的修改已经在master分支所指向的提交快照中,可以着手发布该修复了。

 关于这个紧急问题的解决方案发布之后,现在回到iss53。此时应该首先删除hotfix分支,因为已经不再需要它了——master分支已经指向了同一位置。可以使用带-d选项的git branch命令来删除分支:

1
2
$ git branch -d hotfix
Deleted branch hotfix (3a0874c).

 现在可以切回正在工作的分支继续你的工作,也就是针对#53问题的那个分支(iss53分支)。

1
2
3
4
5
6
$ git checkout iss53
Switched to branch "iss53"
$ vim index.html
$ git commit -a -m 'finished the new footer [issue 53]'
[iss53 ad82d7a] finished the new footer [issue 53]
1 file changed, 1 insertion(+)

 你在hotfix分支所做的工作并没有被包含到iss53分支中。如果此时需要拉取hotfix所做的修改,可以使用git merge master命令将master分支合并到iss53分支,或者你也可以等到iss53分支完成其使命,再将其合并到master分支。

分支的合并

 假设你已经修正了#53问题,并且打算将你的工作合并入master分支。此时,你需要合并iss53分支到master分支,这你之前你合并hotfix分支所做的工作差不多。你只需要检出到你想合并入的分支,然后运行git merge命令:

1
2
3
4
5
6
$ git checkout master
Switched to branch 'master'
$ git merge iss53
Merge made by the 'recursive' strategy.
index.html | 1 +
1 file changed, 1 insertion(+)

 这和你之前合并 hotfix 分支的时候看起来有一点不一样。 在这种情况下,你的开发历史从一个更早的地方开始分叉开来(diverged)。 因为,master 分支所在提交并不是 iss53 分支所在提交的直接祖先,Git 不得不做一些额外的工作。 出现这种情况的时候,Git 会使用两个分支的末端所指的快照(C4C5)以及这两个分支的工作祖先(C2),做一个简单的三方合并。

 和之前将分支指针向前推进所不同的是,Git 将此次三方合并的结果做了一个新的快照并且自动创建一个新的提交指向它。 这个被称作一次合并提交,它的特别之处在于他有不止一个父提交。

 既然你的修改已经合并进来了,你已经不再需要 iss53 分支了。 现在你可以在任务追踪系统中关闭此项任务,并删除这个分支。

1
$ git branch -d iss53

遇到冲突时的分支合并

 有时候合并操作不会如此顺利。 如果你在两个不同的分支中,对同一个文件的同一个部分进行了不同的修改,Git 就没法干净的合并它们。 如果你对 #53 问题的修改和有关 hotfix 的修改都涉及到同一个文件的同一处,在合并它们的时候就会产生合并冲突:

1
2
3
4
$ git merge iss53
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.

 此时 Git 做了合并,但是没有自动地创建一个新的合并提交。 Git 会暂停下来,等待你去解决合并产生的冲突。 你可以在合并冲突后的任意时刻使用 git status 命令来查看那些因包含合并冲突而处于未合并(unmerged)状态的文件:

1
2
3
4
5
6
7
8
9
10
11
$ git status
On branch master
You have unmerged paths.
(fix conflicts and run "git commit")

Unmerged paths:
(use "git add <file>..." to mark resolution)

both modified: index.html

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

 任何因包含合并冲突而有待解决的文件,都会以未合并状态标识出来。 Git 会在有冲突的文件中加入标准的冲突解决标记,这样你可以打开这些包含冲突的文件然后手动解决冲突。 出现冲突的文件会包含一些特殊区段,看起来像下面这个样子:

1
2
3
4
5
6
7
<<<<<<< HEAD:index.html
<div id="footer">contact : email.
=======
<div id="footer">
please contact us at support@github.com
</div>
>>>>>>> iss53:index.html

 这表示 HEAD 所指示的版本(也就是你的 master 分支所在的位置,因为你在运行 merge 命令的时候已经检出到了这个分支)在这个区段的上半部分(======= 的上半部分),而 iss53 分支所指示的版本在 ======= 的下半部分。 为了解决冲突,你必须选择使用由 ======= 分割的两部分中的一个,或者你也可以自行合并这些内容。 例如,你可以通过把这段内容换成下面的样子来解决冲突:

1
2
3
<div id="footer">
please contact us at email.support@github.com
</div>

 上述的冲突解决方案仅保留了其中一个分支的修改,并且 <<<<<<< , ======= , 和 >>>>>>> 这些行被完全删除了。 在你解决了所有文件里的冲突之后,对每个文件使用 git add 命令来将其标记为冲突已解决。 一旦暂存这些原本有冲突的文件,Git 就会将它们标记为冲突已解决。

 可以再次运行 git status 来确认所有的合并冲突都已被解决:

1
2
3
4
5
6
7
8
$ git status
On branch master
All conflicts fixed but you are still merging.
(use "git commit" to conclude merge)

Changes to be committed:

modified: index.html

 如果你对结果感到满意,并且确定之前有冲突的的文件都已经暂存了,这时你可以输入 git commit 来完成合并提交。 默认情况下提交信息看起来像下面这个样子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Merge branch 'iss53'

Conflicts:
index.html
#
# It looks like you may be committing a merge.
# If this is not correct, please remove the file
# .git/MERGE_HEAD
# and try again.


# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# All conflicts fixed but you are still merging.
#
# Changes to be committed:
# modified: index.html
#

 如果你觉得上述的信息不够充分,不能完全体现分支合并的过程,你可以修改上述信息,添加一些细节给未来检视这个合并的读者一些帮助,告诉他们你是如何解决合并冲突的,以及理由是什么。

分支管理

 git branch命令不只是可以创建和删除分支。使用git branch指令可以得到当前所有分支的列表。

1
2
3
4
$ git branch
iss53
* master
testing

 注意 master 分支前的 * 字符:它代表现在检出的那一个分支(也就是说,当前 HEAD 指针所指向的分支)。

如果想查看每一个分支的最后一次提交,可以运行git branch -v命令:

1
2
3
4
$ git branch -v
iss53 93b412c fix javascript issue
* master 7a98805 Merge branch 'iss53'
testing 782fd34 add scott to the author list in the readmes

--merged--no-merged 这两个有用的选项可以过滤这个列表中已经合并或尚未合并到当前分支的分支。

如果要查看哪些分支已经合并到当前分支,可以运行 git branch --merged

1
2
3
$ git branch --merged
iss53
* master

 因为之前已经合并了 iss53 分支,所以现在看到它在列表中。 在这个列表中分支名字前没有 * 号的分支通常可以使用 git branch -d 删除掉;你已经将它们的工作整合到了另一个分支,所以并不会失去任何东西。

查看所有包含未合并工作的分支,可以运行 git branch --no-merged

1
2
$ git branch --no-merged
testing

 这里显示了其他分支。 因为它包含了还未合并的工作,尝试使用 git branch -d 命令删除它时会失败:

1
2
3
$ git branch -d testing
error: The branch 'testing' is not fully merged.
If you are sure you want to delete it, run 'git branch -D testing'.

 如果真的想要删除分支并丢掉那些工作,如同帮助信息里所指出的,可以使用 -D 选项强制删除它。

分支开发工作流

长期分支

 许多使用 Git 的开发者都喜欢使用这种方式来工作,比如只在 master 分支上保留完全稳定的代码——有可能仅仅是已经发布或即将发布的代码。 他们还有一些名为 develop 或者 next 的平行分支,被用来做后续开发或者测试稳定性——这些分支不必保持绝对稳定,但是一旦达到稳定状态,它们就可以被合并入 master 分支了。 这样,在确保这些已完成的特性分支(短期分支,比如之前的 iss53 分支)能够通过所有测试,并且不会引入更多 bug 之后,就可以合并入主干分支中,等待下一次的发布。

 事实上我们刚才讨论的,是随着你的提交而不断右移的指针。 稳定分支的指针总是在提交历史中落后一大截,而前沿分支的指针往往比较靠前。

 通常把他们想象成流水线(work silos)可能更好理解一点,那些经过测试考验的提交会被遴选到更加稳定的流水线上去。

 你可以用这种方法维护不同层次的稳定性。 一些大型项目还有一个 proposed(建议) 或 pu: proposed updates(建议更新)分支,它可能因包含一些不成熟的内容而不能进入 next 或者 master 分支。 这么做的目的是使你的分支具有不同级别的稳定性;当它们具有一定程度的稳定性后,再把它们合并入具有更高级别稳定性的分支中。 再次强调一下,使用多个长期分支的方法并非必要,但是这么做通常很有帮助,尤其是当你在一个非常庞大或者复杂的项目中工作时。

特性分支

 特性分支对任何规模的项目都适用。 特性分支是一种短期分支,它被用来实现单一特性或其相关工作。 在 Git 中一天之内多次创建、使用、合并、删除分支都很常见。

 你已经在上一节中你创建的 iss53hotfix 特性分支中看到过这种用法。 你在上一节用到的特性分支(iss53hotfix 分支)中提交了一些更新,并且在它们合并入主干分支之后,你又删除了它们。 这项技术能使你快速并且完整地进行上下文切换(context-switch)——因为你的工作被分散到不同的流水线中,在不同的流水线中每个分支都仅与其目标特性相关,因此,在做代码审查之类的工作的时候就能更加容易地看出你做了哪些改动。 你可以把做出的改动在特性分支中保留几分钟、几天甚至几个月,等它们成熟之后再合并,而不用在乎它们建立的顺序或工作进度。

 考虑这样一个例子,你在 master 分支上工作到 C1,这时为了解决一个问题而新建 iss91 分支,在 iss91 分支上工作到 C4,然而对于那个问题你又有了新的想法,于是你再新建一个 iss91v2 分支试图用另一种方法解决那个问题,接着你回到 master 分支工作了一会儿,你又冒出了一个不太确定的想法,你便在 C10 的时候新建一个 dumbidea 分支,并在上面做些实验。 你的提交历史看起来像下面这个样子:

 现在,我们假设两件事情:你决定使用第二个方案来解决那个问题,即使用在 iss91v2 分支中方案;另外,你将 dumbidea 分支拿给你的同事看过之后,结果发现这是个惊人之举。 这时你可以抛弃 iss91 分支(即丢弃 C5C6 提交),然后把另外两个分支合并入主干分支。 最终你的提交历史看起来像下面这个样子:

远程分支

 远程跟踪分支是远程分支状态的引用。 它们是你不能移动的本地引用,当你做任何网络通信操作时,它们会自动移动。 远程跟踪分支像是你上次连接到远程仓库时,那些分支所处状态的书签。

 它们以 (remote)/(branch) 形式命名。 例如,如果你想要看你最后一次与远程仓库 origin 通信时 master 分支的状态,你可以查看 origin/master 分支。 你与同事合作解决一个问题并且他们推送了一个 iss53 分支,你可能有自己的本地 iss53 分支;但是在服务器上的分支会指向 origin/iss53 的提交。

 这可能有一点儿难以理解,让我们来看一个例子。 假设你的网络里有一个在 git.ourcompany.com 的 Git 服务器。 如果你从这里克隆,Git 的 clone 命令会为你自动将其命名为 origin,拉取它的所有数据,创建一个指向它的 master 分支的指针,并且在本地将其命名为 origin/master。 Git 也会给你一个与 origin 的 master 分支在指向同一个地方的本地 master 分支,这样你就有工作的基础。

 克隆之后的服务器与本地仓库如下图所示:

克隆之后的服务器与本地仓库

 如果你在本地的 master 分支做了一些工作,然而在同一时间,其他人推送提交到 git.ourcompany.com并更新了它的 master 分支,那么你的提交历史将向不同的方向前进。 也许,只要你不与 origin 服务器连接,你的 origin/master 指针就不会移动。

 本地与远程的工作可以分叉,如下图所示:

本地与远程的工作可以分叉

 如果要同步你的工作,运行 git fetch origin 命令。 这个命令查找 “origin” 是哪一个服务器(在本例中,它是 git.ourcompany.com),从中抓取本地没有的数据,并且更新本地数据库,移动 origin/master 指针指向新的、更新后的位置。

 git fetch更新你的远程仓库引用,如下图所示:

git fetch更新你的远程仓库引用

 为了演示有多个远程仓库与远程分支的情况,我们假定你有另一个内部 Git 服务器,仅用于你的 sprint 小组的开发工作。 这个服务器位于 git.team1.ourcompany.com。 你可以运行 git remote add 命令添加一个新的远程仓库引用到当前的项目,这个命令我们会在 Git 基础 中详细说明。 将这个远程仓库命名为 teamone,将其作为整个 URL 的缩写。

添加另一个远程仓库

 现在,可以运行 git fetch teamone 来抓取远程仓库 teamone 有而本地没有的数据。 因为那台服务器上现有的数据是 origin 服务器上的一个子集,所以 Git 并不会抓取数据而是会设置远程跟踪分支 teamone/master 指向 teamonemaster 分支。

远程跟踪分支 teamone/master

推送

 当你想要公开分享一个分支时,需要将其推送到有写入权限的远程仓库上。 本地的分支并不会自动与远程仓库同步 - 你必须显式地推送想要分享的分支。 这样,你就可以把不愿意分享的内容放到私人分支上,而将需要和别人协作的内容推送到公开分支。

 如果希望和别人一起在名为 serverfix 的分支上工作,你可以像推送第一个分支那样推送它。 运行 git push (remote) (branch):

1
2
3
4
5
6
7
8
$ git push origin serverfix
Counting objects: 24, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (15/15), done.
Writing objects: 100% (24/24), 1.91 KiB | 0 bytes/s, done.
Total 24 (delta 2), reused 0 (delta 0)
To https://github.com/schacon/simplegit
* [new branch] serverfix -> serverfix

 下一次其他协作者从服务器上抓取数据时,他们会在本地生成一个远程分支 origin/serverfix,指向服务器的 serverfix 分支的引用:

1
2
3
4
5
6
7
$ git fetch origin
remote: Counting objects: 7, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 3 (delta 0)
Unpacking objects: 100% (3/3), done.
From https://github.com/schacon/simplegit
* [new branch] serverfix -> origin/serverfix

 要特别注意的一点是当抓取到新的远程跟踪分支时,本地不会自动生成一份可编辑的副本(拷贝)。 换一句话说,这种情况下,不会有一个新的 serverfix 分支 - 只有一个不可以修改的 origin/serverfix 指针。

 可以运行 git merge origin/serverfix 将这些工作合并到当前所在的分支。 如果想要在自己的 serverfix 分支上工作,可以将其建立在远程跟踪分支之上:

1
2
3
$ git checkout -b serverfix origin/serverfix
Branch serverfix set up to track remote branch serverfix from origin.
Switched to a new branch 'serverfix'

 这会给你一个用于工作的本地分支,并且起点位于 origin/serverfix

变基

总结

文章目录
  1. 1. 起步
    1. 1.1. 关于版本控制
    2. 1.2. Git简史
    3. 1.3. Git基础
    4. 1.4. 命令行
    5. 1.5. 安装Git
    6. 1.6. 初次运行Git前的配置
      1. 1.6.1. 用户信息
      2. 1.6.2. 文本编辑器
    7. 1.7. 获取帮助
    8. 1.8. 总结
  2. 2. Git基础
    1. 2.1. 获取Git仓库
      1. 2.1.1. 在现有目录中初始化仓库
      2. 2.1.2. 克隆现有的仓库
    2. 2.2. 记录每次更新到仓库
      1. 2.2.1. 检查当前文件状态
      2. 2.2.2. 跟踪新文件
      3. 2.2.3. 暂存已修改文件
      4. 2.2.4. 状态简览
      5. 2.2.5. 忽略文件
      6. 2.2.6. 查看已暂存和尚未暂存的修改
      7. 2.2.7. 提交更新
      8. 2.2.8. 移除文件
      9. 2.2.9. 移动文件
    3. 2.3. 查看提交历史
      1. 2.3.1. 查看提交历史
      2. 2.3.2. 限制输出长度
    4. 2.4. 撤销操作
      1. 2.4.1. 撤销操作
      2. 2.4.2. 取消暂存的文件
      3. 2.4.3. 撤销对文件的修改
    5. 2.5. 远程仓库的使用
      1. 2.5.1. 查看远程仓库
      2. 2.5.2. 添加远程仓库
      3. 2.5.3. 从远程仓库中抓取和拉取
      4. 2.5.4. 推送到远程仓库
      5. 2.5.5. 查看远程仓库
      6. 2.5.6. 远程仓库的移除与重命名
    6. 2.6. 打标签
    7. 2.7. Git别名
  3. 3. Git分支
    1. 3.1. 分支简介
      1. 3.1.1. 创建分支
      2. 3.1.2. 切换分支
    2. 3.2. 分支的新建与合并
      1. 3.2.1. 新建分支
      2. 3.2.2. 分支的合并
      3. 3.2.3. 遇到冲突时的分支合并
    3. 3.3. 分支管理
    4. 3.4. 分支开发工作流
      1. 3.4.1. 长期分支
      2. 3.4.2. 特性分支
    5. 3.5. 远程分支
      1. 3.5.1. 推送
    6. 3.6. 变基
    7. 3.7. 总结
|