Come creare un nuovo (e vuoto!) Ramo "root"?


135

Vorrei definire un nuovo ramo "root" in questo repository git. Per ramo "radice" intendo un ramo che è completamente indipendente da tutti gli altri rami nel repository 1 .

Sfortunatamente, anche il commit (chiamiamolo così A) alla base dell'albero di commit del repository contiene molti file (questo era un repository inizializzato su un progetto già abbastanza maturo).

Ciò significa che anche se avessi dato Acome nuovo ramo <start-point>, questo nuovo ramo non sarebbe partito da una "lavagna pulita", ma piuttosto conterrebbe tutti i file in cui è stato eseguito il commit A.

Esiste un modo per creare un ramo completamente nudo in questo repository, con il <start-point>più vicino Apossibile?


1 A proposito, questo non equivale alla creazione di un nuovo repository. Repos separati sarebbero meno convenienti per molte ragioni.


EDIT : OK, questo è quello che ho fatto, in base alla risposta di vcsjones :

# save rev of the current earliest commit
OLDBASE=$(git rev-list --max-parents=0 HEAD)

# create a new orphan branch and switch to it
git checkout --orphan newbranch
# make sure it's empty
git rm -rf .

# create a new empty commit in the new branch, and
# save its rev in NEWBASE
git commit --allow-empty -m 'base commit (empty)'
NEWBASE=$(git rev-list HEAD)

# specify $NEWBASE as the new parent for $OLDBASE, and
# run filter-branch on the original branch
echo "$OLDBASE $NEWBASE" > .git/info/grafts
git checkout master
git filter-branch

# NOTE: this assumes that the original repo had only one
# branch; if not, a git-filter-branch -f <branch> command
# need to be run for each additional branch.

rm .git/info/grafts

Sebbene questa procedura sia un po 'complicata, il risultato finale è un commit di base vuoto che può fungere da <start-point>per ogni nuovo "ramo clean-slate"; tutto ciò che dovrei fare allora è

git checkout -b cleanslate $(git rev-list --max-parents=0 HEAD)

In futuro creerò sempre nuovi repository come questo:

git init
git commit --allow-empty -m 'base commit (empty)'

... in modo che il primo commit sia vuoto e sempre disponibile per l'avvio di una nuova filiale indipendente. (Questo sarebbe, lo so, una struttura molto raramente necessaria, ma è abbastanza facile renderlo facilmente disponibile.)



1
@Anonymoose: solo per la cronaca (piuttosto che per il dibattito): non sono d'accordo con la tua opinione.
lunedì

1
Sembra che ci sia una soluzione molto più semplice sulla base git rebase --onto, vedere stackoverflow.com/questions/645450/...
Stéphane Gourichon

Risposte:


223

Utilizzare il --orphandurante la creazione del ramo:

git checkout --orphan YourBranchName

Questo creerà un nuovo ramo con zero commit su di esso, tuttavia tutti i tuoi file saranno messi in scena. A quel punto potresti semplicemente rimuoverli.
("rimuovili": A git reset --hardsvuota l'indice, lasciandoti con un albero di lavoro vuoto)

Dai un'occhiata alla pagina man per il checkout per maggiori informazioni su --orphan.


Questo crea essenzialmente una lavagna vuota? Come da zero? O mantiene tutte le tue modifiche precedenti, ma non nella cronologia git?
Con Antonakos,

4
@ConAntonakos Questo crea un nuovo ramo senza un genitore. Le altre filiali non sono interessate, tutte le loro modifiche vengono mantenute.
fuz,

11

Per aggiungere alla risposta accettata, la migliore pratica per ripristinare lo stato di pulizia consiste nel creare un commit vuoto iniziale in modo da poter facilmente rifare le basi durante l'impostazione dei rami per i posteri. Inoltre, poiché si desidera uno stato pulito, è probabile che siano presenti file di cui non è necessario eseguire il commit, quindi è necessario rimuoverli dall'indice. Con questi in mente dovresti:

$ git checkout --orphan dev2
Switched to a new branch 'dev2'
$ git reset # unstage all the files, you probably don't want to commit all of them
$ git commit --allow-empty -m 'Initial empty commit'
[dev2 (root-commit) a515c28] Initial empty commit

1
Ritengo che ciò non sia necessario a partire dal 2012 perché è possibile rifare su root .
Timmmm,


1

C'è un modo per creare un ramo completamente nudo in questo repository.

Il comando effettivo da usare (con Git 2.28+) è:

git switch --discard-changes --orphan newBranch
# or
git switch -f --orphan newBranch

git switchè disponibile da Git 2.23 (agosto 2019) e sostituisce il vecchio git checkoutcomando confuso .

Ma consiglierei Git 2.28 (Q3 2020), perché git switch --discard-changes --orphanè stato ottimizzato in quella versione.

Vedi commit 8d3e33d , commit 8186128 (21 maggio 2020) di brian m. Carlson ( bk2204) .
(Unita da Junio ​​C Hamano - gitster- in commit ded44af , 09 giu 2020)

builtin/checkout: semplifica l'inizializzazione dei metadati

Firmato-fuori-da: brian m. Carlson
Recensito-by: Derrick Stolee

Quando chiamiamo init_checkout_metadatain reset_tree,vogliamo passare l'ID oggetto del commit in questione in modo che possa essere passato a filtri, o se non v'è alcun commettere, l'albero.

Abbiamo anticipato quest'ultimo caso, che può verificarsi altrove nel codice di pagamento, ma non può verificarsi qui.

L'unico caso in cui non abbiamo un oggetto commit è quando si richiama git switchcon --orphan.

Inoltre, possiamo solo colpire questo percorso di codice senza un oggetto commit in aggiunta con uno --forceo --discard-changes.

In tal caso, non ha senso inizializzare i metadati di checkout con commit o albero perché

  • (a) non c'è commit, solo l'albero vuoto, e
  • (b) non useremo mai i dati, dal momento che nessun file verrà macchiato quando si verifica un ramo senza file.

Passa l'ID oggetto di tutti zeri in questo caso, poiché abbiamo solo bisogno di un valore che sia un puntatore valido.

Test:

git switch master
echo foo >foo.txt
git switch --discard-changes --orphan new-orphan2
git ls-files >tracked-files
# test_must_be_empty tracked-files
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.