Comprensione della differenza di diramazione tra SVN e Git


44

Sono un utente di SVN e ora sto imparando Git.

In SVN di solito eseguo il checkout sul mio computer locale un repository, che include tutti i rami del mio progetto e ho usato per selezionare la cartella per il mio ramo a cui sono interessato e ci lavoro.

Vedo una differenza usando Git.

Attualmente sto clonando un repository e clonare un ramo specifico usando gitk.

La cartella del progetto contiene solo il contenuto per quel ramo e non riesco a vedere tutti i rami come in SVN, il che è un po 'confuso per me.

Non riesco a trovare un modo semplice per vedere tutti i rami nel mio repository locale usando Git.

  • Vorrei sapere se il processo Git che ho descritto è "standard" e alcuni quanto è corretto o mi manca qualcosa.

  • Inoltre vorrei sapere come gestire un processo in cui ho bisogno di lavorare su due rami contemporaneamente nel caso, ad esempio, ho bisogno di fare un aggiornamento rapido sul master ma mantenere anche il contenuto di un altro ramo.

  • Quali sono le convenzioni sui nomi raccomandati per rendere ad esempio le cartelle che includono il ramo clonato dal repository in Git myproject-branchname?


8
Interessante: di solito con SVN verifichi solo il tronco o il ramo su cui stai lavorando, ma capisco cosa intendi.
HorusKol

20
Avevi interrotto il flusso di lavoro in SVN, ora provi a rispecchiarlo in Git (anche se un buon flusso di lavoro SVN è applicabile solo parzialmente per Git). La migliore soluzione - dimentica tutte le abitudini SVN e inizia da zero con Git
Lazy Badger

6
git cloneè più simile svnadmin hotcopyo svnrdump+ svnadmin loaddi quello che è come svn checkout. Con git, non richiede pezzi da repository; copiate l' intero repository e lavorate con esso localmente, rinviando le modifiche al repository "sorgente" quando e se ne avete voglia. Git e Subversion usano due modelli completamente diversi per il controllo del codice sorgente.
Chepner,

1
per quanto riguarda la correzione, prendere in considerazione l'usogit-flow
Bakuriu

5
@LazyBadger non è un flusso di lavoro interrotto se facilita lo sviluppo e fornisce valore. Non esiste un modo giusto per utilizzare i rami.
dietbuddha

Risposte:


67

Sono un utente di SVN e ora sto imparando GIT.

Benvenuti nella banda!

Rieducazione SVN

In SVN di solito [...]

Aspetta un momento. Mentre CVS e SVN e altri sistemi di controllo delle versioni tradizionali (cioè centralizzati) soddisfano (principalmente) lo stesso scopo dei moderni sistemi di controllo delle versioni (cioè distribuiti) come mercurial e Git, sarà molto meglio imparare Git da zero invece di nel tentativo di trasferire il flusso di lavoro SVN su Git.

http://hginit.com ( vista su archive.org ) di Joel Spolsky (uno dei fondatori di Stack Exchange) è un tutorial per mercurial, non Git, ma è il capitolo zero, "Subversion Re-education" è utile per le persone di commutazione da SVN per qualsiasi sistema di controllo di versione distribuito, come ci dice che cosa concetti SVN si deve (temporaneamente) un -Imparare (o per stashvia , in quanto gli utenti potrebbero dire Git) per essere in grado avvolgere la testa intorno la periferia concetti di sistema di controllo versione e flussi di lavoro idiomatici stabiliti per lavorare con essi.

Quindi puoi leggere quel capitolo zero e per lo più semplicemente sostituire la parola "mercuriale" con "Git" e quindi preparare correttamente te e la tua mente per Git.

La stampa fine

(Potresti saltare questo per ora.) Mentre mercurial e Git sono molto più simili tra loro che a SVN, ci sono alcune differenze concettuali tra loro, quindi alcune delle dichiarazioni e dei consigli in "Rieducazione Subversion" diventeranno tecnicamente sbagliate quando si sostituisce "mercurial" con "Git":

  • Mentre mercurial rintraccia e memorizza internamente i changeset, Git tiene traccia e memorizza internamente le revisioni (ovvero gli stati del contenuto di un albero di directory), proprio come fa Subversion. Ma a parte Subversion Git esegue le fusioni osservando le differenze tra ciascun ramo coinvolto rispettivamente e una revisione comune degli antenati (una vera unione a 3 punti), quindi il risultato è molto simile a quello del mercuriale: la fusione è molto più facile e meno errore -prone che in SVN.
  • Mentre puoi ramificare in Git clonando il repository (come è consuetudine in mercurial), è molto più comune creare un nuovo ramo all'interno di un repository Git. (Questo perché i rami Git sono semplicemente (spostando) i puntatori alle revisioni, mentre i rami mercuriali sono etichette permanenti applicate a ogni revisione. Queste etichette permanenti sono generalmente indesiderate, quindi i flussi di lavoro mercuriali di solito funzionano clonando il repository completo per uno sviluppo divergente.)

In SVN, tutto è una directory (ma non dovresti necessariamente trattarlo come tale)

Ma ti ho interrotto. Stavi dicendo?

In SVN di solito eseguo il checkout sul mio computer locale un repository, che include tutti i rami del mio progetto e ho usato per selezionare la cartella per il mio ramo a cui sono interessato e ci lavoro.

Se, con ciò, vuoi dire che hai estratto la cartella principale del repository SVN (o qualsiasi altra cartella corrispondente a più di trunko a un singolo ramo, ad esempio nel layout di repository SVN convenzionale la branchescartella contenente tutti i rami non trunk) Oserei dire che probabilmente hai usato SVN nel modo sbagliato (ish), o almeno abusato un po 'del fatto che trunk, rami e tag siano tutti piegati in un singolo (ma gerarchico) spazio dei nomi insieme alle directory all'interno delle revisioni / codebase.

Mentre potrebbe essere allettante cambiare diversi rami SVN in parallelo, questo non è il modo in cui AFAIK intende utilizzare SVN. (Anche se non sono sicuro di quali aspetti negativi specifici potrebbero funzionare).

In Git, solo le directory sono directory

Ogni clone di un repository Git è esso stesso un repository Git: per impostazione predefinita, ottiene una copia completa della cronologia del repository di origine, comprese tutte le revisioni, i rami e i tag. Ma manterrà tutto ciò che ti piace: Git lo memorizza in una specie di database basato su file, situato nella .git/sottocartella nascosta della cartella principale del repository .

Ma quali sono i file non nascosti che vedi nella cartella del repository?

Quando sei git cloneun repository, Git fa diverse cose. Circa:

  1. Crea la cartella di destinazione e, in essa, la .git/sottocartella con il "database"
  2. Trasferisce i riferimenti (tag, rami, ecc.) Del database del repository di origine e ne crea una copia nel nuovo database, dando loro un nome leggermente modificato che li contrassegna come riferimenti "remoti".
  3. Trasferisce tutte le revisioni (ovvero gli stati della struttura dei file) a cui fanno riferimento questi riferimenti, nonché tutte le revisioni che queste revisioni puntano direttamente o transitivamente (i loro genitori e antenati) al nuovo database e le memorizza, in modo che i nuovi riferimenti remoti effettivamente punta a qualcosa che è disponibile nel nuovo repository.
  4. Crea un ramo locale che tiene traccia della revisione remota corrispondente al ramo predefinito del repository di origine (in genere master).
  5. Verifica quella filiale locale.

L'ultimo passaggio indica che Git cerca la revisione a cui punta questo ramo e decomprime l'albero dei file memorizzato lì (il database utilizza alcuni mezzi di compressione e deduplicazione) nella cartella principale del repository. La cartella principale e tutte le sue sottocartelle (esclusa la .gitsottocartella speciale ) sono note come "copia di lavoro" del repository. È qui che interagisci con il contenuto della revisione / filiale attualmente estratta. Sono solo normali cartelle e file. Tuttavia, per interagire con il repository in sé (il "database" di revisioni e riferimenti) usi i gitcomandi.

Vedere i rami di Git e interagire con i rami di Git

Attualmente sto clonando un repository e clonare un ramo specifico usando gitk.

La versione che gitkho ottenuto non può clonare repository. Può solo visualizzare la cronologia dei repository e creare filiali e verificare filiali. Inoltre, non è possibile "clonare" un ramo. Puoi solo clonare repository.

Intendevi clonare un repository utilizzando git clone ...e quindi, utilizzando gitk, controlla un ramo?

La cartella del progetto contiene solo il contenuto per quel ramo e non riesco a vedere tutti i rami come in SVN, il che è un po 'confuso per me.

[...]

  • Vorrei sapere se il processo git che ho descritto è "standard" e alcuni quanto [...]

Sì, è abbastanza standard:

  • Clonare un repository utilizzando git clone ...
  • Scopri il ramo su cui vuoi lavorare git checkout ...o usando uno strumento grafico comegikt

Non riesco a trovare un modo semplice per vedere tutti i rami nel mio repository locale usando GIT.

  • [...] o mi manca smt.

Può essere:

  • puoi elencare le filiali locali con git branch
  • puoi elencare i rami remoti con git branch -ro tutti i rami congit branch -a
  • è possibile utilizzare gitkper visualizzare la cronologia completa (tutti i tag delle filiali, ecc. che il repository locale conosce) invocandola

    gitk --all
    

Come lavorare con più rami in parallelo

  • Inoltre vorrei sapere come gestire un processo in cui ho bisogno di lavorare su due rami contemporaneamente nel caso, ad esempio, ho bisogno di fare un aggiornamento rapido sul master ma mantenere anche il contenuto di un altro ramo.

Ci sono diversi scenari qui:

Una nuova modifica (ancora da creare) deve essere applicata a più filiali

Usa questo flusso di lavoro:

  1. Creare un nuovo ramo cda una revisione che è già nella stirpe di tutti questi rami (ad esempio la revisione che ha introdotto il bug quando la modifica sarà una correzione di bug) o da una revisione che (compresi tutti i suoi antenati) è accettabile per essere introdotta in tutti questi rami.
  2. Apporta e apporta la modifica su quel nuovo ramo c.
  3. Per ogni filiale bche ha bisogno del cambiamento:

    1. Dai un'occhiata b:

      git checkout b
      
    2. Unisci cin b:

      git merge c
      
  4. Rimuovi ramo c:

    git branch --delete c
    

È necessaria una modifica esistente su un ramo diverso

(... ma senza le altre modifiche apportate sul luogo in cui si trova la modifica)

  1. Controlla la filiale in cui è necessaria la modifica
  2. Seleziona la revisione o le revisioni per apportare la modifica, in ordine

Su un ramo a, si desidera modificare uno o alcuni file nello stato esatto che hanno su un ramo diversob

  1. Check-out a
  2. Ottieni il contenuto del file dalla filiale b:

    git checkout b -- path/to/a/file.txt path/to/another/file.cpp or/even/a/complete/directory/ ...
    

    (A parte git checkoutsenza passare i percorsi, questo non passerà al ramo b, ma otterrà solo il contenuto del file richiesto da lì. Questi file potrebbero o non potrebbero già esistere a. Se lo fanno, vengono sovrascritti con il loro contenuto attivo b.)

Mentre lavori su un ramo, vuoi vedere come stanno le cose su un altro ramo

Dai un'occhiata al ramo su cui vuoi lavorare.

Quindi, per guardare l'altro ramo,

  • utilizzare gli strumenti grafici che consentono di visualizzare i contenuti delle revisioni non attualmente verificate (ad esempio, nel gitktentativo di passare i pulsanti di opzione da "patch" a "albero")
  • o clonare il repository in una directory temporanea e controllare l'altro ramo lì
  • o utilizzare git worktreeper creare una directory di lavoro separata dello stesso repository (ovvero utilizzando anche il database nella .git/directory del repository locale corrente) in cui è possibile verificare l'altro ramo

Per l'ultimo flusso di lavoro, stashè l'ideale.
CAD97

@ CAD97 non se vuoi continuare a lavorare sul primo ramo, mentre guardi il secondo per riferimento in parallelo . git stashè utile spostare il lavoro incompiuto, ad esempio per cambiare ramo e poi tornare indietro.
das-g

1
"I flussi di lavoro mercuriali di solito funzionano clonando il repository completo per uno sviluppo divergente" - Eh, ne ho sentito parlare, ma la maggior parte delle persone che vogliono i rami Git usano solo i segnalibri invece di clonare l'intero repository. È molto più facile
Kevin,

@Kevin Potrebbe essere. È da qualche tempo che uso l'ultima volta mercurial, quindi forse era diverso allora. (O forse erano solo i flussi di lavoro della community con cui l'ho usato che erano così.)
das-g

Forse il testo di Hg Init "[...] perché in realtà avresti dovuto ramificare il modo Mercurial, clonando repository [...]" dovrebbe essere aggiornato anche a questo proposito.
das-g

31

In SVN di solito eseguo il checkout sul mio computer locale un repository, che include tutti i rami del mio progetto e ho usato per selezionare la cartella per il mio ramo a cui sono interessato e ci lavoro.

A meno che non si verifichi la directory sopra il trunk, in Subversion generalmente non si hanno tutti i rami disponibili localmente. In Git tutti i contenuti (commit, messaggi, rami ecc.) Vengono sempre clonati su ogni copia.

Attualmente sto clonando un repository e clonare un ramo specifico usando gitk.

Non possibile, vedi sopra. Puoi git checkout branch-name, ma non puoi git clone something branch-name. In Subversion, i rami sono cartelle. In Git, i rami sono puntatori a un commit.

Non riesco a trovare un modo semplice per vedere tutti i rami nel mio repository locale usando GIT.

Corri git branch. Per impostazione predefinita mostra solo le filiali locali. Utilizzare git branch --remoteper vedere i rami remoti.


4
Hm. "Per impostazione predefinita mostra solo le filiali locali." sembra che ci siano altri tipi di rami che non sono locali. Ma dici anche "In Git tutti i contenuti (commit, messaggi, rami ecc.) Vengono sempre clonati su ogni copia". . Lo trovo un po 'confuso, perché se tutti i rami sono clonati nella tua copia, quali sono quei misteriosi rami non locali? Forse è troppo complicato rispondere in un campo di commento.
pipe

2
@pipe Quando esegui il commit delle modifiche, esegui questa operazione creando un commit locale, che modifica il commit a cui punta il ramo. Se si desidera che altri ricevano queste modifiche, è necessario inviarle al repository remoto. Ciò provoca il ramo remoto che punta a questo nuovo commit. Ora tutti gli altri possono estrarre questi cambiamenti da lì e di conseguenza tutti alla fine otterrebbero una copia completa del repository
Frozn

9
@pipe Innanzitutto, puoi infatti clonare solo un ramo usando --single-branch(anche solo la punta con --depth 1). La differenza tra filiali locali e remote note a Git è semplicemente una specie di etichetta. I rami remoti sono preceduti dal nome della fonte remota (spesso origin). Le filiali locali non hanno quel prefisso. Ma alla fine, i dati sono disponibili localmente (a meno che tu non l'abbia fatto --single-brancho qualcosa di simile). Quando si esegue git checkout $branchnamecon un ramo per il quale non esiste un ramo locale ma remoto, git imposta automaticamente un ramo locale che "tiene traccia" del telecomando.
Jonas Schäfer,

"A meno che non si verifichi la directory sopra il trunk" - La mia esperienza è che questo è abbastanza comune, poiché rende l'unione molto più semplice. (Sebbene utilizziamo un singolo ramo "live" invece dei tag di versione, quindi di solito equivale a solo 2-3 copie del codice)
Izkata

1
@Izkata "rende la fusione molto più semplice", vero?
HorusKol

3

Dato che questo è etichettato con spero che la mia mancanza di conoscenza SVN sia trascurabile.

Attualmente sto clonando un repository e clonare un ramo specifico usando gitk.

Stai clonando l'intero repository remoto non solo un ramo specifico.

Il repository è meglio immaginato come un database, quindi stai facendo un clone dello stato corrente del database remoto. Ma dopo ciò, stai lavorando sulla tua copia di quel database; se si commette, si modifica il database locale.

Il fetchcomando viene utilizzato per mantenere sincronizzato il database locale con quello remoto.

Di solito da quel database locale, fai il checkout di un ramo su cui lavorare. Questo non è altro che un marker interno per git, dove è iniziato il tuo lavoro attuale.

Supponiamo che tu stia lavorando su un semplice repository, dove non ci sono rami oltre master, puoi dare un'occhiata alla .gitcartella per svelare la "magia":

Supponiamo che il tuo ultimo commit (attivo master) sia stato 182e8220b404437b9e43eb78149d31af79040c66, lo troverai esattamente sotto cat .git/refs/heads/master.

Da quel ramo di un nuovo ramo git checkout -b mybranch, troverai lo stesso puntatore nel file cat .git/refs/heads/mybranch.

I rami non sono altro che "puntatori". Il marcatore "funzionante" si chiama a HEAD.

Se vuoi sapere dove si HEADtrova:

cat .git/HEADche dice ad esempio ref: refs/heads/mybranch, che a sua volta indica ( cat .git/refs/heads/mybranch) un hash di commit78a8a6eb6f82eae21b156b68d553dd143c6d3e6f

I commit effettivi sono memorizzati nella objectscartella (il come è un argomento a sé stante).

La cartella del progetto contiene solo il contenuto per quel ramo e non riesco a vedere tutti i rami come in SVN, il che è un po 'confuso per me.

Non confondere il working directory"git-database" nel suo insieme. Come ho detto sopra, la tua directory di lavoro è solo un'istantanea di (forse) un sottoinsieme.

Supponi di avere rami diversi, la tua directory di lavoro è dedicata a lavorare solo su quel ramo (anche se potresti spostare il lavoro da lì altrove).

Di solito, se vuoi vedere quali rami sono definiti per il progetto, hai la possibilità di

  • git branch per le filiali locali
  • git branch --remote per filiali remote
  • git branch -a per entrambi

(o git branch -v)

Poiché git è un sistema di controllo della versione distribuita, non è solo possibile, ma incoraggiato, creare filiali diverse a livello locale / remoto.

Il mio flusso di lavoro tipico è:

  • dirama una funzione
  • diramare un ramo WIP (work in progress) da quello
  • funziona come preferisci, anche se ti impegni dopo una sola riga; non importa

Quando la funzionalità è completa:

  • schiaccia / rielabora il WIPramo (con rebasing interattivo) = esegui un singolo commit da quello
  • unisci il WIPramo nel ramo delle caratteristiche e offri che (se lavori con github quell'offerta sarebbe chiamata "richiesta pull") da integrare nel ramo stabile (master).

Inoltre vorrei sapere come gestire un processo in cui ho bisogno di wprl su due rami contemporaneamente nel caso, ad esempio, ho bisogno di fare un aggiornamento rapido sul master ma mantenere anche il contenuto di un altro ramo.

Dipende da come è strutturato il tuo progetto:

Di 'che hai un maestro stabile. E le funzionalità sono sviluppate solo da quel ramo stabile, quindi di solito è dietro un ramo delle caratteristiche. Quindi avresti un ultimo commit sul master che sarebbe la radice del ramo della funzione.

Quindi effettueresti un commit sul ramo master e potresti decidere se unire entrambi i rami o rebase (che è una specie di unione per utenti con esigenze avanzate, per così dire).

Oppure sei sempre in grado di apportare modifiche ai rami (ad esempio master) e inserirli negli altri rami.

Quali sono le convenzioni sui nomi consigliate per rendere le cartelle che includono il ramo clonato dal repository in GIT, ad esempio myproject-branchname

Spetta a voi.

In genere, si finisce con il nome del repository.

Ma ci sono occasioni in cui questo non è voluto:

ad es. clonare oh-my-zsh con git clone git://github.com/robbyrussell/oh-my-zsh.git ~/.oh-my-zsh Here .oh-my-zshviene esplicitamente chiamato come target.


2

Git clone in realtà clona l'intero repository, ma imposta la directory di lavoro sul valore predefinito (di solito master).

Puoi vedere altri rami usando git branchper vedere i rami locali o git branch -rper vedere quelli remoti e quindi git checkoutpassare a un ramo esistente o crearne uno nuovo in base al ramo corrente.

Dovresti leggere la documentazione di Git per maggiori dettagli e anche Atlassian ha alcuni buoni articoli.


0

I rami in git e svn sono cose fondamentalmente diverse.

In svn un ramo (o un tag) è una directory nel repository.

In git un ramo (o un tag) è un puntatore a un commit.

Con svn puoi se desideri controllare la radice del repository. Ciò significa che hai estratto tutti i rami e i tag contemporaneamente. Afaict questo non è il modo normale di usare svn.

Un ramo git non è una directory, quindi non è equivalente a "estrarre la radice del repository". C'è un po 'di supporto per più alberi di lavoro, quindi immagino che potresti mettere insieme uno script per controllare ogni ramo nello stesso momento, se davvero lo volessi, ma sarebbe una cosa piuttosto insolita pensarlo.

Inoltre con SVN esiste esattamente un repository. Con git ogni sviluppatore ha il proprio repository. Ciò significa che gli sviluppatori possono lavorare offline ma anche che sviluppatori diversi possono avere un'idea diversa di ciò che è presente in ciascun ramo.

In virtù dell'essere directory e in virtù del modello di storia lineare semplice di svn, i rami svn hanno solide storie. Se vuoi sapere cosa c'era nella filiale x alla data y puoi facilmente fare questa domanda.

I rami Git d'altra parte non hanno davvero storie. Esiste il "reflog", ma è inteso più come un meccanismo di ripristino di emergenza che una storia a lungo termine. In particolare, il reflog non può essere letto in remoto ed è disabilitato per impostazione predefinita per i repository nudi.

I commit ovviamente hanno delle storie, ma quelle storie non sono sufficienti per rispondere alla domanda "cosa c'era nel ramo x del deposito alla data z".

Non riesco a trovare un modo semplice per vedere tutti i rami nel mio repository locale usando Git.

Puoi elencare tutti i rami locali digitando "git branch".

Puoi elencare tutti i rami sia locali che remoti usando "git branch -a"

Inoltre vorrei sapere come gestire un processo in cui ho bisogno di lavorare su due rami contemporaneamente nel caso, ad esempio, ho bisogno di fare un aggiornamento rapido sul master ma mantenere anche il contenuto di un altro ramo.

Un paio di opzioni.

È possibile eseguire il commit delle modifiche sull'altro ramo, passare al master fare il proprio lavoro lì e quindi tornare indietro.

Puoi anche creare un albero di lavoro aggiuntivo. Google "git-worktree" per i dettagli sulla sintassi.

Quali sono le convenzioni sui nomi consigliate per rendere le cartelle che includono il ramo clonato dal repository in Git, ad esempio myproject-branchname?

Non penso che ci sia una convenzione stabilita. Far controllare più alberi di lavoro contemporaneamente è l'eccezione, non la regola.


2
questa risposta sembra incompleta, perché?
moscerino del
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.