Rebasing de uma ramificação que é pública

6

Não consigo entender como usar git-rebase e considero o seguinte exemplo.

Vamos começar um repositório em ~/tmp/repo :

$ git init

Em seguida, adicione um arquivo foo

$ echo "hello world" > foo

que é então adicionado e confirmado:

$ git add foo
$ git commit -m "Added foo"

Em seguida, iniciei um repositório remoto. Em ~/tmp/bare.git eu corri

$ git init --bare

Para vincular repo a bare.git , eu corri

$ git remote add origin ../bare.git/
$ git push --set-upstream origin master

Em seguida, vamos ramificar, adicionar um arquivo e definir um upstream para o novo branch b1 :

$ git checkout -b b1
$ echo "bar" > foo2
$ git add foo2
$ git commit -m "add foo2 in b1"
$ git push --set-upstream origin b1

Agora é hora de voltar para master e alterar algo:

$ echo "change foo" > foo
$ git commit -a -m "changed foo in master"
$ git push

Neste ponto, em master , o arquivo foo contém changed foo , enquanto em b1 ainda é hello world . Por fim, quero sincronizar b1 com o progresso feito em master .

$ git checkout b1
$ git fetch origin
$ git rebase origin/master

Neste ponto, git st retorna:

# On branch b1
# Your branch and 'origin/b1' have diverged,
# and have 2 and 1 different commit each, respectively.
#   (use "git pull" to merge the remote branch into yours)
#
nothing to commit, working directory clean

Neste ponto, o conteúdo de foo na ramificação b1 também é change foo . Então, o que esse aviso significa? Eu esperava que eu fizesse um git push , git sugere fazer git pull ... De acordo com esta resposta , esta é mais ou menos isso, e em seu comentário, @FrerichRaabe diz explicitamente que não preciso puxar nada. Oque esta acontecendo aqui? Qual é o perigo, como deve proceder? Como a história deve ser mantida consistente? Qual é a interação entre o caso descrito acima e a seguinte citação:

Do not rebase commits that you have pushed to a public repository.

tirada do livro de projeções .

Eu acho que é de alguma forma relacionado, e se não, eu gostaria de saber o porquê. Qual é a relação entre o cenário acima e o procedimento que descrevi neste post .

    
por Dror 30.10.2013 / 00:20

2 respostas

7

O motivo pelo qual você não deseja rebase de commits que você enviou para um repositório público é porque o comando git-rebase altera o histórico.

Agora, o que isso significa e por que isso é ruim? Primeiramente, sugiro ler esta seção do livro do Git. A partir disso, você aprenderá que commits consistem em um ponteiro para um objeto de árvore (captura instantânea dos arquivos) e um ponteiro para a confirmação pai. Agora, quando você "muda o histórico" ao rebasing commits em cima de novos commits, você está mudando o ponteiro pai dos commits que você já fez, o que muda o id dos seus commits.

A razão pela qual isso é ruim é que, se você compartilha seus commits publicamente, e outros iniciam um trabalho adicional baseado nesses commits, então você muda esses commits, suas árvores não estão mais sincronizadas.

Você pode ver tudo isso emitindo alguns comandos git-log ao executar seu exemplo. Eu corri isso antes de executar o comando rebase:

$ git log --pretty=oneline origin/master
9b077261d1619803213201d5c7cefb757eb66b67 Changed foo in master
911ce5b247e79682ec9f73ad9a15fd3167b7e76d Added foo

$ git log --pretty=oneline origin/b1
63a57ef54e301314a9dab38de0cd9d88c59a5fba added foo2 in b1
911ce5b247e79682ec9f73ad9a15fd3167b7e76d Added foo

$ git log --pretty=oneline b1
63a57ef54e301314a9dab38de0cd9d88c59a5fba added foo2 in b1
911ce5b247e79682ec9f73ad9a15fd3167b7e76d Added foo

E agora, depois de realizar o rebase, origin/master e origin/b1 são iguais, mas b1 é agora:

$ git log --pretty=oneline b1
6687c64c37db0ee21a4d87e45d6ccb0913b8686d added foo2 in b1
9b077261d1619803213201d5c7cefb757eb66b67 Changed foo in master
911ce5b247e79682ec9f73ad9a15fd3167b7e76d Added foo

Você notará que a confirmação "added foo2 in b1" tem um id diferente do que nos comandos de log anteriores. Se você cometer esta alteração para o seu repositório público, agora você tem dois commits que têm o mesmo trabalho feito neles e causa problemas.

Agora, suponha que, em vez de rebasing b1 no topo do master, você apenas mesclou o master em b1, seu log seria assim:

$ git checkout b1
$ git merge origin/master
$ git log --pretty=oneline b1
518eb2dc6b2da0ff43ddd6837332031cc00eaad1 Merge remote-tracking branch 'origin/master' into b1
9b077261d1619803213201d5c7cefb757eb66b67 Changed foo in master
63a57ef54e301314a9dab38de0cd9d88c59a5fba added foo2 in b1
911ce5b247e79682ec9f73ad9a15fd3167b7e76d Added foo

Você notará a confirmação extra que representa a mesclagem dos dois commits anteriores. Esta história pode agora ser compartilhada e todos serão felizes.

git-log --graph também pode ajudar a esclarecer o que está acontecendo.

    
por 30.10.2013 / 04:53
3

Tarde da festa, mas aqui está a resposta para a posteridade:

git rebase deve ser usado localmente. Ele reescreve a história, o que permite uma "linha principal" muito boa, mas é perigosa em um ambiente multiusuário

Se:

  • você é o único usando o repositório remoto,
  • E você está usando em apenas uma área de trabalho,

Então pode fazer sentido para fazer isso forçando o histórico a ser reescrito. Faça o seu rebase e depois:

git push -f origin remotebranch

Se qualquer uma dessas suposições não foi cumprida, você pode se arrepender desta ação: -)

Leia mais nesta postagem do blog

    
por 13.02.2017 / 17:44