In effetti, esiste un modo semplice per introdurre un ramo non correlato in un repository?


329

Mentre aiutavo un amico con un problema git oggi, ho dovuto introdurre un ramo che doveva essere totalmente separato dal masterramo. I contenuti di questo ramo avevano davvero un'origine diversa da quella che era stata sviluppata sul masterramo, ma sarebbero stati fusi nel masterramo in un secondo momento.

Mi sono ricordato dalla lettura di Git di John Wiegley dal basso verso l'alto come i rami sono essenzialmente un'etichetta per un commit che segue una certa convenzione e come un commit è legato a un albero di file e, facoltativamente, ai commit dei genitori. Siamo andati a creare un commit senza genitori per il repository esistente usando l'idraulico di git:

Quindi ci siamo sbarazzati di tutti i file nell'indice ...

$ git rm -rf .

... estratti directory e file da un tarball, aggiunti quelli all'indice ...

$ git add .

... e creato un oggetto ad albero ...

$ git write-tree

( git-write-treeci ha detto lo sha1sum dell'oggetto albero creato.)

Quindi, abbiamo eseguito il commit dell'albero, senza specificare i commit dei genitori ...

$ echo "Imported project foo" | git commit-tree $TREE

( git-commit-treeci ha detto lo sha1sum dell'oggetto commit creato.)

... e creato un nuovo ramo che punta al nostro commit appena creato.

$ git update-ref refs/heads/other-branch $COMMIT

Alla fine, siamo tornati alla masterfiliale per continuare a lavorare lì.

$ git checkout -f master

Questo sembra aver funzionato come previsto. Ma questo non è chiaramente il tipo di procedura che consiglierei a qualcuno che sta appena iniziando a usare git, per dirla in parole povere. Esiste un modo più semplice per creare un nuovo ramo completamente estraneo a tutto ciò che è accaduto finora nel repository?

Risposte:


510

C'è una nuova funzionalità (dalla V1.7.2) che rende questo compito un po 'più alto di quello che c'è in una qualsiasi delle altre risposte.

git checkoutora supporta l' --orphanopzione. Dalla pagina man :

git checkout [-q] [-f] [-m] --orphan <new_branch> [<start_point>]

Crea un nuovo ramo orfano , chiamato <new_branch>, avviato da <start_point> e passa ad esso. Il primo commit fatto su questa nuova filiale non avrà genitori e sarà la radice di una nuova storia totalmente disconnessa da tutte le altre filiali e commit.

Questo non fa esattamente quello che voleva il richiedente, perché popola l'indice e l'albero di lavoro da <start_point>(poiché questo è, dopo tutto, un comando di checkout). L'unica altra azione necessaria è rimuovere eventuali elementi indesiderati dall'albero di lavoro e dall'indice. Sfortunatamente, git reset --hardnon funziona, ma git rm -rf .può essere usato invece (credo che questo sia equivalente a quello rm .git/index; git clean -fdxdato in altre risposte).


In sintesi:

git checkout --orphan newbranch
git rm -rf .
<do work>
git add your files
git commit -m 'Initial commit'

Ho lasciato <start_point>non specificato perché il valore predefinito è HEAD, e comunque non ci interessa davvero. Questa sequenza fa essenzialmente la stessa cosa della sequenza di comandi nella risposta di Artem , solo senza ricorrere a spaventosi comandi idraulici.


Sai se è possibile creare un ramo orfano che rimane visibile quando esegui il checkout di un ramo dall'altro "albero"?
JJD

1
@JJD: Penso che quello che vuoi sia git merge. ( git checkout master && git merge --no-commit "orphan-branch" ) Alcuni trucchi simili funzioneranno usando git-reset o giocando con l'indice. Ma dipende dal flusso di lavoro desiderato.
phord,

@Matthew Ecco il log delle modifiche per git 1.7.2 .
JJD,

@phord Ho pensato più o meno all'orfano di contenere elementi come unit test o documentazione separati dal codice sorgente del progetto.
JJD,

2
@JJD: La sceneggiatura che ti ho dato dovrebbe darti quello che vuoi, a meno che non ti abbia frainteso. Quando hai detto "resta visibile", vuoi dire che i file rimarranno nella directory di lavoro anche se hai estratto un ramo diverso? Usando --no-commiton git mergeraggiungerai questo obiettivo. Potrebbe essere necessario eseguire un follow-up in git reset origin/mastermodo che il tuo prossimo commit vada dove vuoi, ma poi i file dal tuo ramo orfano verranno visualizzati come "file non tracciati" a meno che tu non li includa anche nel tuo file .gitignore.
phord,

32

Dal libro della comunità Git :

git symbolic-ref HEAD refs/heads/newbranch 
rm .git/index 
git clean -fdx 
<do work> 
git add your files 
git commit -m 'Initial commit'

1
La prossima risposta è migliore per le versioni moderne di git.
kikito,

14
@kikito: Re: "La prossima risposta è migliore" ... L'ordinamento qui su SO non è stabile. Potresti aggiungere un link che punta a ciò che pensi sia una risposta migliore.
David J.

2
Mi riferivo a stackoverflow.com/a/4288660/312586 . Hai ragione, non avrei dovuto dire "prossimo". Aveva un punteggio inferiore a questa risposta quando ho commentato.
kikito,


Questo funziona meglio per il caso "Voglio impegnarmi nel ramo, creandolo se non esistesse prima"
max630

22

Sebbene la soluzione con git symbolic-refe la rimozione dell'indice funzioni, potrebbe essere concettualmente più pulito creare un nuovo repository

$ cd /path/to/unrelated
$ git init
[edit and add files]
$ git add .
$ git commit -m "Initial commit of unrelated"
[master (root-commit) 2a665f6] Initial commit of unrelated
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 foo

quindi recuperalo

$ cd /path/to/repo
$ git fetch /path/to/unrelated master:unrelated-branch
warning: no common commits
remote: Counting objects: 3, done.
Unpacking objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
From /path/to/unrelated
 * [new branch]      master     -> unrelated-branch

Ora puoi eliminare / percorso / a / non correlato


Secondo me, un concetto pulito implicherebbe un'opzione per git brancho git checkout. Sono contento che Git renda possibile questo genere di cose, ma perché non dovrebbe essere più facile?
hillu,

3
Perché lo è e dovrebbe essere una cosa rara. Se hai un ramo di cose non correlate, di solito appartiene a un repository non correlato, invece di riempirlo in uno esistente (sebbene ci siano eccezioni).
Jakub Narębski,

1
+1. Per i neofiti di git, puoi apparentemente elencare i rami git branche passare da uno all'altro git checkout BRANCH_NAME.
Akavel,

Grazie per questo, non ci avrei pensato da solo. Questo è molto utile perché mantiene la cronologia (se presente) per l'altro repository.
BM5k,

13

Github ha una funzione chiamata Pagine di progetto in cui è possibile creare un determinato ramo denominato nel progetto per fornire file che verranno serviti da Github. Le loro istruzioni sono le seguenti:

$ cd /path/to/fancypants
$ git symbolic-ref HEAD refs/heads/gh-pages
$ rm .git/index
$ git clean -fdx

Da lì hai un repository vuoto al quale puoi quindi aggiungere il tuo nuovo contenuto.


10

La risposta attualmente selezionata è corretta, aggiungerei solo per coincidenza ...

Questo è esattamente il modo in cui github.com consente agli utenti di creare pagine Github per i loro repository, tramite un ramo orfano chiamato gh-pages. I bei passaggi sono indicati e spiegati qui:

https://help.github.com/articles/creating-project-pages-manually

Fondamentalmente i comandi git per impostare questo sono i seguenti:

  1. git checkout --orphan gh-pages (crea un ramo senza genitori chiamato gh-pages sul tuo repository)
  2. git rm -rf . (rimuove qualsiasi file dall'albero di lavoro del ramo)
  3. rm '.gitignore' (anche il gitignore)
  4. Ora aggiungi il contenuto del sito web (aggiungi index.html, ecc.) E esegui il commit e il push.
  5. Profitto.

Nota che puoi anche designare una cartella / docs sul tuo repository come fonte del "Sito del progetto" che Github utilizza per costruire il sito web.

Spero che questo ti aiuti!


3

A volte voglio solo creare immediatamente un ramo vuoto nel progetto, quindi iniziare a fare il lavoro, mi limiterò a scuotere il seguente comando:

git checkout --orphan unrelated.branch.name
git rm --cached -r .
echo "init unrelated branch" > README.md
git add README.md
git commit -m "init unrelated branch"

1

Se i tuoi contenuti esistenti sono già git rebase -i --rootstati sottoposti a commit, ora (Git 2.18 Q2 2018) puoi estrarli nel suo nuovo ramo orfano, poiché l'implementazione di " " è stata aggiornata per utilizzare di più il macchinario del sequencer.

Quel sequencer è quello che ora consente di trapiantare l'intera topologia del grafico di commit altrove .

Vedi commit 8fa6eea , commit 9c85a1c , commit ebddf39 , commit 21d0764 , commit d87d48b , commit ba97aea (03 maggio 2018) di Johannes Schindelin ( dscho) .
(Unita da Junio ​​C Hamano - gitster- in commit c5aa4bc , 30 maggio 2018)

sequencer: consenti l'introduzione di nuovi commit root

Nel contesto della nuova --rebase-mergesmodalità, progettata appositamente per consentire di modificare liberamente la topologia di ramo esistente, un utente potrebbe voler estrarre i commit in un ramo completamente nuovo che inizia con un commit root appena creato .

Questo è ora possibile inserendo il comando reset [new root]prima di inserire il pickcommit che vuole diventare un commit root. Esempio:

reset [new root]
pick 012345 a commit that is about to become a root commit
pick 234567 this commit will have the previous one as parent

Ciò non è in conflitto con altri usi del resetcomando perché [new root]non è (parte di) un nome di riferimento valido: sia la parentesi di apertura che lo spazio sono illegali nei nomi di riferimento.


0

Ho trovato questo script su http://wingolog.org/archives/2008/10/14/merging-in-unrelated-git-branches e funziona benissimo!

#!/bin/bash

set -e

if test -z "$2" -o -n "$3"; then
    echo "usage: $0 REPO BRANCHNAME" >&2
    exit 1
fi

repo=$1
branch=$2

git fetch "$repo" "$branch"

head=$(git rev-parse HEAD)
fetched=$(git rev-parse FETCH_HEAD)
headref=$(git rev-parse --symbolic-full-name HEAD)

git checkout $fetched .

tree=$(git write-tree)

newhead=$(echo "merged in branch '$branch' from $repo" | git commit-tree $tree -p $head -p $fetched)
git update-ref $headref $newhead $head
git reset --hard $headref

1
Credo che si possa ottenere lo stesso effetto usando git fetch $REMOTE $REMOTE_BRANCH:$LOCAL_BRANCH. Mi sto perdendo qualcosa?
Hillu,

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.