Passare da CVS a Git: $ Id $ equivalente?


124

Ho letto un mucchio di domande su semplici strumenti di controllo del codice sorgente e Git mi è sembrata una scelta ragionevole. Ho installato e funzionante, e funziona bene finora. Un aspetto che mi piace di CVS è l'incremento automatico di un numero di versione.

Capisco che questo ha meno senso in un repository distribuito, ma come sviluppatore, voglio / ho bisogno di qualcosa del genere. Lasciami spiegare perché:

Uso Emacs. Periodicamente, cerco nuove versioni dei file sorgente Lisp per pacchetti di terze parti. Supponiamo di avere un file, foo.el, che, secondo l'intestazione, è la versione 1.3; se cerco l'ultima versione e vedo che è 1.143 o 2.6 o qualsiasi altra cosa, so di essere abbastanza indietro.

Se invece vedo un paio di hash di 40 caratteri, non saprò quale sarà più tardi o non avrò idea di quanto sarà più tardi. Lo oderei assolutamente se dovessi controllare manualmente ChangeLogs solo per avere un'idea di quanto io sia obsoleto.

Come sviluppatore, voglio estendere questa cortesia, come la vedo io, alle persone che usano il mio output (e forse sto prendendo in giro me stesso che qualcuno lo è, ma lasciamolo da parte per un momento). Non voglio ricordarmi di aumentare ogni volta quel dannato numero, o un timestamp o qualcosa del genere. È un vero PITA, e lo so per esperienza.

Quindi quali alternative ho? Se non riesco a ottenere un $ Id: $ equivalente, in che altro modo posso fornire quello che sto cercando?

Devo dire che la mia aspettativa è che l'utente finale NON disporrà di Git installato e, anche se lo fosse, non disporrà di un repository locale (anzi, mi aspetto di non renderlo disponibile in questo modo).

Risposte:


67

Lo SHA è solo una rappresentazione di una versione (sebbene canonica). Il git describecomando offre agli altri e lo fa abbastanza bene.

Ad esempio, quando corro git describenel mio ramo principale della mia sorgente client memcached Java , ottengo questo:

2.2-16-gc0cd61a

Questo dice due cose importanti:

  1. Ci sono stati esattamente 16 commit in questo albero dal 2.2
  2. L' albero di origine esatto può essere visualizzato sul clone di chiunque altro.

Diciamo, ad esempio, che hai impacchettato un versionfile con l'origine (o addirittura riscritto tutto il contenuto per la distribuzione) per mostrare quel numero. Diciamo che la versione in pacchetto era 2.2-12-g6c4ae7a(non una versione, ma una versione valida).

Ora puoi vedere esattamente quanto sei dietro (4 commit) e puoi vedere esattamente quali 4 commit:

# 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.

1
L'uso di questo fallirà man mano che unisci il ramo di sviluppo, mentre il master ha avuto alcuni hotfix. Il numero di commit dall'ultima versione cambierà. L'hash non è affidabile in quanto qualcuno può ricostruire il tutto con filter-brancho qualcosa del genere.
LeMike,

La scrittura descrive solo l'apprendimento delle informazioni, non l'incorporamento nel tuo eseguibile. Per questo è necessario eseguire il git describecomando appena prima di creare, salvare l'output in un file di intestazione o incorporare in altro modo il valore nel codice.
Jesse Chisholm,

55

Ormai c'è supporto per $ Id: $ in Git. Per abilitarlo per il file README , inserire "README ident" in .gitattributes . Sono supportati i caratteri jolly sui nomi dei file. Vedi man gitattributes per i dettagli.


14
Questo ti dà lo sha1 del BLOB, ma non lo sha1 del commit. Utile, non solo come identificativo per il commit.
Stephen Jennings,

4
git non ha alcun meccanismo di espansione delle parole chiave come il $Id$menzionato. Ciò che viene archiviato è esattamente ciò che ottieni. In ogni caso, la versione appartiene alla raccolta completa di file che compongono un commit, non a un file in particolare (quell'idea è un residuo dei giorni RCS, o forse SCCS è da biasimare qui ... Poiché CVS è solo un glorificato frontend per RCS, e SVN cerca di essere un CVS-workalike, si è bloccato.).
vonbrand,

32

Questa non è una richiesta irragionevole dall'OP.

Il mio caso d'uso è:

  1. Uso Git per il mio codice personale, quindi nessuna collaborazione con altri.
  2. Tengo gli script di Bash di sistema lì dentro che potrebbero andare /usr/local/binquando sono pronti.

Uso tre macchine separate con lo stesso repository Git. Sarebbe bello sapere in quale "versione" del file ho attualmente /usr/local/binsenza dover fare un manuale "diff -u <versione repo> <versione in / usr / local / bin>".

Per quelli di voi che sono negativi, ricordate che ci sono altri casi d'uso là fuori. Non tutti usano Git per il lavoro collaborativo con i file nel repository Git essendo la loro posizione "finale".

Ad ogni modo, il modo in cui l'ho fatto è stato creare un file di attributi nel repository in questo modo:

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

Quindi metti $ Id $ da qualche parte nel file (mi piace metterlo dopo lo shebang).

L'impegno. Nota che questo non fa automaticamente l'espansione come mi aspettavo. Devi ricodificare il file, ad esempio,

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

E poi vedrai l'espansione, ad esempio:

$ head foo.sh
#!/bin/sh

# $Id: e184834e6757aac77fd0f71344934b1cd774e6d4 $

Alcune buone informazioni sono contenute in Come abilitare la stringa ident per un repository Git? .


3
Va notato che questo identifica il file corrente (BLOB), non il commit corrente
CharlesB

3
Cosa git codovrebbe fare? Ho ricevuto il messaggio di errore " git: 'co' is not a git command. See 'git --help'." Dovrebbe esseregit checkout ?
Peter Mortensen,

23

Non sono sicuro che questo sarà mai in Git. Per citare Linus :

"L'intera nozione di sostituzione delle parole chiave è totalmente idiota. È banale fare" al di fuori "del tracciamento del contenuto reale, se si desidera averlo quando si eseguono alberi di rilascio come tar-ball ecc."

È abbastanza facile controllare il registro, tuttavia, se stai monitorando il ramo stabile di foo.el, puoi vedere quali nuovi commit sono nel registro del ramo stabile che non sono nella tua copia locale. Se si desidera simulare il numero di versione interno di CVS, è possibile confrontare il timestamp dell'ultimo commit.

Modifica: dovresti scrivere o usare gli script di qualcun altro per questo, ovviamente, non farlo manualmente.


26
Sì, ho letto parte di quella lunga serie di email sulle espansioni delle parole chiave. L'atteggiamento di Linus è stato quasi sufficiente per farmi scoraggiare completamente.
Joe Casadonte,

15
Sì, a volte gli manca la cortesia, ma di solito ha ragione, ed è sicuramente sul tema dell'espansione delle parole chiave.
Bombe,

È necessario il tracciamento della versione. git è usato in molte altre infrastrutture oltre allo sviluppo puro in cui si può leggere il codice per determinare se ha senso. Ma quando viene utilizzato un sistema di controllo di revisione per tenere traccia dei file con contenuto arbitrario, è necessario disporre di alcuni mezzi per conoscere la versione ufficiale. git, figuriamoci git log non si trova sulla macchina su cui sono stati inviati i file .ini, .conf, .html e altri.
rjt

7
Linus ha commentato solo un aspetto dell'espansione delle parole chiave: il monitoraggio del rilascio. Ma non è l'unico scopo di esso. Quella citazione dimostra chiaramente l'atteggiamento di un uomo, ma non dice nulla di utile sull'argomento. Un tipico trucco politico "dichiarare l'ovvio in modo esaltato" e la folla è tua. Il problema è che la folla per definizione è stupida, perché ha un solo cervello in tutto. Ciò spiega chiaramente la situazione con l'espansione di git e parole chiave. Un idiota ha detto "no" e tutti hanno esultato!
AnrDaemon,

1
@AnrDaemon non solo, git ora da quando ha aggiunto il supporto $Id$tramite l' identattributo, come menzionato in un'altra risposta qui, dimostrando che anche git stesso non è ostaggio dell'opinione di Linus.
Orip

21

Come ho scritto prima :

Avere generato automaticamente ID tag che mostrano un numero di versione ragionevole è impossibile da utilizzare con strumenti DSCM come Bazaar perché la linea di sviluppo di tutti può essere diversa da tutte le altre. Quindi qualcuno potrebbe fare riferimento alla versione "1.41" di un file ma la versione "1.41" di quel file è diversa.

Fondamentalmente, $ Id $ non ha alcun senso con Bazaar, Git e altri strumenti di gestione del codice sorgente distribuiti.


6
Bene, l'ho letto prima di pubblicare, ed è per questo che ho chiesto una soluzione più generale al problema di fondo. Penso che il desiderio che un singolo file abbia una versione # è legittimo, così come l'incapacità di git di fornire una soluzione.
Joe Casadonte,

Non è un'incapacità di Git, è un'incapacità di tutti gli SCM distribuiti. Se vuoi davvero numeri di versione significativi, usa Subversion o CVS o qualche altro sistema centralizzato.
Bombe,

7
cosa c'è di sbagliato nel sputare l'hash invece di un "numero di versione"? Voglio istruzioni di registro, pagine Web di debug e opzioni "--version" su script interni che mi diranno facilmente quale revisione è in esecuzione, quindi posso controllare quel hash specifico e vedere perché si sta comportando così. Semplifica la gestione delle app distribuite .... e non voglio alcun hook di commit che consideri ogni commit come una modifica di ogni file che conteneva un tag $ Id $.
nairbv,

1
l'hash del file su cui stai lavorando funzionerebbe allo stesso modo di una "versione", a patto che tu possa cercarlo in git da
quell'id

2
@Brian: in base alla modifica dell'OP, l'utente finale desidera conoscere il numero di versione ma non ha accesso a git o ai log git. In questo caso, l'hash è un numero senza significato, non un numero di versione. Un DSCM non fornisce assistenza per risolvere questa esigenza.
Jesse Chisholm,

10

Ho avuto lo stesso problema. Avevo bisogno di avere una versione più semplice di una stringa hash e disponibile per le persone che utilizzano lo strumento senza la necessità di connettersi al repository.

L'ho fatto con un hook pre-commit di Git e ho modificato il mio script per potermi aggiornare automaticamente.

Baso la versione sul numero di commit eseguiti. Questa è una leggera condizione di competizione perché due persone potrebbero impegnarsi contemporaneamente ed entrambe pensano di impegnare lo stesso numero di versione, ma non abbiamo molti sviluppatori in questo progetto.

Ad esempio, ho uno script che sto controllando che si trova in Ruby e aggiungo questo codice ad esso - è un codice piuttosto semplice, quindi è facile portarlo in lingue diverse se si sta effettuando il check in qualcosa in una lingua diversa (anche se ovviamente questo non funzionerà facilmente con check-in non eseguibili come file di testo). Ho aggiunto:

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 poi aggiungo un'opzione della riga di comando (-updateVersion) allo script, quindi se la chiamo come "strumento -updateVersion", allora chiama solo updateVersion per lo strumento che modifica il valore "MYVERSION" in sé e quindi esce (potresti farlo aggiornare anche altri file se sono aperti anche se lo si desidera).

Una volta installato, vado alla testa di Git e creo uno script bash di una riga eseguibile in .git/hooks/pre-commit.

Lo script cambia semplicemente in testa alla directory Git e chiama il mio script con -updateVersion.

Ogni volta che controllo lo script pre-commit viene eseguito che esegue il mio script con -updateVersion, e quindi la variabile MYVERSION viene aggiornata in base al numero di commit. Magia!


Quindi il tuo script Ruby deve essere chiamato updateVersion per avere git updateVersion? Si prega di inserire alcuni esempi di come viene chiamato.
rjt

Faccio un'opzione (-updateVersion) allo script che sto controllando che chiama la funzione 'updateVersion' (in questo caso sto provando a cambiare il numero di versione nello script stesso). Quindi eseguo un comando shell oneliner che chiama il mio script con -updateVersion e quindi si aggiorna da solo prima di ogni check-in.
David Ljung Madison Stellar,

8

Se avere $ Keywords $ è essenziale per te, forse potresti provare a dare un'occhiata Mercurial ? Ha un'estensione hgkeyword che implementa ciò che vuoi. Mercurial è comunque interessante come DVCS.


8

Qualcosa che viene fatto con i repository Git è usare l' tagoggetto. Questo può essere usato per taggare un commit con qualsiasi tipo di stringa e può essere usato per contrassegnare le versioni. Puoi vedere quei tag in un repository con il git tagcomando, che restituisce tutti i tag.

È facile controllare un tag. Ad esempio, se esiste un tag v1.1, puoi controllarlo su un ramo in questo modo:

git checkout -b v1.1

Essendo un oggetto di livello superiore, vedrai l'intera cronologia di quel commit, oltre a poter eseguire diff, apportare modifiche e unire.

Non solo, ma un tag persiste, anche se il ramo su cui si trovava è stato cancellato senza essere ricollegato alla riga principale.


6
Esiste quindi un modo per inserire automaticamente questo tag nel file con git? Grazie!
Joe Casadonte,

1
Se intendi per espansione delle parole chiave? Non per quanto ne so. se stai costruendo prodotti puoi ottenere le informazioni come parte del tuo script di build e inserirle da qualche parte nel tuo prodotto costruito. Prova man git-descritto che fornisce l'ultimo tag, il numero di commit da questo tag e l'hash corrente.
Abizern,

Sì, i tag e altre informazioni correlate ora possono essere modificati automaticamente in file tramite git tramite la export-substfunzione di gitattributes(5). Questo ovviamente richiede l'uso di git archiveper creare versioni, e solo nel file tar risultante saranno visibili le modifiche di sostituzione.
Greg A. Woods,

4

Se ho capito bene, in sostanza, vuoi sapere quanti commit sono stati eseguiti su un determinato file dall'ultimo aggiornamento.

Prima ottieni le modifiche nell'origine remota, ma non unirle nel tuo masterramo:

% git fetch

Quindi ottenere un registro delle modifiche apportate a un determinato file tra il masterramo e il telecomando origin/master.

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

Questo ti dà i messaggi di registro di tutti i commit che sono avvenuti nel repository remoto dall'ultima fusione origin/masternel tuo master.

Se vuoi solo un conteggio delle modifiche, esegui il pipe a wc. Di 'così:

% git rev-list master..origin/master foo.el | wc -l

1
Quindi, non usare log: git rev-list master..origin / master | wc -l
Dustin il

4

Se vuoi solo che le persone siano in grado di farsi un'idea di quanto siano obsolete, Git può informarle in diversi modi abbastanza facili. Confrontano le date dell'ultimo commit sul proprio trunk e sul trunk, ad esempio. Possono usare git cherryper vedere quanti commit si sono verificati nel bagagliaio che non sono presenti nel loro.

Se questo è tutto ciò che desideri, cercherò un modo per fornirlo senza un numero di versione.

Inoltre, non mi preoccuperei di estendere la cortesia a nessuno a meno che tu non sia sicuro che lo vogliano. :)


Se le date sono OK per il confronto, inserire DateTImeStamp nel file. git ha molti altri casi d'uso oltre ai soli sviluppatori. L'IT sul campo deve sapere se il file .INI o .conf sulla workstation attualmente in fase di risoluzione dei problemi è vicino a quello attuale.
rjt

Sarà sufficiente un semplice timestamp? Il ramo sbagliato può avere un timestamp accattivante ed essere ancora meno corretto.
user2066657

4

Per applicare l'espansione a tutti i file in tutte le sottodirectory del repository, aggiungere un .gitattributesfile alla directory di livello superiore nel repository (ovvero dove normalmente si inserisce il .gitignorefile) contenente:

* ident

Per vederlo in vigore, devi prima fare un checkout efficace dei file, come eliminarli o modificarli in alcun modo. Quindi ripristinali con:

git checkout .

E dovresti vedere $Id$sostituito con qualcosa di simile:

$Id: ea701b0bb744c90c620f315e2438bc6b764cdb87 $

Da man gitattributes:

ident

Quando l'attributo ident è impostato per un percorso, Git sostituisce $ Id $ nell'oggetto BLOB con $ Id :, seguito dal nome dell'oggetto BLOB esadecimale di 40 caratteri, seguito da un simbolo di dollaro $ al momento del pagamento. Qualsiasi sequenza di byte che inizia con $ Id: e termina con $ nel file worktree viene sostituita con $ Id $ al momento del check-in.

Questo ID cambierà ogni volta che viene impegnata una nuova versione del file.


3

Gli ID RCS sono utili per i progetti a file singolo, ma per ogni altro $ Id $ non dice nulla sul progetto (a meno che non si eseguano controlli fittizi su un file di versione fittizio).

Uno potrebbe essere interessato a come ottenere gli equivalenti di $ Author $, $ Date $, $ Revision $, $ RCSfile $, ecc. A livello di file o a livello di commit (come metterli dove sono alcune parole chiave è un altro domanda). Non ho una risposta su questi, ma vedo la necessità di aggiornarli, specialmente quando i file (ora in Git) provengono da sistemi compatibili con RCS (CVS).

Tali parole chiave possono essere interessanti se le fonti sono distribuite separatamente da qualsiasi repository Git (è quello che faccio anche io). La mia soluzione è così:

Ogni progetto ha una propria directory e nella radice del progetto ho un file di testo chiamato .version cui contenuto descrive la versione corrente (il nome che verrà utilizzato durante l'esportazione delle fonti).

Mentre si lavora per la prossima versione uno script estrae quel .versionnumero, alcuni descrittori di versione Git (come git describe) e un numero di build monotonico in .build(più host e data) in un file sorgente generato automaticamente che è collegato al programma finale, in modo da poter trovare fuori da quale fonte e quando è stato costruito.

Sviluppo nuove funzionalità in rami separati e la prima cosa che faccio è aggiungere n(per "successivo") alla .versionstringa (più rami originati dalla stessa radice utilizzerebbero lo stesso .versionnumero temporaneo ). Prima del rilascio decido quali rami unire (speriamo che abbiano tutti lo stesso .version). Prima di confermare l'unione, aggiorno .versional numero successivo (aggiornamento principale o secondario, a seconda delle funzionalità unite).


3

Se si desidera che il git commit informazioni accessibili nel codice, quindi è necessario fare una fase di pre-build per arrivare lì. In bash per C / C ++ potrebbe essere simile a questo:

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

con l' version.haspetto di:

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

Quindi, ovunque sia necessario nel codice #include "version.h"e nel riferimento git_tago git_commitsecondo necessità.

E Makefilepotresti avere qualcosa del genere:

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

Questo ha il vantaggio di:

  • ottenere i attualmente valori corretti per questa configurazione indipendentemente ramificazione, fondendo ciliegia-picking e simili.

Questa implementazione di prepublish.shha gli svantaggi di:

  • forzare una ricompilazione anche se git_tag/ git_commitnon è cambiato.
  • non tiene conto dei file modificati locali che non sono stati sottoposti a commit ma che hanno effetto sulla build.
    • usare git describe --tags --always --dirtyper catturare quel caso d'uso.
  • inquina lo spazio dei nomi globale.

Un appassionato prebuild.shche potrebbe evitare questi problemi viene lasciato come esercizio per il lettore.


1

Concordo con coloro che ritengono che la sostituzione del token appartenga agli strumenti di creazione piuttosto che agli strumenti di controllo della versione.

Dovresti disporre di uno strumento di rilascio automatico per impostare gli ID di versione nelle tue fonti nel momento in cui la versione viene taggata.


2
.INI .conf e .txt di solito non hanno uno strumento di compilazione.
rjt

Ma puoi mettere insieme uno script di rilascio che prende il tag Git corrente e lo scrive in un file o qualcosa del genere.
Marnen Laibow-Koser,

1

I nomi dei tag e altre informazioni correlate possono ora essere modificati direttamente in file automaticamente da Git tramite la export-substfunzione di gitattributes(5). Questo ovviamente richiede l'uso di git archiveper creare versioni, e solo nel file tar risultante saranno visibili le modifiche di sostituzione.

Ad esempio nel .gitattributesfile metti la seguente riga:

* export-subst

Quindi nei file di origine è possibile aggiungere una riga come questa:

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

E si espanderà in questo modo in una versione creata, ad esempio, da 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"

0

Dato che usi Emacs, potresti essere fortunato :)

Mi sono imbattuto in questa domanda per coincidenza, e anche per coincidenza sono venuto da Lively pochi giorni fa, un pacchetto Emacs che consente di avere vivaci pezzi di Emacs Lisp nel tuo documento. Non ho provato ad essere sincero, ma mi è venuto in mente leggendo questo.


0

Vengo anche da SCCS, RCS e CVS ( %W% %G% %U%).

Ho avuto una sfida simile. Volevo sapere quale versione era un pezzo di codice su qualsiasi sistema che la eseguiva. Il sistema può o non può essere collegato a nessuna rete. Git potrebbe non essere installato nel sistema. Il sistema può avere o meno il repository GitHub installato su di esso.

Volevo la stessa soluzione per diversi tipi di codice (.sh, .go, .yml, .xml, ecc.). Volevo che qualsiasi persona senza conoscenza di Git o GitHub fosse in grado di rispondere alla domanda "Quale versione stai utilizzando?"

Quindi, ho scritto quello che chiamo wrapper attorno ad alcuni comandi Git. Lo uso per contrassegnare un file con un numero di versione e alcune informazioni. Risolve la mia sfida. Potrebbe esserti d'aiuto.

https://github.com/BradleyA/markit

git clone https://github.com/BradleyA/markit
cd markit

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.