2011-10-15 10 views
8

Tengo a) un directorio de trabajo sin el directorio .git y b) un repositorio. a es una revisión en el medio de la historia de b.Buscar la revisión de Git de un directorio de trabajo que falta el directorio .git

¿Cómo puedo saber qué revisión a coincide en b?

Pensé en un shellscript haciendo un diff desde el directorio de trabajo para todas las revisiones y escogí el que tenía menos (con suerte 0) diferencias.

Eso sería un poco crudo (y no estoy seguro de cómo hacerlo), ¿hay alguna manera más fácil?

Respuesta

4

Puede escribir un script para ejecutar diff gitdir workdir | wc -c para cada confirmación. Luego puede cotejar los resultados y decir que la confirmación que tiene la diferencia más pequeña (medida por wc -c) es la confirmación más cercana al directorio de trabajo simple.

Esto es lo que podría parecer en 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() 

Ejemplo:

% 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

La secuencia de comandos python falla totalmente cuando la probé. Salió un commit que estaba totalmente equivocado. –

1

Puede reducir el número de revisiones que tiene que verificar con el pickaxe. Diff su directorio de trabajo contra la última revisión, y seleccione alguna línea diferente que parece lo más rara posible. Supongamos que su última revisión tiene una línea que contiene foobar pero su directorio de trabajo no; Ejecute git log -Sfoobar que emite todos los commits agregando o eliminando foobar. Ahora puede mover su repositorio de vuelta a la primera (última) revisión en esa lista, ya que todas las revisiones posteriores a esa van a ser diferentes de su directorio de trabajo. Repita con otra diferencia hasta que encuentre la revisión correcta.

1

Dado que git utiliza un almacén de archivos con contenido accesible, debería ser posible encontrar un árbol arbitrario en alguna parte, pero no conozco los detalles. Supongo que podría copiar los archivos del directorio de trabajo separado en el directorio de trabajo del repositorio, luego confirmar todo, de alguna manera descubrir el hash del árbol creado por el commit y buscar los commit existentes para uno que haga referencia al mismo árbol .

Para que esto funcione, el árbol obviamente tendrá que coincidir perfectamente, por lo que no debe obtener ningún archivo no rastreado en la confirmación (como archivos de objeto, copias de seguridad de editor, etc.).

Editar: Acabo de probar esto en un repositorio (con git cat-file commit HEAD para mostrar el objeto de árbol en HEAD, y buscando el resultado de git log --pretty=raw para ese hash de árbol), y no funcionó (no encontré el hash en la historia). Obtuve un montón de advertencias sobre la conversión CRLF cuando hice la confirmación, por lo que ese podría haber sido el problema, es decir, probablemente obtenga hashes diferentes para el mismo árbol, dependiendo de cómo esté configurado su git para manipular los archivos de texto. Estoy marcando esta wiki de la comunidad de respuestas en caso de que alguien sepa cómo hacerlo de manera confiable.

0

Suponiendo que el en-árbol y b/.git los ajustes de ignorar son como eran cuando se creó el compromiso y que no hay ningún archivo no rastreado no ignorado en el árbol de trabajo, debería poder ejecutar algo como esto.

La estrategia es recrear la id de git del árbol de trabajo y luego buscar cualquier confirmación que contenga este árbol.

# 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 
Cuestiones relacionadas