1

I have this question.

  1. Imagine I have 5 files on master
  2. Imagine I make a new branch from master test
  3. I go back to master, make commit which deletes one file
  4. again make another commit which deletes one file,
  5. again make commit which deletes file
  6. Now, I want to merge test branch (which is basically intact) back to master

My question is: Can it happen that now git will get confused and as a result of merge basically recreate the files which were deleted in steps 3,4,5 because they were present in test branch?

If no, can you please tell me which mechanism helps git to resolve this issue? Where can I read about this?

I looked into three way merge but I can't see how it relates here because what would be common ancestor for test branch and say master at step 5?

2 Answers2

1

My question is: Can it happen that now git will get confused and as a result of merge basically recreate the files which were deleted in steps 3,4,5 because they were present in test branch?

No. Git is never "confused" about this sort of thing. It follows clear, deterministic rules, in a consistent manner. You (or I) might be confused, but that would be just because we don't know those rules.

So let's know them. How does a merge work?

  • It re-enacts the contributions from both branches since the time they most recently diverged. That is the logic described in the documentation as a "3-way merge", because three points are taken into account: the point of divergence and the ends of the two branches.

  • Doing nothing does not count as a "contribution" in this sense. If a branch end is the same, in some regard, as the point of divergence, then nothing happened and no contribution is made by that branch in that regard.

So if master deletes a file that was present at the point of divergence, but test does not, then merging one into the other will result in a commit where the file is deleted — because not deleting the file is "doing nothing" which doesn't count for anything.


(However, if master deletes a file and test edits that file, then merging one into the other will result in a merge conflict which you will have to resolve manually. That's because they are now both contributing something, and those contributions cannot both be enacted. Still, this is not a case of Git getting confused and restoring the deleted file; it is confused, in a way, but it knows that, so it takes no action on the matter, leaving it to you to decide what to do. As long as you don't force the resolution of the conflict beforehand by specifying an ours or theirs strategy, you still won't accidentally restore the file.)

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Is that it? Where does the three way merge come in though? I would appreciate if you can link some readings maybe which addresses this (you can see my comments with leviathan above to see what things confused me, thanks!). –  Jan 28 '21 at 19:52
  • I don't know what "Where does the three way merge come in" means. I didn't mention any three way merge. My answer stands, for what it is worth. I have described the logic followed by a merge that Git performs automatically, and you can confirm this experimentally. – matt Jan 28 '21 at 19:55
  • If you can link me to article which explains this more I would appreciate. Thanks! with 3 merge I meant: git does perform that sometimes right? So in which cases does it? –  Jan 28 '21 at 20:05
  • Three way merge is mentioned [here](https://git-scm.com/docs/merge-strategies) though, maybe that is what OP was referring to? – Giorgi Moniava Jan 29 '21 at 07:34
  • Well that’s the logic my answer describes. – matt Jan 29 '21 at 09:10
0

Simple case: No changes in test

In this case, the merge won't do anything (unless you force git to make a merge commit). All changes in test are already part of the history of master:

* Delete file three [master]
* Delete file two
* Delete file one
* Add 5 files [test]

Changes in test: unrelated to deleted files

But what if the history isn't a simple line like this? What if we made changes in test?

Even then the solution is simple: The last shared ancestor is the original commit, and no changes in test touched any of the deleted files.

* Delete file three [master]
| \
|  * Change file four [test]
*  | Delete file two
*  | Delete file one
| /
* Add 5 files

Changes in test: inside deleted files

Finally, let's look at the case where a file that had been deleted in master gets changed in test. This is the only case where we have a true conflict. When you try to merge here, git will tell you that there is a conflict:

CONFLICT (modify/delete): one deleted in HEAD and modified in test. Version test of one left in tree. Automatic merge failed; fix conflicts and then commit the result.

You can then either decide to add the file (git add one) or delete it (git rm one). In both cases you'll then have to commit (which will create a merge commit):

* Merge branch 'test' [master]
| \
|  * Change file one [test]
*  | Delete file three
*  | Delete file two
*  | Delete file one
| /
* Add 5 files
L3viathan
  • 26,748
  • 2
  • 58
  • 81
  • before I read other parts of your answer I want to address the **"Simple Case"**. So you say in this case, git will not recreate the 5 files from test? After merge, will master have all 5 files, or only 2? If 2, my question was also how did Git determine that it should have 2 files instead of 5? –  Jan 28 '21 at 18:23
  • And finally if git will do three way merge you say, what is common ancestor for test now and master from step 5 (which is now three commits away from what it was when it was branched off)? –  Jan 28 '21 at 18:31
  • In the simple case: Git will not do anything (compared to master); the files will stay deleted. Git knew that `test` is an ancestor of `master`, and therefore does nothing. – L3viathan Jan 28 '21 at 18:43
  • And the "hard" case is also not a three-way merge, but a normal merge with two parent commits. The common ancestor is the first commit. – L3viathan Jan 28 '21 at 18:44
  • **Simple case**: So git on one hand has `test` which has 5 files and master from step 5 with 2 files, **how** does it decide which version to keep that was my question? Not three way merge in this case you say? –  Jan 28 '21 at 18:57
  • @don Every commit has a list of parents. `test` in that case points to the original commit, which is the parent of the first file-deleting commit. When merging those branches, it knows the deletion happened "after" the files were added. Merging is really just "making two histories consistent". Since the history of `master` is a decendent of that of `test`, this is a trivial task ("do nothing"). If you're still confused look into a basic Git tutorial, e.g. [this one](https://www.slideshare.net/nnja/nina-zakharenko-introduction-to-git-start-slc-2015). – L3viathan Jan 28 '21 at 19:07
  • "it knows the deletion happened "after" the files were added." --> Yeah this was my question :) So based on which algorithm does it decide to not add the files? I thought it was three way merge, but you say no? or using which algorithm is used to make the histories consistent? When is the three way merge used if not in this case? –  Jan 28 '21 at 19:18
  • The strategy used for merging is called "recursive merge" when merging a single head, "octopus merge" otherwise; see `man git-merge` (or [this](https://git-scm.com/docs/merge-strategies)). I was mistaken wrt. the three-way merge; that would occur in case 3, I was confusing it with a merge of multiple heads. – L3viathan Jan 28 '21 at 19:46
  • Thanks it seems there are different merges that is what I wanted to know, where to read about those –  Jan 28 '21 at 19:53