Git常用操作和技巧

[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分支,这样日后在查看版本树的时候会相对清晰,而且也便于维护代码。

解决冲突

  1. 查询冲突的文件

    $ git status
    sample-file: needs merge
    # On branch master
    # Changed but not updated:
    ...
    
  2. 解决冲突
  3. 重新添加该文件(说明已解决冲突)

    $ git add sample-file
    
  4. 提交已解决的冲突

    $ 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 里调用它即可。

参考书籍