Passando do CVS para o Git: $ Id $ equivalente?

124

Eu li várias perguntas sobre ferramentas simples de controle de código-fonte e o Git parecia uma escolha razoável. Eu o tenho instalado e funcionando, e funciona bem até agora. Um aspecto que eu gosto no CVS é ​​o incremento automático de um número de versão.

Entendo que isso faz menos sentido em um repositório distribuído, mas como desenvolvedor, quero / preciso de algo assim. Deixe-me explicar o porquê:

Eu uso o Emacs. Periodicamente, procuro novas versões dos arquivos de origem Lisp para pacotes de terceiros. Digamos que eu tenho um arquivo, foo.el, que, de acordo com o cabeçalho, é a versão 1.3; se eu procurar a versão mais recente e ver a versão 1.143 ou 2.6 ou o que for, sei que estou muito atrasada.

Se, em vez disso, eu vejo alguns hashes de 40 caracteres, não saberei o que é mais tarde nem faço ideia de quanto é mais tarde. Eu absolutamente odiaria se tivesse que verificar manualmente os ChangeLogs apenas para ter uma idéia de como estou desatualizado.

Como desenvolvedor, quero estender essa cortesia, a meu ver, às pessoas que usam minha saída (e talvez eu esteja brincando que alguém seja, mas vamos deixar isso de lado por um momento). Eu não quero ter que lembrar de incrementar o número maldito sempre, ou um carimbo de data / hora ou algo assim. Essa é uma verdadeira PITA, e eu sei disso por experiência própria.

Então, quais alternativas eu tenho? Se eu não conseguir um equivalente $ Id: $, de que outra forma posso fornecer o que estou procurando?

Devo mencionar que minha expectativa é que o usuário final NÃO tenha o Git instalado e, mesmo que o tenha, não terá um repositório local (de fato, espero não disponibilizá-lo dessa maneira).

Joe Casadonte
fonte

Respostas:

67

O SHA é apenas uma representação de uma versão (embora canônica). O git describecomando oferece aos outros e o faz muito bem.

Por exemplo, quando executo git describena minha ramificação principal da fonte do cliente memcached Java , obtenho o seguinte:

2.2-16-gc0cd61a

Isso diz duas coisas importantes:

  1. Houve exatamente 16 confirmações nesta árvore desde 2.2
  2. A árvore de origem exata pode ser exibida no clone de qualquer outra pessoa.

Digamos, por exemplo, você empacotou um versionarquivo com a fonte (ou mesmo reescreveu todo o conteúdo para distribuição) para mostrar esse número. Digamos que a versão empacotada era 2.2-12-g6c4ae7a(não uma versão, mas uma versão válida).

Agora você pode ver exatamente a que distância está (4 confirmações) e pode ver exatamente quais 4 confirmações:

# The RHS of the .. can be origin/master or empty, or whatever you want.
% git log --pretty=format:"%h %an %s" 2.2-12-g6c4ae7a..2.2-16-gc0cd61a
c0cd61a Dustin Sallings More tries to get a timeout.
8c489ff Dustin Sallings Made the timeout test run on every protocol on every bui
fb326d5 Dustin Sallings Added a test for bug 35.
fba04e9 Valeri Felberg Support passing an expiration date into CAS operations.
Dustin
fonte
1
Usar isso falhará quando você mesclar a ramificação de desenvolvimento, enquanto o mestre teve alguns hotfixes. O número de confirmações desde a última versão será alterado. O hash não é confiável, pois alguém pode reconstruir a coisa toda filter-branchou algo assim.
LeMike 29/05
O artigo descreve apenas o aprendizado das informações, não a incorporação no seu executável. Para isso, você precisa executar o git describecomando antes de criar, salvar a saída em um arquivo de cabeçalho ou incorporar o valor ao seu código.
Jesse Chisholm
55

Até agora, há suporte para $ Id: $ no Git. Para habilitá-lo para o arquivo README, você deve colocar "README ident" em .gitattributes . Caracteres curinga nos nomes de arquivo são suportados. Veja man gitattributes para detalhes.

Sebastian Pipping
fonte
14
Isso fornece o sha1 do blob, mas não o sha1 do commit. Útil, mas não como um identificador para a confirmação.
Stephen Jennings
4
O git não possui nenhum mecanismo de expansão de palavras-chave como o $Id$mencionado. O que é armazenado é exatamente o que você recebe. De qualquer forma, a versão pertence à coleção completa de arquivos que compõem um commit, não a um arquivo em particular (essa idéia é um remanescente dos dias do RCS, ou talvez o SCCS seja o culpado aqui ... Como o CVS é ​​apenas um frontend glorificado para RCS, e SVN tenta ser um CVS-workalike, ele preso.).
vonbrand
32

Esta não é uma solicitação irracional do OP.

Meu caso de uso é:

  1. Eu uso o Git para meu próprio código pessoal, portanto não há colaboração com outras pessoas.
  2. Eu mantenho os scripts do Bash do sistema, que podem ser usados /usr/local/binquando estiverem prontos.

Eu uso três máquinas separadas com o mesmo repositório Git. Seria bom saber em qual "versão" do arquivo eu tenho atualmente /usr/local/binsem precisar fazer um manual "diff -u <repo version> <versão em / usr / local / bin>".

Para aqueles que são negativos, lembre-se de que existem outros casos de uso por aí. Nem todo mundo usa o Git para trabalho colaborativo, com os arquivos no repositório Git sendo seu local "final".

Enfim, o jeito que eu fiz foi criar um arquivo de atributos no repositório assim:

cat .git/info/attributes
# see man gitattributes
*.sh ident
*.pl ident
*.cgi ident

Em seguida, coloque $ Id $ em algum lugar do arquivo (eu gosto de colocá-lo após o shebang).

O commit. Observe que isso não faz a expansão automaticamente como eu esperava. Você precisa refazer o arquivo, por exemplo,

git commit foo.sh
rm foo.sh
git co foo.sh

E então você verá a expansão, por exemplo:

$ head foo.sh
#!/bin/sh

# $Id: e184834e6757aac77fd0f71344934b1cd774e6d4 $

Algumas boas informações estão em Como habilito a cadeia de caracteres ident para um repositório Git? .

Imran-UK
fonte
3
É de salientar que este não identificar o atual arquivo 's (blob), e não o atual comprometer
charlesb
3
O que git codeveria fazer? Recebi a mensagem de erro " git: 'co' is not a git command. See 'git --help'." Deve sergit checkout ?
Peter Mortensen
23

Não tenho certeza se isso será no Git. Para citar Linus :

"Toda a noção de substituição de palavras-chave é totalmente idiota. É trivial fazer" fora "do rastreamento de conteúdo real, se você quiser tê-lo ao liberar árvores como bolas de alcatrão etc."

Porém, é muito fácil verificar o log - se você estiver rastreando a ramificação estável do foo.el, poderá ver quais novas confirmações estão no log da ramificação estável que não estão na sua cópia local. Se você deseja simular o número da versão interna do CVS, pode comparar o registro de data e hora do último commit.

Editar: você deve escrever ou usar os scripts de outra pessoa para isso, é claro, não faça isso manualmente.

orip
fonte
26
Sim, li parte dessa longa série de emails sobre expansões de palavras-chave. A atitude de Linus foi quase o suficiente para me deixar completamente louco.
Joe Casadonte
15
Sim, às vezes ele não tem educação, mas geralmente está correto e definitivamente está no tópico da expansão de palavras-chave.
Bombe
O rastreamento de versão é necessário. O git é usado em muitas outras infraestruturas além do puro desenvolvimento, onde é possível ler o código para determinar se faz sentido. Mas quando um sistema de controle de revisão é usado para rastrear arquivos com conteúdo arbitrário, você precisa ter alguns meios de conhecer o release oficial. git, muito menos o git log não está na máquina em que arquivos .ini, .conf, .html e outros arquivos foram enviados.
RJT
7
Linus comentou apenas um aspecto da expansão de palavras-chave - rastreamento de lançamento. Mas esse não é o único objetivo. Essa citação demonstra claramente a atitude de um homem, mas não diz nada de útil sobre o assunto. Um típico truque político do tipo "declare o óbvio de uma maneira exaltada", e a multidão é sua. O problema é que a multidão, por definição, é estúpida, porque tem apenas um cérebro em tudo isso. O que explica a situação com a expansão git e palavra-chave claramente. Um idiota disse "não" e todos aplaudiram!
AnrDaemon
1
O @AnrDaemon não apenas isso, o git agora desde que adicionou suporte $Id$ao identatributo, como mencionado em outra resposta aqui, mostrando que mesmo o git em si não é refém da opinião de Linus.
orip 01/10/19
21

Como eu escrevi antes :

A geração automática de tags de identificação que mostram um número de versão sensato é impossível com ferramentas DSCM como o Bazaar, porque a linha de desenvolvimento de todos pode ser diferente de todas as outras. Portanto, alguém pode se referir à versão "1.41" de um arquivo, mas sua versão "1.41" desse arquivo é diferente.

Basicamente, $ Id $ não faz sentido com o Bazaar, Git e outras ferramentas de gerenciamento de código-fonte distribuído.

Bombe
fonte
6
Certo, li isso antes de postar, e é por isso que pedi uma solução mais geral para o problema subjacente. Eu acho que o desejo de um arquivo individual ter uma versão # é legítimo, assim como a incapacidade do git de fornecer uma solução.
Joe Casadonte
Não é uma incapacidade do Git, é uma incapacidade de todos os SCMs distribuídos. Se você realmente deseja números de versão significativos, use o Subversion, ou CVS, ou algum outro sistema centralizado.
Bombe
7
o que há de errado em cuspir o hash em vez de um "número de versão"? Quero instruções de log, páginas da Web de depuração e opções "--version" em scripts internos que me digam facilmente qual revisão está sendo executada, para que eu possa verificar esse hash específico e ver por que está se comportando dessa maneira. Ele simplifica o gerenciamento de aplicativos implantados ... e eu não quero um gancho de confirmação que considere cada confirmação como uma alteração em todos os arquivos que contenham uma tag $ Id $.
Nairbv
1
o hash do arquivo no qual você está trabalhando funcionaria da mesma forma que uma "versão", desde que você possa procurá-lo no git por esse id
Erik Aronesty
2
@Brian - de acordo com a edição do OP, o usuário final deseja saber o número da versão, mas não tem acesso ao git ou aos logs do git. Nesse caso, o hash é um número sem sentido, não um número de versão. Um DSCM não oferece assistência para solucionar essa necessidade.
Jesse Chisholm
10

Eu tive o mesmo problema. Eu precisava ter uma versão mais simples que uma string de hash e disponível para pessoas que usavam a ferramenta sem precisar se conectar ao repositório.

Fiz isso com um gancho de pré-confirmação do Git e alterei meu script para poder me atualizar automaticamente.

Baseei a versão no número de confirmações feitas. Essa é uma pequena condição de corrida, porque duas pessoas podem se comprometer ao mesmo tempo e ambas acham que estão comprometendo o mesmo número de versão, mas não temos muitos desenvolvedores nesse projeto.

Como exemplo, eu tenho um script que eu faço check-in em Ruby e adiciono esse código a ele - é um código bastante simples, portanto é fácil portar para idiomas diferentes se você estiver fazendo o check-in de algo em um idioma diferente (embora obviamente isso não funcionará facilmente com checkins não executáveis, como arquivos de texto). Eu já adicionei:

MYVERSION = '1.090'
## Call script to do updateVersion from .git/hooks/pre-commit
def updateVersion
  # We add 1 because the next commit is probably one more - though this is a race
  commits = %x[git log #{$0} | grep '^commit ' | wc -l].to_i + 1
  vers = "1.%0.3d" % commits

  t = File.read($0)
  t.gsub!(/^MYVERSION = '(.*)'$/, "MYVERSION = '#{vers}'")
  bak = $0+'.bak'
  File.open(bak,'w') { |f| f.puts t }
  perm = File.stat($0).mode & 0xfff
  File.rename(bak,$0)
  File.chmod(perm,$0)
  exit
end

E então adiciono uma opção de linha de comando (-updateVersion) ao script, portanto, se eu chamá-lo como "tool -updateVersion", ele chamará updateVersion da ferramenta que modifica o valor "MYVERSION" e sai (você pode também atualize outros arquivos se eles forem abertos e se você quiser).

Uma vez configurada, vou para o cabeçalho Git e crio um script bash executável de uma linha no .git/hooks/pre-commit.

O script simplesmente muda para o cabeçalho do diretório Git e chama meu script com -updateVersion.

Toda vez que eu faço check-in, o script de pré-confirmação é executado, executando meu script com -updateVersion e a variável MYVERSION é atualizada com base no número de confirmações. Magia!

David Ljung Madison Stellar
fonte
Então, seu script Ruby precisa ser chamado de updateVersion git updateVersion? Por favor, coloque algumas amostras de como é chamado.
RJT
Eu faço uma opção (-updateVersion) para o script que estou verificando que chama a função 'updateVersion' (neste caso, estou tentando alterar o número da versão no próprio script). Depois, basta criar um comando shell oneliner que chama meu script com -updateVersion e, em seguida, ele se atualiza antes de cada check-in.
David Ljung Madison Stellar
8

Se ter $ Keywords $ é essencial para você, então você pode tentar olhar para o Mercurial ? Possui uma extensão hgkeyword que implementa o que você deseja. Mercurial é interessante como um DVCS de qualquer maneira.

Keltia
fonte
8

Algo que é feito com os repositórios Git é usar o tagobjeto. Isso pode ser usado para marcar um commit com qualquer tipo de string e pode ser usado para marcar versões. Você pode ver essas tags em um repositório com o git tagcomando, que retorna todas as tags.

É fácil verificar uma tag. Por exemplo, se houver uma tag, v1.1você poderá verificá-la em um ramo como este:

git checkout -b v1.1

Como é um objeto de nível superior, você verá todo o histórico desse commit, além de poder executar diferenças, fazer alterações e mesclagens.

Não apenas isso, mas uma tag persiste, mesmo que a ramificação em que estava foi excluída sem ser mesclada novamente na linha principal.

Abizern
fonte
6
Existe, então, uma maneira de inserir essa tag no arquivo automaticamente pelo git? Obrigado!
Joe Casadonte
1
Se você quer dizer com expansão de palavra-chave? Não tão longe quanto o que sei. se você estiver criando produtos, poderá obter as informações como parte do script de construção e inseri-las em algum lugar do produto. Tente man git-description, que fornece a tag mais recente, o número de confirmações desde essa tag e o hash atual.
Abizern
Sim, tags e outras informações relacionadas agora podem ser editadas em arquivos automaticamente pelo git através do export-substrecurso de gitattributes(5). É claro que isso requer o uso de git archivepara criar releases, e somente no arquivo tar resultante as edições de substituição serão visíveis.
Greg A. Woods
4

Se bem entendi, basicamente, você deseja saber quantas confirmações ocorreram em um determinado arquivo desde a última atualização.

Primeiro, obtenha as alterações na origem remota, mas não as junte à sua masterramificação:

% git fetch

Em seguida, obtenha um log das alterações que ocorreram em um determinado arquivo entre sua masterfilial e o controle remoto origin/master.

% git log master..origin/master foo.el

Isso fornece as mensagens de log de todas as confirmações que ocorreram no repositório remoto desde a última vez que você foi mesclado origin/masterao seu master.

Se você quiser apenas contar as alterações, envie-o para wc. Diga, assim:

% git rev-list master..origin/master foo.el | wc -l
Otto
fonte
1
Portanto, não use log: git rev-list master..origin / master | wc -l
Dustin
4

Se você está apenas querendo que as pessoas possam ter uma idéia de quão desatualizadas elas estão, o Git pode informá-las disso de várias maneiras bastante fáceis. Eles comparam as datas da última confirmação no tronco e no tronco, por exemplo. Eles podem usar git cherrypara ver quantos commits ocorreram no seu tronco que não estão presentes no deles.

Se é isso que você deseja, procuraria uma maneira de fornecê-lo sem um número de versão.

Além disso, eu não me incomodaria em estender a cortesia a ninguém, a menos que você tenha certeza de que eles querem. :)

skiphoppy
fonte
Se as datas estiverem OK para comparar, coloque o DateTImeStamp no arquivo. O git tem muitos outros casos de uso além de desenvolvedores. A TI no campo precisa saber se o arquivo .INI ou .conf na estação de trabalho atualmente em solução de problemas está próximo do atual.
RJT
Um mero timestamp será suficiente? A ramificação errada pode ter um carimbo de data / hora atraente e ainda ser menos correta.
usar o seguinte comando
4

Para aplicar a expansão a todos os arquivos em todos os subdiretórios do repositório, adicione um .gitattributesarquivo ao diretório de nível superior no repositório (ou seja, onde você normalmente colocaria o .gitignorearquivo) contendo:

* ident

Para que isso ocorra, é necessário fazer um checkout efetivo dos arquivos primeiro, como excluí-los ou editá-los de qualquer forma. Em seguida, restaure-os com:

git checkout .

E você deve ver $Id$substituído por algo como:

$Id: ea701b0bb744c90c620f315e2438bc6b764cdb87 $

De man gitattributes:

ident

Quando o atributo ident é definido para um caminho, o Git substitui $ Id $ no objeto blob por $ Id :, seguido pelo nome do objeto do blob hexadecimal de 40 caracteres, seguido por um sinal de dólar $ no check-out. Qualquer sequência de bytes que comece com $ Id: e termina com $ no arquivo da árvore de trabalho é substituída por $ Id $ no check-in.

Esse ID será alterado sempre que uma nova versão do arquivo for confirmada.

Nagev
fonte
3

Os IDs RCS são bons para projetos de arquivo único, mas, para qualquer outro, o $ Id $ não diz nada sobre o projeto (a menos que você faça check-ins forçados em um arquivo de versão fictício).

Ainda pode estar interessado em obter os equivalentes de $ Author $, $ Date $, $ Revision $, $ RCSfile $, etc. em um nível por arquivo ou no nível de confirmação (como colocá-los onde estão algumas palavras-chave questão). Não tenho uma resposta para isso, mas vejo o requisito de atualizá-las, especialmente quando os arquivos (agora no Git) são originários de sistemas compatíveis com RCS (CVS).

Essas palavras-chave podem ser interessantes se as fontes forem distribuídas separadamente de qualquer repositório Git (é o que eu também faço). Minha solução é assim:

Todo projeto tem um diretório próprio e, na raiz do projeto, tenho um arquivo de texto chamado .versionqual conteúdo descreve a versão atual (o nome que será usado ao exportar as fontes).

Enquanto trabalhava para a próxima versão, um script extrai esse .versionnúmero, algum descritor de versão do Git (como git describe) e um número de compilação monotônico .build(mais host e data) em um arquivo de origem gerado automaticamente vinculado ao programa final, para que você possa encontrar de que fonte e quando foi construído.

Desenvolvo novos recursos em ramificações separadas, e a primeira coisa que faço é adicionar n(para "next") à .versionstring (vários ramos originários da mesma raiz usariam o mesmo .versionnúmero temporário ). Antes do lançamento, decido quais ramificações serão mescladas (espero que todas tenham o mesmo .version). Antes de confirmar a mesclagem, atualizo .versionpara o próximo número (atualização maior ou menor, dependendo dos recursos mesclados).

U. Windl
fonte
3

Se você deseja que as informações de confirmação do git sejam acessíveis no seu código, é necessário executar uma etapa de pré-construção para chegar lá. No bash para C / C ++, pode ser algo como isto:

prebuild.sh

#!/bin/bash
commit=$(git rev-parse HEAD)
tag=$(git describe --tags --always ${commit})
cat <<EOF >version.c
#include "version.h"
const char* git_tag="${tag}";
const char* git_commit="${commit}";
EOF

com version.haparência:

#pragma once
const char* git_tag;
const char* git_commit;

Então, onde você precisar, em seu código #include "version.h"e referência git_tagou git_commitconforme necessário.

E você Makefilepode ter algo parecido com isto:

all: package
version:
  ./prebuild.sh
package: version
  # the normal build stuff for your project

Isso tem o benefício de:

  • obtendo os valores corretos atualmente para essa compilação, independentemente da ramificação, mesclando a seleção de cereja e outras coisas.

Esta implementação de prepublish.shapresenta os inconvenientes de:

  • forçando uma recompilação, mesmo que o git_tag/ git_commitnão tenha mudado.
  • ele não leva em consideração os arquivos modificados locais que não foram confirmados, mas afetam a construção.
    • use git describe --tags --always --dirtypara capturar esse caso de uso.
  • polui o espaço para nome global.

Um criador prebuild.shque poderia evitar esses problemas é deixado como um exercício para o leitor.

Jesse Chisholm
fonte
1

Concordo com aqueles que pensam que a substituição do token pertence à construção de ferramentas, e não às ferramentas de controle de versão.

Você deve ter alguma ferramenta de release automatizada para definir os IDs da versão em suas fontes no momento em que o release está sendo marcado.

Agustí Sánchez
fonte
2
.INI .conf e .txt geralmente não têm uma ferramenta de construção.
RJT
Mas você pode hackear um script de release que pega a tag Git atual e a grava em um arquivo ou algo do tipo.
Marnen Laibow-Koser
1

Os nomes de tags e outras informações relacionadas agora podem ser editados diretamente nos arquivos automaticamente pelo Git através do export-substrecurso de gitattributes(5). É claro que isso requer o uso de git archivepara criar releases, e somente no arquivo tar resultante as edições de substituição serão visíveis.

Por exemplo, no .gitattributesarquivo, coloque a seguinte linha:

* export-subst

Em arquivos de origem, você pode adicionar uma linha como esta:

#ident  "@(#)PROJECTNAME:FILENAME:$Format:%D:%ci:%cN:%h$"

E ele será expandido para se parecer com isso em um release criado por, por exemplo git archive v1.2.0.90:

#ident  "@(#)PROJECTNAME:FILENAME:HEAD -> master, tag: v1.2.0.90:2020-04-03 18:40:44 -0700:Greg A. Woods:e48f949"
Greg A. Woods
fonte
0

Desde que você usa o Emacs, você pode ter sorte :)

Encontrei esta questão por coincidência, e também por coincidência, pela Lively há alguns dias, um pacote Emacs que permite ter peças vivas do Emacs Lisp em seu documento. Eu não tentei ser honesto, mas me veio à mente ao ler isso.

Amr Mostafa
fonte
0

Eu também vim do SCCS, RCS e CVS (%W% %G% %U% ).

Eu tive um desafio semelhante. Eu queria saber qual versão de um pedaço de código estava em qualquer sistema que o executasse. O sistema pode ou não estar conectado a nenhuma rede. O sistema pode ou não ter o Git instalado. O sistema pode ou não ter o repositório GitHub instalado.

Eu queria a mesma solução para vários tipos de código (.sh, .go, .yml, .xml, etc). Eu queria que qualquer pessoa sem conhecimento do Git ou GitHub pudesse responder à pergunta "Qual versão você está executando?"

Então, escrevi o que chamo de invólucro em torno de alguns comandos do Git. Eu o uso para marcar um arquivo com um número de versão e algumas informações. Isso resolve meu desafio. Isso pode ajudá-lo.

https://github.com/BradleyA/markit

git clone https://github.com/BradleyA/markit
cd markit
Bradley Allen
fonte
0

Para resolver esse problema, criei um pequeno "hack" como gancho pós-confirmação:

echo | tee --append *
git checkout *

Mais detalhadamente documentado neste post no meu blog .

Richard Ostrochovský
fonte