Fossil Forum

How to merge branches that independently created the same file?
Login

How to merge branches that independently created the same file?

How to merge branches that independently created the same file?

(1) By patmaddox on 2023-03-13 19:41:34 [link] [source]

I am very new to fossil. I've read through a bunch of docs, and read a bunch of forum posts, but I'm just getting started "getting it under my fingers."

I added a file foo.md in a separate checkout and did fossil commit --branch branch1 to put it in a new branch. Then back in trunk checkout I also added foo.md and committed.

In my branch1 checkout, I run fossil merge trunk and get the output WARNING: no common ancestor for bar.md with an exit status of 0. There is no change to the content in branch1 checkout.

  1. Am I completely missing a command? :)
  2. Why does merge show a returning and exit 0? I would expect exit 1 if it can't do the merge.
  3. How do I merge these branches? I would expect fossil to present me with a conflict if it can't merge them automatically.

The only info I've found related to "no common ancestor for FILE" is this 9-year old post that describes my situation but doesn't have a resolution.

This is fossil version 2.21 [f9aa474081] 2023-02-25 19:23:39 UTC

(2) By patmaddox on 2023-03-13 20:09:23 in reply to 1 [link] [source]

Well, I have a little bit more info... I had to fossil up before doing fossil merge (or after - it doesn't seem to make a difference which came first).

Interestingly, fossil just applied the content from trunk, completely overwriting the content in my branch1. So, I guess it doesn't show a conflict and provide an opportunity to resolve.

Conflicting edits to the same file name result in a merge conflict, but conflicting adds to the same file name don't.

(3.1) By Larry Brasfield (larrybr) on 2023-03-13 20:37:33 edited from 3.0 in reply to 2 [link] [source]

[Clarification and spelling edits, and a caveat]

Conflicting edits to the same file name result in a merge conflict, but conflicting adds to the same file name don't.

That's not quite right. A "merge conflict" means that the automatic merge resolution algorithm was unable to suss multiple changes as being independent enough to let a simple algorithm's operation supplant human judgement. The smooth merge (with no conflict) means no conflicting changes were detected.

It should never be assumed that an absence of merge conflict means that the changes are compatible. Clever as it is, the algorithm knows nothing of higher level concepts such as coordinated changes in separate places.

(4) By patmaddox on 2023-03-13 20:44:59 in reply to 3.0 [link] [source]

Well, whatever you want to call it, fossil just threw away the changes I made on a different branch. I'm open to the idea that I did something wrong. But, I would think that two branches adding the same file with different content would prompt about a conflict rather than throwing away content.

In this sequence, the line 'from trunk' simply vanished:

~ % mkdir /tmp/fossil-conflict && cd /tmp/fossil-conflict
fossil-conflict % fossil init proj.fossil
project-id: 571b030ba21c948da5fe9ac1f211961ff16af3aa
server-id:  d26841e5c8e90ae566e6601bb0e8350bbec88984

fossil-conflict % mkdir trunk && cd trunk && fossil open ../proj.fossil
project-name: <unnamed>
repository:   /private/tmp/fossil-conflict/proj.fossil
local-root:   /private/tmp/fossil-conflict/trunk/
config-db:    /Users/patmaddox/.config/fossil.db
project-code: 571b030ba21c948da5fe9ac1f211961ff16af3aa
checkout:     c5f32a1f1369eedb2adddd717ebe660f1f573a39 2023-03-13 20:36:49 UTC
tags:         trunk
comment:      initial empty check-in (user: patmaddox)
check-ins:    1

trunk % cd .. && mkdir branch1 && cd branch1 && fossil open ../proj.fossil
project-name: <unnamed>
repository:   /private/tmp/fossil-conflict/proj.fossil
local-root:   /private/tmp/fossil-conflict/branch1/
config-db:    /Users/patmaddox/.config/fossil.db
project-code: 571b030ba21c948da5fe9ac1f211961ff16af3aa
checkout:     c5f32a1f1369eedb2adddd717ebe660f1f573a39 2023-03-13 20:36:49 UTC
tags:         trunk
comment:      initial empty check-in (user: patmaddox)
check-ins:    1

branch1 % echo 'from branch1' > README.md

branch1 % fossil add README.md 
ADDED  README.md

branch1 % fossil commit -m 'from branch1' --branch branch1
New_Version: c6bece8a86cc5f4d8c162205f0acd6549664dfd0037c79062f0b75ba510dcef3

branch1 % cd ../trunk

trunk % echo 'from trunk' > README.md

trunk % fossil add README.md 
ADDED  README.md

trunk % fossil commit -m 'from trunk'
New_Version: bbcbbc5d8a4e18eaa7b6d4fd5bbd018dfdb2d279a50ce9817060a3d33d437fb7

trunk % cd ../branch1

branch1 % fossil up
-------------------------------------------------------------------------------
checkout:     c6bece8a86cc5f4d8c162205f0acd6549664dfd0 2023-03-13 20:37:56 UTC
tags:         branch1
comment:      from branch1 (user: patmaddox)
changes:      None. Already up-to-date

branch1 % fossil merge trunk
WARNING: no common ancestor for README.md

branch1 % fossil changes
MERGED_WITH bbcbbc5d8a4e18eaa7b6d4fd5bbd018dfdb2d279a50ce9817060a3d33d437fb7

branch1 % fossil commit -m 'merge trunk'
New_Version: e81ffb2d198bfded86aa09b158cbaeb66b7a2fbc433fba01706521d4ede23b5d

branch1 % cat README.md 
from branch1

(5) By Larry Brasfield (larrybr) on 2023-03-13 20:59:32 in reply to 4 [link] [source]

Well, whatever you want to call it, fossil just threw away the changes I made on a different branch.

They are still there, in the repo.

I'm open to the idea that I did something wrong. But, I would think that two branches adding the same file with different content would prompt about a conflict rather than throwing away content.

I submit a middle way: The merge logic is subject to improvement for the rare case you have demonstrated. AND your expectation is subject to improvement.

When anybody makes a set of changes, commits them, then attempts to merge in another branch with changes affecting the same files, it is up to that person to examine the results and make adjustments as needed.

(6) By patmaddox on 2023-03-13 21:58:46 in reply to 5 [source]

When anybody makes a set of changes, commits them, then attempts to merge in another branch with changes affecting the same files, it is up to that person to examine the results and make adjustments as needed

Alright, so I've added the file in both branches. I've done fossil up && fossil merge trunk in branch1.

How do I see that a file was changed in both branches? So that I know to examine the results and adjust if needed?

I may be thinking too git-y here, but I would approach it as "get the union of modified files in each branch starting from the common parent". Which... not sure how to do that with fossil.

Take the following script. The closest I've come up with after running it is cd branch1 && fossil diff -r trunk | grep -v '^ADDED'. It shows conflicting (which is good, because it was changed in both), but also README.md (not good, because it only changed in branch1):

#!/bin/sh
set -e

proj=$(pwd)
fossil=${proj}/proj.fossil

fossil init ${fossil}

echo "First commit to trunk"
mkdir ${proj}/trunk
cd $proj/trunk && fossil open $fossil
echo 'Initial README' >> README.md
fossil add README.md
fossil commit -m 'Initial README'

echo "First commit to branch1"
mkdir ${proj}/branch1
cd $proj/branch1 && fossil open $fossil
echo 'another line' >> README.md
echo 'only in branch1' >> only-in-branch1
echo 'conflicting from branch1' >> conflicting
fossil add .
fossil commit -m 'from branch1' --branch branch1

echo "Second commit to trunk"
cd ${proj}/trunk
echo 'only in trunk' >> only-in-trunk
echo 'conflicting from trunk' >> conflicting
fossil add .
fossil commit -m 'from trunk'

echo "Merging trunk into branch1"
cd ${proj}/branch1
fossil up && fossil merge trunk

(7) By Larry Brasfield (larrybr) on 2023-03-13 22:37:51 in reply to 6 [link] [source]

I'm not going to get into the weeds with you on this. I have followed your example and seen that your characterization of merge behavior is accurate, (overlooking the "fossil just threw away the changes" comment, which is an overstatement.)

I agree, (just as I did before), that the merge behavior for this highly unusual condition might be improved. (I do not meant by this to say it should be improved.)

I still maintain that handling this behavior is your responsibility. Here is how you can discharge that responsibility and get back to ordinary development:

  1. Learn to use 'fossil status', after any merge and prior to any subsequent commit, to see what the SCM has done on your behalf in summary form.

  2. Learn to use 'fossil diff' when step 1 indicates there may be an issue worth your attention after a merge. Almost anything done to one of the files you have locally modified is attention-worthy, IMO.

  3. The 'fossil diff' operation will show you facts such as "The whole content of my newly added file was replaced by somebody else's newly added file with the same name." If you have taken ordinary care to preserve your work product, it will be simple at such time to respond to any discoveries of facts not to your liking.

This is how you can proceed, today, without relying on others to remedy the merge behavior for your highly unusual scenario.

(8) By Andy Bradford (andybradford) on 2023-03-14 04:27:15 in reply to 6 [link] [source]

> How do I see that a file was  changed in both branches? So that I know
> to examine the results and adjust if needed?

Have you perhaps  missed a critical piece in your  description? When you
run "fossil merge trunk", does not  Fossil's WARNING tell you that there
is something wrong?

$ fossil up && fossil merge trunk
-------------------------------------------------------------------------------
checkout:     3506322cb684e6795885f4b7857d3dd901bc404f 2023-03-14 04:21:40 UTC
tags:         branch1
comment:      from branch1 (user: amb)
changes:      None. Already up-to-date
WARNING: no common ancestor for conflicting
ADDED only-in-trunk
 "fossil undo" is available to undo changes to the working checkout.

This WARNING should serve as a  warning to you that something is perhaps
not quite right.

Given that there is no common ancestor, you'll have to figure out how to
manually merge the files (if they can be merged).

Andy

(9) By Martin Gagnon (mgagnon) on 2023-03-16 02:28:33 in reply to 6 [link] [source]

After some discussion on the fossil chat, I came with this branch: merge-conflict-when-no-file-on-pivot.

It's an attempt to improve behaviour of this particular case by letting the merge logic to generate merge conflict marks inside the file, showing both versions + the common-ancestor version (which is empty in this case).

resulting file from the test script:


<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1)
conflicting from branch1
||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1)
======= MERGED IN content follows =============================== (line 1)
conflicting from trunk
>>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


This would force the user to take a decision (choose which version he want to keep) and cleanup the conflict-marks before to commit, like a normal merge conflict.


PS: here a simpler version of the script, modified by Warren, which remove unrelated steps.


#!/bin/bash -e
proj=$(pwd)
fossil=${proj}/proj.fossil

echo "Initting and opening repo"
mkdir ${proj}/trunk
cd $proj/trunk
fossil init ${fossil}
fossil open $fossil

echo "Adding a file to branch1"
mkdir ${proj}/branch1
cd $proj/branch1
fossil open $fossil
echo 'conflicting from branch1' > conflicting
fossil add .
fossil commit -m 'from branch1' --branch branch1

echo "Adding same-named file to trunk"
cd ${proj}/trunk
echo 'conflicting from trunk' > conflicting
fossil add .
fossil commit -m 'from trunk'

echo "Merging trunk into branch1"
cd ${proj}/branch1
fossil up
fossil merge trunk

(10) By Larry Brasfield (larrybr) on 2023-03-16 02:44:36 in reply to 9 [link] [source]

That's a real improvement over the current behavior, Martin. It makes "fossil status" more reliable for noticing or seeing what may have gone awry.

However, would it not still make sense to get the warning during the merge itself, indicating an unusual merge operation? After all, when two developers have not managed to agree upon who will do what to the source, that seems worth an extra complaint as happens currently in the released Fossil.

(11) By Martin Gagnon (mgagnon) on 2023-03-16 04:11:51 in reply to 10 [link] [source]

would it not still make sense to get the warning during the merge itself

I tough about it, but the way I see it is that this particular case is not really different from the case where the file already exist and a merge conflict happens because the same line was modified from the 2 branch.

It just happens that both developper add the same file, the same way both developper could have modify the same line in the same allready existing file. The way to inform the user about this merge conflict is the same for both case:

  • Adding conflict mark in the file
  • Reporting it with: fossil status
  • Prevent to commit until conflict mark are present in the file.
    • user have to resolve the conflict or use the --allow-conflict flag.

The only difference here, is that the "COMMON ANCESTOR content" section is empty for obvious reason.

Since the way to resolve it is identical in both case, the warning may be redundant
(it could even be confusing for users who are accustomed to regular merge conflicts, which are more frequent).

This is only my opinion, I wouldn't mind keeping the warning if this is the consensus.

(12) By Larry Brasfield (larrybr) on 2023-03-16 04:27:45 in reply to 11 [link] [source]

Upon further thought, I suppose you are right. Merge conflicts, when they happen, are more or less accidental or at least coincidental. And, as you say, the resolutions are often the same, except maybe for the case where the file names have collided accidentally (rather than because they are purpose-named.)

What gave me pause is that, normally in a merge, it is differences from a common ancestor being merged, and not having a common ancestor is significantly different. But that will become evident when there is an empty "COMMON ANCESTOR" section. It is at the resolution that we really need to know what is going on. At merge time, a CONFLICT report is adequate. Any sensible person will be looking into that, and with the markers left in the resulting file, that task cannot be easily evaded.

I did not mean to do so here, but it appears that I've engaged in the "tree shaking" role. Sometimes it is useful to hash things out a bit, if only to see that nothing falls out of it.

Upon looking at the diff, I see a code reduction. That ought to count as an extra win.

(13.1) By mark on 2023-03-16 05:16:19 edited from 13.0 in reply to 12 [link] [source]

mgagnon:

the way I see it is that this particular case is not really different from the case where the file already exist and a merge conflict happens because the same line was modified from the 2 branch

larrybr:

At merge time, a CONFLICT report is adequate.

Yes, I agree, mgagnon@ is right: we do not need to special case this conflict with further output.

larrybr:

Upon looking at the diff, I see a code reduction. That ought to count as an extra win

More red lines in the diff are always seductive :)

That's a real improvement over the current behavior

Agreed. This is very nice, definitely an improvement. Thanks, mgagnon@. ok for me!


ETA:

tiny nits in the comment's last sentence: s/marks/markers and slight rephrase

Proceed even if the file doesn't exist on P, just like the common ancestor
of M and V is an empty file. In this case, merge conflict markers will be
added to the file so the user will be forced to make a decision before
committing (or use the --allow-conflict flag).

(14) By Andy Bradford (andybradford) on 2023-03-21 02:30:18 in reply to 10 [link] [source]

> However, would it  not still make sense to get  the warning during the
> merge itself, indicating an unusual merge operation?

Especially when it comes to binary files, this may still be desirable:

$ fossil merge trunk
MERGE picture.jpg
***** Cannot merge binary file picture.jpg
WARNING: 1 merge conflicts
 "fossil undo" is available to undo changes to the working checkout.


If I walk away or don't notice the error, and later do "fossil status":

$ fossil status
repository:   /tmp/new.fossil
local-root:   /tmp/new/
config-db:    /home/amb/.fossil
checkout:     c5bc01ba45f474f44a7c13406a30872d109dc428 2023-03-21 02:26:43 UTC
parent:       34b9206ba18af3028601e4f5966dfbd8c040a861 2023-03-21 02:26:16 UTC
tags:         bin
comment:      addedbin (user: amb)
MERGED_WITH d9dde196b062a9ffb60517fbf61c77724c7a60dae15e6c999da364de530deb36

Andy

(15) By patmaddox on 2023-04-15 11:13:52 in reply to 9 [link] [source]

Very cool! I hope this gets merged and released :) Thank you!

(16) By Larry Brasfield (larrybr) on 2023-04-19 01:52:05 in reply to 15 [link] [source]

(17) By Martin Gagnon (mgagnon) on 2023-04-19 01:52:32 in reply to 15 [link] [source]

First step is done: It's now merged in trunk.