Scripting Mercurial con Python
Domanda
Sto cercando di ottenere il numero/id di revisione mercuriale (è un hash, non un numero) a livello di codice in Python.
Il motivo è che voglio aggiungerlo ai file css/js sul nostro sito Web in questo modo:
<link rel="stylesheet" href="example.css?{% mercurial_revision "example.css" %}" />
In modo che ogni volta che viene apportata una modifica al foglio di stile, otterrà un nuovo URL e non utilizzerà più la vecchia versione memorizzata nella cache.
O se sai dove trovare una buona documentazione per il mercurial modulo Python, anche questo sarebbe utile.Non riesco a trovarlo da nessuna parte.
La mia soluzione
Ho finito per utilizzare subprocess semplicemente per eseguire un comando che ottiene il nodo hg.Ho scelto questa soluzione perché non è garantito che l'API rimanga la stessa, ma probabilmente l'interfaccia bash:
import subprocess
def get_hg_rev(file_path):
pipe = subprocess.Popen(
["hg", "log", "-l", "1", "--template", "{node}", file_path],
stdout=subprocess.PIPE
)
return pipe.stdout.read()
esempio utilizzare:
> path_to_file = "/home/jim/workspace/lgr/pinax/projects/lgr/site_media/base.css"
> get_hg_rev(path_to_file)
'0ed525cf38a7b7f4f1321763d964a39327db97c4'
Soluzione
E 'vero non c'è API ufficiali, ma si può avere un'idea di best practice leggendo altre estensioni, in particolare quelli in bundle con hg. Per questo particolare problema, vorrei fare qualcosa di simile:
from mercurial import ui, hg
from mercurial.node import hex
repo = hg.repository('/path/to/repo/root', ui.ui())
fctx = repo.filectx('/path/to/file', 'tip')
hexnode = hex(fctx.node())
Aggiorna Ad un certo punto l'ordine dei parametri è cambiato, ora è come questo:
repo = hg.repository(ui.ui(), '/path/to/repo/root' )
Altri suggerimenti
questa documentazione ?
Si noti che, come dichiarato in quella pagina, non v'è alcuna ufficiale API, perché riservano comunque il diritto di modificare in qualsiasi momento. Ma si può vedere l'elenco delle modifiche negli ultimi versioni, non è molto ampia.
Un aggiornato, più pulito versione sottoprocesso (usa .check_output()
, aggiunto in Python 2.7 / 3.1) che uso nelle mie impostazioni Django file per un controllo di distribuzione grezza end-to-end (I discarica in un commento HTML):
import subprocess
HG_REV = subprocess.check_output(['hg', 'id', '--id']).strip()
Si poteva avvolgerlo in un try
se non vuoi un po 'strano incidente di percorso per evitare l'avvio:
try:
HG_REV = subprocess.check_output(['hg', 'id', '--id']).strip()
except OSError:
HG_REV = "? (Couldn't find hg)"
except subprocess.CalledProcessError as e:
HG_REV = "? (Error {})".format(e.returncode)
except Exception: # don't use bare 'except', mis-catches Ctrl-C and more
# should never have to deal with a hangup
HG_REV = "???"
dare una prova di l'estensione parola chiave
Se stai usando Python 2, vuoi usare hglib
.
Non so cosa usare se stai usando Python 3, mi dispiace.Probabilmente hgapi
.
Contenuto di questa risposta
- Le API di Mercurial
- Come usare hglib
- Perché hglib è la scelta migliore per gli utenti di Python 2
- Se stai scrivendo un hook, quell'interfaccia interna scoraggiata è terribilmente conveniente
Le API di Mercurial
Mercurial ha due API ufficiali.
- Il server dei comandi Mercurial.Puoi parlarci da Python 2 usando il file
hglib
(wiki, PyPI), gestito dal team Mercurial. - L'interfaccia della riga di comando di Mercurial.Puoi parlarci tramite
subprocess
, Ohgapi
, o qualcosa del genere.
Come usare hglib
Installazione:
pip install python-hglib
Utilizzo:
import hglib
client = hglib.open("/path/to/repo")
commit = client.log("tip")
print commit.author
Ulteriori informazioni sull'utilizzo su pagina wiki di hglib.
Perché hglib è la scelta migliore per gli utenti di Python 2
Perché è gestito dal team Mercurial ed è ciò che il team Mercurial consiglia per l'interfacciamento con Mercurial.
Dal wiki di Mercurial, la seguente dichiarazione sull'interfacciamento con Mercurial:
Per la stragrande maggioranza del codice di terze parti, l'approccio migliore è utilizzare l'API pubblicata, documentata e stabile di Mercurial:l'interfaccia della riga di comando.In alternativa, utilizzare il CommandServer o le librerie che si basano su di esso per ottenere un'interfaccia veloce, stabile e indipendente dalla lingua.
Dalla pagina del server dei comandi:
[Il server dei comandi consente] ad applicazioni e librerie di terze parti di comunicare con Mercurial tramite una pipe che elimina il sovraccarico di avvio per comando.Le librerie possono quindi incapsulare la generazione e l'analisi dei comandi per presentare a questi comandi un'API adatta al linguaggio.
L'interfaccia Python per il server dei comandi Mercurial, come detto, è hglib
.
A proposito, il sovraccarico per comando dell'interfaccia della riga di comando non è uno scherzo.Una volta ho creato una suite di test molto piccola (solo circa 5 test) che utilizzava hg
attraverso subprocess
per creare, commit per commit, una manciata di repository con ad es.unire le situazioni.Durante tutto il progetto, la durata di esecuzione della suite è rimasta compresa tra 5 e 30 secondi, con quasi tutto il tempo trascorso nel file hg
chiamate.
Se stai scrivendo un hook, quell'interfaccia interna scoraggiata è terribilmente conveniente
La firma di una funzione hook Python è così:
# In the hgrc:
# [hooks]
# preupdate.my_hook = python:/path/to/file.py:my_hook
def my_hook(
ui, repo, hooktype,
... hook-specific args, find them in `hg help config` ...,
**kwargs)
ui
E repo
fanno parte dei suddetti scoraggiati non ufficiali API interna.Il fatto che siano proprio lì negli argomenti della tua funzione li rende terribilmente comodi da usare, come in questo esempio di a preupdate
hook che non consente unioni tra determinati rami.
def check_if_merge_is_allowed(ui, repo, hooktype, parent1, parent2, **kwargs):
from_ = repo[parent2].branch()
to_ = repo[parent1].branch()
...
# return True if the hook fails and the merge should not proceed.
Se il tuo codice hook non è così importante e non lo pubblichi, potresti scegliere di utilizzare l'API interna non ufficiale scoraggiata.Se il tuo hook fa parte di un'estensione che stai pubblicando, è meglio utilizzarlo hglib
.
FWIW per evitare il recupero di tale valore in ogni pagina / vista render, ho solo la mia implementazione metterlo nel file settings.py
. Allora posso riferimento settings.REVISION
senza tutto il sovraccarico di accesso mercuriali e / o un altro processo. Ti capita mai di avere questo cambiamento valore w / o ricaricare il vostro server?
Ho voluto fare la stessa cosa il PO voleva fare, ottenere hg id -i
da uno script (get punta revisione dell'intero repository, non di un singolo file in quella dei pronti contro termine), ma non volevo usare popen, ed il codice brendan
mi ha iniziato, ma non era quello che volevo.
Così ho scritto questo ... commenti / critiche di benvenuto. Questo diventa il rev punta in esadecimale come una stringa.
from mercurial import ui, hg, revlog
# from mercurial.node import hex # should I have used this?
def getrepohex(reporoot):
repo = hg.repository(ui.ui(), reporoot)
revs = repo.revs('tip')
if len(revs)==1:
return str(repo.changectx(revs[0]))
else:
raise Exception("Internal failure in getrepohex")