@xtccc
2016-02-19T16:37:24.000000Z
字数 5684
阅读 2874
开发工具
目录:
Git是分布式版本控制系统,同一个Git仓库,可以分布到不同的机器上。怎么分布呢?最早,肯定只有一台机器有一个原始版本库,此后,别的机器可以“克隆”这个原始版本库,而且每台机器的版本库其实都是一样的,并没有主次之分。
通过命令clone来克隆某个分支到本地时,可以不使用public key,而是直接使用username/password来实现。
例如,如果我的Github用户名为 TaoXiao,分支的地址为 https://github.com/GridX/gridx.git ,那么我可以用如下的命令来克隆该分支代码到本地:
git clone https://TaoXiao@github.com/GridX/gridx.git
执行该命令,随后会让你输入该用户名的密码,输入正确后就可以下载了。
clone仓库到本地后,本地分支只有master,看不到其他的分支,我们必须手动地创建本地分支,并将远程分支拉取到本地分支。
git checkout -b feat1 origin/feat1
我们在GitHub上创建一个空的Repository,为其取名为 learn。
在本地用命令创建一个同名的版本库:
mkdir learn; cd learn
git init
这将创建一个名为master的本地分支。
将这个本地版本库与远程仓库进行关联:
git remote add origin git@github.com:TaoXiao/learn.git
这里的TaoXiao是我在Github上的账户名,origin是远程仓库的名称。
在本地版本库中加入一些新文件并commit
touch README.MD
git add README.MD
git commit -m "add README.MD"
将本地版本库的内容推送到远程仓库
git push -u origin master
这里的 origin 是远程仓库的名字,master 是远程仓库的默认分支名。通过命令 git remote
可以查看远程仓库的名称,命令 git remote -v
则可以显示更详细的信息。
由于远程仓库是空的,第一次推送master分支时,我们带上了参数 -u
。这个命令不但会把本地的master分支推送到远程仓库的master分支,还会把它们俩关联起来。
现在,远程仓库中的状态就与本地版本库的状态达到了一致。今年,如果在本地作了提交,就可以通过命令 git push origin master
将本地master分支的最新修改推送到远程仓库的master分支。
可以通过 git diff [filename]
来查看文件的变化情况。这个命令可以比较一个文件在不同版本下的差异
比较doc.txt文件对应于某两次提交的差异
git diff [commit_1 id] [commit_2 id] [file_path]
比较当前文件与其最新提交状态的差异
git diff HEAD -- [file_path]
通过以下两种命令,可以按照从新到旧的顺序查看commit的历史:
git log
git log --pretty=oneline
例:
其中,左侧的一长串文字是commit id (版本号)
将项目内容回退到某个版本时,必须知道目标版本号,这通过 git log
命令就可以看到。
HEAD是当前版本,HEAD^是上一个版本,..., HEAD~100是往前第100个版本。
回退到某个版本的命令:
git reset --hard [commit id]
除了从新版本回退到旧版本,也可以从旧版本回退到新版本。
假如我们从当前版本A回退到了一个旧版本B,然后又想再次回退到A。但是回退到B后,通过命令 git log 已经找不到版本A的commit id了,怎么办?
通过命令 git reflog
就可以重新看到版本A的commit id。
通过命令 git checkout -- [file]
,可以把当前工作区(working directory)内的修改撤销,包括两种情况:
总之,这个命令就是让文件回到最近一次 git add
或者 git commit
时的状态。
如果一个文件修改了之后,被加入了暂存区,那么,命令 git checkout -- [file]
无法让该文件恢复到上次commit后的状态。 通过命令 git reset HEAD [file]
,可以撤销(unstage)暂存区内的修改。但是,工作区内的文件还是不会变化的。如果继续使用命令 git checkout -- [file]
,则可以使该文件的状态与版本库的状态完全相同。
用命令 rm [file] 会将该文件从FS中删掉,但是并没有从版本库中删掉。调用命令 git status 就可以看出来:
如果是误删了该文件,由于版本库中依然存在该文件,因此我们可以用命令 git checkout -- [file]
轻松恢复。
如果是真的想将该文件从版本库中彻底删掉,则可以用命令 git rm [file]
将其删除,并且 git commit
参考
每次提交,Git都把这些commits串成一条时间线,这条时间线就是一个分支。HEAD
将指向当前的分支。
一开始时,只有一个分支master。Git将HEAD指向master,并将master指向最新的提交。这样,就能确定当前的分支,以及当前分支的提交点。
每次提交,master都前进一步,随着我们不断地提交,master分支也越来越长。
首先,创建名为dev的分支:
git branch dev
然后,切换到dev分支:
git checkout dev
或者直接用下面的命令创建并切换分支:
git checkout -b dev
查看当前的分支:
$ git branch
* dev
master
将分支dev合并到当前分支的命令为:
git merge dev
如果没有冲突,合并顺利完成,则可以删除dev分支:
git branch -d dev
因为创建、合并和删除分支非常快,所以Git鼓励你使用分支完成某个任务,合并后再删掉分支,这和直接在master分支上工作效果是一样的,但过程更安全。
假设,当前master分支与feat分支中文件README.MD内容是相同的:
this is a readme file
This branch is 'dev'
下面演示冲突的产生。
切换到分支feat1,编辑该文件,然后提交
this is a readme file
This branch is 'dev'
This line is added within branch 'feat1'
切换到分支master,编辑该文件,然后提交
this is a readme file
This branch is 'dev'
This line is added by branch 'master'
试图将分支feat1合并到分支master:
$ git merge feat1
Auto-merging README.MD
CONFLICT (content): Merge conflict in README.MD
Automatic merge failed; fix conflicts and then commit the result.
合并失败!必须手动解决文件的冲突后再提交。git status 命令可以告诉我们发生冲突的文件:
可以直接在vim中查看README.MD文件的内容:
this is a readme file
This branch is 'dev'
<<<<<<< HEAD
This line is added by branch 'master'
=======
This line is added within branch 'feat1'
>>>>>>> feat1
Git会用 <<<<<<<
、=======
、>>>>>>>
标记出不同分支的内容,我们可以直接修改该文件的内容:
this is a readme file
This branch is 'dev'
This line is added by branch 'master'
This line is added within branch 'feat1'
现在,冲突已经被人为解决了,提交该文件:
$ git commit -m "解决冲突"
[master 3ee94f4] 解决冲突
还可以用命令 git log --graph --pretty=oneline --abbrev-commit
来查看分支的合并情况:
假设现在master分支与feat1分支的状态完全相同(是3.3小节中冲突解决后的状态)。下面演示,master分支中的改动如果没有被commit,是怎样影响到feat1分支的。
在master分支中增加一个新文件LICENSE,并修改文件README.MD的内容:
今天天气不错
以上改动,不要commit
到master分支;是否调用add
都无所谓
尝试一下如果提交master分支的改动,会不会影响feat1分支?
答案是:不会。
有时候我们需要保留工作区的改动,不要提交这些改动,但同时让这些工作区的改动对其他的分支不可见。
考虑这样的一个需求:
master分支中是稳定的代码,此刻我正在feat1分支中修改一些文件。突然,我需要紧急修复一个bug,所以我需要新建一个名为bug的临时分支。但是,feat1分支中的代码改动还未完成,不能提交。而对bug的修改又是必须基于master分支的(即feat1分支中的改动不能让bug分支看到),此时该怎么办?
这时我们可以利用stash
来储藏工作现场。所谓的储藏工作现场,是指将当前的工作区的当前状态储藏起来,等以后恢复现场之后继续工作。当工作现场储藏起来后,工作区就表现为“干净的”工作区。
例子:
假设master分支和feat1分支中的README.MD文件内容是一致的:
this is a readme file
This branch is 'dev'
This line is added by branch 'master'
This line is added within branch 'feat1'
今天天气不错
我正在feat1中添加新的功能,修改了README.MD文件:
this is a readme file
This branch is 'dev'
This line is added by branch 'master'
This line is added within branch 'feat1'
今天天气不错
新增加的feature应该很cool,
但是得花我好几天的时间才行!
master分支的代码上发现一个bug,必须立即修复,但是新feature的代码还没写完,现在不能commit,怎么办?用git stash
来保存feat1分支中的工作现场吧。
可以看到,用命令git status
查看工作区状态,发现现在它是是“干净的”,之前的工作现场被存储了。如果转到master分支查看,发现master分支看不到我在feat1分支中新增加但还未被提交的代码。
由于我们要基于master分支修复bug,所以就转到该分支,然后新建bug分支。在bug分支中进行修改、提交,然后转回master分支,将bug分支合并到master分支,然后删除bug分支。
回到feat1分支,恢复工作现场,继续干活。查看stash内容可以使用命令git stash list
,恢复工作现场有两种方式:
a). 调用命令 git stash apply
,但是stash内容并不删除,需要调用git stash drop
来删除stash内容;
b). 调用命令 git stash pop
,恢复工作现场并删除stash内容
当多个人协作时,各人都需要向同一个分支上推送自己的改动。
下面,我们用两个git目录(p1和p2目录)来模拟两个用户,它们俩要协作开发feat1分支。
假设这个分支中存在文件 LICENSE,它当前的内容为:
任何人都可以自由地分发和修改本软件
首先,用户p1在本地修改该文件:
任何人都可以自由地分发和修改本软件
我是用户p1
我认为:自由软件是最好的。
修改好了之后将其commit,并push到远程仓库的feat1分支。
接着,用户p2也在本地修改该文件:
任何人都可以自由地分发和修改本软件
我是p2
其实,非开源软件也有很牛逼的,比如苹果的iOS和OSX
修改好了之后将其commit.
但是在试图push到远程仓库时发生了冲突:
所以,我们应该先用 git pull
将远程仓库的feat1分支的最新内容拉取到本地,在本地进行合并,解决冲突了再提交并推送到远程仓库。
pull成功了,但是automatic merge失败了:
所以我们需要在本地手动解决冲突,完成合并,然后在提交并推送到远程仓库:
合并冲突的方法在之前的小节中讲过了,这里就不赘述了。
总结:
因此,多人协作的工作模式通常是这样:
- 首先,可以试图用git push origin branch-name推送自己的修改;
- 如果推送失败,则因为远程分支比你的本地更新,需要先用git pull试图合并;
- 如果合并有冲突,则解决冲突,并在本地提交;
- 没有冲突或者解决掉冲突后,再用git push origin branch-name推送就能成功!