Archivo de la etiqueta: .gitignore

Ignorando ficheros en git – Parte IV: Más patrones

En la entrega anterior estuvimos analizando los patrones básicos que podemos utilizar para ignorar ficheros a través de .gitignore, .git/info/exclude y .gitignore_local. En esta entrega veremos cómo git utiliza los patrones de la shell dentro de estos ficheros.

Todos los patrones que usa git

Hagamos un pequeño repaso de lo que ya sabemos:

  • Las líneas en blanco no se consideran un patrón. Se usan para hacer los ficheros más legibles
  • Si la linea comienza con #, se considera un comentario
  • El prefijo ! sirve para negar un patrón
  • Si el patrón termina en / sólo se aplicará a carpetas, es decir, un patrón “web/” ignorará la carpeta web/ pero no ignorará un fichero que se llame web

Hasta aquí lo que ya habíamos visto ¿qué nos falta?

  • Si el patrón no termina en /, git lo interpreta como un “shell glob pattern“. Internamente git llama a la función fnmatch con el flag FNM_PATHNAME: esto significa que ni el * ni el ? incluyen el carácter /. En seguida veremos ejemplos.
  • Una / al principio del patrón indica el inicio de una ruta relativa a la carpeta en la que se encuentre el fichero .gitignore. Una línea del tipo /config.php ignorará el fichero config.php pero no ignorará web/config.php

Shell globs en git

Los ficheros .gitignore, .git/info/exclude y .gitignore_local permiten incluir patrones de tipo shell. Si habéis trabajado con cualquier shell de Unix o el CMD de windows, estaréis familiarizados con el funcionamiento de estos patrones. En caso contrario, aquí tenéis las reglas que se siguen.

En una expresión que contenga el símbolo “?”, éste se corresponde con uno y sólo un carácter

Ejemplos:

# Ignora los ficheros bg-01.jpg bg-lt.jpg
#
# No ignora los ficheros bg-1.jpg y bg-001.jpg
#
bg-??.jpg

Este patrón ignorará los ficheros cuyos nombres empiezan por “bg-“, van seguidos exactamente de dos caracteres, y luego van seguidos de “.jpg”. Por eso el fichero bg-1.jpg no se ignora, porque entre “bg-” y “.jpg” no hay dos caracteres, sólo uno. Este ejemplo ilustra que el símbolo “?” no indica un caracter opcional.

En una expresión que contenga el símbolo ” * “, éste se corresponde con cualquier número de caracteres

Ejemplos:

# Ignora todos los ficheros acabados en .nib
#
# Ignora tanto MainWindow.nib y builds/MainWindow.nib
*.nib

#Ignora todos los ficheros que empiecen por "bg-" y acaben en ".jpg"
bg-*.jpg

En el primer ejemplo, estamos ignorando cualquier fichero que acabe en .nib, independientemente de la carpeta en la que se encuentre. En el segundo ejemplo, estamos ignorando cualquier fichero que empiece por “bg-” y acabe por “.jpg”. Una pregunta: ¿se ignorará el fichero “bg-.jpg” usando este patrón?. La respuesta la tenéis un poco más abajo.


En una expresión que contenga corchetes [ ], éstos se corresponden con uno y sólo un caracter de los que están incluidos en los corchetes.

Ejemplos:

# Ignora los ficheros *.o y *.a
*.[ao]

# Ignora los ficheros .nib y .xib
*.[xn]ib

Estos patrones no ignoran los ficheros liberia.so o los ficheros mainwindow.ib. Es necesario que alguno de los caracteres que están entre corchetes aparezcan en el fichero.

Si se necesita utilizar los caracteres ? * [ ] en un patrón, estos deberán escaparse con \

Ejemplo:

# Ignorando ficheros que contienen [ y ] en el nombre
#
# Este patrón ignorará los ficheros
# cache/cache-[hG7].cache y cache/cache-[009].cache
cache/cache-\[???\].cache

Shell globs y carpetas

¿Qué significa que git utilice el flag FNM_PATHNAME al llamar a la función fnmatch? Que los símbolos * ? y [] no incluyen el caracter ” / ” de separación de directorios.

# El uso de FNM_PATHNAME hace que el siguiente patrón ignore los ficheros
# web/fichero.html, pero no ignore el fichero web/admin/fichero.html
#
# Como el " * " no incluye " / ", el fichero web/admin/fichero.html no 
# encaja con el patrón
web/*.html

¿Qué ocurre si necesitamos justo el efecto contrario? es decir, queremos ignorar todos los ficheros .html que están en las subcarpetas de web pero no ignorar los ficheros .html que están en web. El patrón sería el siguiente:

# Ignora los ficheros .html que están en subcarpetas de la carpeta web
# No ignora los ficheros web/*.html
#
# Con este patrón, el fichero web/fichero.html se incluirá en el repositorio
# mientras que ficheros con web/admin/fichero.html o web/images/index.html se 
# ignorarán
web/**/*.html

Otra pregunta: Este patrón ¿ignora el fichero web/admin/subcarpeta/fichero.txt?

Rutas relativas

Un patrón que comienza por ” / “, indica que para decidir si el fichero se ignora o no, se utiliza una ruta relativa al fichero .gitignore:

# Ignora los fichero *.c en la carpeta raiz del proyecto: memory.c
# No ignora los ficheros en subcarpetas como modules/cache.c
/*.c

Conclusiones

En esta entrega hemos visto cómo interpreta git los patrones glob shell y los patrones a rutas relativas. En la siguiente entrega veremos cómo podemos usar múltiples ficheros .gitignore. Estos nos ayudarán a salvar las limitaciones que tenemos a la hora de trabajar con múltiples subcarpetas.

Respuestas:

  1. Efectivamente, el patrón “bg-*.jpg” ignora que fichero “bg-.jpg”. Eso es porque a diferencia de símobol “?”, el símbolo “*” se corresponde con cualquier número de caracteres, incluido cero caracteres.
  2. El patrón web/**/*.html no ignora el fichero web/admin/subcarpeta/fichero.txt. Dado que se usa el parámetro FNM_PATHNAME, el ” ** ” no incluye el caracter ” / ” y por lo tanto este patrón sólo incluye las carpetas que son hijos de web, no incluye los nietos y niveles de más profundidad en la jerarquía de carpetas.

Ignorando ficheros en git – Parte III: Patrones

En la segunda entrega de esta serie aprendimos en qué orden se aplican los patrones para ignorar cambios en ciertos ficheros del repositorio. En esta tercera entrega veremos los patrones y reglas que git utiliza para interpretar los ficheros it ignore.

Comentarios y líneas en blanco

Podemos hacer nuestro fichero más legible si incluimos comentarios y separadores:

  • Cualquier línea que comience por # es un comentario
  • Una línea en blanco no se considera un patrón por parte de git, por lo que puede ser usada como un separador

Sé considerado con el resto de los miembros del equipo y comenta adecuadamente cada uno de los patrones que utilices. Indica el porqué de ese patrón de forma que el resto de compañeros o colaboradores sepan porqué has decidido en un momento dado ignorar ciertos ficheros. Y una cosa más, no todo el mundo usa las mismas herramientas que tú ni tiene tu mismo nivel de conocimientos en determinadas materias así que piensa un poco en los demás y ayúdales:

# Jetbrains IDEs: PHPStorm / RubyMine  
# (No todo el mundo tiene que saber que los ficheros .idea son de un 
# entorno de desarrollo)
.idea 

# Ficheros de documentación sphynx. Puedes generar tu copia local de la doc
# en /doc/build utilizando los scripts sphynx en /doc
/doc/build/

Negación

Utilizando el carácter podemos invertir un patrón ¿Qué significa invertir un patrón? Veamoslo con un ejemplo:

# Incluir expresamente en el repositorio todos los ficheros con extensión php
!*.php

¿Porqué podemos necesitar este tipo de patrones? Imagina que estás trabajando en un proyecto en PHP; como te puedes imaginar, si tu repositorio sólo tuviese estas dos líneas en los ficheros git/info/exclude, .ignore y .gitignore_local podríamos quitarlas ya que el efecto sería el mismo. Ahora bien ¿para qué podemos necesitar un patrón como este?:

  • Tal y como vimos en la anterior entrega, para invertir patrones de menor prioridad
  • Crear excepciones a patrones muy restrictivos

Veamos un ejemplo del segundo caso. Siguiendo con el ejemplo de un proyecto en PHP, podemos eliminar la línea anterior ya que no hace falta. Ahora bien ¿qué hacemos con los ficheros html?:

# Ignorar todos los ficheros .html...
*.html

 #... excepto en la carpeta web
!web/*.html

En este caso, cualquier fichero con extensión .html en cualquier carpeta se ignorará, excepto los ficheros que estén en la carpeta web. Cuidado porque ficheros .html que estén en una subcarpeta de web, como por ejemplo web/archive, también se ignorarán. Más adelante veremos porqué.

Ignorando carpetas enteras

Si un patrón acaba con una barra /, git ignorará cualquier fichero dentro de esa carpeta:

# Ignorar el contenido de las carpetas log y cache
cache/
log/

Git ignorará cualquier fichero que esté dentro de cache/ o log/ o en cualquier subcarpeta que cuelgue de ellas.

Este patrón no se lleva muy bien con la negación ¿Por qué decimos esto? Imagina que quieres ignorar el contenido de la carpeta web entera (incluyendo todas las subcarpetas que cuelgan de ella) excepto el fichero web/index.php. Con lo que sabemos hasta ahora, intentaríamos una combinación de patrones como la siguiente:

#Esto no funciona:
# git ignorará también el fichero index.php
web/
!web/index.php

Desafortunadamente esto no funciona y git ignorará fichero index.php. Veremos en una futura entrega cómo resolver este problema.

Una última nota sobre el uso de /. Si usamos el siguiente patrón

#Ignorar el contenido de la carpeta web
web/

y web no es una carpeta sino un enlace simbólico o un fichero regular:

total 16
-rw-r--r-- 1 aalba staff 0 22 dic 14:42 Alumno.php
-rw-r--r-- 1 aalba staff 0 22 dic 14:41 Curso.php
-rw-r--r-- 1 aalba staff 0 22 dic 14:42 Licencia.php
-rw-r--r-- 1 aalba staff 339 7 dic 16:39 README.md
drwxr-xr-x 3 aalba staff 238 29 dic 08:29 builds
lrwxr-xr-x 1 aalba staff 11 29 dic 08:29 web -> builds/html

¡el fichero no se ignora!

$ git status
# On branch master
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# web
  ^^^ -> el enlace simbólico o fichero regular no se ignora ya que el patrón
         web/ sólo se aplica a carpetas

Esto es todo por hoy, en la siguiente entrega de la serie veremos los patrones shell que podemos utilizar.

.gitkeep – Incluyendo carpetas en los repositorios

¿Has visto esos ficheros .gitkeep que Ruby on Rails crea cuando generas un proyecto nuevo? ¿por qué están ahí? Git sólo gestiona ficheros, no carpetas. Esto es así por la forma en la que el índice (o staging area) de git se ha diseñado: sencillamente no lo permite. Tal y como se dice en la FAQ, nadie lo suficientemente competente se ha ocupado de implementar el soporte para carpetas. Aún así, nos encontraremos con que en muchas ocasiones nos vendría muy bien poder añadir carpetas vacías al repositorio.

¿Porqué carpetas vacías?

Hay muchas herramientas, aplicaciones y frameworks que requieren de la existencia de ciertas carpetas para funcionar. Un ejemplo: tanto Symfony como Ruby on Rails necesitan disponer de directorios en los que almacenar cachés y logs. El contenido de estas carpetas se ignora siempre del repositorio; son archivos locales que sólo tienen sentido en cada copia local ¿pará que queremos distribuir a nuestros compañeros de trabajo los archivos de log creados por nuestro portátil mientras estamos desarrollando la aplicación? Es un derroche inútil de recursos y una fuente de conflictos que nos quitará mucho tiempo y no nos aportará nada.

El problema que se deriva de esto es que si ignoramos toda la carpeta completa utilizando un patrón de este tipo:

#Ignorando cache y log
cache/
log/

cuando otra persona clone el repositorio esas carpetas no se crearán. A no ser que las cree manualmente, la aplicación no funcionará.

La solución es decirle a git que cree las carpetas cuando clone el repositorio y luego que ignore su contenido. ¿Cómo se hace? creamos un fichero vacío dentro de la carpeta, lo añadimos al repositorio y le decimos a git que ignore el resto de archivos que están dentro. Esto se puede hacer de dos maneras:

  • A través de un fichero .gitkeep
  • A través de un fichero .gitignore

.gitkeep

Lo primero que debemos tener claro es que el uso del fichero .gitkeep es un convenio. En ningún lugar de la documentación de git veréis que se haga referencia a este fichero. De hecho, si no os gusta el nombre nada os impide cambiarlo y usar cualquier otro.

Veamos cómo usar este fichero:

  • Antes de añadir al fichero .gitignore el patrón que ignore la carpeta cache, creamos el fichero cache/.gitkeep, bien usando el comando touch o con nuestro editor de texto o entorno de desarrollo.
# touch cache/.gitkeep
  • Una vez creado el fichero lo añadimos al repositorio y hacemos el commit:
Añadiendo .gitkeep al repositorio con Bitbucket
# git add cache/.gitkeep 
# git commit -m'Añadiendo el fichero cache/.gitkeep
  • Editamos el fichero .gitignore (o lo creamos si no existe) y añadimos el patrón para ignorar la carpeta cache:
# Ignorando los ficheros de la carpeta cache/
cache/
  •  Hacemos commit del fichero .gitignore

Ya está, la próxima vez que alguien cree el repositorio, se creará el fichero cache/.gitkeep a la vez que se ignora cualquier otro fichero que esté en la carpeta cache/.

Esta técnica utiliza la siguiente característica de git: si un fichero ya está en el índice (es decir, git ya está haciendo seguimiento de las modificaciones de ese fichero) a pesar de que el .gitignore lo ignore, git lo seguirá monitorizando.

Utilizar un fichero .gitignore

El método anterior tiene una pega: tenemos patrones para ignorar ficheros que requieren que tengamos varias excepciones en el índice de git (cache/.gitkeep, log/.gitkeep, etc). No hay problema con ello, aunque si no os gusta podemos utilizar otra de las características de git para no tener excepciones en el índice: utilizar un fichero .gitignore dentro de la carpeta que queremos ignorar.

  • Crear el fichero cache/.gitignore con los siguientes patrones
# Ignorar todos los ficheros...
*

# ...excepto el fichero .gitignore
!.gitignore
  • Añadir el fichero cache/.gitignore al repositorio y hacer un commit

Si repetís esta operación con las carpetas cuyos ficheros queráis ignorar, no tendréis necesidad de andar incluyendo excepciones en el índice.

Cualquiera de los dos métodos nos permite tener carpetas “vacías” en el repositorio, aunque vacías, vacías del todo no lo están. ¿Qué método usáis vosotros normalmente? ¡Esperamos vuestros comentarios!

¡Happy gitting!

Ignorando ficheros en git – Parte I: Formas de ignorar ficheros

En esta serie vamos a profundizar un poco más en el fichero .gitignore, para qué sirve y cómo podemos usarlo, empezando por cómo decide git si ignorar un fichero o no.

En cualquier proyecto, existen ficheros que no deben incluirse en el repositorio. Por nombrar algunos de ellos:

  • Ficheros de log
  • Cachés
  • Ficheros que contienen claves privadas (App keys y App secrets) para acceder a las API o servicios web
  • Ficheros de configuración con contraseñas de bases de datos
  • Ficheros binarios compilados
  • Ficheros de configuración del entorno de desarrollo

Cuando empiezas a utilizar git, tardas muy poco en preguntarte cómo puedes evitar subir estos ficheros al repositorio. Si buscas la respuesta en google, encontrarás en todos sitios que lo que tienes que hacer es crear un fichero .gitignore que hay que subir al repositorio o crear un fichero .git/info/exclude para poner las reglas locales. Tú lo creas y entonces empiezan las preguntas: ¿Y cuál de los dos tiene preferencia? ¿Y porqué lo tengo que incluir en el repositorio?  ¿Y porqué subo uno sí y otro no? ¿Un fichero, no son propiedades como en subversion? ¿Y si yo quiero usar un .gitignore y el resto del mundo otro?

Para responder a estas preguntas debemos platearnos la pregunta adecuada que es…

¿Cómo decide git qué ficheros incluir o no en el repositorio?

La respuesta a esta pregunta está en la página de manual de gitignore. Por orden de prioridad, git busca patrones para excluir ficheros en los siguientes lugares:

  1. En la línea de comandos: ciertos comandos, como por ejemplo git ls-files o git read-tree, permiten excluir ficheros a través de argumentos. Estos patrones tendrán la máxima prioridad.
  2. En los ficheros .gitignore
  3. En el fichero $GIT_DIR/info/exclude
  4. En el fichero definido en la variable de configuración core.excludesfile
Todos los ficheros mencionados arriba incluyen en su interior un conjunto de patrones de ficheros que se van a ignorar. Cada patrón va en una línea. Por ejemplo, si incluimos la siguiente línea en alguno de estos ficheros:
*.jar

git ignorará cualquier fichero con extensión .jar en cualquier directorio.

¿Qué quiere decir que git ignora ficheros?

Veamos qué ocurre cuando ignoramos un fichero en un repositorio. Imagina que acabas de empaquetar tu aplicación PHP en un fichero .phar en la carpeta builds. Esto es lo que veríamos en SourceTree:Fichero phar marcado como untracked

Si no hacemos nada, tendremos que acordarnos siempre de no incluir este fichero al hacer commit. Esto es peligroso, ya que si alguna vez tenemos muchos ficheros pendientes de añadir o excluir del commit, puede que se nos “cuele” mynewapp.phar sin darnos cuenta.

Para evitar el estar pendiente de subir o no estos ficheros, usamos un editor de texto y abrimos el fichero .git/info/exclude de nuestro repositorio. Añadimos el siguiente patrón al final del fichero:

*.phar

Cuando volvamos al SourceTree, veremos que el fichero ya no está:SourceTree después de ignorar *.phar

Ya está, ya estamos tranquilos: no añadiremos por accidente el fichero al repositorio.

Seguro que os estaréis preguntando: “¿Porqué no lo ha puesto en .gitignore, como dice nuestro gran San Google?”. A lo largo de la serie veremos si poner este patrón en el fichero .git/info/exclude es la mejor opción o conviene ponerlo en otro lugar. De momento lo dejaremos así.

Esto ha sido todo por hoy. En la siguiente entrega veremos qué patrones incluir en cada uno de los tres métodos que podemos utilizar y cómo trabajar con el orden de prioridades descrito anteriormente.

Crear ficheros en repositorios github a través de la web

Recientemente, github ha añadido una nueva funcionalidad a sus usuarios través de la web: la posibilidad de añadir ficheros directamente al repositorio a través del navegador. ¿Significa esto que podremos programar nuestra próxima aplicación usando la web de github? Pues no, aunque para gustos los colores.

Bromas aparte, la funcionalidad puede parecer una tontería, pero bien utilizada es bastante útil. A modo de ejemplo, la vamos a utilizar en un caso real para crear un fichero README.md en un repositorio que no lo tiene.

Si lo tuviese que hacer por el procedimiento normal, tendría que hacer lo siguiente:

  • Crear el fichero README.md en mi copia local
  • git add README.md para añadir el fichero al índice
  • git commit -m’Añadiendo README.md’ para hacer el commit
  • git pull para bajarme a mi copia local las últimas modificaciones
  • Si en el último paso ha habido algún conflicto, tendríamos que resolverlo (si habéis usado bien las ramas, esto no pasará)
  • git push para subir los cambios

La verdad es que es bastante trabajo para subir un fichero de texto ¿verdad? Bueno, pues vamos a ver cómo lo hacemos desde la web.

En primer lugar, vamos a la página de github, accedemos con nuestro usuario y contraseña y vamos a la página de nuestro repositorio:

Creando ficheros desde github

En la captura anterior veis dónde está el botón. Hacemos clic sobre él y se nos abrirá la pantalla para crear el fichero:

Pantalla para crear un fichero en github

La pantalla no tiene mucho misterio, como podéis ver en la captura:

  • Seleccionamos en qué rama lo queremos crear
  • Introducimos el nombre y extensión del fichero
  • Ponemos el contenido
  • Rellenamos el mensaje para el commit (el resumen es obligatorio)

Una vez tenemos el fichero escrito, hacemos clic sobre “Commit New File” y si no se produce ningún error, volveremos a la pantalla del repositorio con algunos cambios:

  • El fichero ya lo tenemos en el repositorio
  • Hay un commit más en el repositorio

Tras subir el fichero

Pues ya está. Para obtener el fichero en nuestra copia local, hacemos un pull de la rama en la que hemos subido el fichero. Como veis, es un poquito más cómodo que el método normal y nos puede ahorrar tiempo en ciertas tareas.

¿Cuándo conviene usar esta funcionalidad de github?

Como pone en la propia entrada del blog en github, es un método pensado para ficheros como el README, LICENSE, el .gitignore o escribir documentación. Usa el sentido común y sé práctico. Yo, por ejemplo, lo he usado para crear un fichero sencillo con datos de prueba en json y pasárselo a mis compañeros desde el iPad en el sofá o para crear un fichero sencillo con traducciones en formato CSV.

Creando .gitignore desde plantillas

Los chicos de github están en todo, así que para ahorrarnos un poco de trabajo han creado una serie de plantillas para crear ficheros .gitignore. Para verlas y seleccionarlas, creamos un nuevo fichero como hemos visto antes y en el nombre del mismo escribimos “.gitignore”:

Creando un .gitignore desde plantilla

Cuando lo hagamos, veremos a la derecha un desplegable para seleccionar la plantilla. La seleccionamos y ya podemos editar el fichero, quitando reglas o añadiendo las que puedan faltar. Al terminar, no nos olvidamos de hacer clic sobre “Commit New File”.

¿Y si el repositorio no es mío?

Si intentas crea un fichero sobre un repositorio sobre el que no tienes permiso, verás el siguiente mensaje:

Creando fichero sobre repositorio sin permiso de escritura

Lo que nos está diciendo es que antes de crear el fichero va a hacer un fork del repositorio en nuestra cuenta y que el fichero será creado en el nuevo repositorio. Una vez creado, podemos hacer un pull request al administrador del repositorio original para que incorpore el nuevo fichero en su repositorio.

.gitignore en XCode

Cuando creamos un proyecto nuevo en XCode y marcamos la opción para que utilice el control de versiones, ¿qué está haciendo XCode por debajo?

Básicamente sigue los siguientes pasos:

  • Crea e inicializa un repositorio git en la carpeta raíz del proyecto
  • Añade todos los ficheros al repositorio excepto las carpetas [nombre].xcodeproj/project.xcworkspace y [nombre].xcodeproj/xcuserdata/
  • Hace un commit con el comentario “Initial Commit”

Para ver en qué estado ha quedado el repositorio vamos a seguir los siguientes pasos:

  • Creamos proyecto nuevo en XCode, cualquiera de las plantillas que vienen por defecto nos vale (tanto de iOS como de Mac)
  • Abrimos SourceTree
  • En la ventana “bookmarks” hacemos clic sobre el icono para añadir repositorio

Añadiendo repositorio de XCode a SourceTree paso 1

  • A continuación seleccionamos “Add working copy”, ya que XCode ya ha inicializado el repositorio por nosotros. En el campo “Working copy path” buscamos la carpeta en la que está nuestro proyecto e introducimos un alias campo “”Bookmark Name” si el que crea SourceTree no nos gusta. Hacemos clic sobre “Add” cuando hayamos terminado

Añadiendo repositorio XCode a SourceTree paso 2

  • Cuando se cierre la ventana y volvamos al listado de repositorios de SourceTree, hacemos doble clic sobre el repositorio que acabamos de crear

Los ficheros que XCode no incluye

Cuando git crea el repositorio, añade el siguiente contenido al fichero .git/info/exclude

.DS_Store
UserInterface.xcuserstate

Este fichero, a diferencia de .gitignore, contiene reglas que se aplican sólo a nuestro repositorio local (si compartimos el repositorio con otros usuarios a través de github o bitbucket, no verán estas reglas).

Aparte de estas dos reglas, ¿qué más no ha incluido XCode en el repositorio nada más crearlo?

Si no habéis abierto todavía el repositorio en SourceTree (haced doble clic sobre él en la ventana de Bookmarks) hacedlo ahora. A la izquierda haced clic sobre la rama master, veréis que en la sección “Files in the working tree” nos aparecen los ficheros que XCode no ha añadido al repositorio:

Ficheros nos incluidos por XCode en el repositorio

Si os fijáis, estos ficheros no están incluidos y no se están ignorando. Eso significa que cualquiera podría añadirlos al repositorio en cualquier momento, veamos cómo.

Dejemos la ventana del SourceTree como está (luego volveremos a ella) y volvamos a XCode para modificar alguno de los ficheros (como si estuviésemos trabajando en algo serio). Una vez modificado, vamos a Archivo -> Source Control -> Commit (u OPCIÓN-CMD-C) para acceder a la pantalla para enviar un commit:

Commit desde XCode

Como podemos ver, en la parte de arriba a la izquierda tenemos los ficheros que no están incluidos inicialmente en el repositorio, y que son los mismos que hemos visto con SourceTree. Si los marcáis junto a los otros tres ficheros que he modificado, los añadiríamos al repositorio.

La pregunta es…

¿Deben estar los “Workspace Settings” y “User Data” en el repositorio?

“Workspace Settings” contiene información relacionada con los proyectos, así que a no ser que sea algo que estrictamente sólo vamos a usar nosotros, conviene incluirlo en el repositorio.

Sin embargo, la carpeta “User Data” que contiene la configuración propia de nuestro usuario, sí conviene ignorarla ya que si no lo hacemos y trabajamos con otras personas, vamos a encontrarnos constantemente con conflictos difíciles de resolver sobre el IDE, no sobre el proyecto en sí.

Para ignorar estos ficheros, abrimos un editor de textos cualquiera y dentro de la raíz del proyecto creamos un fichero de texto plano al que llamaremos “.gitignore” (no os olvidéis del “punto” delante del nombre del fichero) y añadimos las siguientes líneas:

.DS_Store
UserInterface.xcuserstate
xcuserdata

  • Las dos primeras reglas son el mismo contenido que teníamos en .git/info/exclude. Las ponemos aquí para asegurarnos de que todo el mundo que se descargue el repositorio ignora estos ficheros
  • La tercera línea es la que ignora las preferencias de usuario

Una vez creado el fichero, volvemos a la pantalla de commit del XCode y veremos lo siguiente:

Ignorando User Data

¡Ya no podemos enviar la carpeta “User Data” por error!. Marcamos la carpeta “Workspace Settings” junto al resto de ficheros y hacemos el commit.

¿Ya está?

No, todavía no. Cuando he creado el fichero .gitignore, no lo he creado usando XCode así que si habéis hecho los commits desde XCode como yo, el fichero .gitignore no está incluido en el repositorio. ¿No me crees? Vuelve a la ventana del SourceTree que hemos dejado olvidada hace unos minutos y verás:

Añadiendo .gitignore al repositorio

Efectivamente, no está incluido. En otro post os contaré porqué necesitamos incluirlo; para no desviarnos del tema de esta entrada, vamos a incluirlo arrastrándolo al área “Files staged in the index” y haciendo clic sobre “Commit”.

(Nota: si preferís añadir el fichero por línea de comandos os resumo los pasos. Abrís una terminal, vais a la carpeta raíz del proyecto de XCode, ejecutáis “git add .gitignore” y luego ejecutáis “git commit -m’Añadiendo .gitignore’ “)

Ahora sí, ya tenemos la versión más sencilla del fichero .gitignore.

¿La versión más sencilla? ¿…es que faltan más cosas?

Sí, en función del proyecto en el que estéis trabajando podéis necesitar no incluir otros ficheros como .nib, clases obsoletas, etc. La comunidad ha generado ya un fichero de reglas estándar que podéis consultar en Stack Overflow, y que copio un poco más abajo para que las tengáis a mano.

¿Usáis vosotros alguna regla que no esté en este fichero? ¿Cuáles son las reglas que utilizáis normalmente? Compartid vuestro .gitignore como un gist en github y poned aquí el enlace. ¡Compartir es vivir!

#########################
# .gitignore file for Xcode4 / OS X Source projects
#
# NB: if you are storing "built" products, this WILL NOT WORK,
#   and you should use a different .gitignore (or none at all)
# This file is for SOURCE projects, where there are many extra
#   files that we want to exclude
#
# For updates, see: http://stackoverflow.com/questions/49478/git-ignore-file-for-xcode-projects
#########################

#####
# OS X temporary files that should never be committed

.DS_Store
*.swp
*.lock
profile


####
# Xcode temporary files that should never be committed
# 
# NB: NIB/XIB files still exist even on Storyboard projects, so we want this...

*~.nib


####
# Xcode build files -
#
# NB: slash on the end, so we only remove the FOLDER, not any files that were badly named "DerivedData"

DerivedData/

# NB: slash on the end, so we only remove the FOLDER, not any files that were badly named "build"

build/


#####
# Xcode private settings (window sizes, bookmarks, breakpoints, custom executables, smart groups)
#
# This is complicated:
#
# SOMETIMES you need to put this file in version control.
# Apple designed it poorly - if you use "custom executables", they are
#  saved in this file.
# 99% of projects do NOT use those, so they do NOT want to version control this file.
#  ..but if you're in the 1%, comment out the line "*.pbxuser"

*.pbxuser
*.mode1v3
*.mode2v3
*.perspectivev3
#    NB: also, whitelist the default ones, some projects need to use these
!default.pbxuser
!default.mode1v3
!default.mode2v3
!default.perspectivev3


####
# Xcode 4 - semi-personal settings, often included in workspaces
#
# You can safely ignore the xcuserdata files - but do NOT ignore the files next to them
#

xcuserdata


####
# XCode 4 workspaces - more detailed
#
# Workspaces are important! They are a core feature of Xcode - don't exclude them :)
#
# Workspace layout is quite spammy. For reference:
#
# (root)/
#   (project-name).xcodeproj/
#     project.pbxproj
#     project.xcworkspace/
#       contents.xcworkspacedata
#       xcuserdata/
#         (your name)/xcuserdatad/
#     xcuserdata/
#       (your name)/xcuserdatad/
#
#
#
# Xcode 4 workspaces - SHARED
#
# This is UNDOCUMENTED (google: "developer.apple.com xcshareddata" - 0 results
# But if you're going to kill personal workspaces, at least keep the shared ones...
#
#
!xcshareddata


####
# Xcode 4 - Deprecated classes
#
# Allegedly, if you manually "deprecate" your classes, they get moved here.
#
# We're using source-control, so this is a "feature" that we do not want!

*.moved-aside


####
# UNKNOWN: recommended by others, but I can't discover what these files are
#
# ...none. Everything is now explained.
.

¡Happy gitting!