Por que o Git diz que meu branch master está “já atualizado” embora não esteja?

118

Problema básico

Acabei de deletar TODO o código de um arquivo em meu projeto e enviar a alteração para meu git local (propositalmente). eu fiz

git pull upstream master

para buscar e mesclar do upstream (então, em teoria, o código excluído deveria estar de volta).

Git me diz que tudo está atualizado.

Definitivamente, tudo NÃO está atualizado - todo o código excluído ainda é excluído.

Outras Informações Relevantes

Eu só tenho um branch chamado "master".

Recentemente, configurei "master" para rastrear o upstream assim:

Branch master configurado para rastrear branch master remoto.

O comando git branch -vvproduz:

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

Por que por que isso está acontecendo? Estou prestes a enviar um e-mail ao meu gerente de projeto sobre as alterações que fizer em nosso código.

Atualizar

Achei que fosse óbvio, mas de qualquer forma este é o meu objetivo:

Obtenha o código mais recente em meu sistema.

Desculpe minha raiva aqui, mas por que uma tarefa tão simples como essa tem que ser tão difícil?

user1971506
fonte
1
É óbvio que se você deletar seu código-fonte, então tente puxar com um simples "pull" que o git não irá sobrescrever suas mudanças locais (neste caso, suas exclusões) sem garantir que você realmente deseja sobrescrever suas alterações locais. A primeira resposta parece explicar exatamente como você pode fazer isso.
CashCow

Respostas:

208

Acho que seu problema básico aqui é que você está interpretando mal e / ou entendendo mal o que o git faz e por que o faz.

Quando você clona algum outro repositório, o git faz uma cópia de tudo o que está "ali". Ele também pega "seus" rótulos de branch, como master, e faz uma cópia desse rótulo cujo "nome completo" em sua árvore git é (normalmente) remotes/origin/master(mas no seu caso, remotes/upstream/master). Na maioria das vezes, você também pode omitir a remotes/parte, então pode se referir a essa cópia original como upstream/master.

Se você agora fizer e enviar algumas alterações em algum (s) arquivo (s), você é o único com essas alterações. Enquanto isso, outras pessoas podem usar o repositório original (do qual você fez o seu clone) para fazer outros clones e alterar esses clones. Eles são os únicos com suas mudanças, é claro. Porém, eventualmente, alguém pode ter alterações que enviará de volta ao proprietário original (via "push" ou patches ou qualquer outro).

O git pullcomando é basicamente uma abreviação de git fetchseguido de git merge. Isso é importante porque significa que você precisa entender o que essas duas operações realmente fazem.

O git fetchcomando diz para voltar ao local de onde você clonou (ou configurou como um lugar de onde buscar) e encontrar "coisas novas que outra pessoa adicionou, alterou ou removeu". Essas alterações são copiadas e aplicadas à sua cópia do que você obteve antes . Eles não se aplicam ao seu próprio trabalho, apenas ao deles.

O git mergecomando é mais complicado e é onde você está errando. O que ele faz, simplificando um pouco, é comparar "o que você alterou em sua cópia" com "alterações que você obteve de outra pessoa e, portanto, foi adicionado à sua cópia do trabalho de outra pessoa". Se suas mudanças e suas mudanças não parecem entrar em conflito, a mergeoperação os junta e dá a você um "commit de mesclagem" que une seu desenvolvimento e o desenvolvimento deles (embora haja um caso "fácil" muito comum no qual você não tem muda e você obtém um "avanço rápido").

A situação que você está enfrentando agora é aquele em que você tiver feito alterações e os entregou-nove vezes, de fato, daí o "na frente por 9" -e eles fizeram mudanças. Portanto, fetchobedientemente não busca nada, e então mergepega a falta de mudanças e também não faz nada.

O que você quer é examinar, ou talvez até "redefinir" para a versão "deles" do código.

Se você deseja apenas dar uma olhada, pode simplesmente verificar essa versão:

git checkout upstream/master

Isso diz ao git que você deseja mover o diretório atual para o branch cujo nome completo é na verdade remotes/upstream/master. Você verá o código da última vez em que executou git fetche obteve o código mais recente.

Se você quiser abandonar todas as suas próprias mudanças, o que você precisa fazer é mudar a ideia do git de qual revisão seu rótulo,, masterdeve nomear. Atualmente, ele nomeia seu commit mais recente. Se você voltar para aquele galho:

git checkout master

então o git resetcomando permitirá que você "mova o rótulo", por assim dizer. O único problema restante (assumindo que você está realmente pronto para abandonar tudo o que você vestiu) é descobrir onde o rótulo deve apontar.

git logpermitirá que você encontre os nomes numéricos - coisas como 7cfcb29- que são nomes permanentes (nunca mudam), e há um número ridículo de outras maneiras de nomeá-los, mas neste caso você só quer o nome upstream/master.

Para mover o rótulo, acabando com suas próprias mudanças (qualquer que você cometeu são realmente recuperável por um bom tempo, mas é muito mais difícil após este assim que seja muito certo):

git reset --hard upstream/master

O --hardcomando diz ao git para apagar o que você tem feito, mover o rótulo do branch atual e então verificar o commit fornecido.

Não é muito comum querer realmentegit reset --hard e acabar com um monte de trabalho. Um método mais seguro (tornando muito mais fácil recuperar esse trabalho se você decidir que algo valeu a pena afinal) é renomear seu branch existente:

git branch -m master bunchofhacks

e então fazer um novo branch local chamado master"tracks" (eu realmente não gosto deste termo porque acho que confunde as pessoas, mas esse é o termo git :-)) o mestre de origem (ou upstream):

git branch -t master upstream/master

que você pode fazer com:

git checkout master

O que os três últimos comandos fazem (há atalhos para torná-los apenas dois comandos) é alterar o nome colado no rótulo existente, criar um novo rótulo e, em seguida, alternar para ele:

antes de fazer qualquer coisa:

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

depois git branch -m:

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

depois git branch -t master upstream/master:

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

Aqui C0está o último commit (uma árvore de código-fonte completa) que você obteve quando fez o seu git clone. C1 a C9 são seus commits.

Observe que se você fosse git checkout bunchofhackse então git reset --hard HEAD^^, isso mudaria a última imagem para:

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

A razão é que HEAD^^nomeia a revisão dois acima do cabeçalho do branch atual (o que seria um pouco antes da redefinição bunchofhacks) e, em reset --hardseguida, move o rótulo. Os commits C8 e C9 agora são quase invisíveis (você pode usar coisas como o reflog e git fsckencontrá-los, mas não é mais trivial). Seus rótulos são seus para mover como quiser. O fetchcomando cuida daqueles que começam com remotes/. É convencional combinar "seu" com "deles" (então, se eles tiverem um, remotes/origin/mauvevocê também deve nomear o seu mauve), mas você pode digitar "deles" sempre que quiser nomear / ver os commits que obteve "deles". (Lembre-se de que "um commit" é uma árvore de origem inteira. Você pode escolher um arquivo específico de um commit, com, git showpor exemplo,

Torek
fonte
12
Aprendi muito com sua excelente resposta. Mas ainda estou confuso sobre por que recebo a mensagem de status do git: "Seu branch está atualizado com 'origin / master'" quando o repo upstream está vários commits à frente do meu repo atual. Posso me atualizar com o git pull, mas como sei que preciso fazer o pull se a mensagem diz que estou atualizado?
Christopher Werby de
@ChristopherWerby: parece que você está executando git mergecomo um comando de linha de comando (que é algo que eu mesmo faço, é um método razoável). Observe, porém, que git pullcomeça primeiro executando git fetch, depois executando git merge(ou git rebasese você disser para fazer isso). É a etapa de busca que realmente traz os novos commits a serem mesclados (ou apenas com base no rebaseamento).
Torek de
@torek Existe algum comando, diferente de git status, que eu possa usar para ver se o repositório upstream está à frente do meu repositório atual? A mensagem que recebo git statusque diz "Seu branch está atualizado com 'origin / master'" me confunde pensando que tenho as alterações mais recentes, quando não tenho. Às vezes, recebo uma mensagem que diz algo como "origin / master is 5 commits before your branch" (parafraseando). Mas não é consistente.
Christopher Werby
1
Isso ainda não responde por que git statusdiz "Seu ramo está atualizado" quando um imediato git fetchpuxa mais de uma centena de objetos e resolve quase uma centena de deltas, menciona vários novos ramos e algumas novas tags e altera o principal (remoto tracking) branch head commit - e então outro status git alegremente, e insinuamente, ainda diz "Seu branch está atualizado". Claramente, muito veio da origem, mas o ramo estava "atualizado com a origem" antes e depois?
NeilG
1
git pullexecuta git fetchprimeiro, então git merge(ou outro segundo comando de sua escolha). A git fetchetapa atualiza sua memória do Git seu estatuto de Git, chamando-se a sua Git e recebendo nada de novo a partir deles. Você git statusapenas verifica a memória do Git do Git deles.
rasgou em
18

Eu tive o mesmo problema que você.

Eu fiz git status git fetch git pull, mas meu galho ainda estava atrasado na origem. Eu tinha pastas e arquivos enviados para o remoto e vi os arquivos na web, mas no meu local eles estavam faltando.

Finalmente, esses comandos atualizaram todos os arquivos e pastas no meu local:

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

ou se você quer uma filial

git checkout your_branch_name_here
git reset --hard origin/your_branch_name_here
Jturi
fonte
1
obrigado! isso realmente funcionou. Lembre-se de que os nomes dos ramos diferenciam maiúsculas de minúsculas!
André Ramon
4

Todas as alterações que você confirmar, como excluir todos os arquivos do seu projeto, ainda estarão em vigor após um pull. Tudo o que um pull faz é mesclar as mudanças mais recentes de algum outro lugar em seu próprio branch, e se seu branch excluiu tudo, então, na melhor das hipóteses, você obterá conflitos de mesclagem quando as alterações do upstream afetarem os arquivos que você excluiu. Então, em suma, sim, está tudo atualizado.

Se você descrever o resultado que gostaria de ter em vez de "todos os arquivos excluídos", talvez alguém possa sugerir um curso de ação apropriado.

Atualizar:

OBTER O MAIS RECENTE DO CÓDIGO NO MEU SISTEMA

O que você parece não entender é que já tem o código mais recente, que é seu. Se o que você realmente deseja é ver o trabalho mais recente de outra pessoa que está no branch master, basta fazer:

git fetch upstream
git checkout upstream/master

Observe que isso não o deixará em posição de (re) iniciar seu próprio trabalho imediatamente. Se você precisa saber como desfazer algo que você fez ou reverter as alterações que você ou outra pessoa fez, forneça os detalhes. Além disso, considere ler sobre para que serve o controle de versão, visto que você parece não entender seu propósito básico.

Ryan Stewart
fonte
Na verdade, quero a versão mais recente do código de outra pessoa em meu sistema. No entanto, esses dois comandos não fizeram isso. Tudo o que recebo é "já está no branch master". O código em meus arquivos não reflete o código da outra pessoa.
user1971506
Desculpe, deveria ter sido upstream/master. Atualizei minha resposta.
Ryan Stewart
3

Como dizem os outros participantes, puxe as alterações mescladas do upstream para o seu repositório. Se você deseja substituir o que está em seu repositório pelo que está no upstream, você tem várias opções. Imediatamente, eu iria com

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
Paulo
fonte
1

A resposta principal é muito melhor em termos de amplitude e profundidade das informações fornecidas, mas parece que se você quisesse que seu problema fosse resolvido quase imediatamente e não se importasse de seguir alguns dos princípios básicos do controle de versão, você poderia ...

  1. Mudar para mestre

    $ git checkout upstream master
    
  2. Exclua seu ramo indesejado. (Nota: deve ter o -D, ao invés do sinalizador -d normal porque seu branch está muitos commits à frente do master.)

    $ git branch -d <branch_name>
    
  3. Crie uma nova filial

    $ git checkout -b <new_branch_name>
    
BrotherDonkey
fonte
1

Embora nenhuma dessas respostas tenha funcionado para mim, consegui corrigir o problema usando o seguinte comando.

git fetch origin

Isso fez um truque para mim.

Doctiger
fonte
0

Apenas um lembrete amigável se você tiver arquivos localmente que não estão no github e ainda assim o seu git statusdiz

Sua filial está atualizada com 'origem / mestre'. nada para cometer, trabalhando de forma limpa

Isso pode acontecer se os arquivos estiverem em .gitignore

Tente correr

cat .gitignore 

e ver se esses arquivos aparecem lá. Isso explicaria porque o git não quer movê-los para o remoto.

Stevec
fonte