(0)目录
分布式版本管理神器--GIT个人GitHub欢迎访问
一:起因
(0):最先听说的版本管理系统是svn,那是大学本科期间接触的了,当时也没有多想 —— 一个带我们的学长,告诉我们如何如何操作;
(1):研究生就开始接触GIT,当时也是仅仅了解几个常用的命令,仅仅能够使用简单的日常所需要的命令;也没有深入的系统的了解,现在终于停下匆忙的脚步,思考一番,打算在这里做一个简单的小结
(2):分布式版本管理神器--GIT —— 最初由Linus Torvalds编写,用作Linux内核代码的管理。在推出后,Git在其它项目中也取得了很大成功,尤其是在Ruby社区中。(来自百度百科)码农老毕的读书笔记(非常好的)
二:基本知识小结
(0)GIT安装(本人是在Ubuntu下面操作的) —— sudo apt-get install git,Windows下的安装网上也有教程,在此不一一细说
配置 git:
# 常用的命令都设置alias,尽量少敲键盘
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.co checkout
git config --global alias.st status
# 很好看地显示git log
git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen (%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --"
# 设置用户信息
git config --global user.name "Your Name"
git config --global user.email you@email.com
# 缺省使用颜色显示
git config --global color.ui true
(1)本地版本库的增(add)删(delete)操作 参考自 http://www.liaoxuefeng.com/wiki
1)创建一个版本库非常简单,首先,选择一个合适的地方,创建一个空目录:
$ mkdir learngit
$ cd learngit
$ pwd
/Users/michael/learngit
pwd命令用于显示当前目录。在我的Mac上,这个仓库位于/Users/michael/learngit。
第二步,通过git init命令把这个目录变成Git可以管理的仓库:
$ git init
Initialized empty Git repository in /Users/michael/learngit/.git/
2)一定要放到learngit目录下(子目录也行),因为这是一个Git仓库,放到其他地方Git再厉害也找不到这个文件。
添加文件到Git仓库,分两步:
第一步,使用命令git add <file>,注意,可反复多次使用,添加多个文件;
第二步,使用命令git commit,完成。
要随时掌握工作区的状态,使用git status命令。
如果git status告诉你有文件被修改过,用git diff file-name可以查看修改内容
3)HEAD指向的版本就是当前版本,因此,Git允许我们在版本的历史之间穿梭,使用命令git reset --hard commit_id。
穿梭前,用git log可以查看提交历史,以便确定要回退到哪个版本。
要重返未来,用git reflog查看命令历史,以便确定要回到未来的哪个版本。
工作区(Working Directory)
就是你在电脑里能看到的目录,比如我的learngit文件夹就是一个工作区
版本库(Repository)
工作区有一个隐藏目录.git,这个不算工作区,而是Git的版本库。
Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master,以及指向master的一个指针叫HEAD。
4)现在,你又理解了Git是如何跟踪修改的,每次修改,如果不add到暂存区,那就不会加入到commit中。
命令git checkout -- readme.txt意思就是,把readme.txt文件在工作区的修改全部撤销,这里有两种情况:
一种是readme.txt自修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态;
一种是readme.txt已经添加到暂存区后,又作了修改,现在,撤销修改就回到添加到暂存区后的状态。
总之,就是让这个文件回到最近一次git commit或git add时的状态。
又到了小结时间。
场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令git checkout -- file。
场景2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令git reset HEAD file,就回到了场景1,第二步按场景1操作。
场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,参考版本回退一节,不过前提是没有推送到远程库。
(2)分支操作
创建分支:git branch <branchname>分支,git merge <branchname>合并分支到当前分支上(查看当前分支git branch);删除分支git branch -d <name>;切换分支git checkout <name>
因为创建、合并和删除分支非常快,所以Git鼓励你使用分支完成某个任务,合并后再删掉分支,这和直接在master分支上工作效果是一样的,但过程更安全。
Git鼓励大量使用分支:
查看分支:git branch
创建分支:git branch <name>
切换分支:git checkout <name>
创建+切换分支:git checkout -b <name>
合并某分支到当前分支:git merge <name>
删除分支:git branch -d <name>
注意:当Git无法自动合并分支时,就必须首先手动解决冲突(就是把冲突的两个文件的内容修改为一样的,也就是说两个文件同时修改了,但是修改的又不一样)。解决冲突后,再提交,合并完成。用git log --graph命令可以看到分支合并图。
$ git merge --no-ff -m "merge with no-ff" dev
因为本次合并要创建一个新的commit,所以加上-m参数,把commit描述写进去。
(3)分支的管理
在实际开发中,我们应该按照几个基本原则进行分支管理:
首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;
每个人都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就可以了;
例如:(假设master 和 dev已经存在) git branch dev_zyp(创建个人开发分支) --> git checkout dev_zyp(在个人开发分支上开发) --> git checkout dev(切换到dev分支,进行合并) --> git merge dev_zyp(把个人最新的开发结果合并到dev上) --> git branch -d dev_zyp( 删除 )
1)看看主分支(master)和测试分支(test)之间的差异,可以使用git diff命令来查看它们之间的diff:
$git diff test
diff --git a/readme.txt b/readme.txt
index ebe01d6..4b5fa63 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1,2 +1 @@ hello, world
-In test branch
大家可以以到当前分支与测试分支(test)相比,少了一行内容:“-In test branch”。
2)如果执行完git diff命令后认为测试分支(test)的修改无误,能合并时,可以用git merge命令把它合并到主分支(master)中:
$git merge test
Updating b765df9..7f3c997
Fast-forward readme.txt | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
“Updating b765df9..7f3c997”表示现在正在更新合并“b765df9”和“7f3c997”两个提交(commit)之间的内容;“b765df9”代表着主分支(master),“7f3c997”代表测试分支(test)。
“Fast-forward”在这里可以理解为顺利合并,没有冲突。“readme.txt | 1 +”表示这个文件有一行被修改,“1 files changed, 1 insertions(+), 0 deletions(-)”,表示这一次合并只有一个文件被修改,一行新数据插入,0 行被删除。
3)主分支(master) 和测试分支(test)里的内容已经各自改变了(diverged),我们现在用“git merge”命令来把两个分支合一下看看:
$git merge test
Auto-merging readme.txt
CONFLICT (content): Merge conflict in readme.txt
Automatic merge failed; fix conflicts and then commit the result.
合并命令的执行结果不是“Fast-foward”,而是“CONFLICT”。是的,两个分支的内容有差异,致使它们不能自动合并(Auto-merging)。
(4)来了新的紧急任务,但是原来的任务还没有完成,怎么办?
( 新的紧急任务来到 --> git stash(保存现场) --> git branch bug_zyp(创建,如(3)中步骤) --> 最后git branch -d but_zyp;;; 回到现场git stash pop)
修复bug时,我们会通过创建新的bug分支进行修复,然后合并,最后删除;
当手头工作没有完成时,先把工作现场git stash一下,然后去修复bug,修复后,再git stash pop,回到工作现场。
首先确定要在哪个分支上修复bug,假定需要在master分支上修复,就从master创建临时分支:
$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 6 commits.
$ git checkout -b issue-101
Switched to a new branch 'issue-101'
$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 2 commits.
$ git merge --no-ff -m "merged bug fix 101" issue-101
Merge made by the 'recursive' strategy.
readme.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
$ git branch -d issue-101
Deleted branch issue-101 (was cc17032).
太棒了,原计划两个小时的bug修复只花了5分钟!现在,是时候接着回到dev分支干活了!
(5)多人模式的开发
git push origin branch-name推送自己的修改;
如果推送失败,则因为远程分支比你的本地更新,需要先用git pull试图合并;
查看远程库信息,使用git remote -v:
$ git remote -v
origin git@github.com:michaelliao/learngit.git (fetch)
origin git@github.com:michaelliao/learngit.git (push)
从本地推送分支,使用git push origin branch-name,如果推送失败,先用git pull抓取远程的新提交;
在本地创建和远程分支对应的分支,使用git checkout -b branch-name origin/branch-name,本地和远程分支的名称最好一致;
建立本地分支和远程分支的关联,使用git branch --set-upstream branch-name origin/branch-name;
从远程抓取分支,使用git pull,如果有冲突,要先处理冲突。
(6)远程仓库和本地仓库关联 或 克隆
小结 (要关联一个远程库,使用命令)git remote add origin git@server-name:path/repo-name.git --> (关联后,使用命令git push -u origin master第一次推送master分支的所有内容;)git push -u origin master --> git push origin master
1)首先,登陆GitHub,然后,在右上角找到“Create a new repo”按钮,创建一个新的仓库:
在Repository name填入learngit(你的本地仓库的名称),其他保持默认设置,点击“Create repository”按钮,就成功地创建了一个新的Git仓库:
在本地的learngit仓库下运行命令:
$ git remote add origin git@github.com:michaelliao/learngit.git
把上面的michaelliao替换成你自己的GitHub账户名;添加后,远程库的名字就是origin,这是Git默认的叫法,也可以改成别的,但是origin这个名字一看就知道是远程库。
2)下一步,就可以把本地库的所有内容推送到远程库上:
$ git push -u origin master
把本地库的内容推送到远程,用git push命令,实际上是把当前分支master推送到远程。
由于远程库是空的,我们第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master
分支关联起来,在以后的推送或者拉取时就可以简化命令
3)只要本地作了提交,就可以通过命令:
$ git push origin master
把本地master分支的最新修改推送至GitHub,现在,你就拥有了真正的分布式版本库!
(7)git文件push到远程服务器上
1)$ git push sl HEAD
这样单个文件的提交就完成了。可以看出一个完整的提交过程有一下步骤:
添加到本地仓库: git add fielname ----> 提交到本地仓库: git commit -m "message" ----> 推送到远程仓库: git push sl HEAD
期间,可以用 git status擦看状态。
2) 多个新文件的提交:
如我在vendor目录下新建了一个目录,并创建了多个文件:
$ mkdir dir
$ echo "file 1" >> dir/file1
$ echo "file 2" >> dir/file2
$ echo "file 3" >> dir/file3
这时,要提交dir目录中的所有文件可以用下面的方法:
$ git add dir
$ git commit -m "add new dir"
$ git push sl HEAD
(如果仅仅是修改后的文件,他会自动的和远程的合并;如果是提交不同的文件夹下的文件,可以多次add,一次commit即可)
4)添加两条repo的使用方法:
$ repo sync //同步源码,会同步所有子git项目的源码
$ repo diff //查看所有git项目中的改变
$ repo forall -c "git branch" //查看每个git项目的branch
(8)Git远程操作
1)运行git remote –v或者查看.git/config可以看到origin的含义)
$git branch -a (to show all the branches git knows about) :(本地和远程的库文件)
* master
remotes/origin/HEAD -> origin/master
remotes/origin/master
2)$git branch -r (to show remote branches git knows about) :(仅仅远程的库文件)
origin/HEAD -> origin/master
origin/master
3)$git diff origin/master master (show me the changes between the remote master branch and my master branch).
需要注意的是,remotes/origin/master和origin/master的指向是相同的
$git diff origin/master remotes/origin/master
4)$ git push origin test:master // 提交本地test分支作为远程的master分支
$ git push origin test:test // 提交本地test分支作为远程的test分支
如果想删除远程的分支呢?类似于上面,如果:左边的分支为空,那么将删除:右边的远程的分支。
$ git push origin :test // 刚提交到远程的test将被删除,但是本地还会保存的,不用担心。
git push origin master
origin指定了你要push到哪个remote
master其实是一个“refspec”,正常的“refspec”的形式为”+<src>:<dst>”,冒号前表示local branch的名字,冒号后表示remote repository下 branch的名字。注意,如果你省略了<dst>,git就认为你想push到remote repository下和local branch相同名字的branch。听起来有点拗口,再解释下,push是怎么个push法,就是把本地branch指向的commit push到remote repository下的branch,比如
$git push origin master:master (在local repository中找到名字为master的branch,使用它去更新remote repository下名字为master的branch,如果remote repository下不存在名字是master的branch,那么新建一个)
$git push origin master (省略了<dst>,等价于“git push origin master:master”)
$git push origin master:refs/for/mybranch (在local repository中找到名字为master的branch,用他去更新remote repository下面名字为mybranch的branch)
$git push origin HEAD:refs/for/mybranch (HEAD指向当前工作的branch,master不一定指向当前工作的branch,所以我觉得用HEAD还比master好些)
$git push origin :mybranch (再origin repository里面查找mybranch,删除它。用一个空的去更新它,就相当于删除了)
(8)上次我们讲了先有本地库,后有远程库的时候,如何关联远程库
现在是你的开发团队有一个公开的GitHub远程库,一个还有大部分功能的远程库了,你作为一个新的加入者,首先需要从远程库克隆一个到本地:
即远程库已经准备好了,下一步是用命令git clone克隆一个本地库:
$ git clone git@github.com:michaelliao/gitskills.git
Cloning into 'gitskills'...
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (3/3), done.
$ cd gitskills
$ ls
README.md