2010-06-30 21 views
20

Recibí un código fuente y decidí usar git para él ya que mi compañero de trabajo usó el enfoque mkdir $VERSION etc. Si bien el pasado del código actualmente no parece importante, me gustaría ponerlo bajo control git también para comprender mejor el proceso de desarrollo. Entonces:¿Cómo anteponer el pasado a un repositorio de git?

¿Cuál es una manera conveniente de poner esas versiones pasadas en mi repositorio git ya existente? Actualmente no hay ningún repositorio remoto, así que no me importa reescribir el historial, pero una solución que tenga en cuenta los repositorios remotos será, por supuesto, preferible a menos que sea mucho más complicado. Puntos de bonificación para una secuencia de comandos que no necesita más interacción en función de un directorio o un archivo basado en el historial de archivos.

+1

Ver también [Editar/modificar/modificar/cambiar la primera confirmación/inicial/raíz en Git?] (Http://stackoverflow.com/q/2119480/456814), [¿Cambiar la primera confirmación del proyecto con Git?] (http://stackoverflow.com/q/2246208/456814) y [Git: cómo agregar confirmaciones antes de la primera/inicial/confirmación raíz?] (http://stackoverflow.com/q/16762160/456814). –

+0

@Cupcake Gracias, nunca vi la notificación para sus enlaces –

Respuesta

21

Para importar las instantáneas antiguas, encontrará algunas de las herramientas en Git's contrib/fast-import directory útiles. O, si ya tiene cada una vieja foto en un directorio, es posible hacer algo como esto:

# Assumes the v* glob will sort in the right order 
# (i.e. zero padded, fixed width numeric fields) 
# For v1, v2, v10, v11, ... you might try: 
#  v{1..23}  (1 through 23) 
#  v?{,?}  (v+one character, then v+two characters) 
#  v?{,?{,?}} (v+{one,two,three} characters) 
#  $(ls -v v*) (GNU ls has "version sorting") 
# Or, just list them directly: ``for d in foo bar baz quux; do'' 
(git init import) 
for d in v*; do 
    if mv import/.git "$d/"; then 
     (cd "$d" && git add --all && git commit -m"pre-Git snapshot $d") 
     mv "$d/.git" import/ 
    fi 
done 
(cd import && git checkout HEAD -- .) 

A continuación, buscar a la vieja historia en su repositorio de trabajo:

cd work && git fetch ../import master:old-history 

vez tiene el historial antiguo y su historial basado en Git en el mismo repositorio, tiene un par de opciones para la operación de anteponer: injertos y reemplazos.

Los injertos son un mecanismo por repositorio para (posiblemente temporalmente) editar el origen de varias confirmaciones existentes. Los injertos están controlados por el archivo $GIT_DIR/info/grafts (descrito en "información/injertos" del gitrepository-layout manpage).

INITIAL_SHA1=$(git rev-list --reverse master | head -1) 
TIP_OF_OLD_HISTORY_SHA1=$(git rev-parse old-history) 
echo $INITIAL_SHA1 $TIP_OF_OLD_HISTORY_SHA1 >> .git/info/grafts 

Con el injerto en su lugar (el original inicial commit no tiene padres, el injerto se dio uno de los padres), se pueden utilizar todas las herramientas normales de Git para buscar a través y ver el historial extendido (por ejemplo git log ahora debería mostrarle el historial anterior después de sus commits).

El principal problema con los injertos es que están limitados a su repositorio. Pero, si decide que deberían ser una parte permanente de la historia, puede usar git filter-branch para que así sea (haga una copia de seguridad tar/zip de su .git dir primero; git filter-branch guardará referencias originales, pero en algún momento es más fácil usar una copia de seguridad simple).

git filter-branch --tag-name-filter cat -- --all 
rm .git/info/grafts 

El mecanismo de sustitución es más reciente (Git 1.6.5 +), pero se puede desactivar en función de cada mando (git --no-replace-objects …) y pueden presionado para facilitar el uso compartido. El reemplazo funciona en objetos individuales (blobs, árboles, confirmaciones o etiquetas anotadas), por lo que el mecanismo también es más general. El mecanismo de reemplazo está documentado en el git replace manpage. Debido a la generalidad, la configuración “anteponiendo” es un poco más complicado (tenemos que crear un nuevo commit en lugar de sólo nombrar el nuevo padre):

# the last commit of old history branch 
oldhead=$(git rev-parse --verify old-history) 
# the initial commit of current branch 
newinit=$(git rev-list master | tail -n 1) 
# create a fake commit based on $newinit, but with a parent 
# (note: at this point, $oldhead must be a full commit ID) 
newfake=$(git cat-file commit "$newinit" \ 
     | sed "/^tree [0-9a-f]\+\$/aparent $oldhead" \ 
     | git hash-object -t commit -w --stdin) 
# replace the initial commit with the fake one 
git replace -f "$newinit" "$newfake" 

compartir esta sustitución no es automático.Debe compartir una parte (o la totalidad de) refs/replace para compartir el reemplazo.

git push some-remote 'refs/replace/*' 

Si decide hacer el reemplazo permanente, utilice git filter-branch (igual que con los injertos; hacer una copia de seguridad tar/zip del directorio de .git primero):

git filter-branch --tag-name-filter cat -- --all 
git replace -d $INITIAL_SHA1 
+0

gracias, esto funciona muy bien para un pequeño subconjunto de prueba, ahora fuera del completo :) (Utilicé la opción de reemplazo) –

+0

Esto no es un problema para mí en el momento, pero lo preguntaré de todos modos: utilizando la opción de reemplazo hasta el punto anterior a 'git filter-branch'ing no reescribe el historial y, por lo tanto, es más fácil de compartir, ¿no? –

+2

Sin * git filter branch *, ni los injertos, ni los reemplazos en realidad reescriben el historial (solo producen un efecto en el DAG de confirmación como si hubieran reescrito el historial). Los beneficios de los reemplazos son 1) pueden ser deshabilitados por el argumento de la línea de comando o la variable de entorno, 2) pueden ser empujados/recuperados, 3) funcionan en cualquier objeto, no solo en los "atributos" de las confirmaciones. La capacidad de enviar reemplazos hace que sean fáciles de compartir a través de los protocolos normales de Git (puede compartir entradas de injerto, pero debe usar algún mecanismo "fuera de banda" (es decir, no empujar/recuperar) para propagarlos). –

1

El enfoque más fácil es, por supuesto, la creación de un nuevo repositorio git, comprometiendo el historial para anteponer primero y luego volver a aplicar los parches del repositorio anterior. Pero preferiría una solución que consuma menos tiempo mediante la automatización.

2

Si no desea cambiar las confirmaciones en su repositorio, puede usar injertos para anular la información principal para una confirmación. Esto es lo que hace el repositorio de Linux Kernel para obtener el historial antes de que comenzaran a usar Git.

Este mensaje: http://marc.info/?l=git&m=119636089519572 parece tener la mejor documentación que puedo encontrar.

Debería crear una secuencia de confirmaciones relacionadas con su historial de pregit, luego use el archivo .git/info/grafts para hacer que Git use el último commit en esa secuencia como el padre del primer commit que generó usando Git.

+1

+1 ah sí, ya veo, gracias. Esto se detalla como la opción de injerto en [respuesta de Chris Johnsen] (http://stackoverflow.com/questions/3147097/how-to-prepend-the-past-to-a-git-repository/3148117#3148117) –

Cuestiones relacionadas