[TOC]
Git不同于从前的cvs、svn这种集中式的版本管理系统,它是分布式的。你可以不依赖于任何网络和服务在本地使用git,同样也可以很方便的多人协作。Git创建分支的代价非常小,甚至你可以为你的任何改动创建一个新的分支,然后修改后合并回去。
对于个人项目我经常会保持这样一些分支:
- master 主线分支
- develop 开发分支,不断迭代到milestone
- milestone_xxx 一些milestone留存分支
- 20081118_feature_xxx 新增feature或者某个bugfix会另开一个分支,开发完成后合并回develop分支
建议对分支保持一定的命名规范,这样同时有很多分支时便于分辨。
Git 协作示例
部署Git
如果只是本地使用git,可以略过这部分。Git有多种部署方式,ssh应该是最简单的部署办法,甚至只要有ssh服务,就能利用git远程协作了。当然相对安全的办法是为git使用一个专用帐号:
$ sudo adduser git
$ su git
$ mkdir ~/.ssh
安全起见,请限制git账户的权限,编辑 /etc/passwd
把登录shell改为 /usr/bin/git-shell
。然后,建立自己的代码仓库,比如位于 /home/git/sample-repo.git,就可以干活了。
创建远程空仓库
$ cd ~git
$ mkdir sample-repo.git
$ cd sample-repo.git
$ git --bare init
本地推送项目到远程仓库
$ cd sample-proj
$ git init
$ git add .
$ git commit -m "initilize sample project"
$ git remote add origin git@git:~/sample-repo.git
$ git push origin master
同伴从远程克隆该项目协作
$ git clone git@git:~/sample-repo.git
$ cd sample-repo
$ edit sample-file
$ git commit -m "fix for sample-file"
$ git push origin master
操作分支
创建并切换到新分支
$ git checkout -b sample-branch
该命令等同于
$ git branch sample-branch
$ git checkout sample-branch
列出分支
$ git branch -v # 查看最后一次提交日志
$ git branch -a # 列出本地和远程的所有分支
$ git branch --merged # 列出已经并入当前分支的分支
$ git branch --no-merged # 列出未合并的分支
$ git log --graph --pretty=oneline # 可视化分支
合并分支
$ git checkout develop
$ git merge --no-ff sample-branch
创建合并分支的要点在于,建议都从develop分支创建feature分支,而进来不要在feature分支之间创建分支,开发完毕后同样在合并到develop分支,这样日后在查看版本树的时候会相对清晰,而且也便于维护代码。
解决冲突
-
查询冲突的文件
$ git status sample-file: needs merge # On branch master # Changed but not updated: ...
- 解决冲突
-
重新添加该文件(说明已解决冲突)
$ git add sample-file
-
提交已解决的冲突
$ git commit
删除分支
已经合并的分支可以删除掉
$ git branch -d sample-branch
没有合并的分支只能强制删除
$ git branch -D sample-branch
衍合分支
$ git checkout master
$ git rebase sample-branch
衍合(rebase)与合并(merge)的区别:前者是一种在推送之前清理提交历史的手段,仅仅衍合那些永远不会公开的 commit,千万不要衍合已经公开的 commit,这在多人协作中需要注意。
远程分支
操作远程分支
$ git push origin sample-branch # 把本地分支push到远程
$ git push origin :sample-branch # 删除远程分支
合并远程分支
$ git pull origin sample-branch # 同步远程分支到本地分支
该命令等同于
$ git fetch origin # 同步远程分支到本地,会更新remote索引
$ git merge origin/sample-branch # 把远程分支合并到当前分支
diff
比较暂存文件和最新的版本
$ git diff --cached
比较两个版本之间的文件差异
$ git diff <SHA1-1>:/path/to/file <SHA1-2>:/path/to/file
回滚(rollback)
回滚到某个版本
一般不建议trunk回滚,最好建立一个分支,比如
$ git checkout -b rollback_1226
$ git reflog # 查看一下reset之前的那个ref叫什么,比如 HEAD@{2}
$ git reset --hard HEAD@{2}
其中 git reset --hard
表示彻底回退到某个版本,本地源代码也会变成该版本,而git reset --soft
只回退commit信息,不会恢复到index file一级。
其他的一些回滚指令
$ git reset HEAD^ #回退所有内容到上一个版本
$ git reset HEAD^ a.py #回退a.py这个文件的版本到上一个版本
$ git reset --soft HEAD~3 #向前回退3个版本
$ git reset --hard origin/master #将本地的状态回退到和远程的一样
$ git reset 057d #回退到某个版本
恢复未commit的文档,丢弃当前修改
$ git checkout HEAD
$ git checkout HEAD sample.cpp
查看某个版本的文件
$ git show <SHA1>:/path/to/file
常见错误处理
处理 non-fast forward error
出现该错误,说明你在push之前没有和远程分支合并,远程分支已经在你之前了。
具体的冲突日志,可以同步远程分支后查看:
$ git fetch origin
$ git log master..origin/master
合并远程分支后,再push到远程分支
$ git merge origin/master
$ git push origin master
特殊情况:强制push到远程分支,一般不推荐,这会冲掉远程分支(除非你知道后果且有权限)。
$ git push origin +master
找不到git-upload-pack或git-receive-pack
如果遇到错误找不到 git-upload-pack
或者 git-receive-pack
命令,说明你的git没有安装在默认的PATH里,可以在 ~/.git/config
中指定其路径,比如:
[remote "origin"]
receivepack = /home/jqian/local/bin/git-receive-pack
uploadpack = /home/jqian/local/bin/git-upload-pack
url = jqian@10.166.135.168:/home/jqian/repo/wbdata.git
fetch = +refs/heads/*:refs/remotes/origin/*
或者在git clone
时候使用 -u
参数,比如:
$ git clone -u /home/jqian/local/bin/git-upload-pack jqian@10.166.135.168:repo/wbdata.git
提醒pull不是git指令
如果碰到错误 git ‘pull’ is not a git command,可能是因为你的git安装目录挪动了位置,使用如下命令:
$ git --exec-path
/home/jqian/local/libexec/git-core
如果发现安装路径不正确,重新指定GIT_EXEC_PATH
环境变量即可。
$ export GIT_EXEC_PATH=/data/jqian/local/libexec/git-core
其他
给Mac OS X原生git添加bash-completion支持
从git源码里下载一份 git-complete.bash 代码,放到任意位置,比如 ~/.git-completion.bash,在 ~/.bash_profile 里调用它即可。
参考书籍
- 《Pro Git》