分支,合并
分支是git众多强大的特点之一。如果你使用过其他的版本控制系统,对你忘记你理解的分支是有用的,事实上,它可能更有助于把他们几乎在上下文因为这是你如何将最常被使用。当你检出不同的分支,您可以快速上下文切换来回几个不同的分支。
简而言之,你可以创建一个分支用git branch(branchname),切换分支用git checkout(branchname),记录提交快照虽然在这种背景下,然后可以轻松地来回切换。当你切换分支,Git取代你的工作目录和快照最新的承诺在那树枝所以你不必有多个目录为多个分支。你一起合并分支用git
merge。你可以很容易地合并多次从同一分支随着时间的推移,或交替地你可以选择一个分支合并后立即删除。
git checkout
切换到新的分支
git branch 命令是常用的git管理工具,可以做一系列的事情。我们将介绍最常用的几个:列出分支,建立分支和删除分支。也会介绍git checkout 切换分支,git branch list 列出活动的分支。
无参数情况下,git branch 将列出所有的本地分支。当前工作的分支将有*,如果你有着色打开,将显示当前分支在绿色。
$ git branch
* master
这意味着当前我们有一个“master”分支。当你运行git init git将默认为你建立一个master分支,然而没有任何特殊的名字——你不必真的有“master”分支,但因为它是默认创建的。大多数项目都是这么做的:git branch (branchname) 建立一个新分支。所以让我们建立一个新分支,切换到它:
$ git branch testing
$ git branch
* master
testing
现在,可以看到我们有一个新分支。在提交之后用这种方法建立分支,然后切换到‘testing',他会将你的工作目录恢复到你建立时的状态你可以认为它是一个书签,我们看下例子:$ 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
现在我们看到切换到’testing‘分支,我们的新文件消失了。我们可以切换回’master‘看到它们又回来了。
$ ls
README hello.rb
$ git checkout master
Switched to branch 'master'
$ ls
README hello.rb more.txt test.txt
git branch -v
查看每个分支的上次提交
如果要查看每个分支的上次提交,我们可以使用git branch -v
$ git branch -v
* master 54b417d fix javascript issue
development 74c111d modify component.json file
testing 62a557a update test scripts
git checkout -b (branchname)
建立并立即切换到一个分支
大多数情况下,你回立即切换到新建的分支,然后你在这工作,待到工作得内容完成之后合并到稳定的分之上像’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
你可以看到,我是怎么样建立分支,删除分支上的几个文件,然会回到主分支,文件又回来了,分支之间工作是独立的,可以在他们之间切换。在分支上工作很有用,(因为简单快捷)在完成时合并,删除。如果你没有实现它,可以方便的丢弃,回到稳定的内容之上。
git branch -d (branchname)
删除一个分支
如果你要删除一个分支(例如’tesitng‘分支),我们可以运行git branch -d (branch) 删除它。
$ git branch
* master
testing
$ git branch -d testing
Deleted branch testing (was 78b2670).
$ git branch
* master
git push (remote-name) :(branchname)
删除一个远程分支
当你完成一个任务要删除一个远程分支,你将会用到git push 命令删除远程分支。
$ git push origin :tidy-cutlery
To git@github.com:octocat/Spoon-Knife.git
- [deleted] tidy-cutlery
在上面的例子里你删除了远程’tidy-cutlery‘分支。记住这种方法有个窍门:git push remote-name local-branch:remote-branch 你推送你的本地分支到远程分支,你的本地分支是空的,远程分支也就什么也没有了。简而言之你使用git branch list 列出你当前的分支,创建新分支和删除不必要的或已经合并分支。
git merge
合并一个分支到当前分支
当你工作在一个独立的分支,你最终需要将它合并到主分支之上。你可以用git merge命令将任何分支合并到当期分支之上。让我们做一个例子:
$ 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 也会合并修改文件。事实上,git很擅长这个。例如:让我们在一个分支里修改一个文件,在另一个分支里重命名并修改相同的文件,然后合并:
$ git branch
* master
$ cat hello.rb
class HelloWorld
def self.hello
puts "Hello World"
end
end
HelloWorld.hello
现在 我们建立一个新分支叫“chang_class” 切换到它,这样修改是独立的。我们将修改'HelloWorld' to 'HiWorld'$ git checkout -b change_class
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”分支类名称将会恢复到以前的水平,我们什么切换分支。在这里我们可以改变不同的东西(在这种情况下,输出)和同时重命名文件从hello.rb2ruby 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'记录。注意:类名是”helloWord“,不是”HiWord“。要包含”Hiword“修改,我们需要在'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 搞定它了。注意:会有合并冲突。合并冲突
So,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(-)
现在见证奇迹的时刻----我讲把第一个分支合并到主分支,引起冲突。$ 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
你可以看到git 插入了标准的冲突标记,向svn一样。现在让我们解决冲突。可以手动修改,但是也可以使用图形界面的合并工具(kdiff3, 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
这里可以使用git diff查看两个文件的不同,提交修改,
$ git status -s
UU README
$ git add README
$ git status -s
M README
$ git commit
[master 8d585ea] Merge branch 'fix_readme'
现在我们已经成功的解决了合并冲突,提交奥了修改结果。简而言之你使用git合并到另一分支结合上下文到当前分支。它会自动找出如何最好地结合成一个新的快照不同快照与独立的工作的两个分支。
git log
显示分支的提交历史
目前为止,我们已经提交了项目快照,在不同的分支之间切换,但是我们一旦忘记了做过什么怎么办?或者想要知道一个分支与另一份哪里不同?git 提供了一个可以显示提交信息的工具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'
...
To see a more compact version of the same history, we can use the --oneline option.查看更简单的信息可以加上--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
它会告诉我们项目开发的历史。如果提交信息是描述性的,这可以告诉我们什么变化已应用或影响了当前状态的快照。我们还可以使用它来查看当历史被分支和合并与非常有用——图选项。这是同样的命令,但是与拓扑图形打开:
$ 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
git tag
标记一个重要的历史点
让我们看看怎么把我们的helloword项目的1.0版本发布。使用git tag -a (意思是做一个注释标签),也可以不用-a但是这样git 就不会记录下标记的时间,作者或者其他的信息。
$ git tag -a v1.0
当你运行这条命令,git将会打开你的编辑器,让你输入标记信息,就像提交信息一样,。现在,注意:什么时候运行run 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
我们做跟多的提交,标记会在正确的提交之上,我们不需要在提交时标记,然而。如果我们忘了标记一个提交,我们释放,我们可以追溯标签通过运行相同的命令,但上次提交。例如,假设我们已经提交558151发布一个(几个提交后)但忘了标记它。我们可以标记它现在:
$ 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
当你从远程的分支获取,tags将指向的跟踪对象将会自动的下载。$ git fetch origin --tags
remote: Counting objects: 1832, done.
remote: Compressing objects: 100% (726/726), done.
remote: Total 1519 (delta 1000), reused 1202 (delta 764)
Receiving objects: 100% (1519/1519), 1.30 MiB | 1.21 MiB/s, done.
Resolving deltas: 100% (1000/1000), completed with 182 local objects.
From git://github.com:example-user/example-repo
* [new tag] v1.0 -> v1.0
* [new tag] v1.1 -> v1.1
如果你仅仅需要一个简单的标记,使用git fetch <remote> tag <tag-name>即可。默认,push 到远程分支不会包括标签。可以在push时使用--tags参数包含标签
总而言之,你使用git tag 标记一个重要的提交或者点。比使用SHA更难以忘记。