Para expandir a resposta de Ben Jackson , o que é bom, vamos examinar a pergunta original de perto. (Veja sua resposta para perguntas do tipo por que incomodar ; isso é mais sobre o que está acontecendo .)
Sou novo no controle de versão e entendo que "confirmar" é essencialmente criar um backup enquanto atualiza a nova versão 'atual' do que você está trabalhando.
Isso não está certo. Backups e controle de versão estão certamente relacionados - exatamente quão fortemente depende de algumas coisas que são, até certo ponto, questões de opinião - mas certamente existem algumas diferenças, mesmo que apenas na intenção: Backups são normalmente projetados para recuperação de desastres (falha de máquina, destruição de fogo edifício inteiro, incluindo todos os meios de armazenamento, etc.). O controle de versão é normalmente projetado para interações mais refinadas e oferece recursos que os backups não oferecem. Normalmente, os backups são armazenados por algum tempo e, em seguida, descartados como "muito antigos": um backup mais recente é tudo o que importa. O controle de versão normalmente salva cada versão confirmada para sempre.
O que eu não entendo é para que serve a encenação de uma perspectiva prática. Encenar é algo que existe apenas no nome ou tem um propósito? Quando você se compromete, vai comprometer tudo de qualquer maneira, certo?
Sim e não. O design do Git aqui é um tanto peculiar. Existem sistemas de controle de versão que não requerem uma etapa de teste separada. Por exemplo, o Mercurial, que é muito parecido com o Git em termos de uso, não requer uma hg addetapa separada , além da primeira que apresenta um arquivo totalmente novo. Com o Mercurial, você usa o hgcomando que seleciona alguns commit, então você faz seu trabalho, então você executa hg commite pronto. Com o Git, você usa git checkout, 1 então você faz seu trabalho, então você executa git add, e então git commit. Por que a git addetapa extra ?
O segredo aqui é o que o Git chama, de forma variada, de índice ou área de teste ou, às vezes - raramente hoje em dia - de cache . Todos esses são nomes para a mesma coisa.
Edit: Eu acho que posso estar confundindo a terminologia. Um arquivo 'testado' é a mesma coisa que um arquivo 'rastreado'?
Não, mas eles estão relacionados. Um arquivo rastreado é aquele que existe no índice do Git. Para entender o índice corretamente, é bom começar entendendo os commits.
Desde Git versão 2.23, você pode usar em git switchvez de git checkout. Para este caso específico, esses dois comandos fazem exatamente a mesma coisa. O novo comando existe porque git checkoutficou sobrecarregado com muitas coisas; eles foram divididos em dois comandos separados git switche git restore, para tornar mais fácil e seguro o uso do Git.
Compromissos
No Git, um commit salva um instantâneo completo de todos os arquivos que o Git conhece. (Quais arquivos o Git conhece? Veremos isso na próxima seção.) Esses instantâneos são armazenados em um formato especial, somente leitura, somente Git, compactado e desduplicado, que em geral apenas o próprio Git pode ler . (Há mais coisas em cada commit do que apenas este instantâneo, mas isso é tudo que cobriremos aqui.)
A eliminação da duplicação ajuda com espaço: normalmente mudamos apenas alguns arquivos e, em seguida, fazemos um novo commit. Portanto, a maioria dos arquivos em um commit são basicamente os mesmos que os arquivos no commit anterior. Simplesmente reutilizando esses arquivos diretamente, o Git economiza muito espaço: se tocarmos em apenas um arquivo, o novo commit ocupará apenas espaço para uma nova cópia. Mesmo assim, ele é compactado - às vezes muito compactado, embora isso realmente aconteça mais tarde - de forma que um .gitdiretório pode realmente ser menor do que os arquivos que contém, uma vez que são expandidos para arquivos normais do dia a dia. A eliminação da duplicação é segura porque os arquivos confirmados ficam congelados para sempre. Ninguém pode mudar um, então é seguro que os commits dependam das cópias uns dos outros.
Como os arquivos armazenados estão neste formato especial, congelado para sempre, somente Git, o Git precisa expandir cada arquivo em uma cópia comum do dia a dia. Esta cópia comum não é a cópia do Git : é a sua cópia, para fazer o que quiser. O Git apenas escreverá para eles quando você disser para fazer isso, para que você tenha suas cópias para trabalhar. Essas cópias utilizáveis estão em sua árvore de trabalho ou árvore de trabalho .
O que isso significa é que quando você verifica algum commit em particular, há automaticamente duas cópias de cada arquivo:
Git tem uma cópia congelada para sempre, Git-ified no commit atual . Você não pode mudar esta cópia (embora você possa, é claro, selecionar um commit diferente, ou fazer um novo commit).
Você tem, em sua árvore de trabalho, uma cópia em formato normal. Você pode fazer o que quiser, usando qualquer um dos comandos do seu computador.
Outros sistemas de controle de versão (incluindo Mercurial como mencionado acima) param aqui, com essas duas cópias. Você apenas modifica sua cópia da árvore de trabalho e depois efetua o commit. Git ... não.
O índice
Entre essas duas cópias, o Git armazena uma terceira cópia 2 de cada arquivo. Esta terceira cópia está no formato congelado , mas ao contrário da cópia congelada no commit, você pode alterá-la. Para mudar, você usa git add.
O git addcomando significa fazer com que a cópia do índice do arquivo corresponda à cópia da árvore de trabalho . Isto é, você está dizendo ao Git: Substitua a cópia congelada e não duplicada que está no índice agora, compactando minha cópia da árvore de trabalho atualizada, deduplicando-a e preparando-a para ser congelada em um novo commit. Se você não usar git add, o índice ainda mantém a cópia em formato congelado do commit atual.
Quando você executa git commit, Git pacotes até tudo o que está no índice direito, em seguida, para o uso como o novo snapshot. Como já está no formato congelado e pré-desduplicado, o Git não precisa fazer muito trabalho extra.
Isso também explica o que são os arquivos não rastreados . Um arquivo não rastreado é um arquivo que está em sua árvore de trabalho, mas não está no índice do Git agora . Não importa como o arquivo terminou neste estado. Talvez você tenha copiado de algum outro lugar em seu computador, em sua árvore de trabalho. Talvez você o tenha criado fresco aqui. Talvez haja era uma cópia no índice do Git, mas você removeu essa cópia com git rm --cached. De uma forma ou de outra, existe uma cópia aqui na sua árvore de trabalho, mas não existe uma cópia no índice do Git. Se você fizer um novo commit agora, esse arquivo não estará no novo commit.
Note que git checkoutinicialmente preenche o índice do Git a partir do commit que você verificou. Portanto, o índice começa correspondendo ao commit. Git também preenche sua árvore de trabalho a partir desta mesma fonte. Então, inicialmente, todos os três combinam. Quando você altera arquivos em sua árvore de trabalho e git addeles, bem, agora o índice e sua árvore de trabalho combinam. Então você executa git commite o Git faz um novo commit do índice, e agora todos os três se casam novamente.
Como o Git faz novos commits do índice, podemos colocar as coisas desta forma: o índice do Git contém o próximo commit que você planeja fazer. Isso ignora a função expandida que o índice do Git assume durante uma mesclagem em conflito, mas gostaríamos de ignorar isso por enquanto. :-)
É só isso - mas ainda é muito complicado! É particularmente complicado porque não há uma maneira fácil de ver exatamente o que está no índice do Git. 3 Mas não é um comando Git que lhe diz o que está acontecendo, de uma maneira que é muito útil, e que comando é git status.
2 Tecnicamente, isso não é realmente uma cópia . Em vez disso, é uma referência ao arquivo Git-ified, pré-deduplicado e tudo. Há mais coisas aqui também, como o modo, nome do arquivo, um número de teste e alguns dados de cache para tornar o Git mais rápido. Mas, a menos que você comece a trabalhar com alguns dos comandos de baixo nível do Git - git ls-files --stagee git update-indexem particular - você pode apenas pensar nisso como uma cópia.
3 O git ls-files --stagecomando mostrará os nomes e números de teste de cada arquivo no índice do Git, mas geralmente isso não é muito útil de qualquer maneira.
git status
O git statuscomando realmente funciona executando dois git diffcomandos separados para você (e também fazendo algumas outras coisas úteis, como informar em qual branch você está).
O primeiro git diffcompara o commit atual - que, lembre-se, está congelado para sempre - com o que quer que esteja no índice do Git. Para arquivos iguais , o Git não dirá nada. Para arquivos que são diferentes , Git irá dizer-lhe que este arquivo é encenado para cometer . Isso inclui todos os novos arquivos-se a cometer não tem sub.pynele, mas o índice não têm sub.pynele, então é acrescentado e esse arquivo quaisquer arquivos removidos, que eram (e são) no cometer, mas não estão em o índice mais ( git rm, talvez).
O segundo git diffcompara todos os arquivos no índice do Git com os arquivos em sua árvore de trabalho. Para arquivos que são iguais , o Git não diz nada. Para arquivos que são diferentes , o Git dirá que esse arquivo não foi testado para confirmação . Ao contrário do primeiro diff, esta lista particular não inclui arquivos que são todos novos: se o arquivo untrackedexiste em sua árvore de trabalho, mas não no índice do Git, Git apenas o adiciona à lista de arquivos não rastreados . 4
No final, tendo acumulado esses arquivos não rastreados em uma lista, git statusirá anunciar os nomes desses arquivos também, mas há uma exceção especial: se o nome de um arquivo estiver listado em um .gitignorearquivo, isso suprime esta última listagem. Observe que listar um arquivo rastreado - aquele que está no índice do Git - em um .gitignorenão tem efeito aqui : o arquivo está no índice, então ele é comparado e confirmado, mesmo se estiver listado em .gitignore. O arquivo para ignorar apenas suprime as reclamações de "arquivo não rastreado". 5
4 Ao usar a versão curta de git status- git status -s—os arquivos não rastreados não são separados, mas o princípio é o mesmo. Acumular os arquivos dessa forma também permite git statusresumir vários nomes de arquivos não rastreados, apenas imprimindo um nome de diretório, às vezes. Para obter a lista completa, use git status -uallou git status -u.
5 Listar um arquivo também faz com que em massa adicione muitas operações de arquivo , como git add .ou git add *pule o arquivo não rastreado. Esta parte fica um pouco mais complicada, pois você pode usar git add --forcepara adicionar um arquivo que normalmente seria ignorado. Existem alguns outros casos especiais normalmente menores, todos os quais se somam a isso: o arquivo .gitignorepode ser chamado mais apropriadamente .git-do-not-complain-about-these-untracked-files-and-do-not-auto-add-themou algo igualmente difícil de manejar. Mas isso é muito ridículo, .gitignoreé.
git add -u, git commit -aetc
Existem vários atalhos úteis que você deve conhecer aqui:
git add .irá adicionar todos os arquivos atualizados no diretório atual e qualquer subdiretório. Isso respeita .gitignore, portanto, se um arquivo que não está sendo rastreado git statusnão receber uma reclamação de , ele não será adicionado automaticamente.
git add -uirá adicionar automaticamente todos os arquivos atualizados em qualquer lugar em sua árvore de trabalho . 6 Isso afeta apenas os arquivos rastreados . Observe que se você removeu a cópia da árvore de trabalho, isso removerá a cópia do índice também ( git addfaz isso como parte de fazer com que o índice corresponda à árvore de trabalho ).
git add -Aé como correr git add .do nível superior de sua árvore de trabalho (mas veja a nota de rodapé 6).
Além disso, você pode correr git commit -a, o que equivale a aproximadamente 7 a correr git add -ue então git commit. Ou seja, você obtém o mesmo comportamento conveniente no Mercurial.
Em geral, desaconselho o git commit -apadrão: acho que é melhor usar com git statusfrequência, observe atentamente a saída e, se o status não for o que você esperava, descubra por que é esse o caso. Usando git commit -a, é muito fácil modificar acidentalmente um arquivo e enviar uma alteração que você não pretendia. Mas isso é principalmente uma questão de gosto / opinião.
6 Se a sua versão do Git for anterior ao Git 2.0, tome cuidado aqui: git add -usó funciona no diretório e subdiretórios atuais, então você deve subir ao nível superior da sua árvore de trabalho primeiro. A git add -Aopção tem um problema semelhante.
7 Digo aproximadamente equivalente porque git commit -ana verdade funciona criando um índice extra e usando esse outro índice para fazer o commit. Se o commit funcionar , você terá o mesmo efeito que fazer git add -u && git commit. Se o commit não funcionar - se você fizer o Git pular o commit de qualquer uma das muitas maneiras que você pode fazer isso - então nenhum arquivo será git addexecutado depois, porque o Git joga fora o índice extra temporário e volta a usar o índice principal .
Existem complicações adicionais que surgem se você usar git commit --onlyaqui. Nesse caso, o Git cria um terceiro índice, e as coisas ficam muito complicadas, especialmente se você usar ganchos de pré-confirmação. Este é outro motivo para usar git addoperações separadas .