简体   繁体   中英

GIT merging does not work with multiple branches

(a) Created folder GIT_MERGE_TEST1 followed by 'git init'

(b) Created a new file 'a.txt' (and only 1 file) added and commited to 'master'

(c) Created branch ' git checkout -b feature1 master ' and modified the file 'a.txt'

(d) Created branch ' git checkout -b feature2 master ' and modified the same file 'a.txt'

Then tried merging

(e) 'git checkout master' followed by 'git merge --no-ff feature2 ' was successful and has the contents of 'Master' + 'Feature2'

But, 'git merge --no-ff feature1 ' says 'Already up-to-date'

How can I have the changes of both the files ? And I am curious to know why it did not complain about merge conflict ?

You will only get a merge conflict if git cannot determine which feature branch should be contributing to the final output for the whole document, and it's up to the human to make the choice in this case.

Here's an example flow that you can follow to see the effect:

$ git init
Initialized empty Git repository in /home/markf/so/.git/
$ vi a.txt
$ git add .
$ git commit -m '1'
[master (root-commit) d92b23b] 1
 1 file changed, 1 insertion(+)
 create mode 100644 a.txt
$ git hist
* d92b23b 2015-08-29 | 1 (HEAD, master) [mark.fisher]

$ git checkout -b f1 master
Switched to a new branch 'f1'
$ vi a.txt
$ git add .
$ git commit -m '2 on f1'
[f1 1db4fed] 2 on f1
 1 file changed, 1 insertion(+), 1 deletion(-)

$ git checkout -b f2 master
Switched to a new branch 'f2'
$ git hist
* d92b23b 2015-08-29 | 1 (HEAD, master, f2) [mark.fisher]

$ vi a.txt
$ git add .
$ git commit -m '3 on f2'
[f2 807e4a7] 3 on f2
 1 file changed, 1 insertion(+), 1 deletion(-)

$ git hist
* 807e4a7 2015-08-29 | 3 on f2 (HEAD, f2) [mark.fisher]
* d92b23b 2015-08-29 | 1 (master) [mark.fisher]

$ git checkout master
Switched to branch 'master'

$ git merge --no-ff f2
Merge made by the 'recursive' strategy.
 a.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

$ git hist
*   d193d0a 2015-08-29 | Merge branch 'f2' (HEAD, master) [mark.fisher]
|\
| * 807e4a7 2015-08-29 | 3 on f2 (f2) [mark.fisher]
|/
* d92b23b 2015-08-29 | 1 [mark.fisher]

$ git merge --no-ff f1
Auto-merging a.txt
CONFLICT (content): Merge conflict in a.txt
Automatic merge failed; fix conflicts and then commit the result.

$ git status
On branch master
You have unmerged paths.
  (fix conflicts and run "git commit")

Unmerged paths:
  (use "git add <file>..." to mark resolution)

        both modified:   a.txt

In order to force a merge conflict, at each edit, the file was changed to contain only the single line "1", "2", and "3" at each commit point.

At this point, if you edit a.txt you will see markers from git telling you it couldn't merge those sections of the file (the chevrons >>>>> and <<<<< show the changed from each side of the merge conflict).

git hist is an alias:

hist = !sh -c 'git log --pretty=format:\"%h %ad | %s%d [%an]\" --graph --date=short -${1:-20} ${2:-HEAD}' -

git conflicts are a normal thing, they just indicate changes to common sections of a file, and someone has to guide git into deciding how the common change should look, and then add that change and commit it as part of the merge commit.

Alternatively, if you're sure that one feature is absolutely correct, you can instead use a strategy to force the merge. See these docs.

$ git reset --hard master
HEAD is now at d193d0a Merge branch 'f2'

$ git st
On branch master
nothing to commit, working directory clean

$ git merge --no-ff f1 -s ours
Merge made by the 'ours' strategy.

$ git hist
*   689a6f5 2015-08-29 | Merge branch 'f1' (HEAD, master) [mark.fisher]
|\
| * 1db4fed 2015-08-29 | 2 on f1 (f1) [mark.fisher]
* |   d193d0a 2015-08-29 | Merge branch 'f2' [mark.fisher]
|\ \
| |/
|/|
| * 807e4a7 2015-08-29 | 3 on f2 (f2) [mark.fisher]
|/
* d92b23b 2015-08-29 | 1 [mark.fisher]

By using an "ours" strategy, it forces the file to stay as the same value in current master (ie. the one in feature2). This is also dangerous if you use it blindly, as it will ignore changes from the other side of the merge for sections that are in conflict. I have never ever run this myself in a real situation, and have always manually resolved the conflict (just edit the file, tidy it up by removing the chevrons, fix it so both features are as you want them and add it back into git and continue the merge).

You will want to experiment with this, in my case, the file only has a single line, so there's no way I can truely merge both features. Normally if there's no conflicting section, there will be no conflict and git can proceed fine as the 2 roots of the feature branches give it enough information to understand how to merge the non-overlapping changes. See below for an example.

When git stops and tells you there's a conflict (as in my example above), you either have to force it with a strategy, or have someone review the changes and manually resolve the conflict. If this is a code change (for example) you probably don't want to use the strategy and do want to review why both sides were changing the common sections, and then run tests etc.

Just to show that git isn't completely insane, if you started with a file like this:

This is a common file
The top section is in both features and won't change.
The bottom section also will not change.

# This section should be changed by feature 1:

# This section should be changed by feature 2:

# This is the common bottom section
Hello, I am at the bottom.

and then only put changes in each feature branch where indicated in the file, eg:

feature 1 edit:

...
# This section should be changed by feature 1:
This is a change from feature 1

# This section should be changed by feature 2:

# This is the common bottom section
Hello, I am at the bottom.

feature 2 edit:

...
# This section should be changed by feature 1:

# This section should be changed by feature 2:
This is a change from feature 2

# This is the common bottom section
Hello, I am at the bottom.

then you will get no merge conflict after doing both merges. As I mention above, because each feature branch is rooted in the same original commit, git can work out that each feature contributed to the file separately and the changes were not overlapping, and thus merges them easily.

It is only where git cannot sensibly determine what to put in because the changes are overlapping that you get the merge conflict. At this point you have to turn to the person who made the changes and say 'sort this out'.

By the way, the --no-ff flag just forces a new commit object. You don't need it in the first merge that you do, as master can simply skip ahead. You only use that flag if you want to keep the history of the feature changes and have it merge back into master with a new commit object. It will not help you when you have merge conflicts. You are going to have to work those ones out yourself.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM