Repasando GIT de arriba a abajo
Después de haber hecho una formación muy interesante sobre GIT, aquí puedo mostrar todas mis conclusiones, un pequeño "walk-through" por toda su funcionalidad, y sobre todo me sirve a mi personalmente con una pequeña guía online por si acaso tengo que revisarla algún día por si se me olvidase algún comando importante.
La verdad que me gustaría algún día, darle un poco de formato mas sofisticado y organizarlo mejor por secciones. Pero de momento, consideremoslo "mis apuntes sobre GIT en borrador" :)
Configuración Básica de GIT
Para identificarse en los commits: git config --global user.name "Manuel Camargo" git config --global user.email [email protected] Para colorear el prompt: git config --global color.ui true Para ver todos los datos de configuracion: git config --list Para ver un dato en concreto: git config user.email Para configurar un dato en conreto para el repo local (no para todos los repos en el sistema): git config user.email [email protected]
Iniciando un repositorio Git: git init
Información acerca del estado de los ficheros: git status
Para meter los ficheros en estado "preparado": git add <nombre del fichero> (Añade solo un fichero) git add --all (Añade todos los ficheros) git add <directorio> (Añade los ficheros de ese directorio) git add *.ext (añade todos los ficheros con una extension ext concreta en el directorio donde se ejecuta el comando) git add "*.ext" (añade todos los ficheros con una ext concreta en todo el proyecto desde el directorio donde se ejecuta) git add -A (creo que es lo mismo que --all) git add . (Igual que el -A pero mete en el commit todo menos los deleted) git add -u (Igual que el -u pero mete en el commit todo excepto nuevos ficheros)
Sacar del estado preparación: git reset HEAD <fichero> (HEAD puede ser el tag que se encuentre en concreto, last commit en el ultimo branch (master generalmente))
Para actualizar el repositorio local (subir los ficheros que están en estado preparado) git commit -m "Descripción del commit" git commit -a -m (añade al estado preparación y envia el commit de golpe, pero sin contar nuevos ficheros recién creados, para ediciones masivas es muy cómodo)
Saber la historia de los commits: git log
Ver diferencias en los ficheros que fueron "preparados" con respecto al anterior estado: git diff --staged Para ver cambios desde el ultimo commit: git diff y git diff HEAD ses lo mismo Para ver cambios desde commits atras: git diff HEAD^^ (dos atras) git diff HEAD~5 (cinco atras) git diff HEAD^..HEAD (para comparaciones entre dos commits) Para hacer comparaciones directamente entre los SHA git diff <sha1>..<sha2>
Retrotraer un fichero al estado del anterior commit (borrar los cambios hechos básicamente, una especie de Undo): git checkout -- <fichero>
Cancelar el ultimo commit y volver al estado preparación: git reset --soft HEAD^
Cancelar el ultimo commit y volver a un estado sin preparación: git reset --hard HEAD^ Cuantos mas ^ pongamos, mas vamos hacia atrás en los ultimos commits.
Reenviar el ultimo commit (cambia hasta el ultimo mensaje del commit): git commit --amend -m "Nuevo Mensaje"
Para push y pulls especificar nombre del repositorio GIT: git remote add origin <url del repositorio> (origin es como el nombre del "favorito" para ese repositorio) git remote remove <nombre del "favorito"> git remote show origin (para ver todos los branches) git remote prune origin (para limpiar remote branches que fueron borradas por otros usuarios del sistema)
Para saber los "favoritos" creados: git remote -v
Para subir nuestro repositorio MASTER al repositorio: git push -u origin master git push origin <branch> (si queremos subir un branch al repo general) git push origin :<branch> (para borrar un branch remoto) git push origin <branch>:master (para subir un branch al master del repositorio remoto)
Para bajarnos un proyecto: git pull
Clonando Repositorio: git clone <git_url> <directorio local>
Creando una Rama: git branch <nombre> git branch (comprueba en que rama estamos) git branch -d <nombre> (borrar una rama) git branch -D <nombre> (si hay commits en el branch que no han sido fusionados da un error con -d, pero con -D borraria definitivamente) git branch -r (lista los branches remotos)
Cambiar de Rama: git checkout <nombre> git checkout -b <nombre> (crea y cambia simultaneamente)
Fusionar Ramas: git merge <nombre>
Etiquetas, pueden servir para darle a un commit un numero de versión: git tag (para listar las etiquetas) git checkout <etiqueta> (para ir a dicha etiqueta) git tag -a <etiqueta> -m "Descripcion" (para crear una nueva etiqueta) git push --tags (para subir al servidor remoto nuestros tags)
Para "fusionar" (realmente no es fusionar, en este caso vamos a recolocar los commits como si hubiera sido todo un flujo de trabajo único) pero sin hacer commits de merge utilizamos: git rebase master (o donde queramos hacer el rebase) git rebase --continue (cuando hayamos arreglado los problemas)
Proceso de Rebase de un Branch con modificaciones - Vamos a la rama que vamos a hacer rebase git checkout -rama- git rebase master git checkout master git merge -rama-
Rebase desde un Remote git fetch git rebase --> Puede ser que haga falta editar cambios y arreglarlos git rebase --continue git rebase --skip (si no queremos arreglar problemas) git rebase --abort (si queremos parar el rebase y volver atras)
Gestionando el Log: git log --pretty=oneline (sha + Descripcion del commit) git log --pretty=format:"%h %ad- %s [%an]" (en este caso muestra un log personalizado, si ponemos git help log muestra las opciones existentes para formatearlo, seria interesante crear un alias para este comando una vez que lo hayamos personalizado a nuestro gusto) En vez crear un alias de sistema, también se puede hacer con git: git config --global alias.<nombre_del_comando> "log --pertty=format:'.........' --graph" git log --oneline -p (muestra el online para la cabecera (sha + Descripcion) y aparte muestra todos los cambios en los ficheros realizados en formato patch (con + para las añadiduras y - para las borradas)) git log --oneline --stat (en vez de mostrar detalle de los cambios, muestra solo el numero de cambios hechos) git log --oneline --graph (podemos ver como un grafico de la linea del tiempo de los commits y los branches y los merges) git log --until=1.minute.ago git log --since=2012-01-01 --until=2012-12-31 (muestra el log de un periodo de tiempo)
Si queremos ver los cambios de un fichero como han ido evolucionado: git blame <fichero> --date short (pone la fecha en formato corto)
IGNORANDO FICHEROS DE LOS COMMITS:
Ignore en Repositorio Local: .git/info/exclude -> aqui se pueden poner directamente ficheros o directorios, o patrones que no queremos subir a los commits Ignore en los Repositorios de todo el Mundo: .gitignore -> aquí podemos poner lo mismo que en exclude
Para borrar ficheros del repo: git rm <fichero> git rm --cached <fichero> (solo se borra del repositorio, pero no a nivel del directorio local, simplemente se pasa de dicho fichero a la hora de adds y commits)
Se pueden recortar con Alias como vimos antes comando mas comunes haciéndolos mas cortos a voluntad. Por ejemplo para status: git config --global alias.st status
Rebase Manuales (interactivos) git rebase -i HEAD~3 (permite modificar hasta tres commits hacia atras) Con log vemos los commits de mas nuevos a mas viejos, pero con commits interactivos vemos de mas viejos a mas nuevos
Tipos de comandos en el rebase interactivo: pick: sirve para elegir la posición del commit reword: sirve para cambiar la descripción del commit edit: Sirve para modificar código de un commit, por ejemplo para dividir código de un commit en dos commmits, o añadir mas código a un commit
Si hacemos un edit podríamos: a) Añadir mas código al commit 1. Subir nuevos ficheros a modo "Preparacion" y hacer un commit --amend 2. Luego hacer un rebase --continue para terminar la edición del Rebase Interactivo
b) Dividir un commit en dos: 1. Primero sacamos del estado preparación todos los ficheros: git reset HEAD^ 2. Luego subimos los ficheros que queramos hacer el primer commit y hacemos el commit 3. Luego subimos el resto de los ficheros que queramos hacer un segundo commit y hacemos el commit 4. Y hacemos el rebase --continue para terminar con el proceso de rebase
squash: Sirve para fusionar dos commits, se fusionaria el que seleccionamos con el precedente (el que esta justo encima en la lista del rebase). Al finalizar el squash hay que elegir una nueva Descripcion.
Para guardar borradores (manda los últimos cambios en ficheros a un estado temporal, y carga el ultimo commit): git stash save
Para recuperar los cambios del stash de la rama que estabamos trabajando: git checkout <branch> git stash apply Se puede regresar a otros puntos del stash: git stash apply <nombre> Para saber los stash en memoria: git stash list git stash drop (sirve para eliminar del stash) git stash == git stash save git stash pop = git stash apply + git stash drop (Atencion: si hay conflictos en el merge, el drop no se haria automaticamente) Stash guarda los cambios tanto en estado Preparacion como en estado Inicial (sin preparacion) git stash save --keep-index (mantiene los cambios en Preparación) Si hay ficheros nuevos "untracked" Stash no los tiene en cuenta (solo los que esten en estado edicion). Para añadir los untracked: git stash save --include-untracked
git stash list --stat (sirve para ver los cambios que estan en el stash en formato "stat" (solo se ven numero de lineas añadidas o borradas) git stash show <nombre stash> (Sirve para ver los datos de un stash especifico) ((Se podrían usar cualquiera de los parámetros de log para ver el estado de los stashes tanto en list como en show))
git stash save "Descripcion" (se podria poner una descripcion a los stashes como los commits)
git branch <nuevo-nombre-de-rama> <nombre del stash> (Sirve para meter los cambios de un stash en una nueva rama diferente a la que estaba asociada originalmente)
git stash clear (limpia todos los "stashes")
Destruyendo commits de la historia de git 1. Hacer un backup git clone <repo> <carpeta> 2. Hacer la modificacion git filter-branch --tree-filter <comando> Por ejemplo: git filter-branch --tree-filter 'rm -f config.php' (para borrar ese fichero de configuración con passwords que nos equivocamos en todos los commits donde aparezca) git filter-branch --tree-filter 'find . -name "*.mp4" -exec rm {} \;' (en este caso borraria todos los ficheros de video mp4 en todos los commits que se hubieran dado de alta) git filter-branch --tree-filter '<comando>' -- --all (recorre no solos los commits sino los branches) git filter-branch --tree-filter '<comando>' -- HEAD (recorre solo la rama actual) Importante: Si el comando UNIX falla, el comando git filter-branch fallara. Por eso es interesante poner -f en el rm por ejemplo (fuerza el rm incluso aunque no exista, no volcaria errores) Si no queremos tener que hacer checkouts y re- commits a todos los commits donde vamos a realizar este filter-branch que tardaría mucho, lo ideal seria solo operar en el estado Preparacion de los ficheros: git filter-branch --index-filter '<comando>' - El comando unix debe operar dentro del staging por tanto ya no valdria rm -f... sino: git rm --cached --ignore-unmatch config.php (cached sirve para operar en el staged e -- ignore-unmatch es equivalente a -f)
Cada vez que hacemos un git filter-branch se guarda un backup en directorio .git por tanto no podemos hacer el filter branch con el mismo comando dos veces. Si queremos forzarlo añadimos: git filter-branch -f --tree-filter ....
Si un commit acaba quedándose vacío después de un git filter-branch podríamos eliminarlo con: git filter-branch -f --prune-empty -- --all Tambien se puede incluir este --prune-empty durante el proceso de filter-branch
Problemas específicos de Sistemas Operativos. Si utilizamos linux o MAC para evitar problemas con los espacios: git config --global core.autocrlf input En Windows: git config --global core.autocrlf true Si solo usamos Windows en todo el equipo de desarrollo: git config core.autocrlf false Pero si no nos fiamos de que todo el equipo tenga estos comandos correctamente, podemos forzarlos editando el fichero: .gitattributes <- a la izquierda van los tipos y a la derecha las opciones de conversión -> Por ejemplo: * text=auto (para todos los tipos de ficheros) * .sh text eol=lf (para ficheros sh linux) * .bat text eol=crlf (para ficheros bat windows)
Tipos de conversión: text=auto (conversión automáticamente) text (sirve para entender el fichero como texto para arreglar los finales de linea) text eol= (sirve para adaptar en funcion del sistema operativo) binary (no hace conversiones ninguna), ficheros de imagen, video, etc...
Fusionar commits específicos de un branch dentro de otro branch (esto se llama Cherry Pick) 1. git checkout master 2. git cherry-pick <sha> Podríamos cambiar tambien el mensaje de Descripción del commit que hemos hecho cherry pick: git cherry-pick --edit <sha> (se abre un fichero de texto para hacer la edición)
Si queremos fusionar multiples commits de un branch simultáneamente en otro branch: 1. git cherry-pick --no-commit <sha1> <sha2> 2. Luego habria que hacer el commit manualmente porque los deja en estado unstaged
Si queremos dejar constancia de que hicimos un cherry pick en un commit en concreto (solo es util en repositorios publicos): git cherry-pick -x <sha>
Si queremos dejar constancia del que hizo el commit original cuando hacemos un cherry pick: git cherry-pick --signoff <sha>
Submódulos: Son repositorios de submódulos, se cargan como: git submodule add <git_url> git commit -m "Descripcion" git push
Como modificar los submódulos en el repo general: 1. Vamos al directorio del submódulo 2. Cambiamos al Master de este "sub-repositorio": git checkout master 3. Hacemos el Stage, Commit y Push 4. Luego vamos al repo general 5. Tenemos que luego hacer Stage, Commit y Push desde el Repo General. Importante: Cuando hagamos cambios sobre submódulos es importante recordar que siempre debemos hacer un push al repositorio del submódulo y luego otro push al repo principal
Clonando Proyectos con Submódulos: 1. git clone <url_repo> 2. git submodule init 3. git submodule update
Para actualizar los submódulos: 4. git submodule update
Cuando trabajamos con submódulos, si hacemos git submodule update podemos quedarnos fuera de un branch, por eso es importante hacer siempre git checkout <rama> (git checkout master) Pero en el caso de haber hecho un commit sin rama, ese commit se quedaría en el aire dando un error. Para poder llevarlo a la rama que queramos: 1. git checkout <rama> (master p.e.) 2. git merge <sha> (con el sha del commit en el aire)
Si la rama no existe: git branch <nombre> <sha> (siendo sha que queremos asociar que estaba en el aire)
Si hemos actualizado un submódulo y hacemos un push desde el principal y no al submódulo, podríamos dejar a la gente con un problema de referencias. Para ello podemos hacer dos cosas: git push --recurse-submodules=check (comprueba si hay cambios en submodulos antes de pushear) git push --recurse-submodules=on-demand (lo mismo que check, pero encima pusheara también los submodulos) Idea: Tener un alias por ejemplo git config alias.pushall 'push --recurse-submodules=on-demand'
El log de referencias (un log que no muestra la situación real del proyecto, sino todo lo que se ha hecho, incluyendo borrados o resets): git reflog
El reflog solo es Local. Si hacemos un clone, el reflog empieza vacío. Si borramos el PC donde se hicieron los cambios originales, los damos por perdidos. Digamos que en realidad reflog solo es una ultima oportunidad ante una acción mal hecha.
Podemos volver atras a commits que fueron reseteados con el mismo comando reset: git reset --hard <sha_reseteado>
Para mas informacion sobre reflog (informacion sobre a que branch pertenecia cada commit inclusive): git log --walk-reflogs
Si borramos un branch (con sus correspondientes commits), podríamos recuperarla si sabemos el SHA obtenido de los reflogs: git branch <nombre_nuevo> <sha_del_commit_del_branch_borrado>












