Gravar operação de cópia de arquivo com Git

141

Quando movo um arquivo no git usando o git-mv, o status mostra que o arquivo foi renomeado e, mesmo que eu altere algumas partes, ele ainda considera quase a mesma coisa (o que é bom porque me permite seguir o histórico) .

Quando copio um arquivo, o arquivo original tem um histórico que gostaria de associar à nova cópia.

Tentei mover o arquivo e tentar fazer o check-out novamente no local original - uma vez que o git movido não permita que eu faça o checkout do local original.

Eu tentei fazer uma cópia do sistema de arquivos e, em seguida, adicionar o arquivo - git a lista como um novo arquivo.

Existe alguma maneira de fazer com que o git grave uma operação de cópia de arquivo, da mesma maneira que registra a renomeação / movimentação de um arquivo, onde o histórico pode ser rastreado até o arquivo original?

Hexdoll
fonte

Respostas:

112

O Git não renomeia o rastreamento nem o rastreamento de cópias, o que significa que não registra renomeações ou cópias. O que ele faz é renomear e detectar cópias . Você pode solicitar a detecção de renomeação em git diff(e git show) usando a -Mopção, você pode solicitar detecção de cópia adicional em arquivos alterados usando a -Copção ( -Cimplica -M) e pode solicitar uma detecção de cópia mais cara entre todos os arquivos com --find-copies-harderou -C -C(o que implica -C, o que implica -M) Veja a página de manual do git-diff .

Você também pode configurar o git para sempre renomear a detecção, definindo diff.renamesum valor booleano verdadeiro (por exemplo, trueou 1), e você pode solicitar ao git que faça a detecção de cópia também, definindo-o como copyou copies. Veja a página de manual do git-config .

Verifique também a -lopção git diffe a variável de configuração relacionada diff.renameLimit.


Observe que git log <pathspec>funciona de maneira diferente no Git: aqui <pathspec>está um conjunto de delimitadores de caminho, em que caminho pode ser um nome de (sub) diretório. Ele filtra e simplifica o histórico antes que a detecção de renomeação e cópia entre em ação. Se você deseja seguir renomeações e cópias, use git log --follow <filename>(que atualmente é um pouco limitado e funciona apenas para um único arquivo).

Jakub Narębski
fonte
1
@allyourcode: Sobre o que você está confuso? Para ativar a detecção de cópia por padrão, defina diff.renamescomo copies(por exemplo, ' git config diff.renames copies'). Concordo que é um pouco contra-intuitivo.
Jakub Narębski
Uma seção que não consigo analisar é "e você pode solicitar que, por padrão, também renomeie a detecção". Você está dizendo que existem quatro valores que diff.renames podem usar (verdadeiro, 1, cópia, cópias) e que todos fazem a mesma coisa?
Allyourcode
1
@allourcode: me desculpe, eu não notei isso. Corrigido agora, obrigado.
Jakub Narębski
4
@ peschü: O Git usa o banco de dados de objetos endereçados a conteúdo como um armazenamento de repositório. O conteúdo do arquivo é armazenado no conteúdo 'blob' no endereço que é o hash de conteúdo SHA-1 (bem, tipo + comprimento + conteúdo). Isso significa que o conteúdo fornecido é armazenado apenas uma vez. Nb. essa desduplicação automática foi a razão por trás da criação do sistema de backup "bup", usando o formato git pack.
Jakub Narębski 23/11
1
Ao contrário da solução abaixo, isso não funciona com o rastreamento de alterações em um intervalo. O log do Git permite um argumento de intervalo ( git log -L123,456:file.xyz) que segue corretamente as renomeações, mas não as cópias, e você não pode passar --follow nesse caso; Além disso, AFAICT, isso não funciona com a culpa do git.
Clément
57

19-05-20 2020: A solução a seguir tem as vantagens de não alterar o log do arquivo original, não criar um conflito de mesclagem e ser mais curta.

Você pode forçar o Git a detectar o histórico do arquivo copiado em três confirmações:

  • Em vez de copiar, alterne para uma nova ramificação e mova o arquivo para seu novo local.
  • Adicione novamente o arquivo original lá.
  • Mesclar a nova ramificação à ramificação original com a opção de avanço rápido --no-ff.

(Os créditos vão para Raymond Chen .)


A solução anterior tinha quatro confirmações:

  • Em vez de copiar, alterne para uma nova ramificação e mova o arquivo para seu novo local.
  • Alterne para a ramificação original e renomeie o arquivo.
  • Mesclar a nova ramificação na ramificação original, resolvendo o conflito trivial mantendo os dois arquivos.
  • Restaure o nome do arquivo original em uma confirmação separada.

(Solução obtida em https://stackoverflow.com/a/44036771/1389680 .)

Robert Pollak
fonte
7
Simplicidade, concisão, 100% ... Esta resposta é serviço público ... upvoting tudo à vista
PTIM
1
Qual a diferença entre movee rename?
vovan
@vovan Você está se referindo ao fato de que no bash você usaria mvpara as duas operações? Eu estava usando 'mover' para o caso que pode envolver a alteração do diretório do arquivo e 'renomear' para o caso em que não ocorre.
Robert Pollak
1
Tentei seguir esta (nova) receita e ela não funcionou. Pode ajudar se você mostrou os comandos reais.
Greg Lindahl
@RobertPollak Eu tentei várias versões disso, mas elas não funcionaram. Por "mover o arquivo", você quer dizer git mv orig new? Por "leia o original", você quer dizer cp new orig && git add orig?
ᆼ ᆺ ᆼ 25/07