Ci sono 2 passaggi per raggiungere questo obiettivo:
- Crea un nuovo commit vuoto
- Riscrivi la cronologia per iniziare da questo commit vuoto
Metteremo il nuovo commit vuoto su un ramo temporaneo newroot
per comodità.
1. Creare un nuovo commit vuoto
Esistono diversi modi per farlo.
Usando solo l'impianto idraulico
L'approccio più pulito è utilizzare l'idraulico di Git per creare direttamente un commit, che evita di toccare la copia di lavoro o l'indice o quale ramo è stato estratto, ecc.
Creare un oggetto ad albero per una directory vuota:
tree=`git hash-object -wt tree --stdin < /dev/null`
Avvolgi un commit attorno ad esso:
commit=`git commit-tree -m 'root commit' $tree`
Crea un riferimento ad esso:
git branch newroot $commit
Ovviamente puoi riorganizzare l'intera procedura in una sola riga se conosci bene il tuo guscio.
Senza impianto idraulico
Con i normali comandi di porcellana, non è possibile creare un commit vuoto senza estrarre il newroot
ramo e aggiornare ripetutamente l'indice e la copia di lavoro, senza una buona ragione. Ma alcuni potrebbero trovare questo più facile da capire:
git checkout --orphan newroot
git rm -rf .
git clean -fd
git commit --allow-empty -m 'root commit'
Nota che nelle versioni molto vecchie di Git che non hanno il --orphan
passaggio a checkout
, devi sostituire la prima riga con questa:
git symbolic-ref HEAD refs/heads/newroot
2. Riscrivi la cronologia per iniziare da questo commit vuoto
Hai due opzioni qui: rebasing o una riscrittura della cronologia pulita.
ribasamento
git rebase --onto newroot --root master
Questo ha la virtù della semplicità. Tuttavia, aggiornerà anche il nome e la data del committer su ogni ultimo commit sul ramo.
Inoltre, con alcune case history limite, potrebbe persino fallire a causa della fusione di conflitti, nonostante il fatto che si stia rifacendo su un commit che non contiene nulla.
Riscrittura della storia
L'approccio più pulito è quello di riscrivere il ramo. A differenza di git rebase
, dovrai cercare da quale commit inizia la tua filiale:
git replace <currentroot> --graft newroot
git filter-branch master
La riscrittura avviene nel secondo passaggio, ovviamente; è il primo passo che necessita di spiegazioni. Quello che git replace
fa è dire a Git che ogni volta che vede un riferimento a un oggetto che si desidera sostituire, Git dovrebbe invece guardare alla sostituzione di quell'oggetto.
Con l' --graft
interruttore, stai dicendo qualcosa di leggermente diverso dal normale. Stai dicendo che non hai ancora un oggetto sostitutivo, ma vuoi sostituire l' <currentroot>
oggetto di commit con una copia esatta di se stesso, tranne per il fatto che i commit principali della sostituzione dovrebbero essere quelli che hai elencato (cioè il newroot
commit ). Quindi git replace
procede e crea questo commit per te, quindi dichiara quel commit come sostituto del commit originale.
Ora se fai un git log
, vedrai che le cose sembrano già come le vuoi tu: il ramo inizia da newroot
.
Tuttavia, tieni presente che in git replace
realtà non modifica la cronologia , né si propaga dal tuo repository. Aggiunge semplicemente un reindirizzamento locale al repository da un oggetto a un altro. Ciò significa che nessun altro vede l'effetto di questa sostituzione - solo tu.
Ecco perché il filter-branch
passaggio è necessario. Con git replace
te crei una copia esatta con i commit parent corretti per il commit root; git filter-branch
quindi ripete questo processo anche per tutti i seguenti commit. È qui che la storia viene effettivamente riscritta in modo da poterla condividere.