Cambia ramo Git senza checkout di file


100

È possibile in Git passare a un altro ramo senza estrarre tutti i file?

Dopo aver cambiato ramo, devo eliminare tutti i file, rigenerarli, eseguire il commit e tornare indietro. Quindi estrarre i file è solo una perdita di tempo (e ci sono circa 14.000 file - è un'operazione lunga).

Per chiarire tutto:

Ho bisogno di tutto questo per caricare la documentazione su GitHub.

Ho un repository con il ramo gh-pages . Quando ricostruisco la documentazione localmente, la copio nella directory del repository, eseguo il commit e inserisco GitHub. Ma non ero contento, perché avevo due copie della documentazione a livello locale. E ho deciso di creare un ramo vuoto e dopo aver eseguito il commit, passare a svuotare ed eliminare i file. Ma tornare indietro è un'operazione lunga, quindi ho posto questa domanda.

So che posso semplicemente lasciare il ramo gh-pages ed eliminare i file, ma non mi piacciono gli alberi che lavorano sporchi.


Quanto tempo è "lungo" per te? Su quale piattaforma stai lavorando? Stai lavorando su una rete come con NFS o altra condivisione di file?
Greg Hewgill

Qual è lo scopo di questo esercizio? Vuoi avere due rami, uno con commit dettagliati, il secondo che registra solo i cambiamenti principali (grana grossa)?
Jakub Narębski

Forse è più economico creare un clone temporaneo (o permanente?) Della tua copia di lavoro. La mia risposta correlata e un articolo mostrano come funziona anche come sottodirectory del repository principale.
krlmlr

Risposte:


106

Si, puoi fare questo.

git symbolic-ref HEAD refs/heads/otherbranch

Se devi eseguire il commit su questo ramo, ti consigliamo di reimpostare anche l'indice, altrimenti finirai per eseguire il commit di qualcosa basato sull'ultimo ramo estratto.

git reset

1
Utilizzare echo "ebff34ffb665de0694872dceabe0edeaf50ec5a9" > .git/HEADseguito da git resetper indicare un riferimento invece di un ramo.
cadorn

1
Scrivere direttamente nel file HEAD è meno affidabile. E se ti trovi in ​​una sottodirectory? Per la testa staccata (testa che punta direttamente a SHA1), prova questo: git update-ref HEAD refs/heads/otherbranch
Alexander Bird

2
Se desideri effettuare il check-out in un nuovo ramo dal ramo corrente, un altro modo per farlo è quello di 1. git stash2. git checkout -b otherBranch3.git stash pop
Winny

@AlexanderBird: git update-refè utile, ma sposta anche la punta del ramo corrente.
tomekwi

47

Utilizzando solo i comandi git di base:

Questa risposta è un po 'più lunga di quella di Charles, ma consiste esclusivamente in comandi git di base che posso capire e quindi ricordare, eliminando la necessità di continuare a cercarla.

Contrassegna la tua posizione corrente (conferma prima se necessario):

git checkout -b temp

Ripristina (sposta) il marker sull'altro ramo senza modificare la directory di lavoro:

git reset <branch where you want to go>

ora temp e altri branch puntano allo stesso commit e la directory di lavoro non viene modificata.

git checkout <branch where you want to go>

poiché la tua HEAD sta già puntando allo stesso commit, la directory di lavoro non viene toccata

git branch -d temp

Notare che questi comandi sono anche prontamente disponibili da qualsiasi client grafico.


7
Preferirei git reset --soft <branch where you want to go>evitare di aggiornare l'indice
JoelFan

7
Concordo con la tua strategia di evitare i comandi idraulici git e privilegiare quelli di porcellana.
user64141

26

Nella v2.24 git switchè qualcosa come una cassaforte git checkout.
Quindi ho rinominato l'alias di seguito per git hopper
"hop sul ramo senza cambiare worktree"

A beneficio del lettore:

Anche se penso che la soluzione di Charles Bailey sia corretta, questa soluzione necessita di una modifica quando si passa a qualcosa, che non è una filiale locale. Inoltre dovrebbe esserci un modo per farlo con comandi regolari che è facile da capire. Ecco cosa mi è venuto in mente:

git checkout --detach
git reset --soft commitish
git checkout commitish

Spiegato:

  • git checkout --detachè lo stesso git checkout HEAD^{}che abbandona l'attuale ramo e va in "stato di testa distaccato". Quindi la prossima modifica di HEADno more influisce su qualsiasi ramo. Il HEADdistacco non influisce sulla struttura di lavoro né sull'indice.
  • git reset --soft commitishquindi passa HEADallo SHA del dato commitish. Se vuoi aggiornare anche l'indice, esci --soft, ma non ti consiglio di farlo. Questo, ancora una volta, non tocca l'albero di lavoro e ( --soft) non l'indice.
  • git checkout commitishquindi si attacca HEADdi nuovo al dato commitish(ramo). (Se commitishè un SHA non succede nulla.) Anche questo non influisce sull'indice né sull'albero di lavoro.

Questa soluzione accetta tutto ciò che fa riferimento a un commit, quindi è l'ideale per alcuni gitalias. Il rev-parseseguente è solo un test per assicurarsi che nulla si interrompa nella catena, in modo tale che gli errori di battitura non passino accidentalmente allo stato di testa staccata (il recupero degli errori sarebbe molto più complesso).

Questo porta al seguente git hop treeishalias:

git config --global alias.hop '!f() { git rev-parse --verify "$*" && git checkout "HEAD^{}" && git reset --soft "$*" && git checkout "$*"; }; f'

Cordiali saluti, puoi trovarlo nel mio elenco di gitalias .


Non vuoi usare $@piuttosto che $*? La differenza è che $ @ senza argomenti tra virgolette espanse, che hanno spazi all'interno.
kyb

1
@kyb Il trucco della funzione è stato rubato da un'altra risposta SO . E non $@è definitivamente inteso qui. $*viene utilizzato al posto di $1, in modo tale che git switch -f bdiventi lo stesso git switch '-f b'che dovrebbe essere un errore. In questo modo posso abbreviare l'alias tralasciando alcuni errori di gestione come!f() { [ 1 = $# ] || { echo 'WTF!'; return 1; }; ..
Tino

Ottima soluzione. Soprattutto che può essere utilizzato per filiali remote!
Nils-o-mat il

14

Non sarebbe una soluzione migliore avere due directory di lavoro (due aree di lavoro) con un repository o anche due repository?

C'è lo strumento git-new-workdir nella contrib/sezione per aiutarti con questo.


Git-new-workdir è lo stesso del comando worktree di git? Uso worktree quando voglio estrarre un ramo in una cartella diversa (senza dover clonare l'intero repository).
Ryuu

Lo git-new-worktreescript precede il git worktreesottocomando; questo comando non era disponibile quando la risposta è stata scritta. Lo script, ad esempio, richiede il supporto del collegamento simbolico; IMHO è meglio usare il supporto nativo.
Jakub Narębski

8

Penso che tu stia cercando il comando idraulico git read-tree. Ciò aggiornerà l'indice ma non aggiornerà alcun file nella directory di lavoro. Ad esempio, supponendo che branchsia il nome del ramo da leggere:

git read-tree branch

Se vuoi poi impegnarti nel ramo che hai appena letto, dovrai anche:

git simbolico-ref HEAD refs / heads / branch

no, ho solo bisogno di cambiare ramo, nessun altro cambiamento - quindi il riferimento simbolico è abbastanza buono
tig

read-treegenera l'errore: fatal: Not a valid object name branchse non ce git switch branch
n'era


0

Con così tanti file, potrebbe essere meglio tenere solo due repository, uno per ogni ramo. Puoi spostare le modifiche avanti e indietro secondo necessità. Questo sarà meno sorprendente che provare a giocare brutti scherzi con git.


Puoi usare git-new-worktreeper quello invece (in contrib/)
Jakub Narębski

Spesso ho fatto qualcosa di simile, che è copiare la mia directory locale prima di fare qualsiasi "roba git spaventosa" come un principiante (come cambiare rami, ecc.). Incoraggio le persone a seguire quella strada finché non ti senti sicuro del tuo git-fu, ma ad allontanarti da esso quando possibile. Mantenere due repository distinti va bene, ma aggiunge uno strato di complicazioni e non ti consente di sfruttare le molte utili funzionalità di git (fusione, selezione, ecc.).
David

0

Se stai semplicemente cercando di cambiare dove punta un ramo remoto, puoi farlo con "git push" senza toccare la tua copia locale.

http://kernel.org/pub/software/scm/git/docs/git-push.html

Il formato di un parametro <refspec> è un segno più + opzionale, seguito dal riferimento di origine <src>, seguito da due punti:, seguito dal riferimento di destinazione <dst>. Viene utilizzato per specificare con quale oggetto <src> deve essere aggiornato il ref <dst> nel repository remoto.

ad esempio, per aggiornare foo per eseguire il commit di c5f7eba, procedi come segue:

git push origin c5f7eba:foo

Non sono sicuro se è quello che stavi cercando o no.


La domanda ha già ottenuto una risposta: stackoverflow.com/questions/1282639/…
tig

0

puoi usare

      1. git checkout -f <new-branch>
      2. git cherry-pick -x <previous-branch-commit-id>

previous-branch-commit-id è il commit da cui vuoi copiare i vecchi dati.


0

Oppure usa semplicemente un file di patch per applicare la patch dal tuo altro ramo al tuo master

git diff otherbranch master > ~/tmp/otherbranch.diff
git checkout master
git apply ~/tmp/otherbranch.diff

-1

diciamo che vuoi essere nel ramo A, ma con i file del ramo B.

trova il rif commit corrente del ramo A con git log, ad esempio "99ce9a2",

git checkout A
git reset --hard B
git reset 99ce9a2

ora dovresti trovarti sul ramo A, con una struttura di cartelle corrispondente a B, che vengono visualizzate come modifiche non organizzate (la cronologia A non è cambiata).

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.