git: Aplicar alterações introduzidas por commit em um repo para outro repo

115

Eu tenho um repo1e repo2na máquina local. Eles são muito semelhantes, mas o último é algum tipo de outro ramo ( repo1não é mais mantido).

/path/to/repo1 $ git log HEAD~5..HEAD~4
<some_sha> Add: Introduce feature X

Como aplicar as alterações feitas por cometer <some_sha>em repo1que repo2?

Preciso preparar algum patch ou é possível fazer algum cherry-pickentre os repositórios?

Que tal fazer o mesmo, mas por intervalo de commits?

Takehin
fonte
2
Você não pode simplesmente puxar de repo1 para repo2?
zwol
para o caso um pouco mais específico em que você deseja aplicar alterações a um arquivo ou arquivos que foram movidos em um dos repositórios, veja aqui: stackoverflow.com/questions/3491270/…
Braham Snyder

Respostas:

31

Como um hack, você pode tentar modificar a receita para comparar commits em dois repositórios diferentes na página GitTips , ou seja:

GIT_ALTERNATE_OBJECT_DIRECTORIES=../repo/.git/objects \
git cherry-pick $(git --git-dir=../repo/.git rev-parse --verify <commit>)

onde ../repoé o caminho para o outro repositório.

Com o Git moderno, você pode usar várias revisões e intervalos de revisão com a escolha certa .

O $(git --git-dir=../repo/.git rev-parse --verify <commit>) está aqui para traduzir <commit>(por exemplo HEAD, ou v0.2, ou master~2, que são valores no segundo repositório do qual você copiou) no identificador SHA-1 de confirmação. Se você conhece o SHA-1 de uma alteração que deseja escolher, não é necessário.

NOTE, entretanto, que o Git pode pular a cópia de objetos do repositório de origem, pois ele não sabe que o repositório de objetos alternativos é apenas temporário, para uma operação. Pode ser necessário copiar objetos do segundo repositório com:

GIT_ALTERNATE_OBJECT_DIRECTORIES=../repo/.git/objects git repack -a -d -f

Isso coloca os objetos emprestados do segundo repositório no armazenamento do repositório original

Não testado.


Uma solução não tão hacky é seguir a resposta knittl :

  • Vá para o segundo repositório do qual deseja copiar os commits e gere os patches dos commits que deseja git format-patch
  • Opcionalmente, copie os patches (0001- * etc.) para o seu repositório
  • Use git am --3waypara aplicar patches
Jakub Narębski
fonte
1
Funciona bem. Se você tiver problemas com o commit, execute 'git reset HEAD; git add. '.
gumik
5
isso é incrível - como você faria uma série de commits? apenas sha1 ... sha2?
hvgotcodes
Eu também entendo, fatal: unable to read tree ...mas depois que git reset HEAD^tudo funcionar bem
jmarceli
@hvgotcodes funcionou para mim simplesmente passando o intervalo como, <commit>mas o rev-parse --verifycomando não gosta, pois aceita apenas valores de confirmação únicos. Mas como cherry-pickaceita valores de commit único e de intervalo, pergunto: por que é rev-parsenecessário?
Chuim
1
@Chuim: git rev-parseé necessário se você quiser para se referir a uma submissão pelo seu nome à base de ref em outro repositório, por exemplo master, HEAD^^ou algo assim; rev-parse o transforma em identificador SHA-1 universal.
Jakub Narębski,
207

Você provavelmente deseja usar git format-patche git amaplicar esse patch ao seu repositório.

/path/to/1 $ git format-patch sha1^..sha1
/path/to/1 $ cd /path/to/2
/path/to/2 $ git am -3 /path/to/1/0001-…-….patch

Ou, em uma linha:

/path/to/2 $ git --git-dir=/path/to/1/.git format-patch --stdout sha1^..sha1 | git am -3
tricô
fonte
9
Esta solução provou ser mais simples e segura do que a resposta aceita de seleção direta usando GIT_ALTERNATE_OBJECT_DIRECTORIES(isso corromperia meu repositório).
Chuim
2
Quando há conflitos, não funciona porque falha em encontrar os commits no outro branch.
Roger Far
2
Adicionar --ignore-whitespaceao git amcomando pode resolver quaisquer conflitos e evitar a necessidade de realizar uma fusão de 3 vias
Hugheth
97

Você pode fazer cherry-pick se adicionar o segundo repo como remoto ao primeiro (e depois fetch).

wRAR
fonte
11
Essa é realmente a maneira correta de fazer isso.
Wilbert
5
Este parece ser o caminho certo para mim também. E eu apenas usei e funcionou bem para mim.
Ricky Nelson de
10
Eu prefiro dizer: faça git fetch [remote-name]no segundo repo e depois git cherry-pick [sha1].
5
Essa abordagem funcionou muito bem para mim, obrigado. Como o segundo repo também era local, bastava usar um URI de arquivo ao adicioná-lo como remoto.
palimpsestor
2
No meu caso, tenho dois clones de um gigantesco repositório git remoto (para permitir o trabalho paralelo), o que significa que todo o seu histórico já foi baixado e armazenado duas vezes no meu HD. Se eu também tivesse que adicionar cada um como um controle remoto do outro, isso criaria ainda duas cópias extras da mesma história e potencialmente exigiria sincronizações entre elas antes que eu pudesse cherry-pick. Portanto, embora possa parecer a maneira "certa", nem sempre é a mais prática.
Chuim