origin & master

origin 是远端服务器的默认名字.

master 是默认 branch 的名字. 无论是远端还是本地都可以有 master. 远端的那个 master 分支就叫做 origin/master.

$ git branch -a
* master                                   # 本地 master 分支
  remotes/origin/HEAD -> origin/master     # 远端 HEAD 指针
  remotes/origin/master                    # 远端 master 分支

remote

查看远端信息使用 git remote -v 命令, 添加 remote 使用 git remote add.

$ git remote -v
origin  git@github.com:zhanglintc/Mmrz-Sync.git (fetch)
origin  git@github.com:zhanglintc/Mmrz-Sync.git (push)

$ git remote add origin git@github.com:zhanglintc/Mmrz-Sync.git
fatal: remote origin already exists.

branch

创建 branch, 然后切换到该分支:

$ git branch
* master
$ git branch dev
$ git branch
  dev
* master
$ git checkout dev
Switched to branch 'dev'
$ git branch
* dev
  master

创建并切换到某分支:

$ git branch
* dev
  master
$ git checkout -b new_branch
Switched to a new branch 'new_branch'
$ git branch
  dev
  master
* new_branch

pull & push

正常来说 pull 应该使用如下命令:

$ git pull origin master
From github.com:zhanglintc/Mmrz-Sync
 * branch            master     -> FETCH_HEAD
Already up-to-date.

push 类似:

$ git push origin master
Everything up-to-date

但是我们一般使用的是仅仅只是 git pull, git push. 原因是我们指定了默认的 pullpush 地址.

指定的方法是:

$ git branch --set-upstream-to=origin/master
Branch master set up to track remote branch master from origin.

# 以下方法同理, 只是更方便记忆而已
$ git branch -u origin/master
Branch master set up to track remote branch master from origin.

merge & rebase

以下 merge 用法等效:

# 方法一, 分两步操作:
git checkout feature        # 当前分支为 feature
git merge master            # 将 master merge 到当前分支, 即 master merge 到 feature

# 方法二, 一行完成:
git merge feature master    # 将 master 分支 merge 到 feature 分支

rebase 基本用法:

# <upstream> <branch> 均可省略
git rebase <upstream> <branch>

git rebase 后跟的第一个参数作为 <upstream>, 第二个参数作为 <branch>.

当 <upstream> 被省略时, 默认以远端对应的 branch 作为 <upstream>. 例如当前处在 master 分支, 使用命令 git rabse, 可以理解为git reabse origin/master.

当 <branch> 被省略时, 默认以当前 branch 作为 <branch>, 例如当前处于 feature 分支, 使用命令 git rebase master, 可以理解为 git rebase master feature.

当指定了 <branch> 参数之时, 会首先自动执行 git checkout <branch>, 也就是说无论你当前处于哪个分支, 首先会切换到给定的 分支, 然后再执行 rebase 操作. 例如当前处于 master 分支, 使用命令 git rebase master feature 后, 使用 git branch 命令查看分支信息, 会发现当前已经自动跳转到了 feature 分支.

以下 rebase 用法等效:

# 方法一, 作用于 feature 分支:
git checkout feature        # 切换当前分支到 feature 分支
git rebase master            # 重新以 master 分支最新版本作为 feature 分支的 base 

# 方法二, 作用于 feature 分支:
git rebase master feature    # 自动切换到 feature 分支, 然后重新以 master 最新版作为 base

git rebase --onto 的用法:

git rebase --onto <newbase> <upstream> <branch>

使用 git rebase --onto master next topic 的效果如下:

# rebase --onto 前的结构图
o---o---o---o---o  master
     \
      o---o---o---o---o  next
                       \
                        o---o---o  topic


# rebase --onto 后的结构图
o---o---o---o---o  master
    |            \
    |             o'--o'--o'  topic
     \
      o---o---o---o---o  next

可以看出来效果是把 topic 分支的内容从 next 分支中摘出来 rebase --onto 到了 master 分支.

一点思考:

其实我最开始想使用的是这样的命令: git rebase --onto master topic. 感觉就是直接把 topic 分支 rebase --onto master 分支不是就可以了吗, 为什么要写这么复杂?

但是试验后发现我自己设想的这种写法是完全不可行的, 原因在多次重新阅读 git rebase --help 后终于明白了过来:

All changes made by commits in the current branch but that are not in <upstream> are 
saved to a temporary area. This is the same set of commits that would be shown by git log
<upstream>..HEAD (or git log HEAD, if --root is specified).

The current branch is reset to <upstream>, or <newbase> if the --onto option was
supplied. This has the exact same effect as git reset --hard <upstream> (or <newbase>).
ORIG_HEAD is set to point at the tip of the branch before the reset.

The commits that were previously saved into the temporary area are then reapplied to the
current branch, one by one, in order. Note that any commits in HEAD which introduce the
same textual changes as a commit in HEAD..<upstream> are omitted (i.e., a patch already
accepted upstream with a different commit message or timestamp will be skipped).

我们来仔细阅读一下 git rebase 的运行机制:

  1. 首先摘取的是当前 branch 中所有没在 upstream 中出现的 commit, 将这些 commit 缓存一个临时区域.
  2. 然后将当前 branch reset --hard 到 <upstream> 或者 <newbase>.
  3. 最后将缓存区的 commit 一个一个的重新提交到当前 branch 的末尾.

那么现在可以分析为什么 git rebase --onto master topic 达不到我们要的效果了. 首先, 我们我们在 --onto 之后只提供了两个参数: mastertopic. 那么此时 master 会作为 <newbase>, 而 topic 会作为 <upstream>, 最后 <branch> 参数是留空的. 无论我们当前处于哪个分支, 假设是 topic 分支好了, git 首先需要取出的是当前分支中 upstream 里没有的那些 commit, 那么 topic 分支中没有出现在 topic 分支中的 commit 自然是不存在了. 此后 git 把当前分支(topic) reset 到了 master 分支, 再接下来把缓存区的东西一次补上来, 然而缓存区里没有东西, 所以 topic 分支就变得跟 master 分支一模一样了, 这当然不是我们需要的效果.

只有完全按照官方例程里的样子写才能达到效果: git rebase --onto master next topic. 分析一下, master 是 <newbase>, next 是 <upstream>, topic 是 <branch>. 首先取出 <branch> 在 <upstream> 中没有出现的部分缓存起来, 也就是 topic 在 next 中没有出现过的部分. 然后把 topic reset --hard 成 master 最新版本. 最后把缓存起来的部分依次重新提交到 topic 分支中. 据此分析可以很容易看出来可以得到上面 "rebase --onto 前后效果图" 中的效果.

Comments
Write a Comment