Git in realtà non rinomina. Sono tutti calcolati in modo "after the fact": git confronta un commit con un altro e, in corrispondenza del tempo di confronto, decide se è stato rinominato. Ciò significa che se git considera qualcosa "un rinominare" cambia dinamicamente. So che mi stai chiedendo di un impegno che non hai ancora fatto, ma abbi pazienza con me, questo in realtà è tutto in pareggio (ma la risposta sarà lunga).
quando si chiede git (via git show
o git log -p
o git diff HEAD^ HEAD
) "ciò che è accaduto nel corso dell'ultimo commit", viene eseguito un diff del precedente commit (HEAD^
o HEAD~1
o l'attuale grezzo SHA-1 per il precedente commit-any di questi farà per identificarlo) e il commit corrente (HEAD
). Nel fare questo diff può scoprire che c'era un old.txt
e non c'è più; e non c'era lo new.txt
ma ora c'è.
Questi nomi di file, i file che prima erano lì, ma non lo sono, e i file che sono lì ora che non sono stati messi nella pila contrassegnati come "candidati per la rinomina". Quindi, per ogni nome nella pila, git confronta "vecchi contenuti" e "nuovi contenuti". Il confronto per la corrispondenza esatta è semplicissimo a causa del modo in cui git riduce i contenuti in SHA-1; se la corrispondenza esatta fallisce, git passa a un diff alternativo "sono i contenuti almeno simili" per verificare la presenza di rinomina. Con git diff
questo passaggio facoltativo è controllato dal flag -M
. Con altri comandi è impostato dai valori git config
o codificato nel comando.
Ora, torna all'area di staging e git status
: ciò che git memorizza nell'indice/area di staging è fondamentalmente "un prototipo per il prossimo commit". Quando si esegue il comando git add
, git memorizza il contenuto del file proprio in quel punto, calcolando l'SHA-1 nel processo e quindi memorizzando l'SHA-1 nell'indice. Quando fai qualcosa su git rm
, git memorizza una nota nell'indice dicendo "questo nome di percorso viene deliberatamente rimosso al prossimo commit".
Il comando git status
, quindi, fa semplicemente un diff o realmente due differ: HEAD
vs indice, per ciò che sta per essere commesso; e indice vs albero di lavoro, per quale cosa potrebbe essere essere (ma non è ancora) sarà impegnato.
In quella prima diff, git utilizza lo stesso meccanismo di sempre per rilevare i nomi. Se c'è un percorso nel commit HEAD
inserito nell'indice e un percorso nell'indice nuovo e non nel commit HEAD
, è un candidato per il rilevamento dei nomi. Il comando git status
consente di rinominare il rilevamento su "on" (e il limite di conteggio dei file su 200; con un solo candidato per il rilevamento dei nomi questo limite è sufficiente).
Cosa significa tutto questo per il tuo caso? Bene, hai rinominato un file (senza utilizzare git mv
, ma non importa perché git status
trova il nome o non riesce a trovarlo, al tempo git status
) e ora ha una versione nuova e diversa del nuovo file.
Se si è git add
la nuova versione, quella versione più recente va nel repository, e il suo SHA-1 è nell'indice, e quando git status
fa un confronto confronterà il nuovo e il vecchio. Se sono almeno "simili al 50%" (il valore cablato per git status
), git ti dirà che il file è stato rinominato.
Naturalmente, git add
-ing i modificati contenuto non è del tutto quello che hai chiesto: si voleva fare commettere un intermedio in cui il file è solo rinominato, vale a dire, un commit con un albero con il nuovo nome , ma i vecchi contenuti.
Non è possibile eseguire a causa di tutti i suddetti rilevamenti di ridenominazione dinamici. Se si vuole farlo (per qualsiasi motivo) ... beh, git non rende tutto così facile.
Il modo più diretto è proprio come lei suggerisce: spostare i contenuti modificati da qualche parte fuori del modo, utilizzare git checkout -- old-name.txt
, quindi git mv old-name.txt new-name.txt
, quindi eseguire il commit. Lo git mv
rinominerà entrambi il file nell'indice/area di staging e rinominerà la versione dell'albero di lavoro.
Se git mv
avuto un'opzione --cached
come git rm
fa, si può solo git mv --cached old-name.txt new-name.txt
e poi git commit
. Il primo passo rinominerebbe il file nell'indice, senza toccare l'albero del lavoro. Ma non è così: insiste a sovrascrivere la versione dell'albero di lavoro, e insiste sul fatto che il vecchio nome deve esistere nell'albero di lavoro per iniziare.
Il metodo a passo singolo per eseguire questa operazione senza toccare l'albero di lavoro è utilizzare git update-index --index-info
, ma anche questo è un po 'disordinato (lo mostrerò comunque tra un momento). Fortunatamente, c'è un'ultima cosa che possiamo fare.Ho installato la stessa situazione si ha, rinominando il vecchio nome a quello nuovo e la modifica del file:
$ git status
On branch master
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
deleted: old-name.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
new-name.txt
Quello che facciamo ora è, primo luogo, mettere manualmente il file di nuovo sotto il suo vecchio nome , quindi utilizzare git mv
per passare di nuovo per il nuovo nome:
$ mv new-name.txt old-name.txt
$ git mv old-name.txt new-name.txt
Questa volta git mv
aggiorna il nome nell'indice, ma mantiene i contenuti originali come l'indice di SHA-1, ma si muove il lavoro- versione albero (nuovi contenuti) in posizione nel lavoro-albero:
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
renamed: old-name.txt -> new-name.txt
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: new-name.txt
Ora basta git commit
per fare un commit con la ridenominazione in atto, ma non i nuovi contenuti.
(Si noti che questo dipende non ci sia un nuovo file con il vecchio nome!)
Cosa succede ad usare git update-index
? Beh, prima cerchiamo di riportare le cose alla "cambiato nel lavoro-albero, indice corrisponde TESTA commettere" stato:
$ git reset --mixed HEAD # set index=HEAD, leave work-tree alone
Ora vediamo cosa c'è nella indice per old-name.txt
:
$ git ls-files --stage -- old-name.txt
100644 2b27f2df63a3419da26984b5f7bafa29bdf5b3e3 0 old-name.txt
Allora, che cosa abbiamo bisogno git update-index --index-info
fare è quello di spazzare via la voce per old-name.txt
ma fare una voce altrimenti identico per new-name.txt
:
$ (git ls-files --stage -- old-name.txt;
git ls-files --stage -- old-name.txt) |
sed -e \
'1s/^[0-9]* [0-9a-f]*/000000 0000000000000000000000000000000000000000/' \
-e '2s/old-name.txt$/new-name.txt/' |
git update-index --index-info
(nota: ho rotto t lui sopra per scopi di pubblicazione, era tutto una riga quando l'ho digitato; in sh/bash, dovrebbe funzionare interrotto in questo modo, dati i backslash che ho aggiunto per continuare il comando "sed").
Ci sono altri modi per farlo, ma semplicemente estraendo la voce di indice due volte e modificando la prima in una rimozione e la seconda con il nuovo nome sembrava la più semplice qui, da qui il comando sed
. La prima sostituzione cambia la modalità file (100644 ma qualsiasi modalità dovrebbe essere convertita in zeri) e SHA-1 (corrisponde a qualsiasi SHA-1, sostituisce con gli speciali zeri tutti di zit SHA-1), e la seconda lascia la modalità e SHA-1 da solo durante la sostituzione del nome.
Al termine dell'indice di aggiornamento, l'indice ha registrato la rimozione del vecchio percorso e l'aggiunta del nuovo percorso (con la stessa modalità e SHA-1 presenti nel vecchio percorso).
Si noti che questo potrebbe non funzionare correttamente se l'indice contiene voci non raggruppate per old-name.txt
poiché potrebbero esserci altre fasi (da 1 a 3) per il file.
Provato 'git mv'? – vaultah
'git mv' da solo non funzionerà se il file originale è già stato eliminato, o il percorso di destinazione esiste. Devi fare il processo di salvataggio/ripristino che hai delineato ... durante il quale, puoi usare 'git mv' invece di' mv + git add'. Dato che git non stava eseguendo il monitoraggio di 'new-name.txt' quando hai apportato delle modifiche ad esso, così com'è-non può aiutare a separare queste modifiche. – hinerm