Tabla de Contenido
English version of this post.
Git es un sistema de control de código fuente flexible y poderoso que permite versionar el código de manera sencilla y rápida, Git fue creado por Linus Torvarlds con la idea de reemplazar el software de versión de código que se utilizaba para mantener el código del kernel de Linux el cual se convirtió en software cerrado ocasionando el nacimiento de Git.
Git utiliza un paradigma de repositorios distribuidos, esto significa que cada cliente contiene una copia completa del repositorio de código, ésto hace que trabajar sobre un repositorio de código no requiera de ningún tipo de conexión, a diferencia de los sistemas centralizados como subversion que requieren marcar un archivo en modo edición en el servidor para después poder empezar a modificarlo.
Esta guía no pretende para nada ser una referencia completa de comandos y descripciones, ésa ya existe en la web, más bien esta guía pretende ayudar al cualquier persona que intenta empezar a utilizar Git como repositorio de código y quiere obtener una imagen rápida de como funciona y de que manera empezar a ser productivo rápidamente sin tener que invertir tanto tiempo en la curva de aprendizaje.
Configuración
Básicamente la primera cosa que hago cuando instalo git por primera vez es configurar las opciones globales que estaré utilizando al momento de usar git, por ejemplo, aumentar el buffer para postear, esto generalmente se hace debido a que muchas veces quieres hacer push de archivos más grandes del tamaño default que git permite
# increment git post buffer to 500MB git config --global http.postBuffer 524288000
A continuación establecer un cache de 10 horas para que git no esté preguntando constantemente por las credenciales de los repositorios
# cache credentials for 10 hours git config --global credential.helper cache git config --global credential.helper "cache --timeout=36000"
Después se configura algo de color en el output de git para hacerlo mas atractivo 🙂
# and give some love to the git output git config --global color.ui auto git config --global color.branch auto git config --global color.diff auto git config --global color.status auto
A continuación establecer el nombre de usuario y el email los cuales aparecerán al momento de hacer algún movimiento en el repositorio
# add commit global information git config --global user.name "Yohan Jasdid" git config --global user.email yohan.jasdid@gmail.com git config --global push.default simple
Después, configurar la mergetool, ésta será la herramienta a utilizar al momento de resolver algún conflicto en git, los conflictos pueden darse al momento de modificar algún código que alguna otra persona ha modificado también, las herramientas de merge sirven para mostrar las diferencias visualmente entre la versión local y la remota del código que se esté integrando para de esta manera resolver fácilmente los conflictos, en este caso yo utilizo kdiff pero hay muchas otras herramientas muy buenas
# configure merge tool global setting git config --global --add merge.tool kdiff3 git config --global --add mergetool.kdiff3.path "/usr/bin/kdiff3" git config --global --add mergetool.kdiff3.trustExitCode false
A continuación se configura la herramienta de diff, esta es la herramienta que se utilizara al momento de querer ver las diferencias de archivos entre revisiones, para este caso también utilizo kdiff
# configure diff tool global setting git config --global --add diff.guitool kdiff3 git config --global --add difftool.kdiff3.path "/usr/bin/kdiff3" git config --global --add difftool.kdiff3.trustExitCode false
Por último he deshabilitado el chequeo del certificado SSL, cabe mencionar que en mi configuración de servidor git utilizo un gitlab el cual está configurado con un certificado self signed creado por mi, probablemente necesite actualizar este certificado para que tenga un CA el cual git pueda verificar y deshacerme de esta opción, por el momento seguirá de esta manera
# disable ssl checking (not recommended for global) git config --global http.sslVerify false
Clonando Repos
Clonar un repositorio en git significa obtener una copia completa del repositorio remoto en la máquina local y preparar el repositorio para poder agregar cambios, para clonar un repositorio se utiliza el siguiente comando
# clone a repository git clone https://my_repo.git
Branching
Git está basado en el concepto de branching, los branches son básicamente derivados del branch principal o ramificaciones, es por eso su nombre, un branch está basado en un snapshot del repositorio al momento de crear el branch y éste puede contener cambios de código que se han hecho y que se pueden integrar de nuevo al branch principal o a cualquier otro branch, para crear un branch se pueden utilizar los siguiente comandos
# create branch git branch [branch_name] # create and move to branch git checkout -d [branch_name]
Los branches existen localmente y remotamente, con los comandos anteriores se ha creado un branch localmente, una vez creado el branch es bueno hacerle push a dicho branch y crear un branch remoto que esté mapeado con el branch local, para hacer esto debemos hacer push al branch de la siguiente forma
# send local branch to origin in case the branch is just local git push origin [branch_name]
Y para enviar el branch a origin y mapear el branch con el branch remoto, utilizamos el siguiente comando
# send local branch to origin and map with remote branch and start tracking changes git push --set-upstream origin [branch_name]
Una vez que tenemos creados branches para hacer cambios, probablemente queramos por alguna razón borrar alguno de ellos, a continuación agrego los comandos para borrar branches locales de manera sencilla o de manera forzada, también se agrega el comando para borrar branches remotos
# delete local branch (safely) git branch -d [branch_name] # delete local branch (forced) git branch -D [branch_name] # delete remote branch git push origin --delete [branch_name]
Si queremos listar los branches locales o remotos podemos hacerlo con los siguientes comandos respectivamente
# show local branches git branch # show all branches (local and remote) git branch -a
Después de utilizar o borrar branches es necesario hacer una limpieza y actualizar las referencias a los branches actuales, eso lo podemos hacer con el siguiente comando
# Update references to remote branches, if remote branch is deleted then with this command the branch list gets updated git remote prune origin
Con el comando anterior actualizamos las referencias de los branches remotos que ya fueron borrados. Para ver la lista actualizada de los branches remotos podemos usar el siguiente comando
# Show remote branches list git branch -r
Una vez que comprobamos que la liste de branches remotos esta actualizada y ya no se muestran los branches borrados tenemos otro problema. Los branches locales que estaban mapeados a los branches remotos siguen existiendo en nuestra maquina local. Por cuestiones de mantenimiento lo mas seguro es que querramos borrar los branches que ya no tienen su contraparte remota. Es posible que en algun caso por alguna razon querramos conservar un branch que no esta mapeado con uno remoto pero por lo general lo que queremos es borrar los branches locales de los que su branch remoto fue borrado. Para ver una lista de los branches locales, su mapeo con el branch remoto y el estatus del branch remoto utilizamos el siguiente comando
# Show the list of branches with their remote tracking branch git branch -vv
Una vez identificados los branches locales no deseados podemos proceder a borrarlos con los comandos mencionados anteriormente.
Una vez que hemos trabajado lo suficiente con branches, probablemente surja la necesidad de hacer cambios en branches que no han sido creados por nosotros pero que existen en el repositorio remoto de código, para hacer esto necesitamos primero adquirir el branch localmente para después hacer los cambios deseados, existen varias formas de conseguir esto, las 2 formas que utilizo se listan a continuación, el primer comando adquiere un branch el cual se mapea con un branch local de diferente nombre al remoto, el segundo comando mapea el branch local y remoto utilizando el mismo nombre de branch
# Checkout a remote branch and track it locally (it is possible to use different names for local and remote branches) git checkout -b [local_branch] origin/[remote_branch] # Will create [local_branch] and track origin/[remote_branch] # Checkout a remote branch and track it locally (it is NOT possible to use different names for local and remote branches) git checkout --track origin/[remote_branch] # Will only create '[remote_branch]', not a branch with a different name
Existen ocasiones en que algo va mal en el branch local y queremos simplemente reiniciar el branch obteniendo los últimos cambios del branch que están en el servidor, para eso hacemos un reset hard como se muestra
# Force replace local branch with remote branch git reset --hard origin/[remote_branch]
También, en ciertos casos es útil simplemente borrar el branch local y regenerarlo con el branch remoto, para ésto utilizamos los siguientes comandos
# Recreate branch locally git checkout master git branch -D [local_branch] git checkout --track origin/[remote_branch]
Otro comando de utilidad en cuanto a branches se refiere es status. En casi de que querramos saber si nuestro branch local ha tenido nuevos commits en el tiempo que lo hemos tenido en nuestra maquina y debido a que alguien mas haya utilizado nuestro branch para hacer algunos cambios, podemos ver el status del branch y tener informacion de los commits remotos y locales
# will tell you whether the branch you are tracking is ahead, behind or has diverged. git status -uno
Add
Una vez que hemos dominado y entendido como funcionan los branches, es tiempo de hacer algún cambio de código o simplemente desarrollar una nueva funcionalidad en el sistema que estemos trabajando, git es inteligente, al momento de hacer cambios locales en un branch que hemos creado git inmediatamente detecta los cambios, para desplegar los cambios que git ha detectado automáticamente utilizamos el siguiente comando
# show status of modified files detected by git git status
En git hay varios estados de los archivos modificados, el primero es untracked dicho estado significa que el archivo es completamente nuevo y nunca ha sido trackeado por git, el siguiente estado es trackeado, lo que significa que git ya tiene registro del archivo el cual puede estar en un estado modificado. De cualquier estado en el cual se encuentre el archivo modificado si se quiere agregare cambio al repositorio primeramente debemos agregar el archivo o archivos modificados al área de staging, ésta área es la que identifica los cambios de los archivos que se quieren agregar al repositorio, para agregar un archivo a staging se utiliza el comando add de la siguiente forma
# adds a file to staging git add [file_name]
Es probable que necesitemos agregar una gran cantidad de cambios a staging de una sola vez, para evitar estar agregando estos cambios uno por uno, podemos utilizar pattern matching y agregar un montón de cambios de un jalón como se muestra a continuación
# add all files with pattern match to staging git add * # add all files with pattern match to staging git add .
Y por último, para agregar los cambios a staging de solamente los archivos nuevos, es decir, los que no están trackeados por git, utilizamos el siguiente comando
# add all files that are untracked to staging git add -u
Commits
Una vez que agregamos un cambio o un grupo de cambios a staging, se puede proceder a hacer commits, un commit es básicamente eso, un cambio o un grupo de cambios encapsulados en una bolsa con un código de barras hahah. para generar un commit “nuestra bolsa de cambios” se puede generar con los siguientes comandos
# Standard commit with simple message git commit -m "Message of commit" # Commit with message and description - this commit is intended for when having a title and a long description git commit -m "Title of commit" -m "Detail message of commit"
Pushing
Una vez que tenemos un commit o varios commits de los cambios que fueron agregados a staging en nuestro branch local, lo que queremos ahora es enviar estos cambios al branch remoto para que los cambios sean públicos y todo mundo los pueda ver, esto es lo que hace precisamente el comando push. Cabe mencionar que se puede hacer push de un branch que no ha sido publicado en el repositorio remoto. Para hacer push de los commits locales o el branch y sus commits utilizamos los siguientes comandos respectivamente
# Send all changes that are already in commits to remote repository that is tracked git push # Send local branch to origin in case the branch is just local and start tracking remote branch with local branch git push origin [branch_name]
Revert
Hay casos en que por alguna razón decidimos mejor deshacer todos los cambios locales que hemos hecho a algún archivo o grupo de archivos o definitivamente deshacer todos los cambios y volver al estado de un determinado commit, para ese efecto utilizamos los siguientes comandos
# revert a file to a commit state git checkout [commit_hash] path/to/file # discard file changes git checkout path/to/file # discard file changes with pattern matching git checkout .
Merging
Hacer merge significa incorporar cambios de un determinado commit o grupo de commits o branch en el branch actual. Por ejemplo digamos que tenemos un branch llamado branch_a y queremos integrar los cambios de branch_a en master, esto se hacer de la siguiente manera.
# We are currently at [branch_a] git checkout master # move to master git merge [branch_a] # incorporate [branch_a] # In case of merge conflicts we need to launch conflict solver tool git mergetool git commit -m "merge branch_a into testing" # After merge we need to commit the merge git push # and push # Cherry pick - to choose a commit from one branch and apply it to the current branch git cherry-pick [commit-hash] # Apply commit from other branch to this one git push # push changes
En caso de que se haya hecho merge y se tengan conflictos y se quiera cancelar el merge, se puede utilizar el comando reset, el cual hace como lo dice un reseat del estado actual a uno que se le especifique. En este caso para cancelar un merge con conflictos se utiliza lo siguiente
git reset --hard HEAD
Stash y Clean
Hay veces en las que por las prisas empiezas a hacer cambios y a introducir nueva funcionalidad en el sistema, llega un punto en el que decides agregar el set de cambios y seguir con las actividades de desarrollo, al momento te das cuenta de que todo este tiempo estuviste haciendo los cambios en un branch que no era el adecuado para lo que estas haciendo, ¿Y ahora, quién podrá ayudarme? ¡git stash! hahah. el comando stash de git permite guardar los cambios que hiciste en un branch en un espacio temporal lo que te permite cambiar de branch y reestablecer todos tus cambios, ¡que elegancia!. Se utiliza de la siguiente forma
# when having changes in different branch and want them to move to another branch use git stash # git stash saves changes to a temporary location and applies them with a command # sample workflow to move changes from one branch to another git checkout [branch_name] # move to a branch [make some changes] git stash # stash the changes in temp location git checkout [another_branch] # move to a different branch git stash pop # apply the last stash changes to [another_branch] and drops the stash from the stack
Git stash puede almacenar sets de cambios de manera acumulativa, para ver la lista de todos los stash podemos utilizar el siguiente comando
# shows a list of stashes. there can be more than one stash in form of a list git stash list
Si se ocupa hacer stash tambien de los untracked files podemos usar el siguiente comando
git stash --include-untracked
Hay veces en la que los branches locales y remotos divergen, en ese caso lo que se quiere es regenerar el branch local, lo podemos hacer con el comando reset como se muestra
# when remote and local branches had diverged you can use reset to reset your local branch # and sync with the remote branch git fetch git reset --hard origin/[branch_name]
En otras ocasiones se da el caso en el que agregamos un montón de archivos nuevos al branch que en realidad no se requerían y queremos eliminarlos, en este caso usamos el comando clean de la siguiente forma
# delete untracked files - show untracked files to delete using the -n option: git clean -f -n # delete untracked files - beware: this will delete files git clean -f
Y por último si se quieren descartar todos los cambios efectuados se utiliza el siguiente comando
# remove all changes in current branch and checkout branch changes git checkout .
Log y Show
Una vez que tenemos branches, commits, push de los commits y un historial de cambios se nos vuelve una necesidad consultar el historial por los últimos cambios que se han introducido en el repositorio ya sea en la búsqueda de un cambio en específico o simplemente para saber quien realizó dicho cambio. Para esto, el comando log es muy útil, por ejemplo, con el comando log se muestra el historial de commits con descripciones como se muestra a continuación
# show commits history with descriptions git log
También, para ver el historial de commits y los cambios introducidos en cada commit se utiliza el siguiente comando
# show commits history with changes git log -p
Y si queremos buscar los commits por autor de dicho commit
# show commits history with descriptions of a certain author git log --author=bob
Para mostrar el historial en una sola linea o con decoración y en formato más gráfico se puede hacer como se muestra
# to see a very compressed log where each commit is one line git log --pretty=oneline # to see an ASCII art tree of all the branches, decorated with the names of tags and branches git log --graph --oneline --decorate --all
Para mostrar solamente los archivos que han cambiado
# see only which files have changed git log --name-status
Para desplegar la ayuda sobre el comando
# show help for log git log --help
Para mostrar las diferencias de commits entre branches
# show commit difference between branches git log master..[branch_name]
Y para mostrar los detalles de un commit determinado
# show commit diff details git show [commit_hash]
Para mostrar los commits que contienen una palabra clave utilizamos el siguiente comando
# find all commits where commit message contains given word git log --grep=word
Diff
Existe otro comando de extrema utilidad que nos permite mostrar las diferencias entre branches o commits, es el comando diff, a continuación se muestran 3 ejemplos, el primero muestra las diferencias entre 2 branches, el segundo las diferencias entre 2 commits los cuales se identifican por medio del hash del commit y el tercero que muestra solamente archivos modificados entre branches
# show changes between source and target branches git diff [source_branch] [target_branch] # show changes between commit's ancestor and commit git diff [commit_hash] ~ [commit_hash] # show modified files between branches git diff --name-only [source_branch] [target_branch] # show file differences between branches git diff --name-status [branch1] .. [branch2] # show file content differences between branches git diff [branch1] [branch2] -- [file_path_name]
Tags
# it's recommended to create tags for software releases. this is a known concept, create a new tag named 1.0.0 with this command # the 558688a2968362a6826f581a03872e3ec836ad04 stands for the commit id you want to reference with your tag. git tag 1.0.0 558688a2968362a6826f581a03872e3ec836ad04 # By default, the git push command doesn't transfer tags to remote servers. You will have to explicitly push tags to a shared server after you have created them. git push origin 1.0.0 # Show tag list git tag # Create a branch based on tag git checkout -b [branch_name] [tag_name]
Escenarios
# Usually at home in my server I'll do the following git checkout development # Move to dev branch # Do some programming. git status # to see what files I changed. git diff [file] # to see exactly what I modified. git add . # to stage all changes git commit -m [message] # to commit. git push # push changes to branch # Steps to fast forward merge from development branch into master and return to development to continue working git checkout master git pull git merge development git push git checkout development
Merge Parcial
En el supuesto de que querramos hacer un merge parcial a de ciertos commits a otro branch, podriamos hacerlo de esta manera. Para mas contexto, imagina que hay un branch que otro equipo el cual queremos consumir hasta cierto punto e ignorar los ultimos commits, este escenario se me ha presentado y uno de los aproaches a seguir fue el siguiente
# change to branch which contains the desired commits to pull and merge git checkout [branch_name] # fetch all commits but don't integrate into branch yet git fetch # compare local branch with remote branch commits and determine the commit hash point to retrieve commits git log --oneline [branch_name]..origin/[branch_name] # pull all commit changes until commit hash 05a7b1055 git merge 05a7b1055 # push commits that were pulled git push # once we got the commits that we want from remote we can proceed to merge them to another branch git checkout [other_branch_name] git merge [branch_name] git push
Referencias
- Git – Home: https://git-scm.com/
- Git – Reference: https://git-scm.com/docs
- Git – The Simple Guide: http://rogerdudler.github.io/git-guide/
- Git – Documentation: https://git.wiki.kernel.org/index.php/GitDocumentation
- Git – Understanding Git Conceptually: https://www.sbf5.com/~cduan/technical/git/
¡Saludos!
-Yohan