<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Version Control on kenji.blog</title><link>http://kenji.blog/es/tags/version-control/</link><description>Recent content in Version Control on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>es</language><copyright>kenjinote</copyright><lastBuildDate>Sun, 13 Sep 2026 09:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/es/tags/version-control/index.xml" rel="self" type="application/rss+xml"/><item><title>【Comandos Git】Diferencias entre rebase y merge, y su uso correcto en la práctica</title><link>http://kenji.blog/es/p/git-rebase-vs-merge-practical-guide/</link><pubDate>Sun, 13 Sep 2026 09:00:00 +0900</pubDate><guid>http://kenji.blog/es/p/git-rebase-vs-merge-practical-guide/</guid><description>&lt;img src="http://kenji.blog/p/git-rebase-vs-merge-practical-guide/img/eyecatch.jpg" alt="Featured image of post 【Comandos Git】Diferencias entre rebase y merge, y su uso correcto en la práctica" />&lt;h1 id="1-introducción-por-qué-merge-o-rebase-es-un-dilema-eterno">1. Introducción: ¿Por qué &amp;ldquo;merge o rebase&amp;rdquo; es un dilema eterno?
&lt;/h1>&lt;p>Git es un sistema de control de versiones indispensable en el desarrollo de software moderno. Cuando múltiples desarrolladores cambian simultáneamente una base de código, el poderoso modelo de ramas de Git demuestra su valía. Sin embargo, en el desarrollo en equipo, el debate sobre &amp;ldquo;si usar &lt;code>merge&lt;/code> o &lt;code>rebase&lt;/code>&amp;rdquo; es uno de los temas que siempre preocupa a los desarrolladores, desde principiantes hasta expertos.&lt;/p>
&lt;p>En este artículo, desentrañaremos las diferencias en los mecanismos de &lt;code>git merge&lt;/code> y &lt;code>git rebase&lt;/code>, partiendo de la estructura interna de Git, el DAG (Grafo Dirigido Acíclico), y las propiedades matemáticas de los hashes de los commits. Luego, explicaremos a fondo cómo usar y diferenciar ambos en la práctica, acompañándolo con flujos de trabajo concretos. Al ir más allá de la simple introducción de comandos y comprender qué cálculos realiza Git en segundo plano, perderás el miedo a los conflictos y podrás construir un historial limpio y rastreable.&lt;/p>
&lt;hr>
&lt;h1 id="2-estructura-interna-de-git-hashes-de-commits-y-modelo-de-objetos">2. Estructura interna de Git: Hashes de commits y modelo de objetos
&lt;/h1>&lt;p>Para comprender cómo Git integra el historial, primero debemos saber cómo Git almacena los datos. Git no solo almacena las diferencias (parches) de los cambios en los archivos, sino que guarda una instantánea de todo el sistema de archivos en un momento dado.&lt;/p>
&lt;h2 id="21-propiedades-criptográficas-de-los-hashes-de-los-commits">2.1 Propiedades criptográficas de los hashes de los commits
&lt;/h2>&lt;p>Cada commit en Git se identifica de forma única por un número hexadecimal de 40 dígitos generado por la función hash SHA-1 (Secure Hash Algorithm 1) calculada a partir de su contenido. Un objeto commit consta de los siguientes elementos:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Puntero al objeto Tree&lt;/strong>: Una instantánea de la estructura de directorios y archivos (Blob) en ese momento.&lt;/li>
&lt;li>&lt;strong>Puntero al commit padre&lt;/strong>: El valor hash de uno o más commits padres (el primer commit no tiene padre, y un commit de fusión (merge) tiene dos o más padres).&lt;/li>
&lt;li>&lt;strong>Información del autor (Author)&lt;/strong>: Quién escribió el código y cuándo.&lt;/li>
&lt;li>&lt;strong>Información del confirmador (Committer)&lt;/strong>: Quién creó y aplicó el commit y cuándo.&lt;/li>
&lt;li>&lt;strong>Mensaje del commit&lt;/strong>: Texto que explica la intención del cambio.&lt;/li>
&lt;/ol>
&lt;p>Expresado matemáticamente, el valor hash $H(C)$ para un objeto commit $C$ se define de la siguiente manera:&lt;/p>
$$
H(C) = \text{SHA-1}( \text{tree} \parallel \text{parent} \parallel \text{author} \parallel \text{committer} \parallel \text{message} )
$$&lt;p>Aquí, $\parallel$ representa la concatenación de datos. Debido a las propiedades de la función hash, incluso cambiar un solo carácter en el mensaje del commit, o tener un commit padre diferente, generará un valor hash completamente distinto. Es decir, &lt;strong>los commits son inmutables (Immutable)&lt;/strong>. Cuando se dice más adelante que &lt;code>rebase&lt;/code> &amp;ldquo;reescribe el historial&amp;rdquo;, en realidad significa &amp;ldquo;crea nuevos commits con un contenido similar pero con valores hash diferentes&amp;rdquo;.&lt;/p>
&lt;p>El tamaño del espacio de hashes es $2^{160}$, y la probabilidad $P$ de que ocurra una colisión (que diferentes commits tengan el mismo valor hash) se puede aproximar utilizando la teoría de la paradoja del cumpleaños (Birthday Paradox) (donde $n$ es el número de commits):&lt;/p>
$$
P(\text{colisión}) \approx 1 - \exp\left(-\frac{n^2}{2 \times 2^{160}}\right)
$$&lt;p>Esta probabilidad es extremadamente baja, por lo que en la práctica es casi imposible que los hashes de los commits de Git colisionen.&lt;/p>
&lt;hr>
&lt;h1 id="3-teoría-de-grafos-y-dag-modelo-matemático-del-historial-de-git">3. Teoría de grafos y DAG: Modelo matemático del historial de Git
&lt;/h1>&lt;p>El historial de commits de Git se modela como un &amp;ldquo;Grafo Dirigido Acíclico (Directed Acyclic Graph, DAG)&amp;rdquo; en la teoría de grafos.&lt;/p>
&lt;h2 id="31-qué-es-un-dag-grafo-dirigido-acíclico">3.1 ¿Qué es un DAG (Grafo Dirigido Acíclico)?
&lt;/h2>&lt;p>En un grafo $G = (V, E)$, $V$ es el conjunto de commits (vértices), y $E$ es el conjunto de aristas dirigidas que indican la relación padre-hijo entre los commits. En Git, la dirección de la arista va &amp;ldquo;del commit hijo al commit padre&amp;rdquo;. Esto se debe a que un nuevo commit mantiene un puntero a los commits pasados.&lt;/p>
&lt;pre class="mermaid">
graph BT
A[&amp;#34;Commit A (Inicial)&amp;#34;]
B[&amp;#34;Commit B&amp;#34;]
C[&amp;#34;Commit C (Principal)&amp;#34;]
D[&amp;#34;Commit D (Característica)&amp;#34;]
E[&amp;#34;Commit E (Fusión)&amp;#34;]
B --&amp;gt; A
C --&amp;gt; B
D --&amp;gt; B
E --&amp;gt; C
E --&amp;gt; D
&lt;/pre>
&lt;p>La característica principal de un DAG es que &amp;ldquo;no existen ciclos&amp;rdquo;. Gracias a esto, los algoritmos que retroceden en el historial de commits no caen en bucles infinitos y pueden llegar con seguridad al final (el commit inicial).&lt;/p>
&lt;h2 id="32-ordenación-topológica-y-orden-del-historial">3.2 Ordenación topológica y orden del historial
&lt;/h2>&lt;p>Cuando se muestra el historial con comandos como &lt;code>git log&lt;/code> de Git, el DAG se ordena como una lista unidimensional mediante el algoritmo de ordenación topológica (Topological Sort). Para cualquier arista dirigida $u \to v$ en el DAG ($u$ es hijo de $v$), se reordenan para que $u$ aparezca antes que $v$ en la lista.&lt;/p>
&lt;hr>
&lt;h1 id="4-mecanismos-y-tipos-de-git-merge">4. Mecanismos y tipos de git merge
&lt;/h1>&lt;p>El comando más básico para integrar cambios de ramas es &lt;code>git merge&lt;/code>. Sin embargo, Git selecciona automáticamente diferentes estrategias de fusión según el estado actual.&lt;/p>
&lt;h2 id="41-fusión-fast-forward---ff">4.1 Fusión Fast-Forward (&amp;ndash;ff)
&lt;/h2>&lt;p>Si la rama de destino de la integración (ej: &lt;code>main&lt;/code>) es un ancestro directo de la rama de origen (ej: &lt;code>feature&lt;/code>), Git ejecuta una fusión &amp;ldquo;Fast-Forward (avance rápido)&amp;rdquo;. Es una operación que no crea un nuevo commit, sino que simplemente avanza el puntero de la rama.&lt;/p>
&lt;pre class="mermaid">
gitGraph
commit id: &amp;#34;A&amp;#34;
commit id: &amp;#34;B&amp;#34;
branch feature
checkout feature
commit id: &amp;#34;C&amp;#34;
commit id: &amp;#34;D&amp;#34;
checkout main
merge feature
&lt;/pre>
&lt;p>La fusión Fast-Forward mantiene el historial lineal, pero tiene la desventaja de que se pierde el contexto de &amp;ldquo;qué grupo de commits se agruparon como el desarrollo de una sola característica (feature)&amp;rdquo;.&lt;/p>
&lt;h2 id="42-fusión-non-fast-forward---no-ff">4.2 Fusión Non-Fast-Forward (&amp;ndash;no-ff)
&lt;/h2>&lt;p>Si se especifica explícitamente &lt;code>git merge --no-ff&lt;/code>, siempre se creará un nuevo &amp;ldquo;commit de fusión (merge commit)&amp;rdquo;, incluso en situaciones donde es posible hacer un Fast-Forward. Un commit de fusión es un commit especial que tiene dos padres.&lt;/p>
&lt;pre class="mermaid">
gitGraph
commit id: &amp;#34;A&amp;#34;
commit id: &amp;#34;B&amp;#34;
branch feature
checkout feature
commit id: &amp;#34;C&amp;#34;
commit id: &amp;#34;D&amp;#34;
checkout main
commit id: &amp;#34;Trabajo Principal 1&amp;#34;
merge feature type: NORMAL
&lt;/pre>
&lt;p>La ventaja de este método es que la existencia y la historia de la rama de la característica quedan claramente registradas en el DAG. Cuando ocurre un problema, al ejecutar &lt;code>git revert -m 1 &amp;lt;hash del commit de fusión&amp;gt;&lt;/code>, es posible deshacer (revertir) de forma segura toda la característica a la vez.&lt;/p>
&lt;h2 id="43-algoritmo-de-fusión-de-3-vías-3-way-merge">4.3 Algoritmo de fusión de 3 vías (3-Way Merge)
&lt;/h2>&lt;p>Cuando las ramas de destino y origen tienen sus propios commits únicos, Git ejecuta una fusión de 3 vías. En este momento, Git explora el DAG y encuentra el &amp;ldquo;ancestro común más bajo (Lowest Common Ancestor, LCA)&amp;rdquo; de las dos ramas.&lt;/p>
&lt;p>La complejidad temporal $T_{\text{LCA}}$ del algoritmo para encontrar el LCA se puede ejecutar en tiempo lineal con respecto al número de vértices $|V|$ y aristas $|E|$:&lt;/p>
$$
T_{\text{LCA}} = \mathcal{O}(|V| + |E|)
$$&lt;p>Git compara 3 estados: &amp;ldquo;el estado del LCA&amp;rdquo;, &amp;ldquo;el estado de la rama actual&amp;rdquo; y &amp;ldquo;el estado de la otra rama&amp;rdquo;, y si los cambios no entran en conflicto, genera automáticamente un commit de fusión.&lt;/p>
&lt;hr>
&lt;h1 id="5-mecanismo-de-git-rebase-y-reconstrucción-del-historial">5. Mecanismo de git rebase y reconstrucción del historial
&lt;/h1>&lt;p>Mientras que &lt;code>git merge&lt;/code> &amp;ldquo;integra&amp;rdquo; el historial, &lt;code>git rebase&lt;/code> &amp;ldquo;reconstruye (reemplaza)&amp;rdquo; el historial.&lt;/p>
&lt;h2 id="51-los-movimientos-detrás-de-rebase">5.1 Los movimientos detrás de Rebase
&lt;/h2>&lt;p>El comportamiento interno al hacer rebase de la rama &lt;code>feature&lt;/code> a la rama &lt;code>main&lt;/code> (&lt;code>git rebase main&lt;/code>) es el siguiente:&lt;/p>
&lt;ol>
&lt;li>Encontrar el ancestro común (LCA) entre la rama &lt;code>feature&lt;/code> y la rama &lt;code>main&lt;/code>.&lt;/li>
&lt;li>Guardar las diferencias de los commits desde el LCA hasta la punta de la rama &lt;code>feature&lt;/code> en un área temporal.&lt;/li>
&lt;li>Mover el puntero de la rama &lt;code>feature&lt;/code> a la punta de la rama &lt;code>main&lt;/code>.&lt;/li>
&lt;li>Aplicar (Cherry-Pick) secuencialmente las diferencias guardadas sobre la nueva base (la punta de &lt;code>main&lt;/code>), una por una, y generar nuevos commits.&lt;/li>
&lt;/ol>
&lt;pre class="mermaid">
graph TD
A[&amp;#34;Commit A&amp;#34;] --&amp;gt; B[&amp;#34;Commit B&amp;#34;]
B --&amp;gt; C[&amp;#34;Commit C (Principal)&amp;#34;]
B --&amp;gt; D[&amp;#34;Commit D (Característica Antigua)&amp;#34;]
D -.-&amp;gt; E[&amp;#34;Commit D&amp;#39; (Nueva Característica)&amp;#34;]
C --&amp;gt; E
style D stroke-dasharray: 5 5, fill: #f9f9f9, color: #999
&lt;/pre>
&lt;p>Lo importante aquí es que el commit $D'$ generado por el rebase &lt;strong>tiene un commit padre diferente al del commit original $D$, por lo que tiene un valor hash completamente distinto&lt;/strong> (consulta la definición de la función hash $H(C)$ mencionada anteriormente).&lt;/p>
&lt;h2 id="52-rebase-interactivo-interactive-rebase">5.2 Rebase Interactivo (Interactive Rebase)
&lt;/h2>&lt;p>El uso de &lt;code>git rebase -i&lt;/code> (o &lt;code>--interactive&lt;/code>) permite manipular el historial de commits a tu antojo. Esta es la herramienta más poderosa para organizar el historial local.&lt;/p>
&lt;ul>
&lt;li>&lt;code>pick&lt;/code> : Utiliza el commit tal cual&lt;/li>
&lt;li>&lt;code>reword&lt;/code> : Modifica solo el mensaje del commit&lt;/li>
&lt;li>&lt;code>edit&lt;/code> : Pausa para modificar el contenido del commit&lt;/li>
&lt;li>&lt;code>squash&lt;/code> : Fusiona este commit con el commit anterior y combina sus mensajes&lt;/li>
&lt;li>&lt;code>fixup&lt;/code> : Igual que &lt;code>squash&lt;/code>, pero descarta el mensaje de este commit&lt;/li>
&lt;li>&lt;code>drop&lt;/code> : Elimina el commit por completo&lt;/li>
&lt;/ul>
&lt;p>Desde un punto de vista matemático, si una rama tiene $N$ commits, el número de variaciones (permutaciones) del historial lineal que se pueden generar reordenando el rebase, $P$, es:&lt;/p>
$$
P = N!
$$&lt;p>Git otorga a los desarrolladores la libertad de $N!$ formas, lo que permite mantener el historial en un estado lógico y hermoso.&lt;/p>
&lt;hr>
&lt;h1 id="6-la-regla-de-oro-del-rebase-the-golden-rule-of-rebase">6. La regla de oro del Rebase (The Golden Rule of Rebase)
&lt;/h1>&lt;p>Aunque &lt;code>rebase&lt;/code> es extremadamente poderoso, existe una regla absoluta.&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>&amp;ldquo;Nunca debes hacer rebase en historiales públicos que ya se han compartido&amp;rdquo;&lt;/strong>
&lt;em>(Never rebase public history)&lt;/em>&lt;/p>
&lt;/blockquote>
&lt;h2 id="61-por-qué-no-debes-hacer-rebase-en-el-historial-público">6.1 ¿Por qué no debes hacer rebase en el historial público?
&lt;/h2>&lt;p>Git es distribuido. Los commits que has empujado (push) a &lt;code>origin/main&lt;/code> también han sido clonados (duplicados) en los repositorios locales de otros desarrolladores. Si reescribes el historial haciendo rebase a commits que ya han sido empujados y los sobrescribes forzadamente con &lt;code>git push --force&lt;/code>, ¿qué pasará?&lt;/p>
&lt;p>El DAG local de otros desarrolladores y el DAG remoto se bifurcarán fundamentalmente. Cuando otros desarrolladores ejecuten &lt;code>git pull&lt;/code>, Git intentará fusionar por la fuerza grupos de commits con historias diferentes, lo que causará una gran cantidad de conflictos y commits duplicados (mismos cambios pero con diferentes hashes), sumiendo el repositorio en el caos.&lt;/p>
&lt;p>La regla de oro es hacer rebase &lt;strong>&amp;ldquo;solo en ramas locales que aún no se han compartido con nadie&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;hr>
&lt;h1 id="7-resolución-de-conflictos-y-git-rebase---continue">7. Resolución de conflictos y git rebase &amp;ndash;continue
&lt;/h1>&lt;p>Si varias personas modifican la misma parte del mismo archivo, se producirá un conflicto. El proceso de resolución de conflictos es diferente entre &lt;code>merge&lt;/code> y &lt;code>rebase&lt;/code>.&lt;/p>
&lt;h2 id="71-resolución-de-conflictos-en-merge">7.1 Resolución de conflictos en Merge
&lt;/h2>&lt;p>En el caso de &lt;code>git merge&lt;/code>, la resolución del conflicto ocurre &lt;strong>solo una vez&lt;/strong>. Justo antes de crear el commit de fusión final, se corrigen todos los conflictos a la vez.&lt;/p>
&lt;h2 id="72-resolución-de-conflictos-en-rebase">7.2 Resolución de conflictos en Rebase
&lt;/h2>&lt;p>En el caso de &lt;code>git rebase&lt;/code>, debido a su naturaleza de reaplicar los commits uno por uno, &lt;strong>pueden ocurrir conflictos en cada commit&lt;/strong>.&lt;/p>
&lt;p>Cuando ocurre un conflicto durante el rebase, Git pausa el proceso. El flujo de resolución es el siguiente:&lt;/p>
&lt;ol>
&lt;li>Abre tu editor o IDE (como VS Code) y corrige manualmente los marcadores de conflicto (&lt;code>&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&lt;/code>, &lt;code>======&lt;/code>, &lt;code>&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/code>).&lt;/li>
&lt;li>Agrega los archivos modificados al índice (index):
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git add &amp;lt;archivo modificado&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;li>Sin crear un commit, reanuda el proceso de rebase:
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git rebase --continue
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>Si deseas cancelar el rebase por completo y volver al estado original, ejecuta el siguiente comando:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git rebase --abort
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>(※ Si no necesitas resolver un conflicto y quieres omitir ese commit en particular, usa &lt;code>git rebase --skip&lt;/code>)&lt;/p>
&lt;hr>
&lt;h1 id="8-uso-correcto-en-la-práctica-práctica-de-flujo-de-trabajo">8. Uso correcto en la práctica (Práctica de flujo de trabajo)
&lt;/h1>&lt;p>Entonces, en un entorno de desarrollo real, ¿cómo deberíamos diferenciar el uso de &lt;code>merge&lt;/code> y &lt;code>rebase&lt;/code>? Aquí presentamos el enfoque más estándar y seguro.&lt;/p>
&lt;h2 id="81-escenario-1-organización-del-historial-de-trabajo-local-uso-de-rebase">8.1 【Escenario 1】 Organización del historial de trabajo local (Uso de Rebase)
&lt;/h2>&lt;p>Supongamos que durante el desarrollo en una rama de característica (feature), se han acumulado muchos commits pequeños (&amp;ldquo;corrección de error tipográfico&amp;rdquo;, &amp;ldquo;guardado temporal&amp;rdquo;, etc.). Antes de enviar un Pull Request (PR), usas un rebase interactivo para organizar estos en unidades significativas.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Ejecutar estando en la rama feature&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git rebase -i HEAD~5
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># (Se abre el editor, usas squash y fixup para limpiar el historial)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Esto permite crear un historial de commits hermoso y donde la intención sea clara para los revisores.&lt;/p>
&lt;h2 id="82-escenario-2-seguimiento-de-la-rama-main-más-reciente-uso-de-rebase">8.2 【Escenario 2】 Seguimiento de la rama main más reciente (Uso de Rebase)
&lt;/h2>&lt;p>Si el desarrollo se alarga y los cambios de otras personas se siguen fusionando en la rama &lt;code>main&lt;/code>, tu rama &lt;code>feature&lt;/code> quedará desactualizada. En este caso, haces un rebase de tu rama &lt;code>feature&lt;/code> sobre la última &lt;code>main&lt;/code> para seguirla.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Obtener la última información de main&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git fetch origin
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Mover la rama feature a la cima del main más reciente&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git rebase origin/main
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Esto hace que el historial sea lineal y previene conflictos en fusiones posteriores. Además, evita que se generen commits de fusión innecesarios (&amp;ldquo;Merge branch &amp;lsquo;main&amp;rsquo; into feature&amp;rdquo;).&lt;/p>
&lt;h2 id="83-escenario-3-integración-de-una-característica-completada-uso-de-merge">8.3 【Escenario 3】 Integración de una característica completada (Uso de Merge)
&lt;/h2>&lt;p>El desarrollo en la rama &lt;code>feature&lt;/code> ha terminado y finalmente es la fase de integración en la rama &lt;code>main&lt;/code>. Aquí se usa &lt;strong>&lt;code>git merge --no-ff&lt;/code>&lt;/strong> (es equivalente a elegir &amp;ldquo;Create a merge commit&amp;rdquo; en un Pull Request en GitHub, etc.).&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git checkout main
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git merge --no-ff feature -m &lt;span class="s2">&amp;#34;Merge feature: Implementación de la función de inicio de sesión de usuario&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git push origin main
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>De esta manera, en el DAG de la rama &lt;code>main&lt;/code> queda un nodo en la historia (un commit de fusión) que indica &amp;ldquo;aquí se fusionó una característica completa&amp;rdquo;. Al mirar el historial más tarde, será más fácil seguir el código por unidad funcional.&lt;/p>
&lt;hr>
&lt;h1 id="9-conclusión-resumen">9. Conclusión (Resumen)
&lt;/h1>&lt;p>En las operaciones de Git, los enfoques extremos como &amp;ldquo;resolver todo con Merge&amp;rdquo; o &amp;ldquo;hacer todo lineal con Rebase&amp;rdquo; tienen sus propias ventajas y desventajas.&lt;/p>
&lt;p>La mejor práctica en el entorno laboral es un enfoque híbrido: &lt;strong>&amp;ldquo;Organizar bellamente el historial privado local con rebase, y preservar el contexto en el historial público de integración con merge &amp;ndash;no-ff&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Local (Espacio de trabajo personal)&lt;/strong>: Usar &lt;code>rebase&lt;/code> para eliminar commits inútiles, seguir la línea principal más reciente y mantener un historial lineal.&lt;/li>
&lt;li>&lt;strong>Global (Espacio de trabajo compartido)&lt;/strong>: Usar &lt;code>merge --no-ff&lt;/code> para registrar la existencia de ramas de características como commits de fusión en el DAG, facilitando la reversión y el rastreo.&lt;/li>
&lt;/ul>
&lt;p>Al comprender los fundamentos arquitectónicos y matemáticos, como la estructura del DAG y el mecanismo de las funciones hash, los comandos de Git se elevan de la simple memorización al &amp;ldquo;diseño intencional del historial&amp;rdquo;. Siguiendo la regla de oro del rebase, seleccionemos el comando óptimo según la situación y construyamos un historial de commits limpio que sea fácil de leer y mantener para todo el equipo.&lt;/p></description></item><item><title>Colección de errores comunes y comandos de solución para principiantes de Git (resolución de conflictos, etc.)</title><link>http://kenji.blog/es/p/git-beginners-mistakes-and-solutions/</link><pubDate>Sat, 12 Sep 2026 17:00:00 +0900</pubDate><guid>http://kenji.blog/es/p/git-beginners-mistakes-and-solutions/</guid><description>&lt;img src="http://kenji.blog/p/git-beginners-mistakes-and-solutions/img/eyecatch.jpg" alt="Featured image of post Colección de errores comunes y comandos de solución para principiantes de Git (resolución de conflictos, etc.)" />&lt;h1 id="colección-de-errores-comunes-y-comandos-de-solución-para-principiantes-de-git-resolución-de-conflictos-etc">Colección de errores comunes y comandos de solución para principiantes de Git (resolución de conflictos, etc.)
&lt;/h1>&lt;h2 id="1-introducción-por-qué-cometemos-errores-en-git">1. Introducción: ¿Por qué cometemos errores en Git?
&lt;/h2>&lt;p>En el desarrollo de software, Git se ha convertido en algo tan indispensable como el aire o el agua. Sin embargo, para muchos principiantes (y a veces incluso para los expertos), Git puede parecer una &amp;ldquo;caja negra mágica y aterradora&amp;rdquo;. Los commits desaparecen, subes un montón de cambios en la rama equivocada o mensajes de error de conflictos que nunca has visto antes llenan la pantalla&amp;hellip; Cuando caes en estas &amp;ldquo;trampas de Git&amp;rdquo;, el progreso de tu trabajo se detiene por completo y, en el peor de los casos, te invade el miedo de haber destruido el código fuente.&lt;/p>
&lt;p>¿Por qué Git es tan difícil y propenso a inducir errores? La razón principal es que &amp;ldquo;usamos comandos superficiales de memoria sin entender lo que está sucediendo dentro de Git&amp;rdquo;. Git se basa en una filosofía de diseño robusta como un Sistema de Control de Versiones Distribuido (DVCS), pero su interfaz (CLI) no siempre es intuitiva.&lt;/p>
&lt;p>En este artículo, clasificaremos los &amp;ldquo;errores comunes&amp;rdquo; que los principiantes de Git encuentran frecuentemente en el trabajo en múltiples casos y proporcionaremos los comandos específicos para resolver cada uno de ellos. Sin embargo, no será una simple lista de comandos (hoja de trucos). Profundizaremos en &amp;ldquo;por qué ocurre ese error&amp;rdquo; y &amp;ldquo;cómo se mueven los datos dentro de Git cuando ejecutas ese comando&amp;rdquo; a lo largo de más de 10,000 caracteres, incluyendo la estructura del directorio &lt;code>.git&lt;/code>, el trasfondo matemático del algoritmo Diff que se ejecuta en segundo plano y diagramas usando Mermaid.&lt;/p>
&lt;p>Cuando termines de leer este artículo hasta el final, te liberarás del sentimiento de que &amp;ldquo;Git da miedo&amp;rdquo; y, en cambio, estarás seguro de que &amp;ldquo;no hay un compañero más confiable que Git&amp;rdquo;. Entonces, sumerjámonos en el profundo mundo de Git.&lt;/p>
&lt;hr>
&lt;h2 id="2-el-abismo-de-git-entendiendo-la-estructura-interna-del-directorio-git">2. El abismo de Git: Entendiendo la estructura interna del directorio &lt;code>.git&lt;/code>
&lt;/h2>&lt;p>El primer paso para facilitar la resolución de muchos problemas es saber cómo Git guarda los datos. La carpeta oculta &lt;code>.git&lt;/code> que se encuentra en el directorio raíz de tu proyecto es el corazón de Git. Git no es un sistema que simplemente registra las diferencias (parches) de los archivos en orden, sino que gestiona los datos como un &lt;strong>flujo de instantáneas (snapshots)&lt;/strong>.&lt;/p>
&lt;h3 id="21-modelo-de-objetos-blob-tree-commit">2.1 Modelo de objetos: Blob, Tree, Commit
&lt;/h3>&lt;p>Git utiliza principalmente 3 objetos para representar el estado del repositorio. Estos objetos se guardan en &lt;code>.git/objects&lt;/code>.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Blob (Binary Large Object)&lt;/strong>
Es el objeto que guarda el contenido del archivo en sí. La información como el nombre del archivo y los permisos no se incluyen aquí. Es puramente una secuencia de bytes comprimida con zlib y se identifica mediante un valor hash SHA-1 (un número hexadecimal de 40 caracteres).&lt;/li>
&lt;li>&lt;strong>Tree&lt;/strong>
Es el objeto que representa la estructura del directorio. Un objeto Tree contiene punteros (valores hash SHA-1) a otros objetos Tree (subdirectorios) y objetos Blob (archivos), así como sus nombres de archivo y permisos de acceso. Desempeña un papel similar a un directorio de UNIX.&lt;/li>
&lt;li>&lt;strong>Commit&lt;/strong>
Contiene un puntero al objeto Tree de nivel superior de todo el repositorio en un momento dado, metadatos (autor, fecha y hora del commit, mensaje del commit) y un puntero al commit inmediatamente anterior (commit padre).&lt;/li>
&lt;/ol>
&lt;pre class="mermaid">
graph TD
Commit1[&amp;#34;Commit (Hash: 9f8a)&amp;#34;] --&amp;gt; Tree1[&amp;#34;Tree (Hash: 4b82)&amp;#34;]
Tree1 --&amp;gt; Blob1[&amp;#34;Blob (Hash: 8d7e) : index.js&amp;#34;]
Tree1 --&amp;gt; Tree2[&amp;#34;Tree (Hash: 3a2c) : src/&amp;#34;]
Tree2 --&amp;gt; Blob2[&amp;#34;Blob (Hash: 5f1b) : app.js&amp;#34;]
&lt;/pre>
&lt;h3 id="22-la-verdadera-naturaleza-de-head-y-las-referencias-refs">2.2 La verdadera naturaleza de HEAD y las referencias (Refs)
&lt;/h3>&lt;p>Al trabajar con Git, verás frecuentemente la palabra &lt;code>HEAD&lt;/code>. Esto es una &lt;strong>referencia simbólica (Symbolic Reference)&lt;/strong> que apunta a la rama (o commit) que tienes actualmente verificada (checked out).
Si abres el archivo &lt;code>.git/HEAD&lt;/code> con un editor de texto, verás una cadena como esta:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">ref: refs/heads/main
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Esto significa que &amp;ldquo;el estado actual se encuentra en la punta de la rama &lt;code>main&lt;/code>&amp;rdquo;. Y si abres &lt;code>.git/refs/heads/main&lt;/code>, verás escrito un hash SHA-1 de 40 caracteres, que apunta al objeto Commit más reciente.
Una rama de Git es simplemente un puntero ligero (un archivo) que apunta a un commit específico. Solo con saber este hecho, el miedo de que &amp;ldquo;si elimino la rama, se borrarán todos los archivos&amp;rdquo; desaparece.&lt;/p>
&lt;hr>
&lt;h2 id="3-descifrando-git-con-matemáticas-el-algoritmo-diff-y-las-funciones-hash">3. Descifrando Git con matemáticas: El algoritmo Diff y las funciones hash
&lt;/h2>&lt;p>Cuando Git detecta un conflicto o muestra la diferencia entre archivos, se están ejecutando algoritmos avanzados internamente.&lt;/p>
&lt;h3 id="31-algoritmo-diff-de-myers">3.1 Algoritmo Diff de Myers
&lt;/h3>&lt;p>El algoritmo de detección de diferencias predeterminado de Git es un algoritmo ideado por Eugene W. Myers. Cuando hay dos archivos de texto $A$ y $B$, el problema de encontrar la &amp;ldquo;secuencia mínima de ediciones (inserciones y eliminaciones)&amp;rdquo; para convertir $A$ en $B$ se puede modelar como un problema de la ruta más corta en la teoría de grafos.&lt;/p>
&lt;p>Sean las longitudes de las cadenas $N$ y $M$ respectivamente, y la suma sea $V = N + M$. En el algoritmo de Myers, se busca la distancia de edición (Edit Distance) $D$. La complejidad temporal de este algoritmo se expresa con la siguiente fórmula:&lt;/p>
$$ \mathcal{O}(V \cdot D) $$&lt;p>Aquí, si la diferencia entre los archivos es pequeña (es decir, $D$ es pequeña), el algoritmo se ejecuta a una velocidad muy alta $\mathcal{O}(V)$. Sin embargo, si los archivos son completamente diferentes, $D \approx V$, y la complejidad temporal en el peor de los casos es $\mathcal{O}(V^2)$.&lt;/p>
&lt;h3 id="32-patience-diff-e-histogram-diff">3.2 Patience Diff e Histogram Diff
&lt;/h3>&lt;p>El algoritmo de Myers es excelente, pero cuando se cambia significativamente el orden de funciones o clases, puede generar diferencias que no son intuitivas (no tienen sentido) para los humanos. Para solucionar esto, Git implementa &lt;code>Patience Diff&lt;/code> e &lt;code>Histogram Diff&lt;/code>.&lt;/p>
&lt;p>Patience Diff se centra en las &amp;ldquo;líneas únicas que aparecen solo una vez en ambos archivos&amp;rdquo; y encuentra la subsecuencia común más larga (Longest Common Subsequence: LCS) de esas líneas. Si el número de elementos únicos es $U$, el cálculo de LCS se puede resolver con la siguiente complejidad computacional:&lt;/p>
$$ \mathcal{O}(U \log U) $$&lt;p>Cuando sientas que la resolución de un conflicto es difícil, una opción es usar &lt;code>git diff --histogram&lt;/code> o especificar este algoritmo en la estrategia de fusión (&lt;code>git merge -s recursive -X histogram&lt;/code>).&lt;/p>
&lt;h3 id="33-sha-1-y-la-probabilidad-de-colisiones">3.3 SHA-1 y la probabilidad de colisiones
&lt;/h3>&lt;p>Git gestiona todos los objetos con valores hash SHA-1. El tamaño del espacio hash es $2^{160}$. Si aproximamos la probabilidad de colisión de hash $p$ (que contenidos diferentes tengan el mismo valor hash) usando la paradoja del cumpleaños (Birthday Paradox), el número de objetos $k$ necesarios para que la probabilidad de colisión sea del 50% es el siguiente:&lt;/p>
$$ k \approx \sqrt{2 \ln(2)} \cdot 2^{80} \approx 1.2 \times 2^{80} $$&lt;p>Este es un número astronómico, y la probabilidad de que ocurra una colisión involuntaria en el desarrollo de software normal es prácticamente cero. Por lo tanto, Git funciona confiando en el valor hash como una &amp;ldquo;ID única absoluta&amp;rdquo;.&lt;/p>
&lt;hr>
&lt;h2 id="4-estudio-de-caso-1-he-hecho-un-commit-en-la-rama-equivocada">4. Estudio de caso 1: ¡He hecho un commit en la rama equivocada!
&lt;/h2>&lt;p>&lt;strong>【Situación】&lt;/strong>
Sin darte cuenta de que estabas trabajando en la rama &lt;code>main&lt;/code>, escribiste mucho código para una nueva función y, para colmo, hiciste un &lt;code>git commit&lt;/code>. ¡Se suponía que debías crear una rama llamada &lt;code>feature/login&lt;/code> y trabajar allí!&lt;/p>
&lt;h3 id="solución-git-reset-y-crear-una-rama">Solución: &lt;code>git reset&lt;/code> y crear una rama
&lt;/h3>&lt;p>En Git, los commits son objetos independientes y las ramas son solo punteros. Por lo tanto, puedes resolver esto al instante con la operación de &amp;ldquo;crear una nueva rama y luego retroceder el puntero de la rama actual&amp;rdquo;.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1. Crear una nueva rama que apunte al commit actual (el commit hecho por error)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git branch feature/login
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 2. Retroceder el puntero de la rama main al commit anterior (HEAD~1)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Usando --keep, puedes restablecer de forma segura manteniendo los cambios no confirmados (uncommitted) en el directorio de trabajo.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git reset --keep HEAD~1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 3. Cambiar a la rama correcta&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git checkout feature/login
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="ilustración-qué-pasó-internamente">Ilustración: ¿Qué pasó internamente?
&lt;/h3>&lt;p>Visualicemos el movimiento de los punteros de rama en este momento usando &lt;code>gitGraph&lt;/code> de Mermaid.&lt;/p>
&lt;pre class="mermaid">
gitGraph
commit id: &amp;#34;Initial commit&amp;#34;
commit id: &amp;#34;Bugfix&amp;#34;
commit id: &amp;#34;Mistaken Commit&amp;#34; type: HIGHLIGHT
branch feature/login
checkout feature/login
checkout main
&lt;/pre>
&lt;p>Al principio, &lt;code>main&lt;/code> y &lt;code>HEAD&lt;/code> apuntaban a &amp;ldquo;Mistaken Commit&amp;rdquo;, pero con &lt;code>git branch feature/login&lt;/code>, se crea un nuevo puntero allí. Luego, con &lt;code>git reset&lt;/code>, solo el puntero &lt;code>main&lt;/code> vuelve a la posición &amp;ldquo;Bugfix&amp;rdquo;. No se ha eliminado ningún objeto en sí.&lt;/p>
&lt;hr>
&lt;h2 id="5-estudio-de-caso-2-quiero-deshacer-un-commit-que-ya-ha-sido-empujado-pushed">5. Estudio de caso 2: ¡Quiero deshacer un commit que ya ha sido empujado (pushed)!
&lt;/h2>&lt;p>&lt;strong>【Situación】&lt;/strong>
Cometiste un código lleno de errores escrito durante la madrugada impulsado por la tensión y, además, lo publicaste en el repositorio remoto con &lt;code>git push origin main&lt;/code>. Te das cuenta de un error crítico y palideces.&lt;/p>
&lt;h3 id="solución-1-git-revert-para-anular-la-historia-recomendado-y-seguro">Solución 1: &lt;code>git revert&lt;/code> para anular la historia (Recomendado y Seguro)
&lt;/h3>&lt;p>En el desarrollo en equipo, está estrictamente prohibido alterar el historial de commits ya empujados con comandos como &lt;code>git reset&lt;/code>. Incoherencias surgirán con los repositorios locales de otros desarrolladores. El enfoque correcto es &lt;strong>&amp;ldquo;crear un nuevo commit inverso que anule por completo los cambios del commit equivocado&amp;rdquo;&lt;/strong>. Esto es &lt;code>git revert&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Crear un commit que anule el último commit&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git revert HEAD
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>main 7f3a8b2&lt;span class="o">]&lt;/span> Revert &lt;span class="s2">&amp;#34;Mensaje del commit equivocado&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="m">1&lt;/span> file changed, &lt;span class="m">1&lt;/span> insertion&lt;span class="o">(&lt;/span>+&lt;span class="o">)&lt;/span>, &lt;span class="m">10&lt;/span> deletions&lt;span class="o">(&lt;/span>-&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Empujar al remoto&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git push origin main
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;pre class="mermaid">
gitGraph
commit id: &amp;#34;Commit A&amp;#34;
commit id: &amp;#34;Commit B (Mistake)&amp;#34;
commit id: &amp;#34;Revert Commit B&amp;#34; type: REVERSE
&lt;/pre>
&lt;p>La historia sigue avanzando y solo el estado del código vuelve a su estado anterior.&lt;/p>
&lt;h3 id="solución-2-alterar-la-historia-con-git-push---force-with-lease">Solución 2: Alterar la historia con &lt;code>git push --force-with-lease&lt;/code>
&lt;/h3>&lt;p>Si acabas de empujar a una rama que solo tú estás usando, alterar la historia es aceptable.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Restablecer el commit localmente y arreglarlo&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git reset --hard HEAD~1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git add .
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git commit -m &lt;span class="s2">&amp;#34;Correct implementation&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Sobrescribir forzadamente el historial del remoto&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git push origin feature/login --force-with-lease
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;code>--force-with-lease&lt;/code> es un push forzado seguro para evitar el accidente de sobrescribir el trabajo de otra persona por error.&lt;/p>
&lt;hr>
&lt;h2 id="6-estudio-de-caso-3-quiero-cambiar-a-otra-rama-mientras-trabajo-la-magia-de-stash">6. Estudio de caso 3: Quiero cambiar a otra rama mientras trabajo (La magia de Stash)
&lt;/h2>&lt;p>&lt;strong>【Situación】&lt;/strong>
Estás implementando una nueva función en la rama &lt;code>feature/A&lt;/code>, pero el código fuente está a medias y ni siquiera compila. De repente, tu jefe te ordena: &amp;ldquo;¡Hay un error urgente en el entorno de producción en la rama &lt;code>main&lt;/code>, arréglalo ahora mismo!&amp;rdquo;.&lt;/p>
&lt;h3 id="solución-guardar-los-cambios-con-git-stash">Solución: Guardar los cambios con &lt;code>git stash&lt;/code>
&lt;/h3>&lt;p>&lt;code>git stash&lt;/code> es un comando que guarda los cambios no confirmados (uncommitted) en un área temporal.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1. Guardar los cambios en los que estás trabajando&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git stash push -m &lt;span class="s2">&amp;#34;WIP: feature A partially implemented&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 2. Ahora es posible cambiar a la rama main&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git checkout main
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ... (Realizas el trabajo de corrección de errores urgentes, commit, push) ...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 3. Volver a la rama original cuando termines&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git checkout feature/A
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 4. Restaurar los cambios guardados&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git stash pop
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Cuando ejecutas &lt;code>git stash&lt;/code>, Git crea internamente dos objetos de commit especiales y los guarda en una referencia llamada &lt;code>refs/stash&lt;/code>. Es decir, al fin y al cabo, un Stash también es &amp;ldquo;un commit temporal sin nombre&amp;rdquo;.&lt;/p>
&lt;hr>
&lt;h2 id="7-estudio-de-caso-4-el-aterrador-estado-de-detached-head">7. Estudio de caso 4: El aterrador estado de &amp;ldquo;Detached HEAD&amp;rdquo;
&lt;/h2>&lt;p>&lt;strong>【Situación】&lt;/strong>
Querías comprobar el código de un punto específico en el pasado y ejecutaste &lt;code>git checkout 9f8a7b6&lt;/code>. Entonces apareció el mensaje &lt;code>You are in 'detached HEAD' state.&lt;/code>. Hiciste un commit así sin más, pero cuando cambiaste de rama, ¡el commit desapareció!&lt;/p>
&lt;h3 id="el-mecanismo-de-detached-head">El mecanismo de Detached HEAD
&lt;/h3>&lt;p>Normalmente, &lt;code>HEAD&lt;/code> apunta a una rama como &lt;code>refs/heads/main&lt;/code>. Sin embargo, si verificas (checkout) directamente un commit específico, &lt;code>HEAD&lt;/code> apuntará directamente al objeto del commit. A esto se le llama &lt;strong>Detached HEAD (HEAD separado o desprendido)&lt;/strong>.&lt;/p>
&lt;pre class="mermaid">
graph TD
A[&amp;#34;Commit A&amp;#34;] --&amp;gt; B[&amp;#34;Commit B&amp;#34;]
B --&amp;gt; C[&amp;#34;Commit C&amp;#34;]
C --&amp;gt; D[&amp;#34;Commit D&amp;#34;]
BranchMain[&amp;#34;Rama: main&amp;#34;] --&amp;gt; D
HEAD[&amp;#34;HEAD&amp;#34;] --&amp;gt; B
style HEAD fill:#f9f,stroke:#333,stroke-width:4px
&lt;/pre>
&lt;p>Incluso si apilas commits en este estado, ninguna rama seguirá esos nuevos commits. En el momento en que cambies a otra rama, los nuevos commits se perderán.&lt;/p>
&lt;h3 id="solución-guardar-como-una-nueva-rama">Solución: Guardar como una nueva rama
&lt;/h3>&lt;p>Se soluciona creando una nueva rama en el lugar donde te encuentras actualmente.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Crear una nueva rama en la posición actual de HEAD y cambiar a ella&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git checkout -b feature/recovered-work
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h2 id="8-estudio-de-caso-5-resolución-de-conflictos-en-merge-y-rebase">8. Estudio de caso 5: Resolución de conflictos en Merge y Rebase
&lt;/h2>&lt;p>&lt;strong>【Situación】&lt;/strong>
Ejecutaste &lt;code>git merge&lt;/code> o &lt;code>git rebase&lt;/code> y apareció el mensaje &lt;code>CONFLICT (content)&lt;/code>, y el proceso se interrumpió.&lt;/p>
&lt;h3 id="la-diferencia-entre-merge-fusión-y-rebase-reestructuración">La diferencia entre Merge (Fusión) y Rebase (Reestructuración)
&lt;/h3>&lt;ol>
&lt;li>&lt;strong>Merge (Fusión)&lt;/strong>
Realiza una fusión a tres bandas (3-way merge) utilizando los últimos commits de las dos ramas y un ancestro común, creando un commit de fusión.&lt;/li>
&lt;li>&lt;strong>Rebase (Reestructuración)&lt;/strong>
Guarda temporalmente los commits de la rama actual y los vuelve a aplicar en la punta del objetivo. La historia se vuelve lineal.&lt;/li>
&lt;/ol>
&lt;pre class="mermaid">
gitGraph
commit id: &amp;#34;M1&amp;#34;
commit id: &amp;#34;M2&amp;#34;
branch feature
checkout feature
commit id: &amp;#34;F1&amp;#34;
commit id: &amp;#34;F2&amp;#34;
checkout main
commit id: &amp;#34;M3&amp;#34;
merge feature
&lt;/pre>
&lt;h3 id="método-de-resolución-de-conflictos">Método de resolución de conflictos
&lt;/h3>&lt;p>En los archivos donde ocurrió un conflicto, se insertan marcadores como los siguientes:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="o">&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&lt;/span> &lt;span class="nx">HEAD&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">apiUrl&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;https://api.production.example.com&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">=======&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">apiUrl&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;https://api.staging.example.com&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span> &lt;span class="nx">feature&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nx">api&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>El procedimiento de resolución es extremadamente simple.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Eliminar los marcadores y corregir con el código correcto.&lt;/strong>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">apiUrl&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">process&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">env&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">NODE_ENV&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="s1">&amp;#39;production&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">?&lt;/span> &lt;span class="s2">&amp;#34;https://api.production.example.com&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;https://api.staging.example.com&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;li>&lt;strong>Añadir el archivo resuelto al área de preparación (staging).&lt;/strong>
&lt;code>git add&lt;/code> tiene el papel de &amp;ldquo;decirle a Git que has resuelto el conflicto&amp;rdquo;.
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">$ git add index.js
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;li>&lt;strong>Completar el proceso.&lt;/strong>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># En el caso de merge&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git commit -m &lt;span class="s2">&amp;#34;Resolve merge conflict in index.js&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># En el caso de rebase&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git rebase --continue
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>Si entras en pánico, siempre puedes cancelar con &lt;code>$ git merge --abort&lt;/code> o &lt;code>$ git rebase --abort&lt;/code>.&lt;/p>
&lt;hr>
&lt;h2 id="9-estudio-de-caso-6-el-historial-de-commits-es-un-desastre-git-rebase--i">9. Estudio de caso 6: ¡El historial de commits es un desastre! &lt;code>git rebase -i&lt;/code>
&lt;/h2>&lt;p>&lt;strong>【Situación】&lt;/strong>
Se han generado muchos commits pequeños como &amp;ldquo;Corrección de error tipográfico&amp;rdquo;, &amp;ldquo;Corrección de nuevo&amp;rdquo;, &amp;ldquo;Añadir prueba&amp;rdquo;, etc. Si fusionas a &lt;code>main&lt;/code> tal como está, el historial se ensuciará.&lt;/p>
&lt;h3 id="solución-rebase-interactivo">Solución: Rebase interactivo
&lt;/h3>&lt;p>Al usar &lt;code>git rebase -i&lt;/code> (interactive), puedes cambiar el orden de los commits pasados, combinar múltiples commits en uno (squash) o modificar el mensaje de un commit.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Organizar los últimos 3 commits&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git rebase -i HEAD~3
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Se abrirá un editor y se mostrará lo siguiente:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">pick 1a2b3c4 Corrección de error tipográfico
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pick 2b3c4d5 Corrección de nuevo
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pick 3c4d5e6 Añadir prueba
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Lo reescribes de la siguiente manera:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">pick 1a2b3c4 Implementación de la función X
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">squash 2b3c4d5 Corrección de nuevo
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">squash 3c4d5e6 Añadir prueba
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Cuando lo guardes y cierres, estos 3 commits se integrarán hermosamente en uno solo.&lt;/p>
&lt;hr>
&lt;h2 id="10-estudio-de-caso-7-no-sé-cuándo-se-introdujo-el-error-git-bisect">10. Estudio de caso 7: ¡No sé cuándo se introdujo el error! &lt;code>git bisect&lt;/code>
&lt;/h2>&lt;p>&lt;strong>【Situación】&lt;/strong>
Hay un error en la rama &lt;code>main&lt;/code> actual, pero todo era normal en el lanzamiento de hace un mes. Quieres identificar en qué commit se introdujo el error, ¡pero hay más de 100 commits y es imposible hacerlo manualmente!&lt;/p>
&lt;h3 id="solución-identificar-el-error-mediante-búsqueda-binaria">Solución: Identificar el error mediante búsqueda binaria
&lt;/h3>&lt;p>Git tiene una herramienta incorporada para encontrar el commit donde se introdujo un error utilizando una búsqueda binaria matemática (Binary Search). La complejidad temporal es $\mathcal{O}(\log N)$, por lo que, aunque haya 1000 commits, se puede identificar con unas 10 pruebas.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Iniciar la búsqueda&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git bisect start
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># El commit actual tiene el error (bad)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git bisect bad
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Hace un mes (por ejemplo, el hash era a1b2c3d) era normal (good)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git bisect good a1b2c3d
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Git revisará (checkout) automáticamente el commit intermedio, así que ejecutas tu prueba&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Si la prueba es exitosa:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git bisect good
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Si la prueba falla:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git bisect bad
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Simplemente repitiendo esto, Git te dirá exactamente &amp;ldquo;Este es el primer commit defectuoso (Bad commit)&amp;rdquo;. Cuando termines, vuelve al estado original con &lt;code>$ git bisect reset&lt;/code>.&lt;/p>
&lt;hr>
&lt;h2 id="11-la-red-de-seguridad-definitiva-git-reflog">11. La red de seguridad definitiva: &lt;code>git reflog&lt;/code>
&lt;/h2>&lt;p>La técnica final para cualquier &amp;ldquo;metedura de pata&amp;rdquo; en Git es &lt;code>git reflog&lt;/code>. Git registra todo el historial de operaciones locales (historial de movimientos de HEAD) durante un período de tiempo determinado. Incluso si has borrado una rama o has hecho un restablecimiento (reset) incorrecto, puedes encontrar el hash pasado con &lt;code>git reflog&lt;/code> y restaurarlo simplemente haciendo &lt;code>git reset --hard&lt;/code> allí.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">$ git reflog
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">9f8a7b6 &lt;span class="o">(&lt;/span>HEAD -&amp;gt; main&lt;span class="o">)&lt;/span> HEAD@&lt;span class="o">{&lt;/span>0&lt;span class="o">}&lt;/span>: commit: Add new feature
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1a2b3c4 HEAD@&lt;span class="o">{&lt;/span>1&lt;span class="o">}&lt;/span>: reset: moving to HEAD~1
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="12-conclusión">12. Conclusión
&lt;/h2>&lt;p>Hemos explicado con gran detalle los errores comunes en los que suelen caer los principiantes de Git, los mecanismos de Git detrás de ellos y cómo resolverlos. Commits en la rama equivocada, anular commits ya empujados, aprovechar Stash, sobrevivir a Detached HEAD y resolver conflictos. Lo importante en todo esto es imaginar &amp;ldquo;qué objetos y punteros está manipulando Git en segundo plano&amp;rdquo;.&lt;/p>
&lt;p>La diferencia de los archivos se calcula mediante un riguroso algoritmo Diff expresado en fórmulas matemáticas, y la integridad de la historia está garantizada por funciones hash criptográficas. Si entiendes esta hermosa filosofía de diseño, te darás cuenta de que Git no es en absoluto una &amp;ldquo;caja negra incomprensible&amp;rdquo;, sino el escudo más fuerte para proteger firmemente tu código fuente.&lt;/p>
&lt;p>La próxima vez que pienses &amp;ldquo;¡Metí la pata!&amp;rdquo;, en lugar de entrar en pánico y cerrar la terminal, respira hondo y escribe &lt;code>git status&lt;/code>. Git siempre te ofrecerá pistas para la recuperación.&lt;/p></description></item></channel></rss>