理解 Git 的基本概念 ( Merging Collaborating Rebasing)


合並 Merging

在分支上開發新功能后,如何把新功能加入到主分支,讓其它人得到你的修改呢?你需要使用命令 git merge 或 git pull。

這兩個命令的語法如下:

git merge [head]
git pull . [head]

這兩個命令的結果是一樣的(雖然 merge 命令現在看起來要簡單一點,但在多個開發者的環境下 pull 命令會顯得更加明確,我們會在多人協作開發的章節里討論這個問題。)

這兩個命令執行了下面的操作。我們把當前 head 記作 current, 將要被合並的 head 記作 merge。

  1. 找到 current 和 merge 的共同祖先, 記為 ancestor-commit.
  2. 先處理簡單的情況. 如果 ancestor-commit 就是 merge, 那么什么都不用做. 如果 ancestor-commit 是 current, 那么執行 快速向前合並.
  3. 否則, 找出ancestor-commit 和 merge 之間的修改.
  4. 嘗試將這些修改添加到 current 上.
  5. 如果沒有沖突,那么創建一個新的節點, 新節點有兩個父節點,分別是current 和 merge. 將 HEAD 指向新節點, 更新項目文件.
  6. 如果有沖突, 在文件沖突的部位插入相應的標志提示用戶. 這種情況下不會創建新的節點.

重要提示: 在文件的修改還沒有提交的情況下, 請不要執行合並操作.因為這會導致Git出現意想不到的結果.

 

So, to complete the above example, say you check out the master head again and finish writing up the new data for your paper. Now you want to bring in those changes you made to the headers.

The repository looks like this:

         +---------- (D)
        /             |
(A) -- (B) -- (C) -------------- (E)
                      |           |
                 fix-headers    master
                                  |
                                 HEAD

where (E) is the commit reflecting the completed version with the new data.

You would run:

git merge fix-headers

If there are no conflicts, the resulting respository looks like this:

         +---------- (D) ---------------+
        /             |                  \
(A) -- (B) -- (C) -------------- (E) -- (F)
                      |                  |
                 fix-headers           master
                                         |
                                        HEAD

The merge commit is (F), having parents (D) and (E). Because (B) is the common ancestor between (D) and (E), the files in (F) should contain the changes between (B) and (D), namely the heading fixes, incorporated into the files from (E).

Note on terminology: When I say “merge head A into head B,” I mean that head B is the current head, and you are drawing changes from head A into it. Head B gets updated; nothing is done to head A. (If you replace the word “merge” with the word “pull,” it may make more sense.)

Resolving Conflicts

A conflict arises if the commit to be merged in has a change in one place, and the current commit has a change in the same place. Git has no way of telling which change should take precedence.

To resolve the commit, edit the files to fix the conflicting changes. Then run git add to add the resolved files, and run git commit to commit the repaired merge. Git remembers that you were in the middle of a merge, so it sets the parents of the commit correctly.

Fast Forward Merges

A fast forward merge is a simple optimization for merging. Say your repository looks like this:

                +-- (D) -- (E)
               /            |
(A) -- (B) -- (C)           |
               |            |
            current     to-merge
               |
              HEAD

and you run git merge to-merge. In this case, all Git needs to do is set current to point to (E). Since (C) is the common ancestor, there are no changes to actually “merge.”

Hence, the resulting merged repository looks like:

                +-- (D) -- (E)
               /            |
(A) -- (B) -- (C)           |
                            |
                    to-merge, current
                                 |
                                HEAD

That is, to-merge and current both point to commit (E), and HEAD still points to current.

Note an important difference: no new commit object is created for the merge. Git only shifts the head pointers around.

Common Merge Use Patterns

There are two common reasons to merge two branches. The first, as explained above, is to draw the changes from a new feature branch into the main branch.

The second use pattern is to draw the main branch into a feature branch you are developing. This keeps the feature branch up to date with the latest bug fixes and new features added to the main branch. Doing this regularly reduces the risk of creating a conflict when you merge your feature into the main branch.

One disadvantage of doing the above is that your feature branch will end up with a lot of merge commits. An alternative that solves this problem is rebasing, although that comes with problems of its own.

Deleting a Branch

After you have merged a development branch into the main branch, you probably don’t need the development branch anymore. Hence, you may want to delete it so it doesn’t clutter your git branch listing.

To delete a branch, use git branch -d [head]. This simply removes the specified head from the repository’s list of heads.

For example, in this repository from above:

         +---------- (D) ---------------+
        /             |                  \
(A) -- (B) -- (C) -------------- (E) -- (F)
                      |                  |
                 fix-headers           master
                                         |
                                        HEAD

we probably don’t need the fix-headers head any more. So we can use:

git branch -d fix-headers

and the resulting repository looks like:

         +---------- (D) ---------------+
        /                                \
(A) -- (B) -- (C) -------------- (E) -- (F)
                                         |
                                       master
                                         |
                                        HEAD

Important notegit branch -d will cause an error if the branch to be deleted is not reachable from another head. Why? Consider the following repository:

         +----------- (E)
        /              |
(A) -- (B) -- (C)      |
               |       |
             head1   head2

Say you delete head2. Now how can you use commit (E)? You can’t check it out, because it isn’t a head. And it doesn’t appear in any logs or anywhere else, because it isn’t an ancestor of head1. So commit (E) is practically useless. In Git terminology, it is a “dangling commit,” and its information is lost.

Git does allow you to use the -D option to force deletion of a branch that would create a dangling commit. However, it should be a rare situation that you want to do that. Think very carefully before using git branch -D.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM