一、图解:
1 git是分布式的, 可以各自独立,没有中心, 而cvs svn等是集中式的,因为是分布式的,可以在安装了git的任何机器上创建版本库(没有服务器),但通常拿一个远程库大家共同推送到这个上面(相当于服务器)
2 远程仓库 -- 即另外一个电脑上的仓库
3 工作区和暂存区:
工作区有一个隐藏目录.git,这个不算工作区的,而是Git的版本库。
Git的版本库里存了很多东西:
1)其中最重要的就是称为stage(或者叫index)的暂存区,
2)还有Git为我们自动创建的第一个分支master,
3)以及指向master的一个指针叫HEAD。
把文件往Git版本库里添加的时候,是分两步执行的:
第一步是用git add把文件添加进去,实际上就是把文件修改添加到暂存区;
第二步是用git commit提交更改,实际上就是把暂存区的所有内容提交到本地仓库的当前分支。
因为我们创建Git版本库时,Git自动为我们创建了唯一一个master分支,所以,现在,git commit就是往master分支上提交更改。
你可以简单理解为,需要提交的文件修改通通放到暂存区,然后,一次性提交暂存区的所有修改。
4 分支及作用:
作用:假设你准备开发一个新功能,需要10天才能完成,第一天你写了10%的代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别人不能干活了。 如果等代码全部写完再一次提交,又存在丢失每天进度的巨大风险。
现在有了分支,就不用怕了。你创建了一个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样,既安全,又不影响别人工作。
5 Git跟踪并管理的是修改,而非文件
同一个文件, 第一次修改后 add 到暂存区, 然后再修改,
那么commit时,只会把暂存区的内容提交到版本库, 之后修改的因为没有add到暂存区, 所以不会被提交
二、有很多图形化的git工具,但起初最好先熟悉命令,也很快:
———————————————————————————————————————————————————————————————————————————
1) 创建本地仓库:进入一个目录, 然后git init (或者从远端 git clone 比如 git clone git@111.222.33.188:/git/android.git/)
$ cd xxxdir
$ git init
通过git init命令把这个目录变成Git可以管理的仓库:
当前目录下多了一个.git的目录,这个目录是Git来跟踪管理版本库的
如果是用 git init先创建本地仓库,需要使用git remote add命令用于添加远程主机,然后才能把本地的推送到远程仓库。
$ git remote add <主机名> <网址>
但如果是用 git clone从远程仓库克隆的,那么会自动添加该远程主机
可以使用 git remote -v 来查看
———————————————————————————————————————————————————————————————————————————
2)把文件添加到本地仓库
第一步,用命令git add告诉Git,把文件添加到仓库:
$ git add readme.txt
执行上面的命令,没有任何显示,这就对了,Unix的哲学是“没有消息就是好消息”,说明添加成功。
第二步,用命令git commit告诉Git,把文件提交到仓库:
$ git commit -m "wrote a readme file"
-m 本次提交的说明
———————————————————————————————————————————————————————————————————————————
3)运行git status命令看看结果:(比如修改readme.txt文件后)
$ 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.txt
#
no changes added to commit (use "git add" and/or "git commit -a")
git status命令可以让我们时刻掌握仓库当前的状态,上面的命令告诉我们,readme.txt被修改过了,但还没有准备提交的修改。
———————————————————————————————————————————————————————————————————————————
4)用git diff这个命令查看修改的内容:
$ git diff readme.txt
diff --git a/readme.txt b/readme.txt
index 46d49bf..9247db6 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1,2 +1,2 @@
-Git is a version control system.
+Git is a distributed version control system.
Git is free software.
———————————————————————————————————————————————————————————————————————————
5)提交修改
$ git commit -m "add distributed"
-m 本次提交的说明
———————————————————————————————————————————————————————————————————————————
6)git操作日志
$ git log --pretty=oneline
3628164fb26d48395383f8f31179f24e0882e1e0 append GPL
ea34578d5496d7dd233c827ed32a8cd576c5ee85 add distributed
cb926e7ea50ad11b8f9e909c05226233bf755030 wrote a readme file
你看到的一大串类似3628164...882e1e0的是commit id(版本号)
首先,Git必须知道当前版本是哪个版本,在Git中,用HEAD表示当前版本,也就是最新的提交3628164...882e1e0(注意我的提交ID和你的肯定不一样),上一个版本就是HEAD^,上上一个版本就是HEAD^^,当然往上100个版本写100个^比较容易数不过来,所以写成HEAD~100。
———————————————————————————————————————————————————————————————————————————
7) 返回上一个版本
现在,我们要把当前版本“append GPL”回退到上一个版本“add distributed”,就可以使用git reset命令:
$ git reset --hard HEAD^
HEAD is now at ea34578 add distributed
8) 从当前版本append GPL,返回上一个版本后,再回到版本append GPL,需要指定id:
$ git reset --hard 3628164
HEAD is now at 3628164 append GPL
版本号没必要写全,前几位就可以了,Git会自动去找。
9) 找不到新版本的commit id怎么办?
Git提供了一个命令git reflog用来记录你的每一次命令:
$ git reflog
ea34578 HEAD@{0}: reset: moving to HEAD^
3628164 HEAD@{1}: commit: append GPL
ea34578 HEAD@{2}: commit: add distributed
cb926e7 HEAD@{3}: commit (initial): wrote a readme file
终于舒了口气,第二行显示append GPL的commit id是3628164
———————————————————————————————————————————————————————————————————————————
10)用rm命令删除文件:
$ rm test.txt
这个时候,Git会知道你删除了文件,因此,工作区和版本库就不一致了,git status命令会立刻告诉你哪些文件被删除了:
现在你有两个选择,一是确实要从版本库中删除该文件,那就用命令git rm删掉,并且git commit:
$ git rm test.txt
rm 'test.txt'
$ git commit -m "remove test.txt"
现在,文件就从版本库中被删除了。
另一种情况是删错了,因为版本库里还有呢,所以可以很轻松地把误删的文件恢复到最新版本:
$ git checkout -- test.txt
———————————————————————————————————————————————————————————————————————————
11)checkout:丢弃工作区的修改$git checkout -- readme.txt (注:命令中的--很重要,没有--,就变成了“切换到另一个分支”的命令)
用本地仓库里的内容覆盖掉工作区的内容,这里有两种情况:
一种是readme.txt修改后还没有被放到暂存区,现在撤销修改,就是用本地仓库里的内容覆盖掉工作区的内容;
一种是readme.txt已经添加到暂存区后,现在撤销修改, 就是用本地仓库里的内容覆盖掉工作区的内容,但暂存区里的修改不会改变
12)把暂存区的修改撤销掉
$ git reset HEAD readme.txt
可以把暂存区的修改撤销掉,重新放回工作区
小结
场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令git checkout -- file。
场景2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令git reset HEAD file,就回到了场景1,第二步按场景1操作。
场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交使用:
git reset --hard 3628164
———————————————————————————————————————————————————————————————————————————
比如,取回origin主机的master分支。
Git fetch origin master
所取回的更新,会存储在本地主机上
git branch -a 查看存储在本地的所有分支。
$ git branch -a
* master --这是本地仓库的master分支
remotes/origin/master --这是远程仓库的master分支
1. git fetch:相当于是从远程获取最新版本到本地,不会自动merge合并
Git fetch origin master --从远程的origin的master主分支下载最新的版本到origin/master分支上
git log -p master..origin/master --比较本地仓库的master和远程master的区别
git merge origin/master --合并到当前分支
2. git pull:相当于是从远程获取最新版本并merge到本地
git pull命令的作用是,取回远程主机某个分支的更新,再与本地的指定分支合并。它的完整格式稍稍有点复杂。
$ git pull <远程主机名> <远程分支名>:<本地分支名>
比如,取回origin主机的next分支,与本地的master分支合并,需要写成下面这样。
$ git pull origin next:master
如果远程分支是与当前分支合并,则冒号后面的部分可以省略。
$ git pull origin next
http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000/001375840202368c74be33fbd884e71b570f2cc3c0d1dcf000
———————————————————————————————————————————————————————————————————————————
14)将本地仓库推送到远程
1.git push命令用于将本地分支的更新,推送到远程主机
$ git push <远程主机名> <本地分支名>:<远程分支名>
比如:
$ git push origin master
上面命令表示,将本地的master分支推送到origin主机的master分支。如果后者不存在,则会被新建。
2.如果省略本地分支名,则表示删除指定的远程分支,因为这等同于推送一个空的本地分支到远程分支。
$ git push origin :master
# 等同于
$ git push origin --delete master
上面命令表示删除origin主机的master分支。
3 如果当前分支与远程分支之间存在追踪关系,则本地分支和远程分支都可以省略。
$ git push origin
上面命令表示,将当前分支推送到origin主机的对应分支。
4.如果当前分支只有一个追踪分支,那么主机名都可以省略。
$ git push
如果远程主机的版本比本地版本更新,推送时Git会报错,要求先在本地做git pull合并差异,然后再推送到远程主机。
这时,如果你一定要推送,可以使用--force选项。
———————————————————————————————————————————————————————————————————————————
15)分支
Git鼓励大量使用分支:
查看分支:git branch
创建分支:git branch <name>
切换分支:git checkout <name>
创建+切换分支:git checkout -b <name>
合并某分支到当前分支:git merge <name>
删除分支:git branch -d <name>
参考:分支
三、使用工具
http://blog.csdn.net/agul_/article/details/7835786#t0
git checkout .
#本地所有修改的。没有的提交的,都返回到原来的状态
git stash
#把所有没有提交的修改暂存到stash里面。可用git stash pop回复。
git reset --hard HASH
#返回到某个节点,不保留修改。
git reset --soft HASH
#返回到某个节点。保留修改
合并分支时的冲突解决
http://backlogtool.com/git-guide/cn/stepup/stepup2_7.html
http://www.cnblogs.com/sinojelly/archive/2011/08/07/2130172.html
冲突的类型
1 内容冲突
两个用户修改了同一个文件的同一块区域,git会报告内容冲突。我们常见的都是这种
<< << << < HEAD
我修改的内容
== == == =
别人修改的内容
>> >> >> > 6853e5ff961e684d3a6c02d4d06183b5ff330dcc
c
修改完冲突之后,需要重新提交。
$ git add test.txt $ git commit -m "合并分支"
2 逻辑冲突--git自动处理(合并/应用补丁)成功,但是逻辑上是有问题的。
比如另外一个人修改了文件名,但我还使用老的文件名,这种情况下自动处理是能成功的,但实际上是有问题的。
又比如,函数返回值含义变化,但我还使用老的含义,这种情况自动处理成功,但可能隐藏着重大BUG。这种问题,主要通过自动化测试来保障。所以最好是能够写出比较完备的自动化测试用例。
这种冲突的解决,就是做一次BUG修正。不是真正解决git报告的冲突。
3 树冲突
文件名修改造成的冲突,称为树冲突。
比如,a用户把文件改名为a.c,b用户把同一个文件改名为b.c,那么b将这两个commit合并时,会产生冲突。
$ git status
added by b: b.c
both deleted: origin-name.c
added by a: a.c
如果最终确定用b.c,那么解决办法如下:
git rm a.c
git rm origin-name.c
git add b.c
git commit
树冲突也可以用git mergetool来解决,但整个解决过程是在交互式问答中完成的,用d 删除不要的文件,用c保留需要的文件。
最后执行git commit提交即可。