Perché Git dice che il mio ramo principale è "già aggiornato" anche se non lo è?


118

Problema di base

Ho appena cancellato TUTTO il codice da un file nel mio progetto e ho applicato la modifica al mio git locale (apposta). L'ho fatto

git pull upstream master

per recuperare e unire dall'upstream (quindi in teoria il codice cancellato dovrebbe essere ripristinato).

Git mi dice che è tutto aggiornato.

Sicuramente NON è tutto aggiornato: tutto il codice eliminato viene comunque eliminato.

Altre informazioni rilevanti

Ho solo un ramo chiamato "master".

Di recente ho impostato "master" per monitorare a monte in questo modo:

Branch master impostato per monitorare il branch master remoto dall'upstream.

Il comando git branch -vvproduce:

* master 7cfcb29 [upstream/master: ahead 9] deletion test

Perché perché accade questo? Sono sul punto di inviare un'e-mail al mio project manager qualsiasi modifica apporto al nostro codice.

Aggiornare

Pensavo fosse ovvio, ma comunque questo è il mio obiettivo:

Ottieni il codice più recente sul mio sistema.

Scusa la mia rabbia qui, ma perché un compito così semplice deve essere così difficile?


1
È ovvio che se elimini i tuoi sorgenti quindi prova a tirare con un semplice "pull", git non sovrascriverà le tue modifiche locali (in questo caso le tue eliminazioni) senza assicurarti di voler davvero sovrascrivere le tue modifiche locali. La prima risposta sembra spiegare esattamente come puoi procedere.
CashCow

Risposte:


209

Penso che il tuo problema di base qui sia che stai interpretando male e / o fraintendendo cosa fa git e perché lo fa.

Quando cloni qualche altro repository, git crea una copia di tutto ciò che è "laggiù". Prende anche le "loro" etichette di ramo, come master, e crea una copia di quell'etichetta il cui "nome completo" nel tuo albero git è (normalmente) remotes/origin/master(ma nel tuo caso, remotes/upstream/master). La maggior parte delle volte ometti anche la remotes/parte, quindi puoi fare riferimento a quella copia originale come upstream/master.

Se ora apporti e effettui alcune modifiche su alcuni file, sei l'unico con quelle modifiche. Nel frattempo altre persone potrebbero utilizzare il repository originale (da cui hai creato il tuo clone) per creare altri cloni e modificare quei cloni. Sono gli unici con i loro cambiamenti, ovviamente. Alla fine, però, qualcuno potrebbe avere modifiche che rimandano al proprietario originale (tramite "push" o patch o altro).

Il git pullcomando è per lo più solo una scorciatoia per git fetchseguito da git merge. Questo è importante perché significa che devi capire cosa fanno effettivamente queste due operazioni.

Il git fetchcomando dice di tornare al punto da cui hai clonato (o altrimenti impostato come luogo da cui recuperare) e trovare "nuove cose che qualcun altro ha aggiunto, modificato o rimosso". Queste modifiche vengono copiate e applicate alla tua copia di ciò che hai ottenuto da esse in precedenza . Essi sono non applicate per il proprio lavoro, solo per loro.

Il git mergecomando è più complicato ed è dove stai andando storto. Quello che fa, semplificato un po ', è confrontare "ciò che hai cambiato nella tua copia" con "le modifiche che hai preso da qualcun altro e che quindi sei stato aggiunto alla tua-copia-del-lavoro-di-qualcun altro". Se le tue modifiche e le loro modifiche non sembrano essere in conflitto, l' mergeoperazione le mescola insieme e ti dà un "merge commit" che lega il tuo sviluppo e il loro sviluppo insieme (anche se c'è un caso "facile" molto comune in cui non hai modifiche e si ottiene un "avanzamento veloce").

La situazione si sta incontrando ora è quello in cui sono state apportate modifiche e li-nove volte impegnata, infatti, da qui il "davanti 9" -e hanno fatto alcun cambiamento. Quindi, fetchdiligentemente non recupera nulla, quindi mergeprende la loro mancanza di modifiche e inoltre non fa nulla.

Quello che vuoi è guardare, o forse anche "reimpostare", la "loro" versione del codice.

Se vuoi semplicemente guardarlo, puoi semplicemente controllare quella versione:

git checkout upstream/master

Questo dice a git che vuoi spostare la directory corrente nel ramo il cui nome completo è effettivamente remotes/upstream/master. Vedrai il loro codice dell'ultima volta che hai eseguito git fetche hai ricevuto il loro ultimo codice.

Se vuoi abbandonare tutte le tue modifiche, quello che devi fare è cambiare l'idea di git di quale revisione la tua etichetta,, masterdovrebbe nominare. Attualmente nomina il tuo commit più recente. Se torni su quel ramo:

git checkout master

quindi il git resetcomando ti permetterà di "spostare l'etichetta", per così dire. L'unico problema rimanente (supponendo che tu sia davvero pronto ad abbandonare tutto ciò che indossi) è trovare dove dovrebbe puntare l'etichetta.

git logti consentirà di trovare i nomi numerici, quelle cose come, 7cfcb29che sono nomi permanenti (non cambiano mai) e ci sono un numero ridicolo di altri modi per nominarli, ma in questo caso vuoi solo il nome upstream/master.

Per spostare l'etichetta, cancellando le tue modifiche (quelle che hai commesso sono effettivamente recuperabili per un po 'di tempo, ma è molto più difficile dopo questo, quindi sii molto sicuro):

git reset --hard upstream/master

Il messaggio --harddice a git di cancellare quello che stavi facendo, spostare l'etichetta del ramo corrente e quindi controllare il commit dato.

Non è super comune volerlo davverogit reset --hard e spazzare via un sacco di lavoro. Un metodo più sicuro (rendendo molto più facile recuperare quel lavoro se decidi che ne è valsa la pena, dopotutto) è rinominare il tuo ramo esistente:

git branch -m master bunchofhacks

e poi crea un nuovo ramo locale chiamato master"tracce" (non mi piace molto questo termine perché penso che confonda le persone ma questo è il termine git :-)) il master di origine (o upstream):

git branch -t master upstream/master

con cui puoi quindi continuare con:

git checkout master

Quello che fanno gli ultimi tre comandi (ci sono scorciatoie per renderlo solo due comandi) è cambiare il nome incollato sull'etichetta esistente, quindi creare una nuova etichetta, quindi passare ad essa:

prima di fare qualsiasi cosa:

C0 -    "remotes/upstream/master"
    \
     \- C1 --- C2 --- C3 --- C4 --- C5 --- C6 --- C7 --- C8 --- C9    "master"

dopo git branch -m:

C0 -    "remotes/upstream/master"
    \
     \- C1 --- C2 --- C3 --- C4 --- C5 --- C6 --- C7 --- C8 --- C9    "bunchofhacks"

dopo git branch -t master upstream/master:

C0 -    "remotes/upstream/master", "master"
    \
     \- C1 --- C2 --- C3 --- C4 --- C5 --- C6 --- C7 --- C8 --- C9    "bunchofhacks"

Ecco C0l'ultimo commit (un albero dei sorgenti completo) che hai ottenuto la prima volta che hai eseguito il tuo file git clone. Da C1 a C9 sono i tuoi commit.

Nota che se lo facessi git checkout bunchofhackse poi git reset --hard HEAD^^, questo cambierebbe l'ultima immagine in:

C0 -    "remotes/upstream/master", "master"
    \
     \- C1 --- C2 --- C3 --- C4 --- C5 --- C6 --- C7 -    "bunchofhacks"
                                                      \
                                                       \- C8 --- C9

Il motivo è che HEAD^^nomina la revisione due in alto dall'intestazione del ramo corrente (che sarebbe appena prima del ripristino bunchofhacks), reset --hardquindi sposta l'etichetta. I commit C8 e C9 ora sono per lo più invisibili (puoi usare cose come il reflog e git fscktrovarli ma non è più banale). Le tue etichette possono essere spostate come preferisci. Il fetchcomando si prende cura di quelli che iniziano con remotes/. È convenzionale abbinare "tuo" con "loro" (quindi se hanno un remotes/origin/mauvenome mauveanche tuo ), ma puoi digitare "loro" ogni volta che vuoi nominare / vedere i commit che hai ricevuto "da loro". (Ricorda che "un commit" è un intero albero sorgente. Puoi scegliere un file specifico da un commit, git showad esempio con


12
Ho imparato molto dalla tua eccellente risposta. Ma sono ancora confuso sul motivo per cui ricevo il messaggio di stato git: "Il tuo ramo è aggiornato con 'origine / master'" quando il repository upstream è diversi commit prima del mio repository corrente. Posso aggiornarmi con git pull, ma come faccio a sapere che devo eseguire il pull se il messaggio dice che sono aggiornato?
Christopher Werby

@ChristopherWerby: sembra che tu stia eseguendo git mergecome un comando da riga di comando (che è qualcosa che faccio io, è un metodo ragionevole). Nota, tuttavia, che git pullinizia prima in esecuzione git fetch, quindi in esecuzione git merge(o git rebasese gli dici di farlo invece). È il passaggio di recupero che porta effettivamente i nuovi commit da unire (o ribasare solo).
torek

@torek C'è qualche comando diverso da git statusquello che posso usare per vedere se il repository upstream è in anticipo rispetto al mio repository attuale? Il messaggio che ricevo con git statusche dice "Il tuo ramo è aggiornato con 'origine / master'" mi confonde facendomi pensare di avere le ultime modifiche, quando non lo faccio. A volte, ricevo un messaggio che dice qualcosa come "origine / master è 5 commit prima del tuo ramo" (parafrasando). Ma non è coerente.
Christopher Werby

1
Questo ancora in realtà non risponde al motivo per cui git statusdice "Il tuo ramo è aggiornato" quando un immediato git fetchtira giù più di cento oggetti e risolve quasi cento delta, menziona diversi nuovi rami e un paio di nuovi tag e cambia il principale (remoto tracking) branch head commit - e poi un altro stato git allegramente, e in malafede, dice ancora "Il tuo ramo è aggiornato". Chiaramente molto è disceso dall'origine ma il ramo era "aggiornato con l'origine" sia prima che dopo?
NeilG

1
git pullviene eseguito per git fetchprimo, quindi git merge(o un altro secondo comando a scelta). Il git fetchpasso aggiorna la vostra memoria del Git di loro lo status di Git, richiamando la loro Git e ottenere qualcosa di nuovo da loro. Il tuo git statusunico controlla la memoria del tuo Git del loro Git.
torek

18

Ho avuto il tuo stesso problema.

L'ho fatto git status git fetch git pull, ma il mio ramo era ancora indietro rispetto all'origine. Avevo cartelle e file inviati in remoto e ho visto i file sul web, ma sul mio locale mancavano.

Infine, questi comandi hanno aggiornato tutti i file e le cartelle sul mio locale:

git fetch --all
git reset --hard origin/master 

o se vuoi una filiale

git checkout your_branch_name_here
git reset --hard origin/your_branch_name_here

1
Grazie! questo ha effettivamente funzionato. Tieni presente che i nomi dei rami fanno distinzione tra maiuscole e minuscole!
André Ramon

4

Qualsiasi modifica che effettui, come l'eliminazione di tutti i file di progetto, sarà ancora in vigore dopo un pull. Tutto ciò che fa è unire le ultime modifiche da qualche altra parte nel tuo ramo, e se il tuo ramo ha cancellato tutto, nella migliore delle ipotesi otterrai conflitti di unione quando le modifiche a monte interessano i file che hai eliminato. Quindi, in breve, sì, tutto è aggiornato.

Se descrivi quale risultato vorresti ottenere invece di "eliminare tutti i file", forse qualcuno può suggerire una linea di condotta appropriata.

Aggiornare:

OTTIENI IL CODICE PIÙ RECENTE SUL MIO SISTEMA

Quello che sembra non capire è che hai già il codice più recente, che è il tuo. Se quello che vuoi veramente è vedere il lavoro più recente di qualcun altro che si trova nel ramo master, fai:

git fetch upstream
git checkout upstream/master

Nota che questo non ti lascerà nella posizione di (ri) iniziare immediatamente il tuo lavoro. Se hai bisogno di sapere come annullare qualcosa che hai fatto o annullare in altro modo le modifiche che tu o qualcun altro avete apportato, fornisci i dettagli. Inoltre, considera di leggere a cosa serve il controllo di versione, poiché sembra che tu non capisca il suo scopo di base.


In effetti desidero la versione più recente del codice di qualcun altro sul mio sistema. Tuttavia, quei due comandi non lo hanno fatto. Tutto quello che ottengo è "già in branch master". Il codice nei miei file non riflette il codice dell'altra persona.
user1971506

Spiacente, avrebbe dovuto essere upstream/master. Ho aggiornato la mia risposta.
Ryan Stewart

3

Come dicono gli altri poster, il pull unisce le modifiche dall'upstream al tuo repository. Se vuoi sostituire ciò che è nel tuo repository con ciò che è in upstream, hai diverse opzioni. A braccio, ci andrei

git checkout HEAD^1  # Get off your repo's master.. doesn't matter where you go, so just go back one commit
git branch -d master  # Delete your repo's master branch
git checkout -t upstream/master  # Check out upstream's master into a local tracking branch of the same name

1

La risposta migliore è molto migliore in termini di ampiezza e profondità delle informazioni fornite, ma sembra che se volessi risolvere il tuo problema quasi immediatamente e non ti preoccupare di calpestare alcuni dei principi di base del controllo della versione, potresti ...

  1. Passa al master

    $ git checkout upstream master
    
  2. Elimina il tuo ramo indesiderato. (Nota: deve avere il -D, invece del normale flag -d perché il tuo ramo è molti commit prima del master.)

    $ git branch -d <branch_name>
    
  3. Crea un nuovo ramo

    $ git checkout -b <new_branch_name>
    

1

Sebbene nessuna di queste risposte abbia funzionato per me, sono stato in grado di risolvere il problema utilizzando il seguente comando.

git fetch origin

Questo ha fatto un trucco per me.


0

Solo un promemoria amichevole se hai file localmente che non sono in GitHub e tuttavia il tuo git statusdice

Il tuo ramo è aggiornato con "origin / master". niente da commettere, albero di lavoro pulito

Può succedere se i file sono in formato .gitignore

Prova a correre

cat .gitignore 

e vedere se questi file vengono visualizzati lì. Questo spiegherebbe perché git non vuole spostarli sul telecomando.

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.