Gostaria de renomear / mover uma subárvore do projeto no Git, movendo-a de
/project/xyz
para
/components/xyz
Se eu usar um plain git mv project components
, todo o histórico de commit dos xyz project
se perderá. Existe uma maneira de mudar isso de modo que a história seja mantida?
git mv
: stackoverflow.com/questions/1094269/whats-the-purpose-of-git-mvRespostas:
O Git detecta renomeação em vez de persistir na operação com a confirmação, portanto, use
git mv
oumv
não importa.O
log
comando usa um--follow
argumento que continua o histórico antes de uma operação de renomeação, ou seja, procura por conteúdo semelhante usando as heurísticas:http://git-scm.com/docs/git-log
Para pesquisar o histórico completo, use o seguinte comando:
fonte
git config alias.logf "log --follow"
e apenas escrevergit logf ./path/to/file
.É possível renomear um arquivo e manter o histórico intacto, embora isso faça com que o arquivo seja renomeado em todo o histórico do repositório. Provavelmente, isso é apenas para os amantes obsessivos do git-log e tem algumas implicações sérias, incluindo estas:
Agora, como você ainda está comigo, provavelmente é um desenvolvedor solo que renomeia um arquivo completamente isolado. Vamos mover um arquivo usando
filter-tree
!Suponha que você vá mover um arquivo
old
para uma pastadir
e dê o nomenew
Isso poderia ser feito
git mv old dir/new && git add -u dir/new
, mas isso quebra a história.Em vez de:
irá refazer cada commit no branch, executando o comando nos ticks para cada iteração. Muitas coisas podem dar errado quando você faz isso. Normalmente, testo para ver se o arquivo está presente (caso contrário, ainda não está lá para ser movido) e, em seguida, execute as etapas necessárias para calçar a árvore da maneira que eu preferir. Aqui você pode acessar os arquivos para alterar as referências ao arquivo e assim por diante. Bata-se para fora! :)
Quando concluído, o arquivo é movido e o log está intacto. Você se sente como um pirata ninja.
Além disso; O diretório mkdir é necessário apenas se você mover o arquivo para uma nova pasta, é claro. O if evitará a criação desta pasta mais cedo no histórico do que o seu arquivo existe.
fonte
--index-filter
for rename será muito mais rápido, pois a árvore não precisará ser registrada e retornada a cada commit.--index-filter
atua diretamente em cada índice de confirmação.Não.
A resposta curta é NÃO . Não é possível renomear um arquivo no Git e lembrar o histórico. E é uma dor.
Há rumores de que
git log --follow
--find-copies-harder
funcionará, mas não funciona para mim, mesmo se houver zero alterações no conteúdo do arquivo e as alterações tiverem sido feitasgit mv
.(Inicialmente eu usei Eclipse para renomear e pacotes de atualização em uma única operação, o que pode ter confundido Git. Mas isso é uma coisa muito comum de fazer.
--follow
Parece funcionar se apenas umamv
é realizada e, em seguida, umcommit
emv
não é muito longe.)Linus diz que você deve entender todo o conteúdo de um projeto de software de forma holística, sem precisar rastrear arquivos individuais. Infelizmente, meu cérebro pequeno não pode fazer isso.
É realmente irritante que tantas pessoas tenham repetido sem pensar a afirmação de que o Git rastreia automaticamente os movimentos. Eles perderam meu tempo. Git não faz isso. Por design (!) O Git não controla movimentos.
Minha solução é renomear os arquivos de volta aos seus locais originais. Mude o software para caber no controle de origem. Com o Git, você parece ter que "acertar" da primeira vez.
Infelizmente, isso interrompe o Eclipse, que parece usar
--follow
.git log --follow
às vezes não mostra o histórico completo de arquivos com históricos de renomeação complicados, mesmo que ogit log
faça. (Eu não sei porque.)(Existem alguns hacks muito inteligentes que retrocedem e reeditam trabalhos antigos, mas são bastante assustadores. Consulte GitHub-Gist: emiller / git-mv-with-history .)
fonte
mostrará a história através de renomeações.
fonte
git mv
basicamente faz agit rm && git add
. Existem opções como-M90
/--find-renames=90
considerar um arquivo a ser renomeado quando ele é 90% idêntico.Eu faço:
fonte
-A
vez? Novamente, veja aqui: git-scm.com/docs/git-addgit add -u
. Os documentos do Git tendem a ser inúteis e são o último lugar que eu gostaria de procurar. Aqui está um post mostrandogit add -u
em ação: stackoverflow.com/a/2117202 .Não (oito anos depois, Git 2.19, terceiro trimestre de 2018), porque o Git detectará a renomeação do diretório , e isso agora está melhor documentado.
Consulte commit b00bf1c , commit 1634688 , commit 0661e49 , commit 4d34dff , commit 983f464 , commit c840e1a , commit 9929430 (27 jun 2018) e commit d4e8062 , commit 5dacd4a (25 jun 2018) por Elijah Newren (
newren
) .(Incorporado por Junio C Hamano -
gitster
- in commit 0ce5a69 , 24 de jul de 2018)Isso agora é explicado em
Documentation/technical/directory-rename-detection.txt
:Exemplo:
Mas são muitos outros casos, como:
Para simplificar a detecção de renomeação de diretório, essas regras são aplicadas pelo Git:
algumas regras básicas limitam quando a detecção de renomeação de diretório se aplica:
Você pode ver muitos testes
t/t6043-merge-rename-directories.sh
, que também apontam que:fonte
Objetivo
git am
Limitação
Sumário
git log --pretty=email -p --reverse --full-index --binary
cat extracted-history | git am --committer-date-is-author-date
1. Extrair histórico no formato de email
Exemplo: história Extrato de
file3
,file4
efile5
Definir / limpar o destino
Extrair histórico de cada arquivo no formato de email
Infelizmente opção
--follow
ou--find-copies-harder
não pode ser combinada com--reverse
. É por isso que o histórico é cortado quando o arquivo é renomeado (ou quando um diretório pai é renomeado).Histórico temporário em formato de email:
Dan Bonachea sugere inverter os loops do comando git log generation nesta primeira etapa: em vez de executar o git log uma vez por arquivo, execute-o exatamente uma vez com uma lista de arquivos na linha de comando e gere um único log unificado. Dessa maneira, as confirmações que modificam vários arquivos permanecem como uma única confirmação no resultado e todas as novas confirmações mantêm sua ordem relativa original. Observe que isso também requer alterações na segunda etapa abaixo ao reescrever nomes de arquivos no log (agora unificado).
2. Reorganize a árvore de arquivos e atualize os nomes de arquivos
Suponha que você queira mover esses três arquivos neste outro repositório (pode ser o mesmo repositório).
Portanto, reorganize seus arquivos:
Seu histórico temporário é agora:
Altere também nomes de arquivos no histórico:
3. Aplique novo histórico
Seu outro repo é:
Aplicar confirmações de arquivos de histórico temporários:
--committer-date-is-author-date
preserva os carimbos de data e hora de confirmação originais (comentário de Dan Bonachea ).Seu outro repo é agora:
Use
git status
para ver a quantidade de confirmações prontas para serem enviadas :-)Truque extra: verifique arquivos renomeados / movidos em seu repositório
Para listar os arquivos que foram renomeados:
Mais personalizações: você pode concluir o comando
git log
usando as opções--find-copies-harder
ou--reverse
. Você também pode remover as duas primeiras colunas usandocut -f3-
e cumprimentando o padrão completo '{. * =>. *}'.fonte
Eu segui esse processo de várias etapas para mover o código para o diretório pai e reter o histórico.
Etapa 0: criou um ramo 'histórico' a partir de 'mestre' para guarda
Etapa 1: use a ferramenta git-filter-repo para reescrever o histórico. Este comando abaixo moveu a pasta 'FolderwithContentOfInterest' para um nível acima e modificou o histórico de confirmação relevante
Etapa 2: nesse momento, o repositório GitHub perdeu o caminho do repositório remoto. Referência remota adicionada
Etapa 3: extrair informações no repositório
Etapa 4: conectar a ramificação perdida local à ramificação de origem
Etapa 5: conflito de mesclagem de endereços para a estrutura de pastas, se solicitado
Etapa 6: Empurre !!
Nota: O histórico modificado e a pasta movida parecem já estar confirmados.
enter code here
Feito. O código é movido para o diretório pai / desejado, mantendo o histórico intacto!
fonte
Embora o núcleo do Git, o encanamento do Git não registre as renomeações, o histórico que você exibe com o log de porcelana "Git" pode detectá-las, se quiser.
Para um determinado
git log
uso, a opção -M:Com uma versão atual do Git.
Isso funciona para outros comandos como
git diff
também.Existem opções para tornar as comparações mais ou menos rigorosas. Se você renomear um arquivo sem fazer alterações significativas no arquivo ao mesmo tempo, será mais fácil para o log e os amigos do Git detectar a renomeação. Por esse motivo, algumas pessoas renomeiam arquivos em um commit e os alteram em outro.
Há um custo no uso da CPU sempre que você pede ao Git para descobrir onde os arquivos foram renomeados; portanto, você deve usá-lo ou não, e quando, depende de você.
Se você deseja sempre ter seu histórico relatado com detecção de renomeação em um repositório específico, você pode usar:
Arquivos movendo-se de um diretório para outro são detectados. Aqui está um exemplo:
Observe que isso funciona sempre que você estiver usando diff, não apenas com
git log
. Por exemplo:Como avaliação, fiz uma pequena alteração em um arquivo em um ramo de recurso e o confirmei e, no ramo mestre, renomei o arquivo, confirmei e, em seguida, fiz uma pequena alteração em outra parte do arquivo e o confirmei. Quando fui para o recurso branch e mesclado a partir do master, a mesclagem renomeou o arquivo e mesclou as alterações. Aqui está a saída da mesclagem:
O resultado foi um diretório de trabalho com o arquivo renomeado e as duas alterações de texto feitas. Portanto, é possível que o Git faça a coisa certa, apesar de não rastrear explicitamente os renomeados.
Esta é uma resposta tardia a uma pergunta antiga, portanto as outras respostas podem estar corretas para a versão Git na época.
fonte
Primeiro, crie uma confirmação independente com apenas uma renomeação.
Em seguida, quaisquer alterações eventuais no conteúdo do arquivo serão colocadas na confirmação separada.
fonte
Para renomear um diretório ou arquivo (não sei muito sobre casos complexos, pode haver algumas ressalvas):
Para renomear um diretório nos arquivos que o mencionam (é possível usar retornos de chamada, mas não sei como):
expressions.txt
é um arquivo preenchido com linhas comoliteral:OLD_NAME==>NEW_NAME
(é possível usar o RE do Python comregex:
ou glob comglob:
).Para renomear um diretório em mensagens de confirmações:
As expressões regulares do Python também são suportadas, mas devem ser escritas em Python manualmente.
Se o repositório for original, sem controle remoto, você precisará adicionar
--force
para forçar uma reescrita. (Você pode criar um backup do seu repositório antes de fazer isso.)Se você não deseja preservar as referências (elas serão exibidas no histórico de ramificações da Git GUI), será necessário adicionar
--replace-refs delete-no-add
.fonte
Simplesmente mova o arquivo e prepare-o com:
Antes de confirmar, você pode verificar o status:
Isso mostrará:
Eu testei com o Git versão 2.26.1.
Extraído da página de ajuda do GitHub .
fonte
Eu faço mover os arquivos e depois faço
que coloca na área de saturação todos os arquivos excluídos / novos. Aqui o git percebe que o arquivo foi movido.
Não sei por que, mas isso funciona para mim.
fonte