When a field number repeats, the merge rule depends on whether the schema calls it scalar or repeated: a scalar keeps the last value, a repeated field keeps them all. Today you resolve both from the same duplicate bytes.
Resolve a duplicated scalar to its last value while a repeated field accumulates.
The same field number can appear more than once even for a non-repeated field - a
message concatenated from two partial messages is a valid way to build one, and
protobuf embraces it. The merge rule is governed by the field’s cardinality in the
schema. For a scalar field, last one wins: later occurrences overwrite
earlier ones, so age written as 30 then 40 decodes to 40. For a repeated
field, every occurrence is accumulated, so tags written as 1 then 2 decodes to
the list [1, 2].
This single rule is why message concatenation works as a merge: appending the bytes of message B after message A gives a message where B’s scalars override A’s and B’s repeated elements extend A’s. It falls out naturally from the grouped field list - walk the occurrences in order, overwrite for scalars, append for repeated - and it is the last piece of decode semantics before you close the loop by encoding a message back.
// scalar field: overwrite each time, so the last value remains// repeated field: append each value, so all are kept// (the descriptor's repeated flag decides which)