Come mostrare DAVVERO i registri dei file rinominati con git?


132

Sono relativamente nuovo con Git, ho usato Subversion prima.

Ho notato che la maggior parte dei front-end grafici git e dei plug-in IDE non sembrano essere in grado di visualizzare la cronologia di un file se il file è stato rinominato. Quando uso

git log --follow

sulla riga di comando, posso vedere l'intero registro tra i nomi.

Secondo Linus Torvalds l'opzione --follow è un piacere "SVN noob", gli utenti git seri non lo usano:

--follow è un hack totale, pensato per soddisfare solo gli utenti ex-SVN che non hanno mai saputo nulla di cose come la genitorialità o dei bei grafici di revisione.

Non è del tutto fondamentale, ma l'attuale implementazione di "--follow" è in realtà una cosa di preelaborazione rapida collegata alla logica del camminare di revisione, piuttosto che essere qualcosa di veramente integrale.

È stato letteralmente progettato come un piacere "SVN noob", non come una cosa "funzionalità git reale". L'idea era che ti saresti allontanato dalla mentalità (spezzata) di pensare che rinominasse la materia nel quadro generale.

La mia domanda : in che modo gli utenti hardcore git tra di voi ottengono la cronologia di un file quando è stato rinominato? Qual è il modo "reale" per farlo?


17
@ David Hall: git mv oldfile newfilenon provoca la ridenominazione da registrare a tutti - è lo stesso che l'eliminazione di un file e aggiungendo un altro. git risolve solo i nomi e le copie dallo stato dell'albero ad ogni commit dopo il fatto.
Mark Longair,

20
@David Hall: se si rinomina il file con un altro strumento al di fuori di git (ad esempio /bin/mv oldfile newfile), ma poi si git add newfile; git rm oldfile, il risultato è indistinguibile da quello di git mv oldfile newfile.
Mark Longair,

1
Questa ideologia cade a pezzi se si sposta un file in un nuovo repository, nel qual caso l'incapacità di spostare la sua intera storia può essere un grosso problema. Anche se ovviamente ci sono limiti a quanta vera storia può davvero arrivare con un file in un progetto complesso.
Roman Starkov,

1
Nota: git log --followmigliora un po 'con git 2.9 (giugno 2016): vedi la mia risposta di seguito
VonC

1
A partire dalla v2.15, potresti voler sperimentare --color-movedquando diff.
Michael

Risposte:


71

Penso che l'unità generale dietro Linus Point sia che - e lo prenda con un pizzico di sale - gli utenti hardcore git non si preoccupano mai della storia di un "file". Metti il ​​contenuto in un repository git perché il contenuto nel suo insieme ha una storia significativa.

Una ridenominazione di file è un piccolo caso speciale di "contenuto" che si sposta tra i percorsi. Potresti avere una funzione che si sposta tra i file che un utente git potrebbe rintracciare con "piccone" funzionalmente (ad es log -S.).

Altre modifiche al "percorso" includono la combinazione e la divisione dei file; A git non importa davvero quale file consideri rinominato e quale consideri copiato (o rinominato ed eliminato) tiene traccia del contenuto completo dell'albero.

git incoraggia "l'intero albero" a pensare che molti sistemi di controllo delle versioni siano molto incentrati sui file. Questo è il motivo per cui git si riferisce ai "percorsi" più spesso di quanto faccia riferimento ai "nomi dei file".


Ciao Charles, grazie per la risposta. Sembra che io usi git allo stesso modo in cui ho usato SVN. Anche se capisco che git è molto diverso dagli altri sistemi di controllo di versione, molti concetti in git mi sembrano ancora strani ... Dovrei probabilmente finire quel libro di git che ho comprato di recente.
Mike,

24
Il punto di Linus era che una gui "corretta" sarebbe stata in grado di tracciare blocchi di codice attraverso file, e sperava che ormai avremmo avuto tali strumenti. Sfortunatamente, non abbiamo ancora quel lusso e --follow è ancora utile.
Michael Parker,

11
Git ti offre davvero una soluzione diversa da quella --follow?
Fa il

13
Direi che il pensiero dell '"albero intero" è migliorato --followessendo il default. Quello che voglio dire è quando voglio vedere la cronologia del codice all'interno di un file, di solito non mi interessa se il file è stato rinominato o meno, voglio solo vedere la cronologia del codice, indipendentemente dai nomi. Quindi secondo me ha senso --followessere il default perché non mi importa dei singoli file; --followmi aiuta a ignorare i nomi dei singoli file, che di solito sono piuttosto insignificanti.
Eddificato il

2
Quindi ... se decido di occuparmi del "contenuto" anziché del file, come faccio a stampare gli commit relativi al contenuto che è attualmente in questo file? Sarei felice se Git lo avesse rintracciato in diversi file per me e riportato un registro di tutte le modifiche - non vedo come ottenerlo.
Ed Avis,

36

Ho esattamente lo stesso problema che stai affrontando. Anche se non posso darti alcuna risposta, credo che tu possa leggere questa e-mail scritta da Linus nel 2005, è molto pertinente e potrebbe darti un suggerimento su come gestire il problema:

... Sto affermando che qualsiasi SCM che tenta di rintracciare i nomi è sostanzialmente rotto a meno che non lo faccia per motivi interni (cioè per consentire delta efficienti), proprio perché i nomi non contano. Non ti aiutano, e non sono quello che erano interessati a ogni modo .

Ciò che conta è trovare "da dove proviene", e l'architettura git lo fa davvero molto bene, molto meglio di qualsiasi altra cosa là fuori. ...

L'ho trovato referenziato da questo post sul blog, che potrebbe anche essere utile per trovare una soluzione praticabile:

Nel messaggio, Linus ha delineato come un sistema di tracciamento del contenuto ideale possa farti scoprire come un blocco di codice ha preso la forma attuale. Inizieresti dall'attuale blocco di codice in un file, tornerai indietro nella cronologia per trovare il commit che ha cambiato il file. Quindi controlli la modifica del commit per vedere se il blocco di codice che ti interessa è modificato da esso, poiché un commit che modifica il file potrebbe non toccare il blocco di codice che ti interessa, ma solo alcune altre parti del file.

Quando scopri che prima del commit il blocco di codice non esisteva nel file, controlli il commit più a fondo. Potresti scoprire che è una delle tante situazioni possibili, tra cui:

  1. Il commit ha veramente introdotto il blocco di codice. L'autore del commit è stato l'inventore di quella fantastica caratteristica per la quale cercavi la sua origine (o la parte colpevole che ha introdotto il bug); o
  2. Il blocco di codice non esisteva nel file, ma cinque copie identiche di esso esistevano in file diversi, tutti scomparsi dopo il commit. L'autore del commit ha duplicato il codice duplicato introducendo una singola funzione di supporto; o
  3. (come caso speciale) Prima del commit, il file che attualmente contiene il blocco del codice che ti interessa non esisteva, ma esisteva un altro file con contenuti quasi identici e il blocco del codice che ti interessa, insieme a tutti gli altri contenuti nel file esisteva allora, esisteva in quell'altro file. È andato via dopo il commit. L'autore del commit ha rinominato il file mentre gli dava una piccola modifica.

In sostanza, l'ultimo strumento di tracciamento dei contenuti di Linus non esiste ancora in modo completamente automatizzato. Ma la maggior parte degli ingredienti importanti sono già disponibili.

Per favore, tienici informati sui tuoi progressi su questo.


Grazie per aver pubblicato quegli articoli. Solo quando li ho letti ho compreso appieno l'idea della cronologia dei contenuti!. Ci ho pensato nel modo sbagliato!
DavidG

L'e-mail di Linus è fantastica, grazie per aver pubblicato questo.
mik01aj,

Fatto curioso--color-moved , Git v2.15 aggiunge una mossa verso quel "sistema di tracciamento ideale". Ci stavo giocando per vedere che tracciava le linee spostate all'interno di un file, ma mi sono reso conto accidentalmente che tiene traccia delle linee spostate nell'intero diff.
Michael

2
Linus spiega una situazione complessa. Ma qui abbiamo una situazione semplice: un file è stato appena rinominato (o spostato in un'altra directory). Quindi dovrebbe esserci una soluzione semplice. Penso che il problema derivi dal fatto che al contrario di Subversion, l'utente non può istruire Git al momento del commit da dove proviene un file, e ciò --followpuò essere sbagliato (ad es. Se 2 file hanno lo stesso contenuto o se c'è una modifica oltre allo spostamento del file).
vinc17,

13

Ho notato che la maggior parte dei front-end grafici e dei plugin IDE non sembrano essere in grado di visualizzare la cronologia di un file se il file è stato rinominato

Sarai felice di sapere che alcuni popolari strumenti dell'interfaccia utente di Git ora supportano questo. Ci sono dozzine di strumenti dell'interfaccia utente di Git disponibili, quindi non li elencherò tutti, ma per esempio:

  • SourceTree, quando si visualizza un registro file, ha una casella di controllo "Segui i file rinominati" in basso a sinistra
  • TortoiseGit ha una casella di controllo "segui i nomi" nella finestra del registro in basso a sinistra.

Ulteriori informazioni sugli strumenti dell'interfaccia utente di Git:


La fonte funziona alla grande quando si rinomina una volta, quando si rinomina due volte, i dettagli di modifica non sono disponibili per i commit prima di rinominare. Ho segnalato il bug qui: jira.atlassian.com/browse/SRCTREE-5715
Intel

gitk funziona benissimo anche quando un file viene rinominato due volte nella cronologia. Il comando si presenta così "gitk --segui percorso / to / file"
Intel

6

Nota: git 2.9 (giugno 2016) migliorerà un po 'la natura "buggy" di git log --follow:

Vedi commit ca4e3ca (30 mar 2016) di SZEDER Gábor ( szeder) .
(Unita da Junio ​​C Hamano - gitster- in commit 26effb8 , 13 apr 2016)

diffcore: corregge l'ordine di iterazione di file identici durante il rilevamento della ridenominazione

Se i due percorsi ' dir/A/file' e ' dir/B/file' hanno un contenuto identico e la directory principale viene rinominata, ad es. ' git mv dir other-dir', diffcoreSegnala i seguenti esatti nomi:

renamed:    dir/B/file -> other-dir/A/file
renamed:    dir/A/file -> other-dir/B/file

(notare l'inversione qui:, B/file -> A/filee A/file -> B/file)

Sebbene tecnicamente non sia sbagliato, questo non confonde solo per l'utente, ma anche per i comandi git che prendono decisioni basate su informazioni di rinomina, ad esempio ' git log --follow other-dir/A/file' segue ' dir/B/file' oltre la rinomina.

Questo comportamento è un effetto collaterale di commit v2.0.0-rc4 ~ 8 ^ 2 ~ 14 ( diffcore-rename.c: semplifica la ricerca di rinominazioni esatte, 14-11-2013): la hashmap che archivia le fonti restituisce voci dallo stesso bucket, ovvero fonti corrispondenti alla destinazione corrente , in ordine LIFO.
Pertanto, l'iterazione esamina prima " other-dir/A/file" e " dir/B/file" e, trovando contenuto e nome di base identici, riporta una ridenominazione esatta.


2

Su Linux, ho verificato che SmartGit e GitEye sono in grado di seguire le ridenominazioni seguendo la cronologia di un determinato file. Tuttavia, a differenza di gitk e GitEye, SmartGit mostra una vista file e una vista repository separate (che contiene la struttura della directory ma non l'elenco dei file contenuti all'interno)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.