Embora as respostas mais atuais sejam favoráveis à compilação condicional em vez de ramificações, há um cenário em que há um benefício claro no uso de ramificações: se você (agora ou mais tarde) decidir disponibilizar o código-fonte da versão básica, incluindo todo o histórico de versões, mas excluindo todos os recursos premium, você pode fazer isso com a abordagem de ramificações, mas não com uma única ramificação e compilação condicional.
Eu não recomendaria o cherry picking e, em vez disso, mesclaria todas as alterações da versão básica para a versão premium. Não deve haver nenhum recurso ou correção de bug incluído no básico, mas ausente na versão premium. Para tornar as coisas o mais simples possível, você deve certificar-se de que o ramo premium modifica os arquivos comuns o mínimo possível. Portanto, o ramo premium deve conter principalmente arquivos adicionais e talvez algumas pequenas modificações para criar instruções. Dessa forma, as alterações da versão básica serão mescladas automaticamente sem causar conflitos.
A resposta de Greg sugeriu que você “considerasse as ramificações para as coisas que serão (ou podem ser) fundidas novamente de novo mais tarde". Com a abordagem que acabei de descrever, esse é o caso, exceto que a ramificação final de todas as confirmações será master-premium
não master
(que na verdade é master-basic
).
Submódulos seriam, obviamente, uma opção também. Depende do seu processo de construção, mas se você puder fazer a versão premium em um projeto que usa a versão básica como um módulo, isso seria ótimo. No entanto, você pode ter mais dificuldade se, em algum momento, decidir escolher recursos do ramo premium para o ramo básico. Com submódulos essa mudança seria representada como dois commits distintos, enquanto que com branches isso seria um commit único para a versão básica, e a próxima mesclagem na versão premium saberia que essas mudanças já estão incluídas e não têm para ser mesclado novamente.