3

I have the following situation in git:

  • 2 feature branches: feature-01 and feature-02
  • 1 sub-feature branch: sub-feature-01
  • I merged sub-feature-01 to feature-01 but then decided to revert it. Then merged feature-01 to master.
  • I then merge sub-feature-01 to feature-02. Then merged feature-02 to master.

The result is that on the master branch changes introduced in sub-feature-01 are not present!

My question is: why git did not include sub-feature-01 in the master branch? Why are the changes not there? (I am able to apply sub-feature-01 to master, I just need to understand why the merging of master and feature-02 did not include sub-feature-01 branch)

Below is a graph to visualize the situation and a script to easily reproduce it.

*   985c298 (HEAD -> master) Merge branch 'feature-02'
|\  
| *   4f19fd8 (feature-02) Merge branch 'sub-feature-01' into feature-02
| |\  
| * | d568a9f feature-02 commit 1
* | |   14a821c Merge branch 'feature-01'
|\ \ \  
| |/ /  
|/| |   
| * | 2ceae5b (feature-01) Revert "Merge branch 'sub-feature-01' into feature-01"
| * |   1160f07 Merge branch 'sub-feature-01' into feature-01
| |\ \  
| | |/  
| | * 265d0e8 (sub-feature-01) Super-dooper feature
| * | 79b5ad1 feature-01 commit 1
| |/  
* | d8b0552 master commit 2
|/  
* b013ef6 master commit 1
* 2d70803 initial commit
# 1 - initial commit to master & commit to master
git init
echo "hi from master" > file-master.txt
git add file-master.txt
git commit -m "initial commit"

echo "hi from master - part 2" >> file-master.txt
git add file-master.txt
git commit -m "master commit 1"

# 2 - create super-dooper feature branch
git checkout -b sub-feature-01
echo "this is the feature" > file-sub-feature.txt
git add file-sub-feature.txt
git commit -m "Super-dooper feature"
 
# 2 - create feature-01, commit sth, merge super-dooper feature, and then revert it
git checkout master
git checkout -b feature-01
echo "hi from feature-01" > file-feature-01.txt
git add file-feature-01.txt
git commit -m "feature-01 commit 1"

git merge --no-edit sub-feature-01
git revert --no-edit -m1 HEAD

# 3 - master commit
git checkout master
echo "hi from master - part 3" >> file-master.txt
git add file-master.txt
git commit -m "master commit 2"

# 4 - create feature-02, commit sth
git checkout -b feature-02
echo "hi from feature-02" > file-feature-02.txt
git add file-feature-02.txt
git commit -m "feature-02 commit 1"

# 5 - merge feature-01 to master
git checkout master
git merge --no-edit feature-01

# 6 - reapply merging sub-feature-01 to feature-02
git checkout feature-02
git merge --no-edit sub-feature-01
 
# 7 - merge feature-02
git checkout master
git merge-base master feature-02
git merge --no-edit --stat feature-02

ls # I expected to see  file-sub-feature.txt but it's not here !!!

# git log --all --decorate --oneline --graph
mzakrze
  • 53
  • 3
  • IMHO @j6t spotted the main issue in his comment : in this situation, git doesn't spot "a single merge base" (run `git merge-base --all` to see that), and it gets more difficult to figure out what is considered "the starting state". Rather than merging a branch that forked from `feature-01` in `feature-02`, it would be clearer to cherry-pick or rebase the commits for "Super-dooper feature" on `feature-02`. – LeGEC Jul 28 '22 at 09:19
  • the fact that `git merge-base` (without the `--all` option) mentions one single commit without any error or warning is imho pretty misleading – LeGEC Jul 28 '22 at 09:22
  • @LeGEC: I've always been annoyed by this as well. I got a fix into `git diff` for the three-dot notation that detects the sticky case and gives you a warning, at least, but there still are not enough warnings here, in my opinion. – torek Jul 29 '22 at 09:40

1 Answers1

2

why git did not include sub-feature-01 in the master branch? Why are the changes not there?

Because Git does not magically ignore commits. The commit that you wanted it to ignore, but it didn't, is Revert "Merge branch 'sub-feature-01' into feature-01". At some point, this commit was merged into master, hence, its effects are present there.

That this commit is a reversal of some other commits has no particular significance to Git. Think of what you would expect if this were not a reversal, but some unrelated changes; would you then expect the commit's effects to be present in master or not? (Answer: you would expect them to be present.)

j6t
  • 9,150
  • 1
  • 15
  • 35
  • As I understand git and 3-way-merge - the 3 way merge looks at the content of files on (1) branch first, (2) branch second, (3) merge base of those two branches (nearest common ancestor) and only that. It does not list commits, reverts, etcetera. What difference does it make if there was 1 commit and 1 revert or 1000 commits? On feature-02 I have a "file-sub-feature.txt" which does not conflict with anything, so how I understand it, it should be on master after merging – mzakrze Jul 28 '22 at 07:31
  • 3
    There are *two* merge bases (use `git merge-base --all`). `git merge` constructs a virtual merge between these two merge bases and treats the result as the base version of the final merge. You will notice that "our" side now sees the reversal, but "their" side has no change regarding the sub-feature. Hence, the reversal enters the final merge result. – j6t Jul 28 '22 at 08:44