参考信息html
一、http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000
二、http://www.zhihu.com/question/20070065java
三、http://www.ihref.com/read-16369.htmlgit
注意:github
学习前请先配置好Git客户端shell
相关文章:Git客户端图文详解如何安装配置GitHub操做流程攻略数据库
官方中文手册:http://git-scm.com/book/zh编程
本站为 Git 学习参考手册。目的是为学习与记忆 Git 使用中最重要、最广泛的命令提供快速翻阅。 这些命令以你可能须要的操做类型划分,而且将提供平常使用中须要的一些经常使用的命令以及参数。vim
本手册将从入门到精通指导你们。 首先,咱们要从如何以 Git 的思惟方式管理源代码开始。缓存
懂得 Git,第一件重要的事情就是要知道它与 Subversion、Perforce 或者任何你用过的版本控制工具都有着很大的差异。 一般,忘掉你预想的版本控制方式,改以 Git 的方式思考,可以帮助你更好地学习 Git。ruby
让咱们从头开始。假设你正在设计一个新的源代码管理系统。在你使用某个工具以前,是如何完成基本的源码版本控制工做的呢? 十有八九,你只是在项目到达某些阶段的时候,对项目作一份拷贝。
$ cp -R project project.bak
这样,你就能够在事情变得一团糟的时候很方便的返回到以前的状态,或者经过对比当前的项目与以前的拷贝,看看本身在以后的工做中,都作了哪些修改。
若是你有点偏执,你可能会常常做上面说的事情,或许还会给项目拷贝加个日期:
$ cp -R project project.2010-06-01.bak
如此,你就有了一堆项目在各个阶段的快照,来做比较、查看。使用这种模式,你还能够有效地与人分享项目变动。 若是你会在项目到达必定阶段的时候给它打个包,丢到本身的网站上,那其余的开发者们,就能很方便地下载它,作点改动,并给你补丁回馈。
$ wget http://example.com/project.2010-06-01.zip $ unzip project.2010-06-01.zip $ cp -R project.2010-06-01 project-my-copy $ cd project-my-copy $ (作了某些修改) $ diff project-my-copy project.2010-06-01 > change.patch $ (经过E-mail发送修改补丁)
以此方式,原先的开发者就能将其余人的改动应用到他的项目中去,其余开发者也能了解你作的变动。其实这即是许多开源项目采用过多年的协做方式。
这办法其实很好使,因此假设咱们如今想要写个工具,让这个办法更快、更简单。 咱们与其实现一个工具以记录每一个文件的版本,可能不如去实现个工具以使建立、储存项目的快照更加方便,不用每次都去人肉做整个项目的拷贝。
这就是 Git 的精要所在。你经过 git commit
告诉 Git 你想保存一份项目快照, Git 就会为你的项目中的各个文件的当前状态存一份记录。以后,绝大部分的 Git 命令都围绕这些记录展开。 好比查看它们的区别(diff),提取它们的内容,等等。
若是你将 Git 看做一个排序、对比以及合并项目更新的工具,那就容易理解情况和正确作事了。
你得先有一个 Git 仓库,才能用它进行操做。仓库是 Git 存放你要保存的快照的数据的地方。
拥有一个 Git 仓库的途径有两种。在已有的目录中,初始化一个新的,其一。 好比一个新的项目,或者一个已存在的项目,但该项目还没有有版本控制。若是你想要复制一份别人的项目, 或者与别人合做某个项目,也能够从一个公开的 Git 仓库克隆,其二。本章将对二者都作介绍。
若是设置了,在输入命令示界面能够很方便的使用复制和粘贴(用左键选取要复制的,点右键直接就能够复制,粘贴时只需点一下右键。)设置方法:Git Bash快捷图标(桌面图标)右键属性-选项,把快速编辑模式勾上就能够,以下图:
若是设置了,就不用每次打开Git再cd打开目录了。方法:右键Git Bash快捷图标(桌面图标)属性,找到快捷方式-起始位置,把你的项目地址放在这里就能够了。以下图:
用户名邮箱做用 : 咱们须要设置一个用户名 和 邮箱, 这是用来上传本地仓库到GitHub中, 在GitHub中显示代码上传者;
使用命令 :
git config --global user.name "HanShuliang" //设置用户名 git config --global user.email "13241153187@163.com" //设置邮箱
到此Git客户端已安装及GitHub配置完成,如今能够从GitHub传输代码了。
在目录中执行 git init
,就能够建立一个 Git 仓库了。好比,咱们刚好有个目录,里头有些许文件,以下:
$ cd konnichiwa $ ls README hello.rb
在这个项目里头,咱们会用各类编程语言写 “Hello World” 实例。 到目前为止,咱们只有 Ruby 的,不过,这才刚上路嘛。为了开始用 Git 对这个项目做版本控制,咱们执行一下 git init
。
$ git init Initialized empty Git repository in /opt/konnichiwa/.git/ # 在 /opt/konnichiwa/.git 目录初始化空 Git 仓库完毕。
如今你能够看到在你的项目目录中有个 .git
的子目录。 这就是你的 Git 仓库了,全部有关你的此项目的快照数据都存放在这里。
$ ls -a . .. .git README hello.rb
恭喜,如今你就有了一个 Git 仓库的架子,能够开始快照你的项目了。
简而言之,用
git init
来在目录中建立新的 Git 仓库。 你能够在任什么时候候、任何目录中这么作,彻底是本地化的。
若是你须要与他人合做一个项目,或者想要复制一个项目,看看代码,你就能够克隆那个项目。 执行 git clone [url]
,[url] 为你想要复制的项目,就能够了。
$ git clone git://github.com/schacon/simplegit.git
Initialized empty Git repository in /private/tmp/simplegit/.git/
remote: Counting objects: 100, done.
remote: Compressing objects: 100% (86/86), done.
remote: Total 100 (delta 35), reused 0 (delta 0)
Receiving objects: 100% (100/100), 9.51 KiB, done.
Resolving deltas: 100% (35/35), done.
$ cd simplegit/
$ ls
README Rakefile lib
上述操做将复制该项目的所有记录,让你本地拥有这些。而且该操做将拷贝该项目的主分支, 使你可以查看代码,或编辑、修改。进到该目录中,你会看到 .git
子目录。 全部的项目数据都存在那里。
$ ls -a
. .. .git README Rakefile lib $ cd .git $ ls HEAD description info packed-refs branches hooks logs refs config index objects
默认状况下,Git 会按照你提供的 URL 所指示的项目的名称建立你的本地项目目录。 一般就是该 URL 最后一个 /
以后的任何东西。若是你想要一个不同的名字, 你能够在该命令后加上它,就在那个 URL 后面。
简而言之,使用
git clone
拷贝一个 Git 仓库到本地,让本身可以查看该项目,或者进行修改。
Git 的工做就是建立和保存你的项目的快照及与以后的快照进行对比。本章将对有关建立与提交你的项目的快照的命令做介绍。
这里有个重要的概念,Git 有一个叫作“索引”的东东,有点像是你的快照的缓存区。这就使你可以从更改的文件中建立出一系列组织良好的快照,而不是一次提交全部的更改。
简而言之,使用
git add
添加须要追踪的新文件和待提交的更改, 而后使用git status
和git diff
查看有何改动, 最后用git commit
将你的快照记录。这就是你要用的基本流程,绝大部分时候都是这样的。
在 Git 中,在提交你修改的文件以前,你须要把它们添加到缓存。若是该文件是新建立的,你能够执行 git add
将该文件添加到缓存,可是,即便该文件已经被追踪了 —— 也就是说,曾经提交过了 —— 你仍然须要执行 git add 将新更改的文件添加到缓存去。让咱们看几个例子:
回到咱们的 Hello World 示例,初始化该项目以后,咱们就要用 git add
将咱们的文件添加进去了。 咱们能够用 git status
看看咱们的项目的当前状态。
$ git status -s
?? README
?? hello.rb
咱们有俩还没有被追踪的文件,得添加一下。
$ git add README hello.rb
如今咱们再执行 git status
,就能够看到这俩文件已经加上去了。
$ git status -s
A README
A hello.rb
新项目中,添加全部文件很广泛,能够在当前工做目录执行命令:git add .
。 由于 Git 会递归地将你执行命令时所在的目录中的全部文件添加上去,因此若是你将当前的工做目录做为参数, 它就会追踪那儿的全部文件了。如此,git add .
就和 git add README hello.rb
有同样的效果。 此外,效果一致的还有 git add *
,不过那只是由于咱们这还木有子目录,不须要递归地添加新文件。
好了,如今咱们改个文件,再跑一下 git status
,有点古怪。
$ vim README
$ git status -s
AM README A hello.rb
“AM” 状态的意思是,这个文件在咱们将它添加到缓存以后又有改动。这意味着若是咱们如今提交快照, 咱们记录的将是上次跑 git add
的时候的文件版本,而不是如今在磁盘中的这个。 Git 并不认为磁盘中的文件与你想快照的文件必须是一致的 —— (若是你须要它们一致,)得用 git add
命令告诉它。
一言以蔽之, 当你要将你的修改包含在即将提交的快照里的时候,执行
git add
。 任何你没有添加的改动都不会被包含在内 —— 这意味着你能够比绝大多数其余源代码版本控制系统更精确地归置你的快照。
请查看《Pro Git》中 git add
的 “-p” 参数,以了解更多关于提交文件的灵活性的例子。
正如你在 git add
小节中所看到的,你能够执行 git status
命令查看你的代码在缓存与当前工做目录的状态。我演示该命令的时候加了 -s
参数,以得到简短的结果输出。 若没有这个标记,命令 git status
将告诉你更多的提示与上下文欣喜。 如下即是一样状态下,有跟没有 -s
参数的输出对比。简短的输出以下:
$ git status -s
AM README A hello.rb
而一样的状态,详细的输出看起来是这样的:
$ git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
# (use "git rm --cached <file>..." to unstage)
#
# new file: README
# new file: hello.rb # # Changed but not updated: # (use "git add <file>..." to update what will be committed) # (use "git checkout -- <file>..." to discard changes in working directory) # # modified: README #
你很容易发现简短的输出看起来很紧凑。而详细输出则颇有帮助,提示你能够用何种命令完成你接下来可能要作的事情。
Git 还会告诉你在你上次提交以后,有哪些文件被删除、修改或者存入缓存了。
$ git status -s
M README
D hello.rb
你能够看到,在简短输出中,有两栏。第一栏是缓存的,第二栏则是工做目录的。 因此假设你临时提交了 README 文件,而后又改了些,而且没有执行 git add
,你会看到这个:
$ git status -s
MM README D hello.rb
一言以蔽之,执行
git status
以查看在你上次提交以后有啥被修改或者临时提交了, 从而决定本身是否须要提交一次快照,同时也能知道有什么改变被记录进去了。
git diff
有两个主要的应用场景。咱们将在此介绍其一, 在 检阅与对照 一章中,咱们将介绍其二。 咱们这里介绍的方式是用此命令描述已临时提交的或者已修改但还没有提交的改动。
若是没有其余参数,git diff
会以规范化的 diff 格式(一个补丁)显示自从你上次提交快照以后还没有缓存的全部更改。
$ vim hello.rb
$ git status -s
M hello.rb
$ git diff
diff --git a/hello.rb b/hello.rb index d62ac43..8d15d50 100644 --- a/hello.rb +++ b/hello.rb @@ -1,7 +1,7 @@ class HelloWorld def self.hello - puts "hello world" + puts "hola mundo" end end
因此,git status
显示你上次提交更新至后所更改或者写入缓存的改动, 而 git diff
一行一行地显示这些改动具体是啥。 一般执行完 git status
以后接着跑一下 git diff
是个好习惯。
git diff --cached
命令会告诉你有哪些内容已经写入缓存了。 也就是说,此命令显示的是接下来要写入快照的内容。因此,若是你将上述示例中的 hello.rb
写入缓存,由于 git diff
显示的是还没有缓存的改动,因此在此执行它不会显示任何信息。
$ git status -s
M hello.rb
$ git add hello.rb
$ git status -s
M hello.rb $ git diff $
若是你想看看已缓存的改动,你须要执行的是 git diff --cached
。
$ git status -s
M hello.rb
$ git diff
$
$ git diff --cached
diff --git a/hello.rb b/hello.rb index d62ac43..8d15d50 100644 --- a/hello.rb +++ b/hello.rb @@ -1,7 +1,7 @@ class HelloWorld def self.hello - puts "hello world" + puts "hola mundo" end end
若是你想一并查看已缓存的与未缓存的改动,能够执行 git diff HEAD
—— 也就是说你要看到的是工做目录与上一次提交的更新的区别,无视缓存。 假设咱们又改了些 ruby.rb
的内容,那缓存的与未缓存的改动咱们就都有了。 以上三个 diff
命令的结果以下:
$ vim hello.rb
$ git diff
diff --git a/hello.rb b/hello.rb
index 4f40006..2ae9ba4 100644
--- a/hello.rb
+++ b/hello.rb
@@ -1,7 +1,7 @@ class HelloWorld + # says hello def self.hello puts "hola mundo" end end $ git diff --cached diff --git a/hello.rb b/hello.rb index 2aabb6e..4f40006 100644 --- a/hello.rb +++ b/hello.rb @@ -1,7 +1,7 @@ class HelloWorld def self.hello - puts "hello world" + puts "hola mundo" end end $ git diff HEAD diff --git a/hello.rb b/hello.rb index 2aabb6e..2ae9ba4 100644 --- a/hello.rb +++ b/hello.rb @@ -1,7 +1,8 @@ class HelloWorld + # says hello def self.hello - puts "hello world" + puts "hola mundo" end end
若是咱们不想要看整个 diff 输出,可是又想比 git status
详细点, 就能够用 --stat
选项。该选项使它显示摘要而非全文。上文示例在使用 --stat
选项时,输出以下:
$ git status -s
MM hello.rb $ git diff --stat hello.rb | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) $ git diff --cached --stat hello.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) $ git diff HEAD --stat hello.rb | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-)
你还能够在上述命令后面制定一个目录,从而只查看特定文件或子目录的 diff
输出。
简而言之, 执行
git diff
来查看执行git status
的结果的详细信息 —— 一行一行地显示这些文件是如何被修改或写入缓存的。
如今你使用 git add
命令将想要快照的内容写入了缓存, 执行 git commit
就将它实际存储快照了。 Git 为你的每个提交都记录你的名字与电子邮箱地址,因此第一步是告诉 Git 这些都是啥。
$ git config --global user.name 'Your Name' $ git config --global user.email you@somedomain.com
让咱们写入缓存,并提交对 hello.rb
的全部改动。在首个例子中,咱们使用 -m
选项以在命令行中提供提交注释。
$ git add hello.rb
$ git status -s
M hello.rb
$ git commit -m 'my hola mundo changes'
[master 68aa034] my hola mundo changes
1 files changed, 2 insertions(+), 1 deletions(-)
如今咱们已经记录了快照。若是咱们再执行 git status
,会看到咱们有一个“干净的工做目录”。 这意味着咱们在最近一次提交以后,没有作任何改动 —— 在咱们的项目中没有未快照的工做。
$ git status # On branch master nothing to commit (working directory clean)
若是你漏掉了 -m
选项,Git 会尝试为你打开一个编辑器以填写提交信息。 若是 Git 在你对它的配置中找不到相关信息,默认会打开 vim
。屏幕会像这样:
# 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: # (use "git reset HEAD <file>..." to unstage) # # modified: hello.rb # ~ ~ ".git/COMMIT_EDITMSG" 9L, 257C
在此,你在文件头部添加实际的提交信息。以“#”开头的行都会被无视 ——Git 将 git status
的输出结果放在那儿以提示你都改了、缓存了啥。
一般,撰写良好的提交信息是很重要的。以开放源代码项目为例,多多少少以如下格式写你的提示消息是个不成文的规定:
简短的关于改动的总结(25个字或者更少) 若是有必要,更详细的解释文字。约 36 字时换行。在某些状况下, 第一行会被做为电子邮件的开头,而剩余的则会做为邮件内容。 将小结从内容隔开的空行是相当重要的(除非你没有内容); 若是这两个待在一块儿,有些 git 工具会犯迷糊。 空行以后是更多的段落。 - 列表也能够 - 一般使用连字符(-)或者星号(*)来标记列表,前面有个空格, 在列表项之间有空行,不过这些约定也会有些变化。 # 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: # (use "git reset HEAD <file>..." to unstage) # # modified: hello.rb # ~ ~ ~ ".git/COMMIT_EDITMSG" 25L, 884C written
提交注解是很重要的。由于 Git 很大一部分能耐就是它在组织本地提交和与他人分享的弹性, 它很给力地可以让你为逻辑独立的改变写三到四条提交注解,以便你的工做被同仁审阅。由于提交与推送改动是有区别的, 请务必花时间将各个逻辑独立的改动放到另一个提交,并附上一份良好的提交注解, 以使与你合做的人可以方便地了解你所作的,以及你为什么要这么作。
若是你以为 git add
提交缓存的流程太过繁琐,Git 也容许你用 -a
选项跳过这一步。 基本上这句话的意思就是,为任何已有记录的文件执行 git add
—— 也就是说,任何在你最近的提交中已经存在,而且以后被修改的文件。 这让你可以用更 Subversion 方式的流程,修改些文件,而后想要快照全部所作的改动的时候执行 git commit -a
。 不过你仍然须要执行 git add
来添加新文件,就像 Subversion 同样。
$ vim hello.rb
$ git status -s
M hello.rb
$ git commit -m 'changes to hello file'
# On branch master
# Changed but not updated:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: hello.rb
#
no changes added to commit (use "git add" and/or "git commit -a") $ git commit -am 'changes to hello file' [master 78b2670] changes to hello file 1 files changed, 2 insertions(+), 1 deletions(-)
注意,若是你不缓存改动,直接执行 git commit
,Git 会直接给出 git status
命令的输出,提醒你啥也没缓存。我已将该消息中的重要部分高亮,它说没有添加须要提交的缓存。 若是你使用 -a
,它会缓存并提交每一个改动(不含新文件)。
如今你就完成了整个快照的流程 ——改些文件,而后用 git add
将要提交的改动提交到缓存, 用 git status
和 git diff
看看你都改了啥,最后 git commit
永久地保存快照。
简而言之,执行
git commit
记录缓存区的快照。若是须要的话,这个快照能够用来作比较、共享以及恢复。
git reset
多是人类写的最费解的命令了。 我用 Git 有些年头了,甚至还写了本书,但有的时候仍是会搞不清它会作什么。 因此,我只说三个明确的,一般有用的调用。请你跟我同样尽管用它 —— 由于它能够颇有用。
在此例中,咱们能够用它来将不当心缓存的东东取消缓存。假设你修改了两个文件,想要将它们记录到两个不一样的提交中去。 你应该缓存并提交一个,再缓存并提交另一个。若是你不当心两个都缓存了,那要如何才能取消缓存呢? 你能够用 git reset HEAD -- file
。 技术上说,在这里你不须要使用 --
—— 它用来告诉 Git 这时你已经再也不列选项,剩下的是文件路径了。 不过养成使用它分隔选项与路径的习惯很重要,即便在你可能并不须要的时候。
好,让咱们看看取消缓存是什么样子的。这里咱们有两个最近提交以后又有所改动的文件。咱们将两个都缓存,并取消缓存其中一个。
$ git status -s
M README
M hello.rb $ git add . $ git status -s M README M hello.rb $ git reset HEAD -- hello.rb Unstaged changes after reset: M hello.rb $ git status -s M README M hello.rb
如今你执行 git commit
将只记录 README
文件的改动,并不含如今并不在缓存中的 hello.rb
。
若是你好奇,它实际的操做是将该文件在“索引”中的校验和重置为最近一次提交中的值。 git add
会计算一个文件的校验和,将它添加到“索引”中, 而 git reset HEAD
将它改写回原先的,从而取消缓存操做。
若是你想直接执行 git unstage
,你能够在 Git 中配置个别名。 执行 git config --global alias.unstage "reset HEAD"
便可。 一旦执行完它,你就能够直接用 git unstage [file]
做为代替了。
若是你忘了取消缓存的命令,Git 的常规 git status
输出的提示会颇有帮助。 例如,在你有已缓存的文件时,若是你不带 -s
执行 git status
,它将告诉你怎样取消缓存:
$ git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: README # modified: hello.rb #
简而言之,执行
git reset HEAD
以取消以前git add
添加,但不但愿包含在下一提交快照中的缓存。
git rm
会将条目从缓存区中移除。这与 git reset HEAD
将条目取消缓存是有区别的。 “取消缓存”的意思就是将缓存区恢复为咱们作出修改以前的样子。 在另外一方面,git rm
则将该文件完全从缓存区踢出,所以它再也不下一个提交快照以内,进而有效地删除它。
默认状况下,git rm file
会将文件从缓存区和你的硬盘中(工做目录)删除。 若是要在工做目录中留着该文件,可使用 git rm --cached
不像绝大多数其余版本控制系统,Git 并不记录记录文件重命名。它反而只记录快照,并对比快照以找到有啥文件可能被重命名了。 若是一个文件从更新中删除了,而在下次快照中新添加的另外一个文件的内容与它很类似,Git 就知道这极有多是个重命名。 所以,虽然有 git mv
命令,但它有点多余 —— 它作得全部事情就是 git rm --cached
, 重命名磁盘上的文件,而后再执行 git add
把新文件添加到缓存区。 你并不须要用它,不过若是以为这样容易些,尽管用吧。
我本身并不使用此命令的普通形式 —— 删除文件。一般直接从硬盘删除文件,而后执行 git commit -a
会简单些。 它会自动将删除的文件从索引中移除。
简而言之, 执行
git rm
来删除 Git 追踪的文件。它还会删除你的工做目录中的相应文件。
分支是我最喜欢的 Git 特性之一。若是你用过其余版本控制系统,把你所知的分支给忘记,倒可能更有帮助些 —— 事实上,以咱们使用分支的方式,把 Git 的分支看做 上下文 反而更合适。 当你检出分支时,你能够在两三个不一样的分支之间来回切换。
简而言之,你能够执行
git branch (branchname)
来建立分支, 使用git checkout (branchname)
命令切换到该分支,在该分支的上下文环境中, 提交快照等,以后能够很容易地来回切换。当你切换分支的时候,Git 会用该分支的最后提交的快照替换你的工做目录的内容, 因此多个分支不须要多个目录。使用git merge
来合并分支。你能够屡次合并到统一分支, 也能够选择在合并以后直接删除被并入的分支。
git branch
命令是 Git 中的通用分支管理工具,能够经过它完成多项任务。 咱们先说你会用到的最多的命令 —— 列出分支、建立分支和删除分支。 咱们还会介绍用来切换分支的 git checkout
命令。
没有参数时,git branch
会列出你在本地的分支。你所在的分支的行首会有个星号做标记。 若是你开启了彩色模式,当前分支会用绿色显示。
$ git branch
* master
此例的意思就是,咱们有一个叫作“master”的分支,而且该分支是当前分支。 当你执行 git init
的时候,缺省状况下 Git 就会为你建立“master”分支。 可是这名字一点特殊意味都没有 —— 事实上你并不非得要一个叫作“master”的分支。 不过因为它是缺省分支名的缘故,绝大部分项目都有这个分支。
咱们动手建立一个分支,并切换过去。执行 git branch (branchname)
便可。
$ git branch testing
$ git branch
* master
testing
如今咱们能够看到,有了一个新分支。当你以此方式在上次提交更新以后建立了新分支,若是后来又有更新提交, 而后又切换到了“testing”分支,Git 将还原你的工做目录到你建立分支时候的样子 —— 你能够把它看做一个记录你当前进度的书签。让咱们实际运用看看 —— 咱们用 git checkout (branch)
切换到咱们要修改的分支。
$ ls README hello.rb $ echo 'test content' > test.txt $ echo 'more content' > more.txt $ git add *.txt $ git commit -m 'added two files' [master 8bd6d8b] added two files 2 files changed, 2 insertions(+), 0 deletions(-) create mode 100644 more.txt create mode 100644 test.txt $ ls README hello.rb more.txt test.txt $ git checkout testing Switched to branch 'testing' $ ls README hello.rb
当咱们切换到“测试”分支的时候,咱们添加的新文件被移除了。切换回“master”分支的时候,它们有从新出现了。
$ ls README hello.rb $ git checkout master Switched to branch 'master' $ ls README hello.rb more.txt test.txt
一般状况下,你会更但愿当即切换到新分支,从而在该分支中操做,而后当此分支的开发日趋稳定时, 将它合并到稳定版本的分支(例如“master”)中去。 执行 git branch newbranch; git checkout newbranch
也很简单, 不过 Git 还为你提供了快捷方式:git checkout -b newbranch
。
$ git branch * master $ ls README hello.rb more.txt test.txt $ git checkout -b removals Switched to a new branch 'removals' $ git rm more.txt rm 'more.txt' $ git rm test.txt rm 'test.txt' $ ls README hello.rb $ git commit -am 'removed useless files' [removals 8f7c949] removed useless files 2 files changed, 0 insertions(+), 2 deletions(-) delete mode 100644 more.txt delete mode 100644 test.txt $ git checkout master Switched to branch 'master' $ ls README hello.rb more.txt test.txt
如你所见,咱们建立了一个分支,在该分支的上下文中移除了一些文件,而后切换回咱们的主分支,那些文件又回来了。 使用分支将工做切分开来,从而让咱们可以在不一样上下文中作事,并来回切换。
建立新分支,在其中完成一部分工做,完成以后将它合并到主分支并删除。你会以为这很方便,由于这么作很快很容易。 如此,当你以为这部分工做并不靠谱,舍弃它很容易。而且,若是你必须回到稳定分支作些事情, 也能够很方便地这个独立分支的工做先丢在一边,完成要事以后再切换回来。
假设咱们要删除一个分支(好比上例中的“testing”分支,该分支没啥特殊的内容了), 能够执行 git branch -d (branch)
把它删掉。
$ git branch
* master
testing
$ git branch -d testing
Deleted branch testing (was 78b2670).
$ git branch
* master
简而言之 使用
git branch
列出现有的分支、建立新分支以及删除没必要要或者已合并的分支。
一旦某分支有了独立内容,你终究会但愿将它合并回到你的主分支。 你可使用 git merge
命令将任何分支合并到当前分支中去。 咱们那上例中的“removals”分支为例。假设咱们建立了一个分支,移除了一些文件,并将它提交到该分支, 其实该分支是与咱们的主分支(也就是“master”)独立开来的。 要想将这些移除操做包含在主分支中,你能够将“removals”分支合并回去。
$ git branch
* master
removals
$ ls
README hello.rb more.txt test.txt
$ git merge removals
Updating 8bd6d8b..8f7c949
Fast-forward
more.txt | 1 -
test.txt | 1 -
2 files changed, 0 insertions(+), 2 deletions(-)
delete mode 100644 more.txt
delete mode 100644 test.txt
$ ls
README hello.rb
固然,合并并不只仅是简单的文件添加、移除的操做,Git 也会合并修改 —— 事实上,它很会合并修改。 举例,咱们看看在某分支中编辑某个文件,而后在另外一个分支中把它的名字改掉再作些修改, 最后将这俩分支合并起来。你以为会变成一坨 shi?咱们试试看。
$ git branch * master $ cat hello.rb class HelloWorld def self.hello puts "Hello World" end end HelloWorld.hello
首先,咱们建立一个叫作“change_class”的分支,切换过去,从而将重命名类等操做独立出来。咱们将类名从 “HelloWorld” 改成 “HiWorld”。
$ git checkout -b change_class M hello.rb Switched to a new branch 'change_class' $ vim hello.rb $ head -1 hello.rb class HiWorld $ git commit -am 'changed the class name' [change_class 3467b0a] changed the class name 1 files changed, 2 insertions(+), 4 deletions(-)
而后,将重命名类操做提交到 “change_class” 分支中。 如今,假如切换回 “master” 分支咱们能够看到类名恢复到了咱们切换到 “change_class” 分支以前的样子。 如今,再作些修改(即代码中的输出),同时将文件名从 hello.rb
改成 ruby.rb
。
$ git checkout master
Switched to branch 'master'
$ git mv hello.rb ruby.rb
$ vim ruby.rb
$ git diff
diff --git a/ruby.rb b/ruby.rb
index 2aabb6e..bf64b17 100644
--- a/ruby.rb
+++ b/ruby.rb
@@ -1,7 +1,7 @@ class HelloWorld def self.hello - puts "Hello World" + puts "Hello World from Ruby" end end $ git commit -am 'added from ruby' [master b7ae93b] added from ruby 1 files changed, 1 insertions(+), 1 deletions(-) rename hello.rb => ruby.rb (65%)
如今这些改变已经记录到个人 “master” 分支了。请注意,这里类名仍是 “HelloWorld”,而不是 “HiWorld”。 而后我想将类名的改变合并过来,我把 “change_class” 分支合并过来就好了。 可是,我已经将文件名都改掉了,Git 知道该怎么办么?
$ git branch change_class * master $ git merge change_class Renaming hello.rb => ruby.rb Auto-merging ruby.rb Merge made by recursive. ruby.rb | 6 ++---- 1 files changed, 2 insertions(+), 4 deletions(-) $ cat ruby.rb class HiWorld def self.hello puts "Hello World from Ruby" end end HiWorld.hello
不错,它就是发现了。请注意,在这部操做,我没有遇到合并冲突,而且文件已经重命名、类名也换掉了。挺酷。
那么,Git 合并颇有魔力,咱们不再用处理合并冲突了,对吗?不太确切。 不一样分支中修改了相同区块的代码,电脑本身猜不透神马的状况下,冲突就摆在咱们面前了。 咱们看看两个分支中改了同一行代码的例子。
$ git branch * master $ git checkout -b fix_readme Switched to a new branch 'fix_readme' $ vim README $ git commit -am 'fixed readme title' [fix_readme 3ac015d] fixed readme title 1 files changed, 1 insertions(+), 1 deletions(-)
咱们在某分支中修改了 README 文件中的一行,并提交了。咱们再在 “master” 分支中对同个文件的同一行内容做不一样的修改。
$ git checkout master Switched to branch 'master' $ vim README $ git commit -am 'fixed readme title differently' [master 3cbb6aa] fixed readme title differently 1 files changed, 1 insertions(+), 1 deletions(-)
有意思的来了 —— 咱们将前一个分支合并到 “master” 分支,一个合并冲突就出现了。
$ git merge fix_readme Auto-merging README CONFLICT (content): Merge conflict in README Automatic merge failed; fix conflicts and then commit the result. $ cat README <<<<<<< HEAD Many Hello World Examples ======= Hello World Lang Examples >>>>>>> fix_readme This project has examples of hello world in nearly every programming language.
你能够看到,Git 在产生合并冲突的地方插入了标准的与 Subversion 很像的合并冲突标记。 轮到咱们去解决这些冲突了。在这里咱们就手动把它解决。若是你要 Git 打开一个图形化的合并工具, 能够看看 git 合并工具 (好比 kdiff三、emerge、p4merge 等)。
$ vim README here I'm fixing the conflict
$ git diff
diff --cc README index 9103e27,69cad1a..0000000 --- a/README +++ b/README @@@ -1,4 -1,4 +1,4 @@@ - Many Hello World Examples -Hello World Lang Examples ++Many Hello World Lang Examples This project has examples of hello world in
在 Git 中,处理合并冲突的时候有个很酷的提示。 若是你执行 git diff
,就像我演示的这样,它会告诉你冲突的两方,和你是如何解决的。 如今是时候把它标记为已解决了。在 Git 中,咱们能够用 git add
—— 要告诉 Git 文件冲突已经解决,你必须把它写入缓存区。
$ git status -s UU README $ git add README $ git status -s M README $ git commit [master 8d585ea] Merge branch 'fix_readme'
如今咱们成功解决了合并中的冲突,并提交告终果
简而言之 使用
git merge
将另外一个分支并入当前的分支中去。 Git 会自动以最佳方式将两个不一样快照中独特的工做合并到一个新快照中去。
到目前为止,咱们已经提交快照到项目中,在不一样的各自分离的上下文中切换, 但假如咱们忘了本身是如何到目前这一步的那该怎么办?或者假如咱们想知道此分支与彼分支到底有啥区别? Git 提供了一个告诉你使你达成当前快照的全部提交消息的工具,叫作 git log
。
要理解日志(log)命令,你须要了解当执行 git commit
以存储一个快照的时候,都有啥信息被保存了。 除了文件详单、提交消息和提交者的信息,Git 还保存了你的这次提交所基于的快照。 也就是,假如你克隆了一个项目,你是在什么快照的基础上作的修改而获得新保存的快照的? 这有益于为项目进程提供上下文,使 Git 可以弄明白谁作了什么改动。 若是 Git 有你的快照所基于的快照的话,它就能自动判断你都改变了什么。而新提交所基于的提交,被称做新提交的“父亲”。
某分支的按时间排序的“父亲”列表,当你在该分支时,能够执行 git log
以查看。 例如,若是咱们在本章中操做的 Hello World 项目中执行 git log
,咱们能够看到已提交的消息。
$ git log
commit 8d585ea6faf99facd39b55d6f6a3b3f481ad0d3d
Merge: 3cbb6aa 3ac015d
Author: Scott Chacon <schacon@gmail.com>
Date: Fri Jun 4 12:59:47 2010 +0200
Merge branch 'fix_readme'
Conflicts:
README
commit 3cbb6aae5c0cbd711c098e113ae436801371c95e Author: Scott Chacon <schacon@gmail.com> Date: Fri Jun 4 12:58:53 2010 +0200 fixed readme title differently commit 3ac015da8ade34d4c7ebeffa2053fcac33fb495b Author: Scott Chacon <schacon@gmail.com> Date: Fri Jun 4 12:58:36 2010 +0200 fixed readme title commit 558151a95567ba4181bab5746bc8f34bd87143d6 Merge: b7ae93b 3467b0a Author: Scott Chacon <schacon@gmail.com> Date: Fri Jun 4 12:37:05 2010 +0200 Merge branch 'change_class' ...
咱们能够用 --oneline
选项来查看历史记录的紧凑简洁的版本。
$ git log --oneline 8d585ea Merge branch 'fix_readme' 3cbb6aa fixed readme title differently 3ac015d fixed readme title 558151a Merge branch 'change_class' b7ae93b added from ruby 3467b0a changed the class name 17f4acf first commit
这告诉咱们的是,此项目的开发历史。若是提交消息描述性很好,这就能为咱们提供关于有啥改动被应用、或者影响了当前快照的状态、以及这快照里头都有啥。
咱们还能够用它的十分有帮助的 --graph
选项,查看历史中何时出现了分支、合并。如下为相同的命令,开启了拓扑图选项:
$ git log --oneline --graph * 8d585ea Merge branch 'fix_readme' |\ | * 3ac015d fixed readme title * | 3cbb6aa fixed readme title differently |/ * 558151a Merge branch 'change_class' |\ | * 3467b0a changed the class name * | b7ae93b added from ruby |/ * 17f4acf first commit
如今咱们能够更清楚明了地看到什么时候工做分叉、又什么时候归并。 这对查看发生了什么、应用了什么改变颇有帮助,而且极大地帮助你管理你的分支。 让咱们建立一个分支,在里头作些事情,而后切回到主分支,也作点事情,而后看看 log
命令是如何帮助咱们理清这俩分支上都发生了啥的。
首先咱们建立一个分支,来添加 Erlang 编程语言的 Hello World 示例 —— 咱们想要在一个分支里头作这个,以免让可能还不能工做的代码弄乱咱们的稳定分支。 这样就能够切来切去,片叶不沾身。
$ git checkout -b erlang Switched to a new branch 'erlang' $ vim erlang_hw.erl $ git add erlang_hw.erl $ git commit -m 'added erlang' [erlang ab5ab4c] added erlang 1 files changed, 5 insertions(+), 0 deletions(-) create mode 100644 erlang_hw.erl
因为咱们玩函数式编程很开心,以致于沉迷其中,又在“erlang”分支中添加了一个 Haskell 的示例程序。
$ vim haskell.hs $ git add haskell.hs $ git commit -m 'added haskell' [erlang 1834130] added haskell 1 files changed, 4 insertions(+), 0 deletions(-) create mode 100644 haskell.hs
最后,咱们决定仍是把 Ruby 程序的类名改回原先的样子。与其建立另外一个分支,咱们能够返回主分支,改变它,而后直接提交。
$ git checkout master Switched to branch 'master' $ ls README ruby.rb $ vim ruby.rb $ git commit -am 'reverted to old class name' [master 594f90b] reverted to old class name 1 files changed, 2 insertions(+), 2 deletions(-)
如今假设咱们有段时间不作这个项目了,咱们作别的去了。 当咱们回来的时候,咱们想知道“erlang”分支都是啥,而主分支的进度又是怎样。 仅仅看分支的名字,咱们是无从知道本身还在里面有 Haskell 的改动的,可是用 git log
咱们就能够。 若是你在命令行中提供一个分支名字,它就会显示该分支历史中“可及”的提交,即从该分支创立起可追溯的影响了最终的快照的提交。
$ git log --oneline erlang
1834130 added haskell
ab5ab4c added erlang
8d585ea Merge branch 'fix_readme'
3cbb6aa fixed readme title differently
3ac015d fixed readme title
558151a Merge branch 'change_class'
b7ae93b added from ruby
3467b0a changed the class name
17f4acf first commit
如此,咱们很容易就看到分支里头还包括了 Haskell 代码(高亮显示了)。 更酷的是,咱们很容易地告诉 Git,咱们只对某个分支中可及的提交感兴趣。换句话说,某分支中与其余分支相比惟一的提交。
在此例中,若是咱们想要合并“erlang”分支,咱们须要看当合并的时候,都有啥提交会做用到咱们的快照上去。 咱们告诉 Git 的方式是,在不想要看到的分支前放一个 ^
。 例如,若是咱们想要看“erlang”分支中但不在主分支中的提交,咱们能够用 erlang ^master
,或者反之。
$ git log --oneline erlang ^master 1834130 added haskell ab5ab4c added erlang $ git log --oneline master ^erlang 594f90b reverted to old class name
这为咱们提供了一个良好的、简易的分支管理工具。它使咱们可以很是容易地查看对某个分支惟一的提交,从而知道咱们缺乏什么,以及当咱们要合并时,会有什么被合并进去。
简而言之 使用
git log
列出促成当前分支目前的快照的提交历史记录。这使你可以看到项目是如何到达如今的情况的。
若是你达到一个重要的阶段,并但愿永远记住那个特别的提交快照,你可使用 git tag
给它打上标签。 该 tag
命令基本上会给该特殊提交打上永久的书签,从而使你在未来可以用它与其余提交比较。 一般,你会在切取一个发布版本或者交付一些东西的时候打个标签。
好比说,咱们想为咱们的 Hello World 项目发布一个“1.0”版本。 咱们能够用 git tag -a v1.0
命令给最新一次提交打上(HEAD
)“v1.0”的标签。 -a
选项意为“建立一个带注解的标签”,从而使你为标签添加注解。绝大部分时候都会这么作的。 不用 -a
选项也能够执行的,但它不会记录这标签是啥时候打的,谁打的,也不会让你添加个标签的注解。 我推荐一直建立带注解的标签。
$ git tag -a v1.0
当你执行 git tag -a
命令时,Git 会打开你的编辑器,让你写一句标签注解,就像你给提交写注解同样。
如今,注意当咱们执行 git log --decorate
时,咱们能够看到咱们的标签了:
$ git log --oneline --decorate --graph
* 594f90b (HEAD, tag: v1.0, master) reverted to old class name
* 8d585ea Merge branch 'fix_readme'
|\
| * 3ac015d (fix_readme) fixed readme title
* | 3cbb6aa fixed readme title differently
|/
* 558151a Merge branch 'change_class'
|\
| * 3467b0a changed the class name
* | b7ae93b added from ruby
|/
* 17f4acf first commit
若是咱们有新提交,该标签依然会待在该提交的边上,因此咱们已经给那个特定快照永久打上标签,而且可以将它与将来的快照作比较。
不过咱们并不须要给当前提交打标签。若是咱们忘了给某个提交打标签,又将它发布了,咱们能够给它追加标签。 在相同的命令末尾加上提交的 SHA,执行,就能够了。 例如,假设咱们发布了提交 558151a
(几个提交以前的事情了),可是那时候忘了给它打标签。 咱们如今也能够:
$ git tag -a v0.9 558151a
$ git log --oneline --decorate --graph
* 594f90b (HEAD, tag: v1.0, master) reverted to old class name
* 8d585ea Merge branch 'fix_readme'
|\
| * 3ac015d (fix_readme) fixed readme title
* | 3cbb6aa fixed readme title differently
|/
* 558151a (tag: v0.9) Merge branch 'change_class'
|\
| * 3467b0a changed the class name
* | b7ae93b added from ruby
|/
* 17f4acf first commit
Git 并不像 Subversion 那样有个中心服务器。 目前为止全部的命令都是本地执行的,更新的知识本地的数据库。 要经过 Git 与其余开发者合做,你须要将数据放到一台其余开发者可以链接的服务器上。 Git 实现此流程的方式是将你的数据与另外一个仓库同步。在服务器与客户端之间并无实质的区别 —— Git 仓库就是 Git 仓库,你能够很容易地在二者之间同步。
一旦你有了个 Git 仓库,无论它是在你本身的服务器上,或者是由 GitHub 之类的地方提供, 你均可以告诉 Git 推送你拥有的远端仓库尚未的数据,或者叫 Git 从别的仓库把差异取过来。
联网的时候你能够随时作这个,它并不须要对应一个 commit
或者别的什么。 通常你会本地提交几回,而后从你的项目克隆自的线上的共享仓库提取数据以保持最新,将新完成的合并到你完成的工做中去,而后推送你的改动会服务器。
简而言之 使用
git fetch
更新你的项目,使用git push
分享你的改动。 你能够用git remote
管理你的远程仓库。
不像中心化的版本控制系统(客户端与服务端很不同),Git 仓库基本上都是一致的,而且并能够同步他们。 这使得拥有多个远端仓库变得容易 —— 你能够拥有一些只读的仓库,另外的一些也可写的仓库。
当你须要与远端仓库同步的时候,不须要使用它详细的连接。Git 储存了你感兴趣的远端仓库的连接的别名或者昵称。 你可使用 git remote
命令管理这个远端仓库列表。
若是没有任何参数,Git 会列出它存储的远端仓库别名了事。默认状况下,若是你的项目是克隆的(与本地建立一个新的相反), Git 会自动将你的项目克隆自的仓库添加到列表中,并取名“origin”。 若是你执行时加上 -v
参数,你还能够看到每一个别名的实际连接地址。
$ git remote origin $ git remote -v origin git@github.com:github/git-reference.git (fetch) origin git@github.com:github/git-reference.git (push)
在此你看到了该连接两次,是由于 Git 容许你为每一个远端仓库添加不一样的推送与获取的连接,以备你读写时但愿使用不一样的协议。
若是你但愿分享一个本地建立的仓库,或者你想要获取别人的仓库中的贡献 —— 若是你想要以任何方式与一个新仓库沟通,最简单的方式一般就是把它添加为一个远端仓库。 执行 git remote add [alias] [url]
就能够。 此命令将 [url]
以 [alias]
的别名添加为本地的远端仓库。
例如,假设咱们想要与整个世界分享咱们的 Hello World 程序。 咱们能够在一台服务器上建立一个新仓库(我以 GitHub 为例子)。 它应该会给你一个连接,在这里就是“git@github.com:schacon/hw.git”。 要把它添加到咱们的项目以便咱们推送以及获取更新,咱们能够这样:
$ git remote $ git remote add github git@github.com:schacon/hw.git $ git remote -v github git@github.com:schacon/hw.git (fetch) github git@github.com:schacon/hw.git (push)
像分支的命名同样,远端仓库的别名是强制的 —— 就像“master”,没有特别意义,但它广为使用, 由于 git init
默认用它;“origin”常常被用做远端仓库别名,就由于 git clone
默认用它做为克隆自的连接的别名。此例中,我决定给个人远端仓库取名“github”,但我叫它随便什么均可以。
Git addeth and Git taketh away. 若是你须要删除一个远端 —— 再也不须要它了、项目已经没了,等等 —— 你可使用 git remote rm [alias]
把它删掉。
$ git remote -v github git@github.com:schacon/hw.git (fetch) github git@github.com:schacon/hw.git (push) $ git remote add origin git://github.com/pjhyett/hw.git $ git remote -v github git@github.com:schacon/hw.git (fetch) github git@github.com:schacon/hw.git (push) origin git://github.com/pjhyett/hw.git (fetch) origin git://github.com/pjhyett/hw.git (push) $ git remote rm origin $ git remote -v github git@github.com:schacon/hw.git (fetch) github git@github.com:schacon/hw.git (push)
简而言之 你能够用
git remote
列出你的远端仓库和那些仓库的连接。 你可使用git remote add
添加新的远端仓库,用git remote rm
删掉已存在的那些。
Git 有两个命令用来从某一远端仓库更新。 git fetch
会使你与另外一仓库同步,提取你本地所没有的数据,为你在同步时的该远端的每一分支提供书签。 这些分支被叫作“远端分支”,除了 Git 不容许你检出(切换到该分支)以外,跟本地分支没区别 —— 你能够将它们合并到当前分支,与其余分支做比较差别,查看那些分支的历史日志,等等。同步以后你就能够在本地操做这些。
第二个会从远端服务器提取新数据的命令是 git pull
。 基本上,该命令就是在 git fetch
以后紧接着 git merge
远端分支到你所在的任意分支。 我我的不太喜欢这命令 —— 我更喜欢fetch
和 merge
分开来作。少点魔法,少点问题。 不过,若是你喜欢这主意,你能够看一下 git pull
的 官方文档。
假设你配置好了一个远端,而且你想要提取更新,你能够首先执行 git fetch [alias]
告诉 Git 去获取它有你没有的数据,而后你能够执行 git merge [alias]/[branch]
以将服务器上的任何更新(假设有人这时候推送到服务器了)合并到你的当前分支。 那么,若是我是与两三个其余人合做 Hello World 项目,而且想要将我最近链接以后的全部改动拿过来,我能够这么作:
$ git fetch github remote: Counting objects: 4006, done. remote: Compressing objects: 100% (1322/1322), done. remote: Total 2783 (delta 1526), reused 2587 (delta 1387) Receiving objects: 100% (2783/2783), 1.23 MiB | 10 KiB/s, done. Resolving deltas: 100% (1526/1526), completed with 387 local objects. From github.com:schacon/hw 8e29b09..c7c5a10 master -> github/master 0709fdc..d4ccf73 c-langs -> github/c-langs 6684f82..ae06d2b java -> github/java * [new branch] ada -> github/ada * [new branch] lisp -> github/lisp
能够看到自从上一次与远端仓库同步之后,又新赠或更新了五个分支。 “ada”与“lisp”分支是新的,而“master”、“clang”与“java”分支则被更新了。 在此例中,个人团队在合并入主分支以前,将提议的更新推送到远端分支以审核。
你能够看到 Git 作的映射。远端仓库的主分支成为了本地的一个叫作“github/master”的分支。 这样我就能够执行 git merge github/master
将远端的主分支和并入个人本地主分支。 或者,我能够 git log github/master ^master
看看该分支上的新提交。 若是你的远端仓库叫作“origin”,那远端主分支就会叫作 origin/master
。几乎全部能在本地分支上执行的命令均可以在远端分支上用。
若是你有多个远端仓库,你能够执行 git fetch [alias]
提取特定的远端仓库, 或者执行 git fetch --all
告诉 Git 同步全部的远端仓库。
简而言之 执行
git fetch [alias]
来将你的仓库与远端仓库同步,提取全部它独有的数据到本地分支以合并或者怎样。
想要与他人分享你牛鼻的提交,你须要将改动推送到远端仓库。 执行 git push [alias] [branch]
,就会将你的 [branch] 分支推送成为 [alias] 远端上的 [branch] 分支。 让咱们试试推送咱们的主分支到先前添加的“github”远端仓库上去。
$ git push github master Counting objects: 25, done. Delta compression using up to 2 threads. Compressing objects: 100% (25/25), done. Writing objects: 100% (25/25), 2.43 KiB, done. Total 25 (delta 4), reused 0 (delta 0) To git@github.com:schacon/hw.git * [new branch] master -> master
挺简单。如今若是有人从该仓库克隆,他会获得我提交的完彻底全的一份历史记录了。
若是有个像以前建立的“erlang”分支那样的主题分支,想只分享这个,该怎么办呢?你能够相应的只推送该分支。
$ git push github erlang Counting objects: 7, done. Delta compression using up to 2 threads. Compressing objects: 100% (6/6), done. Writing objects: 100% (6/6), 652 bytes, done. Total 6 (delta 1), reused 0 (delta 0) To git@github.com:schacon/hw.git * [new branch] erlang -> erlang
如今当人们从该仓库克隆时,他们就会获得一个“erlang”分支以查阅、合并。 用这种方式,你能够推送任何分支到任何你有写权限的仓库。 若是你的分支已经在该仓库中了,它会试着去更新,若是它再也不,Git 会把它加上。
最后一个当你推送到远端分支时会碰到的主要问题是,其余人在此期间也推送了的状况。 若是你和另外一个开发者同时克隆了,又都有提交,那么当她推送后你也想推送时,默认状况下 Git 不会让你覆盖她的改动。 相反的,它会在你试图推送的分支上执行 git log
,肯定它可以在你的推送分支的历史记录中看到服务器分支的当前进度。 若是它在在你的历史记录中看不到,它就会下结论说你过期了,并打回你的推送。 你须要正式提取、合并,而后再次推送 —— 以肯定你把她的改动也考虑在内了。
当你试图推送到某个以被更新的远端分支时,会出现下面这种状况:
$ git push github master To git@github.com:schacon/hw.git ! [rejected] master -> master (non-fast-forward) error: failed to push some refs to 'git@github.com:schacon/hw.git' To prevent you from losing history, non-fast-forward updates were rejected Merge the remote changes before pushing again. See the 'Note about fast-forwards' section of 'git push --help' for details.
你能够修正这个问题。执行 git fetch github; git merge github/master
,而后再推送
简而言之 执行
git push [alias] [branch]
将你的本地改动推送到远端仓库。 若是能够的话,它会依据你的 [branch] 的样子,推送到远端的 [branch] 去。 若是在你上次提取、合并以后,另有人推送了,Git 服务器会拒绝你的推送,知道你是最新的为止。
如今你有了一堆分支,短时间的主题、长期的特性或者其它。怎样追踪他们呢? Git 有一组工具,能够帮助你弄明白工做是在哪儿完成的,两个分支间的区别是啥,等等。
简而言之 执行
git log
找到你的项目历史中的特定提交 —— 按做者、日期、内容或者历史记录。执行git diff
比较历史记录中的两个不一样的点 —— 一般是为了看看两个分支有啥区别,或者从某个版本到另外一个版本,你的软件都有啥变化。
经过查看分支中另外一分支看不到的提交记录,咱们已经看到如何用 git log
来比较分支。 (若是你不记得了,它看起来是这样的:git log branchA ^branchB
)。 并且,你也能够用git log
去寻找特定的提交。 在此,咱们会看到一些更广为使用的 git log
选项,不过哪有不少。 完整的清单能够看看官方文档。
要过滤你的提交历史,只寻找某个特定做者的提交,你可使用 --author
选项。 例如,比方说咱们要找 Git 源码中 Linus 提交的部分。 咱们能够执行相似 git log --author=Linus
的命令。 这个查找是大小写敏感的,而且也会检索电子邮箱地址。 我在此例中使用 -[number]
选项,以限制结果为最近 [number] 次的提交。
$ git log --author=Linus --oneline -5 81b50f3 Move 'builtin-*' into a 'builtin/' subdirectory 3bb7256 make "index-pack" a built-in 377d027 make "git pack-redundant" a built-in b532581 make "git unpack-file" a built-in 112dd51 make "mktag" a built-in
若是你要指定一个你感兴趣的日期范围以过滤你的提交,能够执行几个选项 —— 我用 --since
和 --before
,可是你也能够用 --until
和 --after
。 例如,若是我要看 Git 项目中三周前且在四月十八日以后的全部提交,我能够执行这个(我还用了 --no-merges
选项以隐藏合并提交):
$ git log --oneline --before={3.weeks.ago} --after={2010-04-18} --no-merges 5469e2d Git 1.7.1-rc2 d43427d Documentation/remote-helpers: Fix typos and improve language 272a36b Fixup: Second argument may be any arbitrary string b6c8d2d Documentation/remote-helpers: Add invocation section 5ce4f4e Documentation/urls: Rewrite to accomodate transport::address 00b84e9 Documentation/remote-helpers: Rewrite description 03aa87e Documentation: Describe other situations where -z affects git diff 77bc694 rebase-interactive: silence warning when no commits rewritten 636db2c t3301: add tests to use --format="%N"
你或许还想根据提交注释中的某个特定短语查找提交记录。能够用 --grep
选项。 好比说我知道有个提交是有关使用 P4EDITOR 环境变量,又想回忆起那个改动是啥样子的 —— 我能够用--grep
选项找到该提交。
$ git log --grep=P4EDITOR --no-merges
commit 82cea9ffb1c4677155e3e2996d76542502611370
Author: Shawn Bohrer
Date: Wed Mar 12 19:03:24 2008 -0500
git-p4: Use P4EDITOR environment variable when set
Perforce allows you to set the P4EDITOR environment variable to your
preferred editor for use in perforce. Since we are displaying a
perforce changelog to the user we should use it when it is defined.
Signed-off-by: Shawn Bohrer <shawn.bohrer@gmail.com>
Signed-off-by: Simon Hausmann <simon@lst.de>
Git 会对全部的 --grep
和 --author
参数做逻辑或。 若是你用 --grep
和 --author
时,想看的是某人写做的而且有某个特殊的注释内容的提交记录, 你须要加上 --all-match
选项。 在这些例子中,我会用上 --format
选项,这样咱们就能够看到每一个提交的做者是谁了。
若是我查找注释内容含有 “p4 depo”的提交,我获得了三个提交:
$ git log --grep="p4 depo" --format="%h %an %s" ee4fd1a Junio C Hamano Merge branch 'master' of git://repo.or.cz/git/fastimport da4a660 Benjamin Sergeant git-p4 fails when cloning a p4 depo. 1cd5738 Simon Hausmann Make incremental imports easier to use by storing the p4 d
若是我加上 --author=Hausmann
参数,与进一步过滤上述结果到 Simon 的惟一提交相反, 它会告诉我全部 Simon 的提交,或者注释中有“p4 demo”的提交:
$ git log --grep="p4 depo" --format="%h %an %s" --author="Hausmann" cdc7e38 Simon Hausmann Make it possible to abort the submission of a change to Pe f5f7e4a Simon Hausmann Clean up the git-p4 documentation 30b5940 Simon Hausmann git-p4: Fix import of changesets with file deletions 4c750c0 Simon Hausmann git-p4: git-p4 submit cleanups. 0e36f2d Simon Hausmann git-p4: Removed git-p4 submit --direct. edae1e2 Simon Hausmann git-p4: Clean up git-p4 submit's log message handling. 4b61b5c Simon Hausmann git-p4: Remove --log-substitutions feature. 36ee4ee Simon Hausmann git-p4: Ensure the working directory and the index are cle e96e400 Simon Hausmann git-p4: Fix submit user-interface. 38f9f5e Simon Hausmann git-p4: Fix direct import from perforce after fetching cha 2094714 Simon Hausmann git-p4: When skipping a patch as part of "git-p4 submit" m 1ca3d71 Simon Hausmann git-p4: Added support for automatically importing newly ap ...
不过,若是加上 --all-match
,结果就是我想要的了:
$ git log --grep="p4 depo" --format="%h %an %s" --author="Hausmann" --all-match 1cd5738 Simon Hausmann Make incremental imports easier to use by storing the p4 d
若是你写的提交注释都极度糟糕怎么办?或者,若是你要找某个函数是什么时候引入的,某些变量是在哪里开始被使用的? 你能够告诉 Git 在每一个提交之间的差值中查找特定字符串。 例如,若是咱们想要找出哪一个提交修改出了相似函数名“userformat_find_requirements”, 咱们能够执行(注意在“-S”与你要找的东东之间没有“=”):
$ git log -Suserformat_find_requirements
commit 5b16360330822527eac1fa84131d185ff784c9fb
Author: Johannes Gilger
Date: Tue Apr 13 22:31:12 2010 +0200
pretty: Initialize notes if %N is used
When using git log --pretty='%N' without an explicit --show-notes, git
would segfault. This patches fixes this behaviour by loading the needed
notes datastructures if --pretty is used and the format contains %N.
When --pretty='%N' is used together with --no-notes, %N won't be
expanded.
This is an extension to a proposed patch by Jeff King.
Signed-off-by: Johannes Gilger
Signed-off-by: Junio C Hamano
每一个提交都是项目的一个快照。因为每一个提交都记录它所基于的快照,Git 可以常常对它们求差值,并以补丁形式向你展现。 这意味着,对任意提交,你均可以获取该提交给项目引入补丁。 你能够用 git show [SHA]
加上某个特定的提交 SHA 获取,或者执行 git log -p
, 它会告诉 Git 输出每一个提交以后的补丁。这是个总结某一分支或者两个提交之间都发生了神马的好途径。
$ git log -p --no-merges -2
commit 594f90bdee4faf063ad07a4a6f503fdead3ef606
Author: Scott Chacon <schacon@gmail.com>
Date: Fri Jun 4 15:46:55 2010 +0200
reverted to old class name
diff --git a/ruby.rb b/ruby.rb index bb86f00..192151c 100644 --- a/ruby.rb +++ b/ruby.rb @@ -1,7 +1,7 @@ -class HiWorld +class HelloWorld def self.hello puts "Hello World from Ruby" end end -HiWorld.hello +HelloWorld.hello commit 3cbb6aae5c0cbd711c098e113ae436801371c95e Author: Scott Chacon <schacon@gmail.com> Date: Fri Jun 4 12:58:53 2010 +0200 fixed readme title differently diff --git a/README b/README index d053cc8..9103e27 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -Hello World Examples +Many Hello World Examples ====================== This project has examples of hello world in
这是个总结改动,以及合并或发布以前重审一系列提交的好方式。
若是 -p
选项对你来讲太详细了,你能够用 --stat
总结这些改动。 这是不用 -p
,而用 --stat
选项时,同一份日志的输出。
$ git log --stat --no-merges -2
commit 594f90bdee4faf063ad07a4a6f503fdead3ef606
Author: Scott Chacon <schacon@gmail.com>
Date: Fri Jun 4 15:46:55 2010 +0200
reverted to old class name
ruby.rb | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) commit 3cbb6aae5c0cbd711c098e113ae436801371c95e Author: Scott Chacon <schacon@gmail.com> Date: Fri Jun 4 12:58:53 2010 +0200 fixed readme title differently README | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
一样的基本信息,但更紧凑 —— 它仍然让你看到相对改动,和改动了哪些文件。
最后,要查看两个提交快照的绝对改动,你能够用 git diff
命令。 这在两个主要状况中广为使用 —— 查看两个分支彼此之间的差值,和查看自发布或者某个旧历史点以后都有啥变了。让咱们看看这俩状况。
你仅需执行 git diff [version]
(或者你给该发布打的任何标签)就能够查看自最近发布以后的改动。 例如,若是咱们想要看看自 v0.9 发布以后咱们的项目改变了啥,咱们能够执行 git diff v0.9
$ git diff v0.9
diff --git a/README b/README
index d053cc8..d4173d5 100644
--- a/README
+++ b/README
@@ -1,4 +1,4 @@ -Hello World Examples +Many Hello World Lang Examples ====================== This project has examples of hello world in diff --git a/ruby.rb b/ruby.rb index bb86f00..192151c 100644 --- a/ruby.rb +++ b/ruby.rb @@ -1,7 +1,7 @@ -class HiWorld +class HelloWorld def self.hello puts "Hello World from Ruby" end end -HiWorld.hello +HelloWorld.hello
正如 git log
,你能够给它加上 --stat
参数。
$ git diff v0.9 --stat README | 2 +- ruby.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-)
要比较两个不一样的分支,你能够执行相似 git diff branchA branchB
的命令。 不过它的问题在于它会完彻底全按你说的做 —— 它会直接给你个补丁文件,该补丁可以将甲分支的最新快照变成乙分支的最新快照的样子。 这意味着若是两个分支已经产生分歧 —— 奔往两个不一样方向了 —— 它会移除甲分支中引入的全部工做,而后累加乙分支中的全部工做。 这大概不是你要的吧 —— 你想要不在甲分支中的乙分支的改动。因此你真的须要的是两个分支叉开去时,和最新的乙分支的差异。 因此,若是咱们的历史记录看起来像这样:
$ git log --graph --oneline --decorate --all * 594f90b (HEAD, tag: v1.0, master) reverted to old class name | * 1834130 (erlang) added haskell | * ab5ab4c added erlang |/ * 8d585ea Merge branch 'fix_readme' ...
而且,咱们想要看“erlang”分支与主分支相比的查别。执行 git diff master erlang
会给咱们错误的结果。
$ git diff --stat master erlang erlang_hw.erl | 5 +++++ haskell.hs | 4 ++++ ruby.rb | 4 ++-- 3 files changed, 11 insertions(+), 2 deletions(-)
你能够看到,它加上了 erlang 和 haskell 文件,这确实是咱们在该分支中作的, 可是它同时恢复了咱们在主分支中改动的 ruby 文件。咱们真心想要的只是“erlang”分支中的改动(添加两个文件)。 咱们能够经过求两个分支分歧时的共同提交与该分支的差值获得想要的结果:
$ git diff --stat 8d585ea erlang erlang_hw.erl | 5 +++++ haskell.hs | 4 ++++ 2 files changed, 9 insertions(+), 0 deletions(-)
这才是咱们在找的,可是咱们可不想要每次都要找出两个分支分歧时的那次提交。 幸运的是,Git 为此提供了一个快捷方式。 若是你执行 git diff master...erlang
(在分支名之间有三个半角的点), Git 就会自动找出两个分支的共同提交(也被成为合并基础),并求差值。
$ git diff --stat master erlang erlang_hw.erl | 5 +++++ haskell.hs | 4 ++++ ruby.rb | 4 ++-- 3 files changed, 11 insertions(+), 2 deletions(-) $ git diff --stat master...erlang erlang_hw.erl | 5 +++++ haskell.hs | 4 ++++ 2 files changed, 9 insertions(+), 0 deletions(-)
几乎每一次你要对比两个分支的时候,你都会想用三个点的语法,由于它一般会给你你想要的。
顺带提一句,你还可让 Git 手工计算两次提交的合并基础(第一个共同的祖提交),即 git merge-base
命令:
$ git merge-base master erlang 8d585ea6faf99facd39b55d6f6a3b3f481ad0d3d
因此你执行下面这个也跟 git diff master...erlang
同样:
$ git diff --stat $(git merge-base master erlang) erlang erlang_hw.erl | 5 +++++ haskell.hs | 4 ++++ 2 files changed, 9 insertions(+), 0 deletions(-)
固然,我会推荐简单点的那个。
简而言之 使用
git diff
查看某一分支自它偏离出来起与过去某一点之间项目的改动。 老是使用git diff branchA...branchB
来查看 branchB 与 branchA 的相对差值,这会让事情简单点。
原文地址:http://gitref.org/zh/index.html
转载请注明:爱分享 » Git版本控制软件结合GitHub从入门到精一般用命令学习手册
原文地址:http://www.ihref.com/read-16369.html