Skip to content

[Guide] Git Tips (Advanced)

Guang Chen edited this page Apr 5, 2016 · 3 revisions

最基本的操作可以参考Github Help的Bootcamp系列(特别是Fork A Repo),或者糙快猛项目上手[Guide] Git Tips (Basics)

目录

Fork后的项目如何更新

Fork出项目后需要做的第一件事情:

$ git remote add upstream [<origin project path>]
$ git fetch upstream

以后rebase的时候:

$ git fetch upstream
$ git rebase upstream/[<branch>]

注意之前先checkout到要rebase的分支中。

用rebase减少merge

当一个新特性在正在开发进程中而主仓库有更新时,如果直接使用git pull upstream master会产生一个merge提交,时间久了commit中会有大量的merge干扰并且网络图也会变得不利于code reveiw.这时如果使用git rebase upstream master,git首先会暂存你的commit,然后应用upsteam/master中的每一个patch,最后依次作用暂存的patch,当某个patch冲突时,需要你手动合并,并在合并后通过git add <filename>来添加合并后的文件到索引中,接着执行git rebase --continue来继续合并.

需要注意的一件事情是,这件事情只建议对本地未公开提交的更改来进行.否则有两种情况:

  • 你已经push到了远程的某个你拥有的分支(这很重要),这样你在本地rebase之后如果要推送到那个分支,还需要进行git pull,否则会被拒绝,但这样会产生merge,如果不想要在以后合并时产生多的merge,使用 git push -f,请务必确保这个分支只有你在进行开发!
  • 你已经push到了远程的某个公共分支(即有人以它为基础开发). 绝对不要rebase.否则其他人rebase你的分支时会遇到很大的迷惑。

正因为如此,建议尽可能多的commit而只在有必要的时候push,因为修正本地仓库比修正远程仓库要容易的多.

使用stash暂存更改

情景:当你working on a feature branch 的时候,很不幸的是stable branch有一个紧急的bug需要你修改.但你当前的进度不足以做一个commit,而当你运行git checkout stable的时候会提示你有未提交的更改.

或许你会选择:git add .,git commit -m "这是一个临时的提交".然后待你回到这个分支的时候再git reset --soft HEAD^或者git reset HEAD^(什么?你直接继续你的工作然后commit然后push了?想想其他人看到"这是一个临时的提交"时是什么想法).

从现在起使用更贴近情景的命令git stash吧!使用git stash将你的工作暂存在一个专门存放unfinished changes的stack中,当你回到这个feature branch时,使用git stash pop来恢复你的变更.

修正由于各种自动格式化导致的换行问题

如果设置了autocrlf或者在eclipse等IDE中格式化代码,可能导致由于换行符改变git显示整个文件都变化了。这时可以通过生成patch来过滤换行符的变化。 首先找到一个换行符没有混乱上游的版本如a08ec.然后通过如下命令生成一个忽略空格变化的patch序列

$ git format-patch a08ec -w

这会生成以序号+commit内容的*.patch文件序列。 然后按照次序运行

$ git am 000x-xx-xx-xx.patch --ignore-whitespace

期间可能会警告你有trailing space,不过并不会影响patch的过程。 要注意的是经过这个过程新的commit的sha会发生改变,如果你已经提交到了公共仓库需要push -f

Fixup Commit

本地开发过程中,发现自己之前某个commit有问题,一种解决办法当然是通过git rebase然后去修改那个commit。 另外一种比较好的办法时,正常的解决这个问题,然后这样提交

$ git add .
$ git commit --fixup=xxxxx # xxxxx is the commit id which has problem
$ git rebase -i --autosquash xxxxx^

另外,如果一个pull request被指出某个commit有问题也可以这样来修改该commit,之后再git push -f即可

如果有问题的commit是当前分支的最后一个commit,你可以在做出必要的改动之后,通过git add添加用来修补的改动,然后通过git commit --amend将改动修补到上一个提交中 Cheat Sheet

All text

Github Cheat Sheet

这个repo展示了Github很多酷炫的特性

Split Project

将一个repo中的某个目录分离出来成为一个新的repo并带有commit history.

  1. 生成子repo分支 repo user$ git subtree split -P <name-of-folder> -b <name-of-new-branch>
  2. 本地初始化子repo subrepo user$ git init && git pull </path/to/repo> <name-of-new-branch>
  3. 推送子repo subrepo user$ git remote add origin <[email protected]:my-user/new-repo.git>,subrepo user $git push origin -u master

reference

手动生成patch或打patch

根据patch命令的p0,p1选项不同,生成patch的方式两种。

第一种方式在生成的patch要在根目录下使用,也就必须保证打patch的地方要与原来的项目具有相同的目录结构。

$ git diff <start-commit-id>..<end-commit-id> <file|dir> -w > xxx.patch # -w忽略空格
$ patch -p1 < xxx.patch

第二种方式生成的patch要在生成patch的file/path对应的file/path所在的同级目录使用,无需打patch的项目与原来的项目有相同目录结构

$ git diff <start-commit-id>..<end-commit-id> <file|dir> -w --no-prefix > xxx.patch # --no-prefix生成的patch中没有目录前缀
$ cp xxx.patch <where-to-patch>
$ cd <where-to-patch>
$ patch -p0 < xxx.patch

部分提交

同一个文件中要分为两个部分来diff并且保证每部分可靠可如下进行:

$ git add -i

通过交互式提交选择patch, 并选择哪些trunk需要stage.

为了测试该部分是否正确,需要将其他未提交的部分stash.

$ git stash -u -k

后面两个参数是重要的, -k表示需要stash未track的文件, 即保证当前提交的部分不依赖于未track的部分. -u则表示保留已经stage的部分, 即不stash在暂存取的部分.

如此可以得到一个只暂存了通过git add -i提交的patch的状态, 并且还未做commit.

在测试完成后, 通过git commit来完成部分提交, 之后git stash pop取出其他部分.