Aqui está o que eu não gosto no git:
Em primeiro lugar, acho que a ideia distribuída vai contra a realidade. Todos que realmente usam o git o fazem de forma centralizada, até mesmo Linus Torvalds. Se o kernel foi gerenciado de forma distribuída, isso significaria que eu não poderia realmente baixar as fontes "oficiais" do kernel - não haveria uma - eu teria que decidir se eu quero a versão de Linus ou a versão de Joe, ou a versão de Bill. Isso seria obviamente ridículo, e é por isso que existe uma definição oficial que Linus controla usando um fluxo de trabalho centralizado.
Se você aceitar que deseja uma definição centralizada de suas coisas, ficará claro que as funções de servidor e cliente são completamente diferentes, de modo que o dogma de que os softwares de cliente e servidor devem ser iguais torna-se puramente limitante. O dogma de que os dados do cliente e do servidor devem ser iguais torna-se patentemente ridículo, especialmente em uma base de código que tem quinze anos de história com a qual ninguém se importa, mas que todos teriam que clonar.
O que realmente queremos fazer com todo aquele material antigo é enfiá-lo em um armário e esquecer que está lá, como qualquer VCS normal faz. O fato de o git transportar tudo para a frente e para trás pela rede todos os dias é muito perigoso, porque te incomoda para podá-lo. Essa poda envolve muitas decisões tediosas e pode dar errado. Portanto, as pessoas provavelmente manterão toda uma série de repositórios de instantâneos de vários pontos da história, mas não era para isso que servia o controle de origem? Esse problema não existia até que alguém inventou o modelo distribuído.
O Git incentiva ativamente as pessoas a reescrever a história, e o que está acima é provavelmente um dos motivos para isso. Todo VCS normal torna a reescrita do histórico impossível para todos, exceto os administradores, e garante que os administradores não tenham razão para considerá-lo. Corrija-me se eu estiver errado, mas pelo que eu sei, git não oferece nenhuma maneira de conceder aos usuários normais acesso de gravação, mas bani-los de reescrever o histórico. Isso significa que qualquer desenvolvedor com ressentimento (ou que ainda está lutando com a curva de aprendizado) pode destruir toda a base de código. Como podemos apertar esse? Bem, ou você faz backups regulares de todo o histórico, ou seja, mantém o histórico ao quadrado, ou bane o acesso de gravação a todos, exceto a alguns pobres coitados que receberiam todos os diffs por e-mail e os mesclaria manualmente.
Vamos dar um exemplo de um grande projeto bem financiado e ver como o git está funcionando para eles: Android. Certa vez, decidi brincar com o próprio sistema Android. Eu descobri que deveria usar um monte de scripts chamados repo para chegar ao git. Alguns repo são executados no cliente e outros no servidor, mas ambos, por sua própria existência, estão ilustrando o fato de que o git está incompleto em ambas as capacidades. O que aconteceu é que não consegui obter as fontes por cerca de uma semana e desisti de vez. Eu teria que extrair uma quantidade verdadeiramente vasta de dados de vários repositórios diferentes, mas o servidor estava completamente sobrecarregado com pessoas como eu. O repo estava expirando e não foi possível retomar de onde havia expirado. Se o git é tão distribuível, você pensaria que eles ' d teria feito algum tipo de coisa ponto a ponto para aliviar a carga naquele servidor. Git é distribuível, mas não é um servidor. Git + repo é um servidor, mas repo não é distribuível porque é apenas uma coleção ad-hoc de hacks.
Uma ilustração semelhante da inadequação do git é o gitolite (e seu ancestral, que aparentemente não funcionou tão bem). Gitolite descreve seu trabalho como facilitar a implantação de um servidor git. Novamente, a própria existência dessa coisa prova que git não é um servidor, assim como não é um cliente. Além do mais, nunca será, porque se se transformasse em qualquer um deles estaria traindo seus princípios fundamentais.
Mesmo se você acreditasse na coisa distribuída, o git ainda seria uma bagunça. O que, por exemplo, é um galho? Eles dizem que você cria implicitamente um branch cada vez que clona um repositório, mas isso não pode ser a mesma coisa que um branch em um único repositório. Portanto, pelo menos duas coisas diferentes são chamadas de branches. Mas então, você também pode retroceder em um repositório e simplesmente começar a editar. É como o segundo tipo de branch ou algo diferente de novo? Talvez dependa do tipo de repo que você tem - ah, sim - aparentemente, o repo também não é um conceito muito claro. Existem normais e normais. Você não pode empurrar para um normal porque a parte vazia pode ficar fora de sincronia com sua árvore de origem. Mas você não pode cvsimportar para um só porque eles não pensaram nisso. Então você tem que cvsimport para um normal, clone isso para um arquivo vazio que os desenvolvedores acertaram e cvsexport para uma cópia de trabalho cvs que ainda precisa ser verificada no cvs. Quem pode ser incomodado? De onde vieram todas essas complicações? Da própria ideia distribuída. Abandonei o gitolite no final porque ele estava me impondo ainda mais dessas restrições.
Git diz que a ramificação deve ser leve, mas muitas empresas já têm um sério problema de ramificação desonesta, então eu pensei que a ramificação deveria ser uma decisão importante com policiamento estrito. É aqui que realmente brilha ...
Forçosamente, você raramente precisa de branches porque pode manipular changesets de uma maneira muito ágil. Por exemplo, o fluxo de trabalho normal é sincronizar com a última versão válida conhecida na linha principal e, em seguida, escrever seu recurso. Sempre que você tenta modificar um arquivo, o diff desse arquivo é adicionado ao seu "conjunto de alterações padrão". Quando você tenta fazer o check-in do changeset, ele automaticamente tenta mesclar as notícias da linha principal em seu changeset (efetivamente rebasando-o) e então se compromete. Esse fluxo de trabalho é aplicado sem que você precise entendê-lo. Dessa forma, a Mainline coleta um histórico de mudanças que você pode facilmente escolher mais tarde. Por exemplo, suponha que você queira reverter um antigo, digamos, aquele que antecede o penúltimo. Você sincroniza com o momento anterior à alteração ofensiva, marca os arquivos afetados como parte do conjunto de alterações, sincronizar com o momento seguinte e mesclar com "sempre meu". (Havia algo muito interessante lá: sincronizar não significa ter a mesma coisa - se um arquivo for editável (ou seja, em um changeset ativo), ele não será prejudicado pela sincronização, mas marcado como devido para resolução.) Agora você tem um changelist que desfaz o ofensivo. Junte-se às notícias subsequentes e você terá uma lista de mudanças que pode colocar no topo da linha principal para ter o efeito desejado. Em nenhum momento reescrevemos qualquer história. Junte-se às notícias subsequentes e você terá uma lista de mudanças que pode plopar no topo da linha principal para ter o efeito desejado. Em nenhum momento reescrevemos qualquer história. Junte-se às notícias subsequentes e você terá uma lista de mudanças que pode colocar no topo da linha principal para ter o efeito desejado. Em nenhum momento reescrevemos qualquer história.
Agora, supondo que no meio do processo, alguém venha até você e diga para você largar tudo e consertar algum bug. Basta dar um nome à sua lista de alterações padrão (na verdade, um número) e então "suspendê-la", consertar o bug na lista de alterações padrão agora vazia, confirmá-la e retomar a lista de alterações nomeada. É comum ter vários changelists suspensos ao mesmo tempo em que você tenta coisas diferentes. É fácil e privado. Você consegue o que realmente deseja de um regime de filial sem a tentação de procrastinar ou evitar a fusão com a linha principal.
Suponho que seria teoricamente possível fazer algo semelhante no git, mas o git torna praticamente tudo possível ao invés de afirmar um fluxo de trabalho que aprovamos. O modelo centralizado é um monte de simplificações válidas em relação ao modelo distribuído, o que é uma generalização inválida. Ele é tão generalizado que basicamente espera que você implemente o controle de origem, como o repo faz.
A outra coisa é a replicação. No git, tudo é possível, então você tem que descobrir por si mesmo. Forçosamente, você obtém um cache eficaz sem estado. A única configuração que ele precisa saber é onde está o mestre, e os clientes podem apontar para o mestre ou para o cache a seu critério. Isso é um trabalho de cinco minutos e não pode dar errado.
Você também tem gatilhos e formulários personalizáveis para avaliar revisões de código, referências de bugzilla etc. e, claro, tem branches para quando realmente precisar deles. Não é claro, mas está perto e é muito fácil de configurar e manter.
Em suma, acho que se você sabe que vai trabalhar de forma centralizada, o que todo mundo faz, é melhor usar uma ferramenta que foi projetada com isso em mente. Git é superestimado por causa da sagacidade temível de Linus junto com a tendência das pessoas de seguirem umas às outras como ovelhas, mas sua principal razão de ser não resiste ao bom senso e, ao segui-lo, git amarra suas próprias mãos com os dois grandes dogmas de que (a) o software e (b) os dados têm que ser os mesmos tanto no cliente quanto no servidor, e isso sempre tornará tudo complicado e coxo no trabalho centralizado.