El Génesis de GIT

17/06/2013

Genesis GIT
La Creación de GIT

  • El primer día: el almacén (repository) y el árbol de trabajo (working tree)

    Soy joven, el mundo es maravilloso y tengo mucho tiempo libre.

    Tengo tanto tiempo, que decido que quiero escribir un libro. El proyecto en el que estoy trabajando en una modesta historia y explicación de todo, con el título provisional de “El Libro”. Tiene una tabla de contenido contenido.txt y un solo capítulo:

    .
    ├── capitulo1.txt
    └── contenido.txt

    Según empiezo a escribir, empiezo a pensar que debería llevar un control de mis cambios. Necesito algún tipo de sistema de control de versiones. ¿Cómo de complicado puede ser esto?

    Comienzo con algunos nombres. Voy a llamar a este conjunto de ficheros en los que estoy trabajando, el árbol de trabajo (working tree). Y como soy así, voy a llamar a mi nuevo sistema de control de versiones eden.

    Decido que necesito guardar el estado de “El Libro” al final de cada día. Para hacer esto, creo un nuevo directorio en mi árbol de trabajo, llamado .eden. Este directorio almacenará “El Libro” según vaya evolucionando y convirtiéndose en un best-seller mundial. Utilizaré el nombre almacén (repository) para denominar al contenido de .eden.

    Al final del día, realizo una copia de todos los ficheros del árbol de trabajo, y los guardo en mi nuevo almacén .eden. De hecho, lo que haré, será crear un nuevo subdirectorio en .eden nombrado con la fecha de hoy y ahí almacenaré una copia de los ficheros del libro. En unix, podría ser algo como lo siguiente:


    mkdir .eden/año0-enero-01
    mkdir .eden/año0-enero-01/ficheros
    cp * .eden/año0-enero-01/ficheros

    Así que todavía tengo el contenido de “El Libro” en el árbol de trabajo, pero ahora, en el almacén, tengo una copia de los ficheros de tal modo que es una captura (snapshot) de “El Libro” tal y como está hoy:

    .
    ├── .eden
    │ └── año0-enero-01
    │ └── ficheros
    │ ├── capitulo1.txt
    │ └── contenido.txt
    ├── capitulo1.txt
    └── contenido.txt

  • El segundo día: preparación (staging) y almacenamientos (commits)

    Hoy trabajo algo más en el libro. Comienzo a trabajar en el capítulo 2 y, mientras pienso en algunas cosas, me doy cuenta de que también estoy escribiendo varias notas para mí mismo acerca del personaje de “Eva”, al que llevo un tiempo dándole vueltas. Guardo estas notas en un fichero llamado notas_sobre_eva.txt. Al llegar al final del día, estoy listo para guardar mi trabajo. Por el momento, mi directorio está tal que así:
    .
    ├── .eden
    │ └── año0-enero-01
    │ └── ficheros
    │ ├── capitulo1.txt
    │ └── contenido.txt
    ├── notas_sobre_eva.txt
    ├── capitulo2.txt
    ├── capitulo1.txt
    └── contenido.txt

    Por algún motivo que no alcanzo a identificar, de momento, no quiero poner notas_sobre_eva.txt en el almacén. De hecho, en general, quiero decidir de qué ficheros quiero hacer copia de seguridad en el almacén y qué cambios quiero dejar para otro día. Finalmente se me ocurre una idea. Crearé un directorio en .eden llamado area_preparacion (stagin_area). Al empezar a trabajar al comienzo del día, copio en el area_preparacion la copia de mis ficheros anteriormente respaldada en el almacén. Ahora estos ficheros están listos para ser almacenado en la siguiente captura (snapshot):


    cp .eden/año0-enero-01/* .eden/area_preparacion

    Ahora tengo:

    ├── .eden
    │ ├─── area_preparacion
    │ │ ├── capitulo1.txt
    │ │ └── contenido.txt
    │ └── año0-enero-01
    │ └── ficheros
    │ ├── capitulo1.txt
    │ └── contenido.txt
    ├── notas_sobre_eva.txt
    ├── capitulo2.txt
    ├── capitulo1.txt
    └── contenido.txt

    Según voy trabajando, decido qué voy a poner en la captura de esta noche. Por ejemplo, quizás haya cambiado el capitulo1.txt y está listo para hacer una copia de seguridad. Entonces copio mi versión modificada del capitulo1.txt de mi árbol de trabajo al area_preparacion. Llamaré a esto preparación (stage-in) del fichero. También ‘prepararé’ el nuevo fichero capitulo2.txt (lo copiaré en el área de preparación). Por el momento no voy a preparar notas_sobre_eva.txt.

    Una vez hecho esto, todo el material que quiero almacenar en la copia de seguridad está listo. Solamente tengo que ponerlo es su propio directorio de captura. Para hacer esto, simplemente ejecuto algo así (otra vez unix):


    mkdir .eden/año0-enero-02
    mkdir .eden/año0-enero-02/ficheros
    cp .eden/area_preparacion/* .eden/año0-enero-02/ficheros

    Termino con un directorio que se ve de este modo:

    .
    ├── .eden
    │ ├── año0-enero-02
    │ │ └── ficheros
    │ │ ├── capitulo2.txt
    │ │ ├── capitulo1.txt
    │ │ └── contenido.txt
    │ ├─── area_preparacion
    │ │ ├── capitulo2.txt
    │ │ ├── capitulo1.txt
    │ │ └── contenido.txt
    │ └── año0-enero-01
    │ └── ficheros
    │ ├── capitulo1.txt
    │ └── contenido.txt
    ├── notas_sobre_eva.txt
    ├── capitulo2.txt
    ├── capitulo1.txt
    └── contenido.txt

    Decido que usaré el nombre del almacenamiento (commit) para cada directorio de captura diaria (año0-enero-01 y año0-enero-02). La acción de añadir ficheros al área de preparación la denominaré preparación (stagin) de ficheros para el almacenamiento. Utilizaré el término almacenar (commiting) para la acción de crear el directorio para la captura y copiar los ficheros desde el área de preparación al directorio de la captura.

  • El tercer día: historial

    Como resultado de ciertos eventos de ayer por la tarde, tengo una nueva amiga, Eva. Quiere ayudarme. Obviamente Eva tiene su propio ordenador y le he enviado mi directorio .eden. Me felicito a mí mismo por no haber añadido notas_sobre_eva.txt al almacén.

    Eva recupera (checks out) nuestro libro (reconstruye mi árbol de trabajo) con algo como esto:


    cp .eden/año0-enero-02/ficheros/* .

    Así que ahora tiene los ficheros del libro, tal y como yo los almacené anoche. También copia lo últimos ficheros almacenados en el área de preparación, tal y como hice yo:

    ├── .eden
    │ ├─── area_preparacion
    │ │ ├── capitulo2.txt
    │ │ ├── capitulo1.txt
    │ │ └── contenido.txt

    Trabaja duro en un nuevo fichero capitulo1_discusion.txt. Da gusto ver cómo disfruta del trabajo. A medida que la tarde se convierte en noche, está lista para guardar su trabajo, así que copia capitulo1_discusion.txt a .eden/area_preparacion. Ya está lista para hacer un almacenamiento:


    mkdir .eden/año0-enero-03
    mkdir .eden/año0-enero-03/ficheros
    cp .eden/area_preparacion/* .eden/año0-enero-03/ficheros

    Eso es lo que Eva iba a hacer, pero Eva es lista, e inmediatamente se da cuenta que hay un problema. Después de que ha realizado su almacenamiento, seguramente ambos tendremos un directorio .eden/año0-enero-03 almacenado pero que tendrán diferentes contenidos. Si luego ella quiere compartir trabajo conmigo, eso podría causarnos confusión.

    Ambos estamos un poco cansados después de todo nuestro trabajó así que nos encontramos para tomarnos una cervecita. Hablamos sobre ello durante un rato. Al principio pensamos que simplemente podríamos añadir la hora a la fecha ya que seguramente sería única para cada uno de nosotros. Pero nos damos cuenta de que eso también podría traernos problemas ya que si Eva realiza un almacenamiento en su ordenador, luego yo hago uno en el mío, y ella hace posteriormente otro en el suyo, la horas indicarían que estos se llevaron a cabo en una secuencia cuando, de hecho, existen dos secuencias, la mía y la de Eva. Necesitamos otro modo de mantener la traza de la secuencia de almacenamientos, que funcione incluso si nosotros dos estamos trabajando de modo independiente.

    Finalmente decidimos que, en vez de la fecha, daremos un identificador literal único a cada almacenamiento. Podríamos tener un problema para asegurarnos de que dicho literal identificador único es realmente único pero supongamos que podemos solventar eso de algún modo. Almacenaremos el contenido del árbol de trabajo del mismo modo en que lo hemos estado haciendo hasta ahora, en el subdirectorio ficheros, pero añadiremos un nuevo fichero a cada almacenamiento, llamado info.txt, que nos dirá quién hizo el almacenamiento, cuándo y, más importante, cual fue el anterior almacenamiento. Llamaremos padre (parent) al almacenamiento anterior.

    Eva tenía razón al suponer que yo había realizado mi propio almacenamiento hoy. Había estado trabajando felizmente en el capítulo 3. Así que, antes de nuestra conversación, mi directorio era tal que así:

    .
    ├── .eden
    │ ├── año0-enero-03
    │ │ └── ficheros
    │ │ ├── capitulo3.txt
    │ │ ├── capitulo2.txt
    │ │ ├── capitulo1.txt
    │ │ └── contenido.txt
    │ ├─── area_preparacion
    │ │ ├── capitulo3.txt
    │ │ ├── capitulo2.txt
    │ │ ├── capitulo1.txt
    │ │ └── contenido.txt
    │ ├── año0-enero-02
    │ │ └── ficheros
    │ │ ├── capitulo2.txt
    │ │ ├── capitulo1.txt
    │ │ └── contenido.txt
    │ └── año0-enero-01
    │ └── ficheros
    │ ├── capitulo1.txt
    │ └── contenido.txt
    ├── notas_sobre_eva.txt
    ├── capitulo3.txt
    ├── capitulo2.txt
    ├── capitulo1.txt
    └── contenido.txt

    pero ahora, una vez hemos trabajado del nuevo modo, se ve así:

    .
    ├── .eden
    │ ├── 5d89f8
    │ │ ├── info.txt
    │ │ └── ficheros
    │ │ ├── capitulo3.txt
    │ │ ├── capitulo2.txt
    │ │ ├── capitulo1.txt
    │ │ └── contenido.txt
    │ ├─── area_preparacion
    │ │ ├── capitulo3.txt
    │ │ ├── capitulo2.txt
    │ │ ├── capitulo1.txt
    │ │ └── contenido.txt
    │ ├── 7ef41f
    │ │ ├── info.txt
    │ │ └── ficheros
    │ │ ├── capitulo2.txt
    │ │ ├── capitulo1.txt
    │ │ └── contenido.txt
    │ └── 6438a4
    │ ├── info.txt
    │ └── ficheros
    │ ├── capitulo1.txt
    │ └── contenido.txt
    ├── notras_sobre_eva.txt
    ├── capitulo3.txt
    ├── capitulo2.txt
    ├── capitulo1.txt
    └── contenido.txt

    y .eden/5d89f8/info.txt contiene esto:

    almacenador = Adan
    mensaje = Tercer día
    fecha = año0-enero-03
    padre = 7ef41f

    Mientras que el directorio de Eva está así:

    .
    ├── .eden
    │ ├── 0a01a0
    │ │ ├── info.txt
    │ │ └── ficheros
    │ │ ├── capitulo1_discusion.txt
    │ │ ├── capitulo2.txt
    │ │ ├── capitulo1.txt
    │ │ └── contenido.txt
    │ ├─── area_preparacion
    │ │ ├── capitulo1_discusion.txt
    │ │ ├── capitulo2.txt
    │ │ ├── capitulo1.txt
    │ │ └── contenido.txt
    │ ├── 7ef41f
    │ │ ├── info.txt
    │ │ └── ficheros
    │ │ ├── capitulo2.txt
    │ │ ├── capitulo1.txt
    │ │ └── contenido.txt
    │ └── 6438a4
    │ ├── info.txt
    │ └── ficheros
    │ ├── capitulo1.txt
    │ └── contenido.txt
    ├── caputulo1_discusion.txt
    ├── capitulo2.txt
    ├── capitulo1.txt
    └── contenido.txt

    y el .eden/0a01a0/info.txt de Eva es:

    almacenador = Eva
    mensaje = Eva día 3
    fecha = año0-enero-03
    padre = 7ef41f

    Después de pensarlo un poco Eva y yo nos damos cuenta de que, cuando realizamos nuestros almacenamientos, vamos a tener que saber cual es el almacenamiento actual, de tal modo que podamos utilizar ese como el padre. Cuando realizamos un nuevo almacenamiento, guardamos el identificador del almacenamiento en un fichero. Llamaremos a este fichero .eden/CABECERA (HEAD), de tal modo que, después de mi último almacenamiento de más arriba, el fichero .eden/CABECERA tendrá el contenido 5d89f8. Usamos el contenido de .eden/CABECERA para identifica el último (actual) almacenamiento. Y, por supuesto, cuando realizamos un nuevo almacenamiento, podemos obtener el padre del nuevo almacenamiento a partir del almacenamiento actual de .eden/CABECERA.

    Así que ahora tenemos un nuevo procedimiento para realizar nuestros almacenamientos. A grandes rasgos sería algo como esto (ahora en sintaxis python)


    def eden_almacenar(almacenador, mensaje):
    # Crear de algún modo un identificador único para este almacenamiento
    nuevo_id = crear_id_unico()
    # Crear un nuevo directorio en eden con el nuevo nombre único
    dir_almacenamiento = '.eden/' + nuevo_id
    mkdir(dir_almacenamiento)
    mkdir(dir_almacenamiento + '/ficheros')
    # Copiar los ficheros del área de preparación al nuevo directorio de captura
    copy_tree('.eden/area_preparacion', dir_almacenamiento + '/ficheros')
    # Obtener el almacenamiento anterior (padre) de .eden/CABECERA
    cabecera_id = file('.eden/CABECERA').read()
    # Crear info con el padre establecido en CABECERA
    info_txt = 'almacenador = ' + almacenador + '\n'
    info_txt += 'mensaje = ' + mensaje + '\n'
    info_txt += 'fecha = ' + date.today() + '\n'
    info_txt += 'padre = ' + cabecera_id + '\n'
    # Escribir info en el fichero info.txt
    info_fichero = file(dir_almacenamiento + '/info.txt', 'w')
    info_fichero.write(info_txt)
    info_fichero.close()
    # Modificar .eden/CABECERA para que contenga en nuevo id de almacenamiento
    file('.eden/CABECERA', 'w').write(nuevo_id)

    Cuando queramos volver a un estado anterior del libro, podremos hacer una recuperación (checkout), tal que así:


    def eden_recuperar(id_almacenamiento):
    dir_almacenamiento = '.eden/' + id_almacenamiento
    # copiar .eden/:math:`id_almacenamiento/ficheros en el árbol de trabajo
    delete_tree('.')
    copy_tree(dir_almacenamiento + '/ficheros', '.')
    # hacer que .eden/CABECERA contenga el id_almacenamiento
    file('.eden/CABECERA', 'w').write(id_almacenamiento)
    # copiar la captura almacenada en el área de preparación
    delete_tree('.eden/area_preparacion')
    copy_tree(dir_almacenamiento + '/ficheros', '.eden/area_preparacion')

    Así, cuando ejecutamos eden_recuperar(‘7ef41f`) obtendremos la copia del árbol de trabajo correspondiente a 7ef41f, y .eden/CABECERA contendrá la cadena 7ef41f.

    En nuestra excitación, inmediatamente nos damos cuenta que ahora es realmente sencillo ver el historial del libro. Fácilmente podemos comprobar el info.txt del almacenamiento actual, mostrarlo, buscar su padre, y comprobar el info.txt del padre, mostrarlo y así una y otra vez.

    Ahora estamos cansados, pero felices, así que descansamos.

  • El cuarto día: referencias (references)

    Nos levantamos con una extraña excitación. La idea de mantener una referencia al almacenamiento actual en .eden/CABECERA parece que podría generalizarse. Hablo con Eva durante en desayuno (por supuesto, se quedó en su propio apartamento, pero vino para trabajar). Juntos desarrollamos el concepto de referencias. Una referencia (reference) es:

    Referencia
    Algo que apunta a un almacenamiento

    Así que, .eden/CABECERA es una referencia – al almacenamiento actual. Pero qué ocurre si decido sacar una versión preliminar de nuestro libro. Digamos que quiero lanzar el libro guardado en .eden//7ef41f/ficheros como ‘lanzamiento-0.1’. Voy a enviárselo a todos mis amigos (para ser honesto, todavía no tengo demasiados amigos, pero aún así). Quiero ser capaz de recordar que versión del libro he enviado. Para ello puedo hacer una referencia a este almacenamiento. Llamaré a esto una etiqueta (tag). Creo un nuevo directorio en .eden llamado refs, y otro directorio en refs, llamado etiquetas, y luego, en .eden/refs/etiquetas/lanzamiento-0.1 sencillamente pondré 7ef41f – una referencia al almacenamiento lanzado. De este modo, si alguna vez tengo que volver a la versión del libro que he lanzado, simplemente tengo que leer el fichero lanzamiento-0.1 para encontrar el almacenamiento, y después recuperar el almacenamiento.

    Un momento, pero, hay un problema. Si recupero el almacenamiento de lanzamiento-0.1, sobrescribiré .eden/CABECERA, y perderé el registro de en qué almacenamiento estaba trabajando antes.

    Guardémosla en otra referencia. Usaremos el nombre ‘maestra’ para mi línea principal de desarrollo. Guardaré dónde está, creando un nuevo fichero .eden/refs/cabeceras/maestra que será una referencia al último almacenamiento. Simplemente contiene el texto ‘5d89f8’. Así, para saber que estoy trabajando en ‘maestra’, haré que .eden/CABECERA contenga el texto ref: refs/cabeceras/maestra. Ahora, cuando realice un nuevo almacenamiento, primero comprobaré .eden/CABECERA; si veo ref: refs/cabeceras/maestra, lo primero que haré será recuperar el id de almacenamiento de .eden/refs/cabeceras/maestra – y lo utilizaré como id padre del almacenamiento. Cuando haya guardado el nuevo almacenamiento, estableceré en .eden/refs/cabeceras/maestra el nuevo identificador de almacenamiento. Así que tengo que modificar mi procedimiento de almacenamiento ligeramente:


    def eden_almacenar(almacenador, mensaje):
    # *** desde aquí hasta la siguiente linea *** es nuevo
    # Obtener el anterior (padre) id de almacenamiento de .eden/CABECERA
    contenido_cabecera = file('.eden/CABECERA').read()
    # Comprobar si es una referencia, desreferenciar si lo es
    # Obtener también el fichero en el que escribir el nuevo id de almacenamiento
    if contenido_cabecera.startswith('ref: '):
    ref_cabecera = contenido_cabecera.replace('ref: ', '')
    fichero_ref_cabecera = '.eden/' + ref_cabecera
    id_cabecera = file(fichero_ref_cabecera).read()
    else:
    fichero_ref_cabecera = '.eden/CABECERA'
    id_cabecera = contenido_cabecera
    # *** el siguiente contenido (hasta el próximo ***) ya lo has visto antes
    # Crear de algún modo un identificador único para este almacenamiento
    nuevo_id = crear_id_unico()
    # Crear un nuevo directorio en eden con el nuevo nombre unico
    dir_almacenamiento = '.eden/' + nuevo_id
    mkdir(dir_almacenamiento)
    mkdir(dir_almacenamiento + '/ficheros')
    # Copiar los ficheros del área de preparación al nuevo directorio de captura
    copy_tree('.eden/area_preparacion', dir_almacenamiento + '/ficheros')
    # Obtener el almacenamiento anterior (padre) de .eden/CABECERA
    cabecera_id = file('.eden/CABECERA').read()
    # Crear info con el padre establecido en CABECERA
    info_txt = 'almacenador = ' + almacenador + '\n'
    info_txt += 'mensaje = ' + mensaje + '\n'
    info_txt += 'fecha = ' + date.today() + '\n'
    info_txt += 'padre = ' + cabecera_id + '\n'
    # Escribir info en el fichero info.txt
    info_fichero = file(dir_almacenamiento + '/info.txt', 'w')
    info_fichero.write(info_txt)
    info_fichero.close()
    # Hacer que el fichero que apunta al almacenamiento actual apunte a nuestro almacenamiento
    # *** algo nuevo y que podremos estar escribiendo en .eden/CABECERA, o
    # algo como .eden/refs/cabeceras/maestra, dependiendo de que
    # contuviese .eden/CABECERA al principio de esta rutina
    file(fichero_ref_cabecera, 'w').write(nuevo_id)

    Así que, digamos que estoy trabajando en el almacenamiento ‘5d89f8’. .eden/CABECERA contiene ref: refs/cabeceras/maestra. .eden/refs/cabeceras/maestra contiene 5d89f8. Ejecuto mi procedimiento de almacenamiento:


    eden_almacenar('Adan', 'La noche sigue al día')

    El procedimiento de almacenamiento ha realizado un nuevo almacenamiento ‘dfbeda’; .eden/CABECERA continua teniendo el texto ref: refs/cabeceras/maestra, pero ahora .eden/refs/cabeceras/maestra contiene dfbeda. De este modo, mantenemos el registro del almacenamiento en el que estamos, actualizando ‘maestra’ constantemente.

    De acuerdo – volvamos a mi recuperando la versión lanzada del libro. Primero recupero el contenido de .eden/refs/etiquetas/lanzamiento-0.1 – es ‘5d89f8’. Luego recupero el arbol de trabajo de esa versión, utilizando mi estupendo procedimiento eden_recuperar:


    eden_recuperar('5d89f8')

    El procedimiento de recuperación hará que .eden/CABECERA contenga el texto 5d89f8.

    Ahora quiero volver a trabajar en mi versión actual del libro. Eso son el conjunto de ficheros apuntados por .eden/refs/cabeceras/maestra. Puedo comprobar el contenido de .eden/refs/cabeceras/maestra – es dfbeda. Ahora recupero la versión actual con el procedimiento normal de recuperación:


    eden_recuperar('dfbeda')

    Finalmente, tendré que hacer que .eden/CABECERA sea ref: refs/cabeceras/maestra. Todo bien.

    Por supuesto, podría automatizar esto modificando mi procedimiento de recuperación ligeramente:


    def eden_recuperar(referencia_almacenamiento):
    # Si es una referencia, desreferenciar
    if referencia_almacenamiento in listdir('.eden/refs/cabeceras'):
    # es una referencia de cabecera, puede que 'maestra'
    referencia_cabecera = True
    nombref = '.eden/refs/cabeceras/' + referencia_almacenamiento
    id_almacenamiento = file(nombref).read()
    elif referencia_almacenamiento in listdir('.eden/refs/etiquetas'):
    # es una referencia de etiqueta
    referencia_cabecera = False
    nombref = '.eden/refs/etiquetas/' + referencia_almacenamiento
    id_almacenamiento = file(nombref).read()
    else: # Simplemente un identificador de almacenamiento estándar
    referencia_cabecera = False
    id_almacenamiento = referencia_almacenamiento
    dir_almacenamiento = '.eden/' + id_almacenamiento
    # copiar .eden/`id_almacenamiento/ficheros en el árbol de trabajo
    delete_tree('.')
    copy_tree(dir_almacenamiento + '/ficheros', '.')
    # hacer que eden/CABECERA apunte al id de almacenamiento
    if referencia_cabecera:
    # Apuntar CABECERA a la referencia de cabecera
    file('.eden/CABECERA').write('ref: refs/cabeceras/' + referencia_almacenamiento)
    # Escribir el identificador de almacenamiento en el fichero de referencia de cabecera
    file('.eden/refs/cabeceras/' + referencia_almacenamiento, 'w').write(id_almacenamiento)
    else:
    file('.eden/CABECERA', 'w').write(id_almacenamiento)
    # copiar la captura almacenada en el área de preparación
    delete_tree('.eden/area_preparacion')
    copy_tree(dir_almacenamiento + ‘/ficheros’, ‘.eden/area_preparacion’)

    Entonces, ¿cuál es la diferencia entre una etiqueta – como nuestro lanzamiento – y un objetivo cambiante como ‘maestra’? La ‘etiqueta’ es una referencia estática – no cambia cuando hacemos un almacenamiento y siempre apunta al mismo almacenamiento. ‘maestra’ es una referencia dinámica – en particular, es una referencia de cabecera:

    Cabecera
    Una cabeceara es una referencia que se actualiza cuando hacemos un almacenamiento

    Me duele un poco la cabeza después de que Eva me explica todo esto pero, tras un rato y una fantástica manzana, me siento positivo con respecto a eden.

  • El quinto día: ramas (branches), mezclas (merges) y remotos (remotes)

    Ayer estaba un poco exhausto, así que hoy hubo algo de tiempo para reflexionar.

    Mientras Eva y yo nos relajamos con el resto de animales, que se llevan todos muy bien los unos con los otros, comenzamos a darnos cuenta que es asunto este de la cabecera podría resultar ser muy util.

    Por ejemplo, qué ocurre si alguno de mi reducido grupo de amigos me dice que hay un error conceptual serio en la versión del libro que he lanzado – ‘lanzamiento-0.1’. ¿Qué ocurre si quiero volver a ella y corregirlo – esto es – realizar otro almacenamiento sobre ese libro lanzado, en vez de hacerlo sobre la versión del libro en la que estoy trabajando actualmente? Simplemente tengo que hacer una nueva cabecera. Lo haría del siguiente modo:


    cp .eden/refs/etiquetas/lanzamiento-0.1 .eden/refs/cabeceras/trabajando-en-0.1

    Luego, miro lo que contiene el almacenamiento trabajando-en-0.1 – por supuesto es 7ef41f. Recupero el estado del libro con mi nuevo procedimiento de recuperación:


    eden_recuperar('trabajando-en-0.1')

    Esto modifica .eden/CABECARA a ref: refs/cabeceras/trabajando-en-0.1. Ahora, cuando hago un almacenamiento con eden_almacenar, se actualizará el fichero .eden/refs/cabeceras/trabajando-en-0.1 para que contenga el nuevo identificador de almacenamiento. A pesar de que la manzana de la pasad noche fuese un poquito más amarga, nos sentimos bien.

    Pensando en esto, empezamos a pensar que ‘maestra’ y ‘trabajando-en-0.1’ son ramas (branches) – ya que se puede pensar en cada una de ellas como un identificador de árbol o grafo de almacenamiento, que puede crecer. Todo lo que necesito para crear una nueva rama, es crear una nueva referencia de cabecera a un almacenamiento. Por ejemplo, si quiero crear una nueva rama comenzando con la posición actual de ‘maestra’, todo lo que necesito hacer es:


    cp .eden/refs/etiquetas/maestra .eden/refs/cabeceras/mi-nueva-rama

    Si quiero trabajar en esta rama, debo recuperarla con:


    eden_recuperar('mi-nueva-rama')

    Esto recuperará el identificador de almacenamiento de .eden/refs/cabeceras/mi-nueva-rama, desempaquetará el árbol de almacenamiento en el árbol de trabajo y establecerá .eden/CABECERA al texto ref: refs/cabeceras/mi-nueva-rama

    Yo tengo mis ramas, pero Eva tendrá sus propias ramas, y esto nos ayudará a saber dónde estamos trabajando cada uno. Imprudentemente acabé sugiriendo que las mujeres no contribuyen a la literatura, y le pregunté cómo es que su cabello no está cubierto por un todavía-no-inventado pañuelo. Al final lo solucionamos, y me comprometo a revisarlo e intentar poner sus cambios.

    Afortunadamente, a pesar de la falta de cosas básicas como ropa, tenemos una excelente red local, así que puedo ver el contenido de su versión del libro en /ordenador_eva/nuestro_libro/.eden. Quiere que revise su rama ‘maestra’. Solo porque la red podría falla, tengo que coger lo que necesite de su ordenador al mío. Así que, para mantener el registro de las cosas, crearé un nuevo directorio, llamado .eden/refs/remotos/eva, y copiaré todas sus cabeceras – en este caso solamente maestra – a ese directorio. Por tanto, ahora tengo .eden/refs/remotos/eva/maestra, que, de hecho, apunta al almacenamiento que ella hizo el tercer día: que era el almacenamiento ‘0a01a0’. Yo no tengo este almacenamiento en mi directorio .eden, así que lo copiaré de /ordenador_eva/nuestro_libro/.eden/0a01a0. Reviso el fichero info.txt de dicho almacenamiento y compruebo quién es el padre. Es ‘7ef41f’. Compruebo si lo tengo, y sí, así que puedo dejar de copiar cosas del directorio de Eva.

    Así que, lo que acabo de hacer es:

    Copiar la referencia de cabecera de Eva de /ordenador_eva/nuestro_libro/.eden/refs/cabeceras a mi .eden/refs/remotos/eva.
    Para cada una de las referencias en .eden/refs/remotos/eva, compruebo si tengo el almacenamiento referenciado y los padres de ese almacenamiento, y, si no, los copio a .eden.

    Decidimos llamar a la secuencia en dos-pasos – una recuperación (fetch).

    Ahora quiero echar un ojo a su versión del libro. Tengo sus referencias de cabecera y los almacenamientos a los que apuntan, así que puedo recuperar su última versión. Primero obtengo el identificador del almacenamiento de .eden/refs/remotos/eva/maestro – ‘0a01a0’. Luego:


    eden_recuperar('0a01a0')

    Esto pondrá ‘0a01a0’ en .eden/CABECERA. Puedo revisar su versión del libro y decidir si me gusta. Si resulta que sí, puedo hacer una mezcla.

    ¿Qué es una mezcla? Es juntar dos almacenamientos. Primero averiguo donde divergió el árbol de Eva del mío, yendo hacia atrás en su historial, siguiendo los padres de los almacenamientos. En este caso es sencillo, ya que el almacenamiento padre (‘7ef41f’) de este almacenamiento (‘0a01a0’) es uno que también está en mi historial (el historial de mi rama ‘maestra’). A este almacenamiento común más reciente lo llamaŕe ancestro común (common ancestor). Ahora obtengo las diferencias entre este almacenamiento ancestro común (‘7ef41f’) y este almacenamiento (‘0a01a0’) – llamemos a esto eva_diff.

    Regreso a mi ‘maestra’ – que recordemos que es (.eden/refs/cabeceras/maestra) – ‘dfbeda’:


    eden_recuperar('maestra')

    Esto cambiará .eden/CABECERA a ref: refs/cabeceras/maestra – y recuperará el árbol de trabajo de .eden/dfbeda/ficheros. Ahora cojo el eva_diff y lo aplico a mi árbol actual de trabajo. Si hubiese algún conflicto, los resuelvo, pero en mi mundo, no existen los conflictos. Tengo la sensación de que puede haber algunos en el futuro. Ese pastel de manzana me hace sentir un poco raro.

    Finalmente, hago un nuevo almacenamiento, con un nuevo ID único – digamos‘80cc85’, con el árbol de trabajo mezclado. Pero, y aquí está el truco: el nuevo almacenamiento ‘80cc85’ – tiene dos padres, el primero ‘dfbeda’ – el anterior almacenamiento en mi ‘maestra’, y un segundo ‘0a01a0’ – el último almacenamiento de la maestra de Eva. Ahora, la próxima vez que revise el árbol de Eva, podré ver que tengo su almacenamiento ‘0a01a0’ en mi propio historial, y no tendré que volver a aplicarlo de nuevo.

  • El sexto día: ahorrando tiempo y espacio con objetos (objects)

    Ahora estoy muy contento con eden, pero Eva claramente no piensa que lo tengamos bien todavía.

    Mientras piensa, decide hacer un par de ilustraciones para “El Libro”, así que añade algunas fotos a su árbol de trabajo.

    .
    ├── .eden
    │ …
    ├── imagenes
    │ ├── adan_con_manzana.jpg
    │ └── leon_con_cordero.jpg
    ├── capitulo1_discusion.txt
    ├── capitulo2.txt
    ├── capitulo1.txt
    └── contenido.txt

    Tan pronto como hace esto, se da cuenta de lo que falla en eden. Las fotos son ficheros grandes. Ahora mismo, cada vez que hacemos un almacenamiento, copiamos todos los ficheros en el directorio de ficheros del almacenamiento para realizar la captura. Con ficheros grandes, acabaremos un un montón de copias idénticas y un gran desperdicio de espacio.

    Eva decide que lo que necesitamos es conseguir que los almacenamientos utilicen referencias a ficheros, en vez de utilizar los propios ficheros. De ese modo, cuando el almacenamiento contiene fichero que no han cambiado, sencillamente apuntará al fichero no modificado, en vez de llevar acarreado una desperdiciante copia del fichero.

    Si los almacenamientos solamente guardar referencias, necesitamos un modo de almacenar el contenido de los ficheros, de modo que estos puedan ser referenciados. ¿Podríamos guardar los ficheros de nuestras capturas en un directorio, y utilizar algún tipo de nombre de fichero único de modo que los almacenamientos puedan referenciar dicho nombre de fichero? Por ejemplo, quizás podamos crear un directorio en .eden tal que así:


    mkdir .eden/objetos

    y usar este directorio para almacenar el contenido de los ficheros de nuestras capturas. Entonces podríamos guardar los almacenamientos como una especie de tabla, donde las entradas nos dirían cómo recuperar del directorio .eden/objetos los ficheros que coinciden.

    Podríamos tener una estructura para los almacenamientos así:

    ├── .eden
    │ ├── 5d89f8
    │ │ ├── info.txt
    │ │ └── lista_ficheros

    donde .eden/5d89f8/lista_ficheros sería una lista de referencias a ficheros en el directorio .eden/objetos, junto con el nombre de fichero que el contenido tiene cuando se reconstruya en la captura. Por ejemplo, quizás lista_ficheros podría tener una serie de pares (referencia objeto, nombre fichero) como la siguiente:

    contenido_version1 contenido.txt
    capitulo1_version1 capitulo1.txt
    capitulo2_version2 capitulo2.txt
    capitulo3_version1 capitulo3.txt
    capitulo1_discusion_version1 capitulo1_discusion.txt

    Las referencias de la primera columna coincidirían con el nombre de los ficheros del directorio .eden/objetos:

    │ ├── objetos
    │ │ ├── capitulo1_version1
    │ │ ├── capitulo2_version1
    │ │ ├── capitulo2_version2
    │ │ ├── capitulo3_version1
    │ │ ├── capitulo1_discusion_version1
    │ │ └── contenido_version1

    Podríamos pensar en el directorio .eden/objetos como una base de datos muy sencilla, donde las claves son los nombres de los ficheros, y el contenido de los ficheros son los valores.

    Pensamos un rato en ello y nos damos cuenta de que va a ser complicado intentar encontrar nombres únicos para utilizar como nombres de fichero en .eden/objetos, ya que habrá un montón de versiones de un montón de ficheros. Por ejemplo capitulo1_version2, capitulo1_version3 y así sucesivamente, claramente no va a funcionar, ya que Eva y yo que trabajamos independientemente, en algún momento tendremos algo como capitulo1_version3 en nuestros respectivos directorios .eden/objetos, pero que serán diferentes, y eso sería confuso.

    Llegados a este punto, Eva me revela que tiene tiene algo de formación en informática. Por supuesto yo no tengo ni idea de qué es eso, o quién se lo pudo enseñar, pero Eva está demasiado acelerada como para explicármelo ahora. Propone que creemos los nombre de fichero (claves de la base de datos) haciendo hashes del contenido del fichero. Me cuenta que los algoritmos de hashing puede partir de un conjunto de bytes, como el contenido de un fichero, y crear una cadena que es casi-suficientemente única para ese conjunto de bytes. Eso es muy bueno, porque significa que, si Eva y yo tenemos un objeto con el mismo nombre de fichero (hash) eso significa que casi con total seguridad contiene el mismo contenido exacto.

    Eva recomienda el algoritmos de hasing ‘SHA1’, y yo no estoy en posición de estar en desacuerdo con ella. Ahora tenemos una cadena única para utilizarla como clave para cada fichero. Por ejemplo, ejecutamos el algoritmo SHA1 en los ficheros de nuestro libro actual y obtenemos lo siguiente:

    NombreFichero Hash SHA1
    capitulo1.txt 9e398c7cf8d56e960aa7769839cc0c38b8e12f11
    capitulo2.txt 65735b3705284cdf4a66c2e4812ca13cbaa7cd5d
    capitulo1_discusion.txt 3c2e09cc43568f13444c075c84b047957f7995a5
    contenido.txt f31bfa1225f9e0eb6741a0ab1122f8cd2cbedc04

    Si cambiamos algo del contenido del fichero, entonces el hash cambia, y tenemos una nueva cadena única y por tanto tenemos un nuevo nombre de fichero único con el que guardar el nuevo contenido. Por ejemplo, la versión original del capítulo 2 era un poco más corta, y tenía una hash de ‘1cf01a1dfbe135b6132362fa8e17eaefcaf00a7f’.

    Ahora tenemos un buen modo de crear las referencias que irán en .eden/5d89f8/lista_ficheros. Primero guardamos las versiones de ficheros en nuestro directorio .eden/objetos, utilizando sus valores hash como nombres de ficheros:

    │ ├── objetos
    │ │ ├── 9e398c7cf8d56e960aa7769839cc0c38b8e12f11 (capitulo1 version 1)
    │ │ ├── 1cf01a1dfbe135b6132362fa8e17eaefcaf00a7f (capitulo2 version 1)
    │ │ ├── 65735b3705284cdf4a66c2e4812ca13cbaa7cd5d (capitulo2 version 2)
    │ │ ├── 3c2e09cc43568f13444c075c84b047957f7995a5 (capitulo1_discusion version 1)
    │ │ └── f31bfa1225f9e0eb6741a0ab1122f8cd2cbedc04 (contenido version 1)

    Después creamos .eden/5d89f8/lista_ficheros con una fila por cada fichero de nuestro directorio. Cafa fila contiene primero – el valor hash (y por tanto el nombre de fichero de .eden/objetos) que me permite recuperar el contenido del fichero, y luego el tipo de cosa que es – aquí fichero – y finalmente, el nombre del fichero tal cual estaba en la captura:

    9e398c7cf8d56e960aa7769839cc0c38b8e12f11 fichero capitulo1.txt
    65735b3705284cdf4a66c2e4812ca13cbaa7cd5d fichero capitulo2.txt
    3c2e09cc43568f13444c075c84b047957f7995a5 fichero capitulo1_discusion.txt
    f31bfa1225f9e0eb6741a0ab1122f8cd2cbedc04 fichero contenido.txt

    Ahora, ¿qué pasa con el nuevo directorio de trabajo de Eva que contenía fotos? Las fotos están en el subdirectorio imagenes, y todavía no tenemos un modo de almacenar directorios. Ajá – ¿porqué no almacenar los directorios también en la base de datos de objetos? Los directorios podrían ser ficheros de árbol similares a lista_ficheros. Los archivos de árbol son listas, una entrada por fila, donde cada fila contiene la referencia hash del contenido del fichero, el tipo de cosa que es (árbol o fichero), y el nombre del fichero tal cual estaba en la captura. Así, para el nuevo almacenamiento de Eva, primero guardaríamos el contenido de las dos fotografías en el directorio .eden/objetos:

    │ ├── objetos
    │ │ ├── 82e6792faa893070dcd6fe3e614b6f147be1a0a9 (adan_con_manzana.jpg)
    │ │ ├── e8b23357995db47e70906d4c7a08114c0c0ba376 (leon_con_cordero.jpg)
    │ │ ├── 9e398c7cf8d56e960aa7769839cc0c38b8e12f11 (capitulo1 version 1)

    etc. Luego crearemos un nuevo fichero de árbol llamado – digamos – ‘listado_imagenes’ como este:

    82e6792faa893070dcd6fe3e614b6f147be1a0a9 fichero adan_con_manzana.jpg
    e8b23357995db47e70906d4c7a08114c0c0ba376 fichero leon_con_cordero.jpg

    y también creamos una hash para ese fichero de árbol, y lo ponemos en .eden/objetos:

    │ ├── objectos
    │ │ ├── be242dba385bc0689be16454e959f4b64c87abce (listado_imagenes)
    │ │ ├── 82e6792faa893070dcd6fe3e614b6f147be1a0a9 (adan_con_manzana.jpg)
    │ │ ├── e8b23357995db47e70906d4c7a08114c0c0ba376 (leon_con_cordero.jpg)
    │ │ ├── 9e398c7cf8d56e960aa7769839cc0c38b8e12f11 (capitulo1 version 1)

    etc. Ahora nuestro listado completo de almacenamiento podría incluir ficheros y directorios para el directorio raíz de nuestro proyecto, algo como:

    9e398c7cf8d56e960aa7769839cc0c38b8e12f11 fichero capitulo1.txt
    65735b3705284cdf4a66c2e4812ca13cbaa7cd5d fichero capitulo2.txt
    3c2e09cc43568f13444c075c84b047957f7995a5 fichero capitulo1_discusion.txt
    f31bfa1225f9e0eb6741a0ab1122f8cd2cbedc04 fichero contenido.txt
    be242dba385bc0689be16454e959f4b64c87abce arbol imagenes

    Oh – pero espera – eso también es un árbol de listado, hagamos también un hash para él, y pongámoslo en el directorio .eden/objetos:

    │ ├── objetos
    │ │ ├── e52dc9dbe358c549df65307652ff2709322812b3 (listado raiz)
    │ │ ├── be242dba385bc0689be16454e959f4b64c87abce (listado_imagenes)
    │ │ ├── 82e6792faa893070dcd6fe3e614b6f147be1a0a9 (adan_con_manzana.jpg)

    Bien – así que ahora toda nuestra receta de almacenamiento se cuece en nuestro fichero info.txt, y el hash del árbol raíz (el de arriba que comienza con ‘e52dc’). Nos podemos deshacer de los viejos ficheros del subdirectorio de almacenamiento, y, en su lugar, añadir el hash del árbol raíz – tal que así:

    almacenador = Eva
    mensaje = Añadiendo imágenes divertidas
    fecha = año0-enero-06
    arbol_raiz = e52dc9dbe358c549df65307652ff2709322812b3
    padre = 0a01a0

    Así hemos solventado el molesto problema de encontrar un único identificador de almacenamiento por cada almacenamiento. Simplemente hacemos un hash para el fichero info.txt, y lo ponemos también en el directorio .eden/objetos, como un fichero de almacenamiento:

    │ ├── objetos
    │ │ ├── 7e0cda8c145b300b519ed28998a31f801b6d626f (ultimo almacenamiento)
    │ │ ├── e52dc9dbe358c549df65307652ff2709322812b3 (listado raiz)
    │ │ ├── be242dba385bc0689be16454e959f4b64c87abce (listado_imagenes)

    El id único del almacenamiento es el hash de su contenido. En este caso el identificador del almacenamiento es ‘7e0cda8c145b300b519ed28998a31f801b6d626f’. No olvidemos es más o menos único para el contenido, de modo que este almacenamiento tendrá un id que será único para la combinación del almacenador, mensaje, fecha, árbol raíz y almacenamiento padre. El hash del árbol raíz es único para el contenido del listado del árbol raíz, y el listado del árbol raíz contiene los hashes de los ficheros, que a su vez son únicos para el contenido de los ficheros, de modo que el hash del árbol raíz será único para los ficheros de contenido del almacenamiento. Así, el id de almacenamiento es único para todas las cosas que van en el almacenamiento, incluyendo los contenidos. Inteligente, ¿no es así?

    Ahora podemos tener tres tipos de fichero en el directorio .eden/objetos – ficheros, arboles, y almacenamientos.

    Ok – así que ahora las cosas son un poquito más complicadas que nuestra anterior configuración con copias de ficheros, pero un montón de cosas también son mucho más sencillas. Por ejemplo, ahora podemos olvidarnos del directorio area_preparacion. El área de preparación puede ser un sencillo fichero que contiene el árbol raíz del listado de capturas. Llamemos a ese fichero .eden/indice. Ahora que Eva ha hecho un nuevo archivado, ese fichero puede ser el directorio raiz de listado del archivado anterior (el archivado que acabamos de hacer):

    9e398c7cf8d56e960aa7769839cc0c38b8e12f11 fichero capitulo1.txt
    65735b3705284cdf4a66c2e4812ca13cbaa7cd5d fichero capitulo2.txt
    3c2e09cc43568f13444c075c84b047957f7995a5 fichero capitulo1_discusion.txt
    f31bfa1225f9e0eb6741a0ab1122f8cd2cbedc04 fichero contenido.txt
    be242dba385bc0689be16454e959f4b64c87abce arbol imagenes

    Cuando Eva edita el capitulo1.txt, en vez de copiar el fichero al area_aparcamiento, crea un hash para el nuevo contenido de capitulo1.txt, guarda el nuevo contenido del capitulo1.txt en el directorio .eden/objetos usando el hash como nombre de fichero, y edita el fichero .eden/indice para que apunte a su nuevo contenido del capítulo 1 en vez de que apunte al viejo. Podría automatizar esto con un pequeño comando como eden_aparcar


    def eden_aparcar(nombref):
    # Obtener el hash del contenido del fichero
    contenido_fichero = file(nombref).read()
    hash_fichero = sha1_hash(contenido_fichero)
    # (asumiendo que el nuevo fichero va a ir en el directorio raíz)
    nueva_entrada_raiz = hash_fichero + ' fichero ' + nombref
    listado_raiz = file('.eden/indice').read()
    if nueva_entrada_raiz in listado_raiz:
    # Exactamente este contenido y nombre de fichero y existe
    return
    # Crear una entrada para este contenido del fichero en la base de datos de objetos
    nombref_basedatos = '.eden/objetos/' + hash_fichero
    file(nombref_basedatos, 'w').write(contenido_fichero)
    # Escribir el listado indice con la nueva entrada
    listado_raiz = listado_raiz + nueva_entrada_raiz + '\n'
    file('.eden/indice', 'w').write(listado_raiz)

    Realizar un nuevo almacenamiento conlleva recuperar el contenido de .eden/indice y utilizarlo para crear un nuevo fichero de almacenamiento en .eden/objectos. Utilizando la estructura de nuestro anterior rutina eden_almacenamiento, podría ser algo así:


    def eden_almacenar(almacenador, mensaje):
    # *** este trozo es el mismo de antes ***
    # Obtener el anterior (padre) id de almacenamiento de .eden/CABECERA
    contenido_cabecera = file('.eden/CABECERA').read()
    # Comprobar si es una referencia, desreferenciar si lo es
    # Obtener también el fichero en el que escribir el nuevo id de almacenamiento
    if contenido_cabecera.startswith('ref: '):
    ref_cabecera = contenido_cabecera.replace('ref: ', '')
    fichero_ref_cabecera = '.eden/' + ref_cabecera
    id_cabecera = file(fichero_ref_cabecera).read()
    else:
    fichero_ref_cabecera = '.eden/CABECERA'
    id_cabecera = contenido_cabecera
    # *** desde aquí es diferente ***
    # Crear entrada de árbol raíz en la base de datos de objetos a partir de .eden/indice
    contenido_indice = file('.eden/indice').read()
    hash_indice = sha1_hash(contenido_indice)
    file('.eden/ojetos/' + hash_indice_hash, 'w').write(contenido_indice)
    # Crear información del almacenamiento con el padre fijado a CABECERA
    info_txt = 'almacenador = ' + almacenador + '\n'
    info_txt += 'mensaje = ' + mensaje + '\n'
    info_txt += 'fecha = ' + date.today() + '\n'
    info_txt += 'arbol_raiz = ' + hash_indice + '\n'
    info_txt += 'padre = ' + id_cabecera + '\n'
    # Escribir fichero de almacenamiento en la base de datos de objetos, con hash
    hash_almacenamiento = sha1_hash(info_txt)
    file('.eden/objetos/' + hash_almacenamiento, 'w').write(info_txt)
    # Hacer que el fichero de almacenamiento actual contenga el nuevo id
    file(fichero_ref_cabecera, 'w').write(hash_almacenamiento)

    ¿Qué ocurre si tenemos que hacer una mezcla? ¿Recuerdas que, en los viejos y malos tiempos, teníamos que comparar un montón de ficheros entre ramas, y al ancestro común? Nunca más. Ahora que utilizamos las referencias hash de fichero, todo lo que tenemos que hacer es revisar los listados de árbol. Si el listado de árbol contiene la misma entrada (nombre de fichero y hash), quiere decir que el fichero es idéntico entre los dos árboles, y no necesitamos cargar su contenido para realizar la comprobación. Esto hace que sea muy rápido realizar la comparación entre árboles que no han cambiado demasiado.

    Por supuesto Eva tenía razón. Ahora, si hacemos un nuevo almacenamiento, cuando se modifica un fichero, todo los que guardamos es el contenido del fichero que ha cambiado y un nuevo listado de árbol con el hash actualizado para el fichero modificado. Esto consigue que el almacenamiento de montones de árboles similares sea muy eficiente.

    Alguien debería escribir esto y dárselo a conocer al mundo. Un momento, esos es sólo a nosotros.

  • El séptimo día: ahí estaba git

    El séptimo día es para descansar. Ya está todo hecho, y el trabajo duro ha terminado. En un estado de profunda paz interior, puedes pensar en todo lo que has descubierto en eden:

    Un almacenamiento designa una captura de un conjunto completo de ficheros para tu proyecto.
    El área de preparación (indice) define lo que cambiará entre tu próximo almacenamiento y el almacenamiento anterior.
    Una rama es simplemente un puntero a un almacenamiento, que se mueve cuando realizas otro almacenamiento.
    El control de versiones es muy sencillo de entender.

    Te recuerdas a ti mismo que la vida es muy buena, porque no necesita utilizar un sistema de control de versiones llamado eden, puedes utilizar un sistema muy similar llamado git.

    Si utilizas git, verá que tienes muchos de los amigos de eden. Verás que git crea un subdirectorio .git que contiene el almacén (repository). Reconocerás el directorio .git/objects (.eden/objetos) que contendrá nombres de ficheros con hashes SHA1. Verás que los almacenamientos (commits) tienen hashes SHA1. Reconocerás el fichero .git/HEAD (.eden/CABECERA), .git/refs/heads (.eden/refs/cabeceras), .git/refs/tags (.eden/refs/etiquetas) y .git/refs/heads/master (.eden/refs/cabeceras/maestra). Existe un fichero .git/index (.eden/indice) y se trata del área de preparación. .git/index es un poco más complicado que .eden/indice ya que está adaptado para ayudar con las mezclas (merges) difíciles, pero es la misma idea.

    Ahora vives en el Paraíso del control de versiones. Recuerda mantenerte alejado del árbol de la manzana.

vía

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: