git 使用笔记 (2)

合并指定文件

假设 master 中有文件 a 和 b,在分支 br1 中对 a 和 b 都进行了修改并且已经提交,如果只想把在 br1 中对 a 的修改合并到 master 中,执行命令:

git co master
git co br1 a

即先切换到要修改的分支(例如这里的 master),然后执行命令:

git co [分支名称] [要合并的文件1] [要合并的文件2] ...

分支的删除

假设有一个中心仓库 git-repos,有分支 master 和 br1;两个工作目录 git-a 和 git-b。如果在 git-a 中删除了中心仓库中的分支 br1(执行了命令“git push origin :br1”),但是在 git-b 中仍然有 br1 的信息。如果想在 git-b 中彻底删除 br1,执行命令:

git br -d -r origin/br1

如果分支和 tag 有相同的名字,那么简单地执行“git push origin :br1”会出现错误:

error: dst refspec br1 matches more than one.

这时如果要删除中心仓库的分支而不是 tag 使用命令:

git push origin :refs/heads/br1

tag 的删除

要删除远程的 tag,使用命令:

git push origin :refs/tags/br1

撤销提交

如果不小心提交错误,想把错误的提交历史都删掉,可以使用命令:

git reset --hard [历史版本]

如果在初始化中心仓库的时候使用了“--shared”选项,则即使加了“--force”选项也无法 push,解决的方法是编辑中心仓库下的 config 文件,把选项

denyNonFastforwards = true

改为 false,然后再执行提交:

git push --force

同步多个仓库

假设原来已经有一个中心仓库 repos,现在需要把一些稳定分支 push 到另一个公共仓库 public 的 master 分支上。先把 repos/master 推送到 public/master 中:

git push /path/to/public-repos master

然后在本地建立一个指向仓库 public 的链接 pub 并更新信息:

git remote add pub /path/to/public-repos
git fetch pub

当本地有修改需要推送到 public 上时使用:

git push public

如果需要合并 public 的修改可以使用:

git pull public master

rebase

git 的 rebase 是一个强大的命令,开始了解 rebase 源于开发时经常提交一些 snapshot,但是合并到主干时这些 snapshot 显得很扎眼,于是想把其去掉,参考资料 [1] 是一个 10 多分钟的小视频,简单地介绍了 rebase。

先看一下例子,假设开发时分支的情况如下:

 dev -> dev1 -> dev2 -> dev3
  |
master -> master1 -> master2

其中 dev 是从 master 分出来的分支,经历了 dev1,dev2 和 dev3 三个更新;master 是主分支,master1 和 master2 是后续更新。

如果在 master 上直接使用“git merge dev”合并 dev 分支上的修改:

 dev -> dev1 -> dev2 -> dev3 -
  |                           \
master -> master1 -> master2 --> merged

会生成一次新的提交 merged,并且 master 分支上包含了 dev 分支上的所有修改历史(dev1,dev2 和 dev3),并且生成了一个新的提交记录 merged。为了避免这种情况出现,merge 之前在 dev 分支上使用下面的命令:

git rebase master

rebase,顾名思义,就是重新调整 base 的位置。在刚开始时,dev 分支的 base 是 master;执行了 rebase 后 base 变成了 master2:

                       dev -> dev1' -> dev2' -> dev3'
                        |
master -> master1 -> master2

rebase 命令做了如下工作:

  1. 将 dev 之后的修改(即 dev 分支和 master 的最近公共祖先之后的修改)暂存起来;
  2. 将 base 调整到 master 的最新提交(也就是 master2);
  3. 将 dev 上的修改补丁(dev1-3)逐个应用到 master2 上,得到新的修改 dev1',dev2' 和 dev3',分别是 dev1,dev2,dev3 和 master2 合并之后的修改。

这时如果再到 master 上执行 merge 操作,则不会出现新的合并分支 merged,但是分支历史(即原来的 dev1-3 的修改)还是会被保留下来。为了去掉这些历史操作,可以在 rebase 时带上选项“-i”:

git rebase -i master

这时在注释中会提示选择需要进行的操作:

pick de1fd72 dev1
pick bb29098 dev2
pick aa100e9 dev3

# Rebase f47cc3b..aa100e9 onto f47cc3b
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.

这里选择的是“squash”,把非注释部分中不想保留的提交历史前的“pick”都改成“s”即可。如果 rebase 过程中有冲突需要手工合并,解决冲突后使用命令

git rebase --continue

继续 rebase 的合并过程。如果需要取消 rebase 则使用命令

git rebase --abort

rebase过程完成后会让你填写最终提交的注释,提交后的分支情况如下:

                       dev -> dev123
                        |
master -> master1 -> master2

其中 dev123 包含了 dev1-3 以及 master1 和 master2 的所有修改。这时再转到 master 上执行正常的 merge 操作即可。

dev1-3 是我开发时的 snapshot,我并不想让其出现在 master 中。如果仅仅想合并多次提交,可以使用命令

git rebase -i HEAD~{num}

其中 num 是要合并的提交次数。操作和上面类似。

(2014.02.09更新)简单的 rebase 会保留最初 fork 分支时的时间,也就是说,rebase 后可能会看到这样的 log:

dev123 (2013.12.31)
  |
master (2014.02.09)

可以在 rebase 时加上选项“--ignore-date”来更新实际 commit 的时间为当前时间,不过这个选项不能和“-i”一起使用。

这里只是简单描述了 rebase 的工作过程,详细原理可以参考 《Pro Git》

参考资料

[1] git-rebase

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注