新手问题 git 里 push request 注意事项

i5ting · 2014年11月18日 · 最后由 luikore 回复于 2014年11月21日 · 10668 次阅读

忽略本地本地

忽略本地本地,而不想给 push request 主项目改变的做法

git update-index --assume-unchanged Gemfile
git update-index --assume-unchanged Gemfile.lock 

fork 出来的项目和原有项目保持同步

当我们 在 github 上 fork 出一个项目后,如果原有的项目更新了,怎样保持我们 fork 出来的项目和原有项目保持同步呢并提交我们的代码更新呢?即怎样保持 fork 出的项目和上游项目保持更新,怎样创建 pull request?关键步骤是使用 git 的 rebase 命令。

步骤:

1、在 Fork 的代码库中添加上游代码库的 remote 源,该操作只需操作一次即可。

如:其中# upstream 表示上游代码库名,可以任意。

git remote add upstream https://github.scm.corp.ebay.com/montage/frontend-ui-workspace

2、将本地的修改提交 commit

3、在每次 Pull Request 前做如下操作,即可实现和上游版本库的同步。

3.1 : git remote update upstream

3.2 : git rebase upstream/{branch name}

需要注意的是在操作 3.2 之前,一定要将 checkout 到{branch name}所指定的 branch,

如:

git checkout develop

4、Push 代码到 Github

git push
  1. 要忽略本地,直接改 .gitignore 就好了。如有些文件确实别人不会有,比如 ctags/GNU Global 生成的 tag 文件。那就修改 $HOME/.gitignore_global 文件。
  2. 同步上游版本应该用 merge 而不是 rebase。

#1 楼 @hbin 首先谢谢回复

我这里的需求是 Gemfile,我用的是淘宝的源,而上游版本的是官方源,这有可能是部署在 linode 上的原因,所以我不能提交 Gemfile 的

用 .gitignore 是做不到这点的

你回复的第二个,我说不清楚,还是请高手们来吧

#1 楼 @hbin 事实上 rebase 比 merge 更好(同步上游的例子)。

当然可以直接使用 rebase 策略来 pull:

$ git pull --rebase upstream

这样可以省去 update 或 fetch 的步骤,不过若你要先预览对比再考虑 rebase(或 merge),那就还是分两步来做。

一般来说,一个人干活的项目无所谓,merge 也无妨,历史记录会难看点,但是只有你一个人总还是看得明白。而多人项目大家都用 merge 的时候,你看历史记录的时间线只会觉得头疼,rebase 会达到 merge 一样的目的,但是历史记录就好看多了。

当然,merge 的好处(without fast forward)是可以把一串历史记录合并成一个,这样你可以选择单分支浏览历史记录倒也不会嫌乱。不过 rebase 的意义在于如果一开始就坚持使用,那就根本不存在“视觉筛选”的难题,而且对于琐碎的提交记录,rebase 也可以做 squash 等等更细粒度的操作(前提是大家都有这个意识和习惯),怎么也比 merge 优雅的多。

不过 rebase 是有门槛的,相对 merge 而言,理解 rebase 的工作方式会更复杂,需要的操作也会比 merge 多一些。rebase 有搞乱历史记录的风险。rebase 不适用于 pull request(但是很适合同步上游库)。等等。

选择 merge 或 rebase 取决于团队的版本管理策略,以 pull request + review 为主的模式,merge 是合适的,特别是从 feature 合并到主干的过程,(不过单个 feature 自己的控制以 rebase 为主会很好)。

以中心式仓库为主的模式,rebase 显然是更好的选择——只要掌握它不会是障碍。

两者都是工具,根据情况选择合适的就是了。

#3 楼 @nightire 亲,我太喜欢你这种回答方式了,赞一个

@nightire 解释得很清楚。我个人也是非常喜欢 rebase,尤其 interactive 模式非常实用。

但是,如果你 fork 出的项目是以下情况,你应该用 merge 而不是 rebase。

  • 多人一起开发一个分支。因为用 rebase 去更新 upstream 之后,再 push 到 origin 就必须用 force update。这样其它的协作者再使用会有很多麻烦。
  • fork 出的项目需要长期开发,多次 commit,尤其是同一文件,同一处地方会多次改动。因为 git-rebase 会把你的 commits 在 upstream/{branch name} 之后 reapply one by one, in order。除非你能记得住你每一次 commit 的改动,否则,每一次 rebase 都会非常多的冲突需要去解决。

在哪用 rebase?

  • 一个 feature 开发完成,可以合到 master 之前,应该 rebase 一次。

#5 楼 @hbin 嗯,你列举的两个例子很有典型性,很认同。不过我想补充一些个人的想法:

  1. 多人一起开发一个分支,是否是 feature 分支?因为多人一起去开发一个主干分支(比如 develop)在我看来是一件不可思议的事情,而通常针对上游的更新都是由主干分支去 tracking 的,所以在我常用的工作模式下,这个问题不存在。如果是 feature 分支,由于它长期处于不稳定状态中(直到 feature stable 为止),因此它不应该直接去 tracking upstream,也就不会有从 upstream rebase 这种操作出现了。一旦 feature 完成可以合并到主干,然后主干 rebase upstream;或者主干直接 rebase upstream 之后再 cherrypick feature(feature 预先 squash 或 merge 了)。因此你描述的状况究其根源还是 Git 模型不太合理。

  2. 无论是 merge 还是 rebase 亦或是 pull 的时候采用其中一种方式,都是可以指定冲突解决策略的,诚如你所说,rebase 在 reapply one by one 的每一步中其实都在做和 merge 一样的差异对比,和 merge 的区别就在于是一次性留到最后再解决还是一步一步去解决罢了。

然而在实际中,每一步的解决策略不一样是非常罕见的(也就是某一步要 ours,某一步要 theirs,某一步要各取一点这种),出现这种情况的,即使是 merge 的最终合并,也一样需要你耗费很大精力去逐行处理。

绝大部分情况下,我们对于每一步的处理决策都是一致的,而且通常都是要 ours(因为如果全都是 theirs 那索性 rollback 得了),于是你可以在执行 rebase 的时候就指明解决冲突的策略,比如这样:

$ git rebase -s recursive -X ours

如此一来,Git 会自动递归的处理每一步的冲突,并且以“我方”的版本优先。于是你的问题也就不是问题了。

当然,绝大部分的情况并不能覆盖所有的可能,我在工作中也遇见过令人头疼的特例,但是 一)特例不应该影响我使用更好的选择,不能因噎废食,往往面对特例束手无策恰恰说明自己不够了解,还有成长的空间;二)就 rebase 这件事情来说,冲突解决策略非常多样化,我提到的 ours theirs 只是其中两种而已,有心人不妨通读一下 man pages 一定会收获更多。

总之我自己遵循的一个原则就是能不用 merge 就不用 merge,遇到棘手的情况就花时间找找更好的办法——不过我也觉得自己是有点偏执的,因此只作陈述,不作推荐。

无脑 pull 当前分支:

gup() {
  local br
  br=`git branch 2> /dev/null|\\grep '^*'|sed -e 's/..//;s/\\n//'`
  tainted=`git status --porcelain | \\grep -v '^\\?\\?'`
  if [[ $br == master ]]; then
    if [[ $tainted == '' ]]; then
      echo git stash
      git stash
    fi
    echo git fetch
    git fetch
    echo git rebase FETCH_HEAD $br
    git rebase FETCH_HEAD $br
  else
    if [[ -n $br ]]; then
      if [[ $tainted == '' ]]; then
        echo git stash
        git stash
      fi
      echo git pull --rebase origin $br
      git pull --rebase origin $br
    else
      echo seems not in any branch
    fi
  fi
}

还是觉得对冲突挨个看挨个删最简单... 有时没冲突也不代表 merge 成功,测试没过还得改

#7 楼 @luikore 这个看起来好高级啊,求出处

#8 楼 @i5ting 自己写的... 就是简单看看在哪个分支就 rebase 哪个,也没有应用什么策略

#9 楼 @luikore 感谢分享,很有用呀很有用,花了 2 分钟用你的代码 wrapper 了一下

npm install -g gup

然后执行gup命令就可以了

#10 楼 @i5ting orz 不至于吧...

需要 登录 后方可回复, 如果你还没有账号请 注册新账号