2011-10-15 14 views
8

Ho una directory di lavoro senza la directory .git e) un repository. a è una revisione nel mezzo della cronologia di b.Trova revisione Git di una directory di lavoro mancante la directory .git

Come posso sapere quale corrispondenza a corrisponde a b?

Ho pensato a uno shellscript che fa un diff dalla directory di lavoro a tutte le revisioni e seleziono quello con le differenze minime (si spera 0).

Sarebbe un po 'grezzo (e non sono sicuro di come farlo), c'è un modo più semplice?

risposta

4

È possibile scrivere uno script per eseguire diff gitdir workdir | wc -c per ogni commit. Quindi è possibile confrontare i risultati e dire che il commit che ha la minima differenza (misurata da wc -c) è il commit più vicino alla directory di lavoro nuda.

Qui è quello che potrebbe apparire come in Python:

find_closest_sha1.py:

#!/usr/bin/env python 
import subprocess 
import shlex 
import sys 
import os 
import operator 

gitdir,workdir=map(os.path.realpath,sys.argv[1:3]) 
os.chdir(gitdir) 
proc=subprocess.Popen(shlex.split('git rev-list --all'),stdout=subprocess.PIPE) 
shas,err=proc.communicate() 
shas=shas.split() 
head=shas[0] 
data={} 
for sha1 in shas: 
    subprocess.Popen(shlex.split('git checkout {s}'.format(s=sha1)), 
          stderr=open('/dev/null')).wait() 
    proc=subprocess.Popen(shlex.split('diff {g} {w}'.format(g=gitdir,w=workdir)), 
          stdout=subprocess.PIPE) 
    out,err=proc.communicate() 
    distance=len(out) 
    data[sha1]=distance 
answer=min(data.items(),key=operator.itemgetter(1))[0] 
print('closest match: {s}'.format(s=answer)) 
subprocess.Popen(shlex.split('git checkout {h}'.format(h=head)), 
       stderr=open('/dev/null')).wait() 

Esempio:

% rsync -a gitdir/ workdir/ 
% cd workdir 
% git checkout HEAD~10 
HEAD is now at b9fcebf... fix foo 

% cd .. 
% /bin/rm -rf workdir/.git 
% find_closest_sha1.py gitdir workdir 
closest match: b9fcebfb170785c19390ebb4a9076d11350ade79 
+0

Lo script python non riesce completamente quando l'ho provato. Emette un commit che era totalmente sbagliato. –

1

È possibile ridurre il numero di revisioni da verificare con pickaxe. Diffudi la tua directory di lavoro rispetto alla revisione più recente e seleziona alcune linee diverse che appaiono il più rare possibile. Supponiamo che la tua ultima revisione abbia una riga contenente foobar ma la tua directory di lavoro no; eseguire git log -Sfoobar che emette tutti i commit che aggiungono o rimuovono foobar. Ora puoi spostare il tuo repository alla prima (più recente) revisione di quell'elenco, poiché tutte le revisioni successive a quella saranno diverse dalla tua directory di lavoro. Ripeti con un'altra differenza finché non trovi la revisione corretta.

1

Poiché git utilizza un archivio di file content-addressable, dovrebbe essere possibile trovare un albero arbitrario da qualche parte, ma non conosco i dettagli. Immagino che potresti copiare i file dalla directory di lavoro distaccata nella directory di lavoro del repository, quindi eseguire il commit di tutto, in qualche modo scoprire l'hash dell'oggetto tree creato dal commit e cercare i commit esistenti per uno che fa riferimento allo stesso albero .

Affinché questo funzioni, l'albero dovrà ovviamente corrispondere perfettamente, quindi non è necessario ottenere file non tracciati nel commit (come file oggetto, backup dell'editor, ecc.).

Edit: Ho appena provato questo su un repository (con git cat-file commit HEAD per mostrare l'oggetto ad albero sul HEAD, e la ricerca l'uscita di git log --pretty=raw per quell'albero hash), e non ha funzionato (non ho trovato l'hash nella storia). Ho ricevuto un sacco di avvertimenti sulla conversione CRLF quando ho eseguito il commit, quindi questo potrebbe essere stato il problema, cioè probabilmente ottieni diversi hash per lo stesso albero a seconda di come il tuo git è configurato per manipolare i file di testo. Segnalo questa wiki della comunità di risposta nel caso qualcuno sappia come farlo in modo affidabile.

0

Supponendo che l'in-albero e b/.git ignorare le impostazioni sono come erano quando è stato creato il commit e che non ci sono file non tracciati non ignorati nell'albero di lavoro dovresti essere in grado di eseguire qualcosa di simile.

La strategia è di ricreare l'id git dell'albero di lavoro e quindi cercare qualsiasi commit che contenga questo albero.

# work from detached working tree 
cd a 

# Use existing repository and a temporary index file 
GIT_DIR=b/.git 
GIT_INDEX_FILE=/tmp/tmp-index 
export GIT_DIR GIT_INDEX_FILE 

# find out the id of the current working tree 
git add . && 
tree_id=$(git write-tree) && 
rm /tmp/tmp-index 

# find a commit that matches the tree 
for commit in $(git rev-list --all) 
do 
    if test "$tree_id" = "$(git rev-parse ${commit}^{tree})"; then 
     git show "$commit" 
     break 
    fi 
done 

unset GIT_DIR 
unset GIT_INDEX_FILE 
Problemi correlati