-
Notifications
You must be signed in to change notification settings - Fork 5
[Guide] Git Tips (Advanced)
最基本的操作可以参考Github Help的Bootcamp系列(特别是Fork A Repo),或者糙快猛项目上手[Guide] Git Tips (Basics)。
- Fork后的项目如何更新
- 用rebase减少merge
- 使用stash暂存更改
- 修正由于各种自动格式化导致的换行问题
- fixup commit
- Cheat Sheet
- Split project
- 手动生成patch或打patch
- 部分提交
Fork出项目后需要做的第一件事情:
$ git remote add upstream [<origin project path>]
$ git fetch upstream
以后rebase的时候:
$ git fetch upstream
$ git rebase upstream/[<branch>]
注意之前先checkout到要rebase的分支中。
当一个新特性在正在开发进程中而主仓库有更新时,如果直接使用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,因为修正本地仓库比修正远程仓库要容易的多.
情景:当你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
本地开发过程中,发现自己之前某个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
这个repo展示了Github很多酷炫的特性
将一个repo中的某个目录分离出来成为一个新的repo并带有commit history.
- 生成子repo分支
repo user$ git subtree split -P <name-of-folder> -b <name-of-new-branch>
- 本地初始化子repo
subrepo user$ git init && git pull </path/to/repo> <name-of-new-branch>
- 推送子repo
subrepo user$ git remote add origin <[email protected]:my-user/new-repo.git>
,subrepo user $git push origin -u master
根据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
取出其他部分.